otsdaq  3.09.00
StringMacros.cc
1 #include "otsdaq/Macros/StringMacros.h"
2 
3 #include <sched.h> // for sched_getaffinity
4 #include <algorithm> // for find_if
5 #include <array>
6 #include <cstdint> // for uintptr_t
7 
8 using namespace ots;
9 
10 std::map<std::string /* system variable */,
11  std::map<std::string /* property */, std::string /* value */>>
12  StringMacros::systemVariables_;
13 const std::string StringMacros::TBD = "To-be-defined";
14 
15 //==============================================================================
16 unsigned int StringMacros::getConcurrencyCount(void)
17 {
18  try
19  {
20  return std::stoul(systemVariables_.at("System").at("logicalCores"));
21  }
22  catch(...)
23  {
24  }
25  cpu_set_t mask;
26  unsigned int hw = 0;
27  if(sched_getaffinity(0, sizeof(mask), &mask) == 0)
28  hw = CPU_COUNT(&mask);
29  systemVariables_["System"]["logicalCores"] = std::to_string(hw);
30  return hw;
31 } //end getConcurrencyCount()
32 
33 #define TLVL_EscapeString 30 // = TLVL_DEBUG + 30
34 #define TLVL_EnvMath 49 // = TLVL_DEBUG + 49
35 #define TLVL_EnvSub 50 // = TLVL_DEBUG + 50
36 
37 //==============================================================================
49 bool StringMacros::wildCardMatch(const std::string& needle,
50  const std::string& haystack,
51  unsigned int* priorityIndex)
52 try
53 {
54  __COUTT__ << "\t\t wildCardMatch: " << needle << " =in= " << haystack << " ??? "
55  << std::endl;
56 
57  // empty needle
58  if(needle.size() == 0)
59  {
60  if(priorityIndex)
61  *priorityIndex = 1; // consider an exact match, to stop higher level loops
62  return true; // if empty needle, always "found"
63  }
64 
65  // only wildcard
66  if(needle == "*")
67  {
68  if(priorityIndex)
69  *priorityIndex = 5; // only wildcard, is lowest priority
70  return true; // if empty needle, always "found"
71  }
72 
73  // no wildcards
74  if(needle == haystack)
75  {
76  if(priorityIndex)
77  *priorityIndex = 1; // an exact match
78  return true;
79  }
80 
81  const bool hasWildcard = (needle.find('*') != std::string::npos);
82  if(!hasWildcard)
83  {
84  if(priorityIndex)
85  *priorityIndex = 0; // no wildcard and not exact => no match
86  return false;
87  }
88 
89  // trailing wildcard
90  if(needle[needle.size() - 1] == '*' &&
91  needle.substr(0, needle.size() - 1) == haystack.substr(0, needle.size() - 1))
92  {
93  if(priorityIndex)
94  *priorityIndex = 2; // trailing wildcard match
95  return true;
96  }
97 
98  // leading wildcard
99  if(needle[0] == '*' &&
100  needle.substr(1) == haystack.substr(haystack.size() - (needle.size() - 1)))
101  {
102  if(priorityIndex)
103  *priorityIndex = 3; // leading wildcard match
104  return true;
105  }
106 
107  // generic wildcard matching with '*' anywhere in needle
108  // '*' matches any sequence (including empty)
109  std::size_t patternPos = 0;
110  std::size_t textPos = 0;
111  std::size_t lastStarPattern = std::string::npos;
112  std::size_t lastStarTextPos = std::string::npos;
113  while(textPos < haystack.size())
114  {
115  if(patternPos < needle.size() && needle[patternPos] == haystack[textPos])
116  {
117  ++patternPos;
118  ++textPos;
119  }
120  else if(patternPos < needle.size() && needle[patternPos] == '*')
121  {
122  lastStarPattern = patternPos++;
123  lastStarTextPos = textPos;
124  }
125  else if(lastStarPattern != std::string::npos)
126  {
127  patternPos = lastStarPattern + 1;
128  textPos = ++lastStarTextPos;
129  }
130  else
131  {
132  if(priorityIndex)
133  *priorityIndex = 0; // no match
134  return false;
135  }
136  }
137 
138  while(patternPos < needle.size() && needle[patternPos] == '*')
139  ++patternPos;
140 
141  if(patternPos == needle.size())
142  {
143  if(priorityIndex)
144  *priorityIndex = 4; // wildcard match
145  return true;
146  }
147 
148  // else no match
149  if(priorityIndex)
150  *priorityIndex = 0; // no match
151  return false;
152 } //end wildCardMatch()
153 catch(...)
154 {
155  if(priorityIndex)
156  *priorityIndex = 0; // no match
157  return false; // if out of range
158 } //end wildCardMatch() catch
159 
160 //==============================================================================
164 bool StringMacros::inWildCardSet(const std::string& needle,
165  const std::set<std::string>& haystack)
166 {
167  for(const auto& haystackString : haystack)
168  {
169  // use wildcard match, flip needle parameter.. because we want haystack to have the wildcards
170  if(haystackString.size() && haystackString[0] == '!')
171  {
172  //treat as inverted
173  if(!StringMacros::wildCardMatch(haystackString.substr(1), needle))
174  return true;
175  }
176  else if(StringMacros::wildCardMatch(haystackString, needle))
177  return true;
178  }
179  return false;
180 }
181 
182 //==============================================================================
185 std::string StringMacros::decodeURIComponent(const std::string& data)
186 {
187  std::string decodeURIString(data.size(), 0); // init to same size
188  unsigned int j = 0;
189  for(unsigned int i = 0; i < data.size(); ++i, ++j)
190  {
191  if(data[i] == '%')
192  {
193  // high order hex nibble digit
194  if(data[i + 1] > '9') // then ABCDEF
195  decodeURIString[j] += (data[i + 1] - 55) * 16;
196  else
197  decodeURIString[j] += (data[i + 1] - 48) * 16;
198 
199  // low order hex nibble digit
200  if(data[i + 2] > '9') // then ABCDEF
201  decodeURIString[j] += (data[i + 2] - 55);
202  else
203  decodeURIString[j] += (data[i + 2] - 48);
204 
205  i += 2; // skip to next char
206  }
207  else
208  decodeURIString[j] = data[i];
209  }
210  decodeURIString.resize(j);
211  return decodeURIString;
212 } // end decodeURIComponent()
213 
214 //==============================================================================
215 std::string StringMacros::encodeURIComponent(const std::string& sourceStr)
216 {
217  std::string retStr = "";
218  char encodeStr[4];
219  for(const auto& c : sourceStr)
220  if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
221  retStr += c;
222  else
223  {
224  sprintf(encodeStr, "%%%2.2X", (uint8_t)c);
225  retStr += encodeStr;
226  }
227  return retStr;
228 } // end encodeURIComponent()
229 
230 //==============================================================================
232 void StringMacros::sanitizeForSQL(std::string& str)
233 {
234  std::map<char, std::string> replacements = {
235  {'\'', "''"}, // Single quote becomes two single quotes
236  {'\\', "\\\\"} //, // Backslash becomes double backslash
237  // {';', "\\;"}, // Semicolon can be escaped (optional)
238  // {'-', "\\-"}, // Dash for comments (optional, context-specific)
239  };
240 
241  size_t pos = 0;
242  while(pos < str.size())
243  {
244  auto it = replacements.find(str[pos]);
245  if(it != replacements.end())
246  {
247  str.replace(pos, 1, it->second);
248  pos += it->second.size(); // Advance past the replacement
249  }
250  else
251  {
252  ++pos;
253  }
254  }
255 } //end sanitizeForSQL
256 
257 //==============================================================================
268 std::string StringMacros::escapeString(std::string inString,
269  bool allowWhiteSpace /* = false */,
270  bool forHtml /* = false */)
271 {
272  unsigned int ws = -1;
273  char htmlTmp[10];
274 
275  __COUTVS__(TLVL_EscapeString, allowWhiteSpace);
276  __COUTVS__(TLVL_EscapeString, forHtml);
277 
278  for(unsigned int i = 0; i < inString.length(); i++)
279  if(inString[i] != ' ')
280  {
281  __COUTS__(TLVL_EscapeString)
282  << i << ". " << inString[i] << ":" << (int)inString[i] << std::endl;
283 
284  // remove new lines and unprintable characters
285  if(inString[i] == '\r' || inString[i] == '\n' || // remove new line chars
286  inString[i] == '\t' || // remove tabs
287  inString[i] < 32 || // remove un-printable characters (they mess up xml
288  // interpretation)
289  (inString[i] > char(126) &&
290  inString[i] < char(161))) // this is aggravated by the bug in
291  // MFextensions (though Eric says he fixed on
292  // 8/24/2016) Note: greater than 255 should be
293  // impossible if by byte (but there are html
294  // chracters in 300s and 8000s)
295  {
296  //handle UTF-8 encoded characters
297  if(i + 2 < inString.size() && inString[i] == char(0xE2) &&
298  inString[i + 1] == char(0x80) &&
299  inString[i + 2] ==
300  char(0x93)) // longer dash endash is 3-bytes 0xE2 0x80 0x93
301  {
302  //encode "--" as &#8211;
303  inString.insert(i,
304  "&#82"); // insert HTML name before special character
305  inString.replace(
306  i + 4, 1, 1, '1'); // replace special character-0 with s
307  inString.replace(
308  i + 5, 1, 1, '1'); // replace special character-1 with h
309  inString.replace(
310  i + 6, 1, 1, ';'); // replace special character-2 with ;
311  i += 7; // skip to next char to check
312  ws = i; // last non white space char
313  --i;
314  continue;
315  }
316 
317  if(inString[i] == '\n') // maintain new lines and tabs
318  {
319  if(allowWhiteSpace)
320  {
321  sprintf(htmlTmp, "&#%3.3d", inString[i]);
322  inString.insert(
323  i, std::string(htmlTmp)); // insert html str sequence
324  inString.replace(
325  i + 5, 1, 1, ';'); // replace special character with ;
326  i += 6; // skip to next char to check
327  --i;
328  }
329  else // translate to ' '
330  inString[i] = ' ';
331  }
332  else if(inString[i] == '\t') // maintain new lines and tabs
333  {
334  if(allowWhiteSpace)
335  {
336  if(0)
337  {
338  // tab = 8 spaces
339  sprintf(htmlTmp,
340  "&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160");
341  inString.insert(
342  i, std::string(htmlTmp)); // insert html str sequence
343  inString.replace(
344  i + 47, 1, 1, ';'); // replace special character with ;
345  i += 48; // skip to next char to check
346  --i;
347  }
348  else // tab = 0x09
349  {
350  sprintf(htmlTmp, "&#009");
351  inString.insert(
352  i, std::string(htmlTmp)); // insert html str sequence
353  inString.replace(
354  i + 5, 1, 1, ';'); // replace special character with ;
355  i += 6; // skip to next char to check
356  --i;
357  }
358  }
359  else // translate to ' '
360  inString[i] = ' ';
361  }
362  else
363  {
364  inString.erase(i, 1); // erase character
365  --i; // step back so next char to check is correct
366  }
367  __COUTS__(31) << inString << std::endl;
368  continue;
369  }
370 
371  __COUTS__(31) << inString << std::endl;
372 
373  // replace special characters
374  if(inString[i] == '\"' || inString[i] == '\'')
375  {
376  //check for extra escaping of the quotes
377  // a quote is escaped only when preceded by an odd number of backslashes
378  {
379  unsigned int backslashCount = 0;
380  for(unsigned int j = i; j > 0 && inString[j - 1] == '\\'; --j)
381  ++backslashCount;
382 
383  if(backslashCount % 2 == 1)
384  {
385  //then this is an escaped quote, so remove the escape character and skip
386  inString.erase(i - 1, 1); // erase escape character
387  --i; // step back so next char to check is correct
388  }
389  }
390 
391  inString.insert(i,
392  (inString[i] == '\'')
393  ? "&apos"
394  : "&quot"); // insert HTML name before quotes
395  inString.replace(i + 5, 1, 1, ';'); // replace special character with ;
396  i += 5; // skip to next char to check
397  //__COUT__ << inString << std::endl;
398  }
399  else if(inString[i] == '&')
400  {
401  inString.insert(i, "&amp"); // insert HTML name before special character
402  inString.replace(i + 4, 1, 1, ';'); // replace special character with ;
403  i += 4; // skip to next char to check
404  }
405  else if(inString[i] == '<' || inString[i] == '>')
406  {
407  if(!forHtml)
408  {
409  inString.insert(
410  i,
411  (inString[i] == '<')
412  ? "&lt"
413  : "&gt"); // insert HTML name before special character
414  inString.replace(
415  i + 3, 1, 1, ';'); // replace special character with ;
416  i += 3; // skip to next char to check
417  }
418  else //double escape
419  {
420  inString.insert(
421  i,
422  (inString[i] == '<')
423  ? "&amp;lt"
424  : "&amp;gt"); // insert HTML name before special character
425  inString.replace(
426  i + 7, 1, 1, ';'); // replace special character with ;
427  i += 7; // skip to next char to check
428  }
429  }
430  else if(inString[i] >= char(161) &&
431  inString[i] <= char(255)) // printable special characters
432  {
433  sprintf(htmlTmp, "&#%3.3d", inString[i]);
434  inString.insert(i, std::string(htmlTmp)); // insert html number sequence
435  inString.replace(i + 5, 1, 1, ';'); // replace special character with ;
436  i += 5; // skip to next char to check
437  }
438 
439  __COUTS__(TLVL_EscapeString) << inString << std::endl;
440 
441  ws = i; // last non white space char
442  }
443  else if(allowWhiteSpace) // keep white space if allowed
444  {
445  if(i - 1 == ws)
446  continue; // dont do anything for first white space
447 
448  // for second white space add 2, and 1 from then
449  if(0 && i - 2 == ws)
450  {
451  inString.insert(i, "&#160;"); // insert html space
452  i += 6; // skip to point at space again
453  }
454  inString.insert(i, "&#160"); // insert html space
455  inString.replace(i + 5, 1, 1, ';'); // replace special character with ;
456  i += 5; // skip to next char to check
457  // ws = i;
458  }
459 
460  __COUTS__(TLVL_EscapeString) << inString.size() << " " << ws << std::endl;
461 
462  // inString.substr(0,ws+1);
463 
464  __COUTS__(TLVL_EscapeString) << inString.size() << " " << inString << std::endl;
465 
466  if(allowWhiteSpace) // keep all white space
467  return inString;
468  // else trim trailing white space
469 
470  if(ws == (unsigned int)-1)
471  return ""; // empty std::string since all white space
472  return inString.substr(0, ws + 1); // trim right white space
473 } // end escapeString()
474 
475 //==============================================================================
480 std::string StringMacros::escapeJSONStringEntities(const std::string& str)
481 {
482  unsigned int sz = str.size();
483  if(!sz)
484  return ""; // empty string, returns empty string
485 
486  std::string retStr = "";
487  retStr.reserve(str.size() * 2); // reserve roughly right size
488  for(unsigned int i = 0; i < sz; ++i)
489  {
490  switch(str[i])
491  {
492  case '\n':
493  retStr += "\\n";
494  break;
495  case '"':
496  retStr += "\\\"";
497  break;
498  case '\t':
499  retStr += "\\t";
500  break;
501  case '\r':
502  retStr += "\\r";
503  break;
504  case '\\':
505  retStr += "\\\\";
506  break;
507  default:
508  retStr += str[i];
509  }
510  }
511  return retStr;
512 } //end escapeJSONStringEntities
513 
514 //==============================================================================
518 std::string StringMacros::restoreJSONStringEntities(const std::string& str)
519 {
520  unsigned int sz = str.size();
521  if(!sz)
522  return ""; // empty string, returns empty string
523 
524  std::string retStr = "";
525  retStr.reserve(str.size()); // reserve roughly right size
526  unsigned int i = 0;
527  for(; i < sz - 1; ++i)
528  {
529  if(str[i] == '\\') // if 2 char escape sequence, replace with char
530  switch(str[i + 1])
531  {
532  case 'n':
533  retStr += '\n';
534  ++i;
535  break;
536  case '"':
537  retStr += '"';
538  ++i;
539  break;
540  case 't':
541  retStr += '\t';
542  ++i;
543  break;
544  case 'r':
545  retStr += '\r';
546  ++i;
547  break;
548  case '\\':
549  retStr += '\\';
550  ++i;
551  break;
552  default:
553  retStr += str[i];
554  }
555  else
556  retStr += str[i];
557  }
558  if(i == sz - 1)
559  retStr += str[sz - 1]; // output last character (which can't escape anything)
560 
561  return retStr;
562 } // end restoreJSONStringEntities()
563 
564 //==============================================================================
567 const std::string& StringMacros::trim(std::string& s)
568 {
569  // remove leading whitespace
570  s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
571  return !std::isspace(ch);
572  }));
573 
574  // remove trailing whitespace
575  s.erase(std::find_if(
576  s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); })
577  .base(),
578  s.end());
579 
580  return s;
581 } // end trim()
582 
583 //==============================================================================
593 std::string StringMacros::convertEnvironmentVariables(const std::string& data)
594 {
595  size_t begin = data.find("$");
596  if(begin != std::string::npos && begin + 1 < data.size())
597  {
598  size_t end;
599  std::string envVariable;
600  std::string converted = data; // make copy to modify
601  bool usedBraces = false; // track if braces were used
602 
603  while(begin && begin != std::string::npos &&
604  converted[begin - 1] ==
605  '\\') //do not convert environment variables with escaped \$
606  {
607  converted.replace(begin - 1, 1, "");
608  begin = converted.find("$", begin + 1); //find next
609  if(begin == std::string::npos)
610  {
611  __COUTS__(TLVL_EnvSub)
612  << "Only found escaped $'s that will not be converted: " << converted
613  << __E__;
614  return converted;
615  }
616  }
617 
618  // check if using $(( )) arithmetic expansion syntax
619  // First expand any $-based variables (including OTS system variables),
620  // then evaluate the arithmetic expression using getNumber() (requires explicit $ for variables, e.g., $A-$B)
621  if(begin + 2 < data.size() && data[begin + 1] == '(' && data[begin + 2] == '(')
622  {
623  end = data.find("))", begin + 3);
624  if(end == std::string::npos)
625  {
626  __SS__ << "Arithmetic expansion '$((...)),' at pos " << begin
627  << " in value, is missing closing '))'! Here was the value: "
628  << data << __E__;
629  __SS_THROW__;
630  }
631 
632  std::string expression = data.substr(begin + 3, end - begin - 3);
633  __COUTVS__(TLVL_EnvMath, expression);
634 
635  // Expand $VAR and ${OTS.*.*} inside the expression
636  expression = convertEnvironmentVariables(expression);
637  __COUTVS__(TLVL_EnvMath, expression);
638 
639  int64_t result;
640 
641  bool isNumber = getNumber(expression, result);
642  if(!isNumber)
643  {
644  __SS__ << "Arithmetic expansion '$((...)),' at pos " << begin
645  << " in value, does not evaluate to a number! Here was the value: "
646  << data << __E__;
647  __SS_THROW__;
648  }
649 
650  __COUTS__(TLVL_EnvMath) << "Arithmetic result: " << result << __E__;
651 
652  // proceed recursively, replacing $((...)) with the result
654  converted.replace(begin, end - begin + 2, std::to_string(result)));
655  }
656  else if(data[begin + 1] == '{') // check if using ${NAME} syntax
657  {
658  end = data.find("}", begin + 2);
659  envVariable = data.substr(begin + 2, end - begin - 2);
660  ++end; // replace the closing } too!
661  usedBraces = true;
662  }
663  else // else using $NAME syntax
664  {
665  // end is first non environment variable character
666  for(end = begin + 1; end < data.size(); ++end)
667  if(!((data[end] >= '0' && data[end] <= '9') ||
668  (data[end] >= 'A' && data[end] <= 'Z') ||
669  (data[end] >= 'a' && data[end] <= 'z') || data[end] == '-' ||
670  data[end] == '_' || data[end] == '.' || data[end] == ':'))
671  break; // found end
672  envVariable = data.substr(begin + 1, end - begin - 1);
673  usedBraces = false;
674  }
675  __COUTVS__(TLVL_EnvSub, data);
676  __COUTVS__(TLVL_EnvSub, envVariable);
677  if(usedBraces && envVariable.starts_with("OTS."))
678  {
679  __COUTS__(TLVL_EnvSub) << "OTS system variable detected!" << __E__;
680  auto sysVarSplit = StringMacros::getVectorFromString(envVariable, {'.'});
681  __COUTVS__(TLVL_EnvSub, StringMacros::vectorToString(sysVarSplit));
682 
683  if(sysVarSplit.size() != 3 ||
684  systemVariables_.find(sysVarSplit[1]) == systemVariables_.end() ||
685  systemVariables_.at(sysVarSplit[1]).find(sysVarSplit[2]) ==
686  systemVariables_.at(sysVarSplit[1]).end())
687  {
688  __SS__
689  << "System variable ${" << envVariable
690  << "} is not valid or was not found!"
691  << "\n\n"
692  << "If you were trying to access an ots System Variable, the correct "
693  "syntax is "
694  << "${OTS.<variable>.<property>}, e.g. "
695  "${OTS.ActiveStateMachine.name}"
696  << "\n\n"
697  << "If you were trying to insert an arithmetic operation, the "
698  "correct "
699  "syntax is $((4 - 3)) or $(($ENVVAR1 - $ENVVAR2))"
700  << "\n\n"
701  << "Available system variables:" << __E__;
702 
703  // Print all available system variables
704  for(const auto& varPair : systemVariables_)
705  {
706  ss << "\n OTS." << varPair.first << ".*";
707  for(const auto& propPair : varPair.second)
708  ss << "\n - OTS." << varPair.first << "." << propPair.first;
709  }
710  ss << __E__;
711  __SS_THROW__;
712  }
713  //else successful
714  // proceed recursively
715  return convertEnvironmentVariables(converted.replace(
716  begin,
717  end - begin,
718  systemVariables_.at(sysVarSplit[1]).at(sysVarSplit[2])));
719  }
720  else
721  {
722  char* envResult = nullptr;
723  try
724  {
725  envResult = __ENV__(envVariable.c_str());
726  }
727  catch(const std::runtime_error& e)
728  {
729  __SS__
730  << ("The environmental variable '" + envVariable +
731  "' is not set! Please make sure you set it before continuing!" +
732  "\n\n" +
733  "If you were trying to access an ots System Variable, the "
734  "correct syntax is " +
735  "${OTS.<variable>.<property>}, e.g. "
736  "${OTS.ActiveStateMachine.name}")
737  << __E__;
738  ss << "\n" << e.what() << __E__;
739  __SS_ONLY_THROW__;
740  }
741 
742  // proceed recursively
744  converted.replace(begin, end - begin, envResult));
745  }
746  }
747  // else no environment variables found in string
748  __COUTS__(TLVL_EnvSub) << "Result: " << data << __E__;
749  return data;
750 } //end convertEnvironmentVariables()
751 
752 //==============================================================================
757 bool StringMacros::isNumber(const std::string& s)
758 {
759  // extract set of potential numbers and operators
760  std::vector<std::string> numbers;
761  std::vector<char> ops;
762 
763  if(!s.size())
764  return false;
765 
767  s,
768  numbers,
769  /*delimiter*/ std::set<char>({'+', '-', '*', '/'}),
770  /*whitespace*/ std::set<char>({' ', '\t', '\n', '\r'}),
771  &ops);
772 
773  //__COUTV__(StringMacros::vectorToString(numbers));
774  //__COUTV__(StringMacros::vectorToString(ops));
775 
776  for(const auto& number : numbers)
777  {
778  if(number.size() == 0)
779  continue; // skip empty numbers
780 
781  if(number.find("0x") == 0) // indicates hex
782  {
783  //__COUT__ << "0x found" << std::endl;
784  for(unsigned int i = 2; i < number.size(); ++i)
785  {
786  if(!((number[i] >= '0' && number[i] <= '9') ||
787  (number[i] >= 'A' && number[i] <= 'F') ||
788  (number[i] >= 'a' && number[i] <= 'f')))
789  {
790  //__COUT__ << "prob " << number[i] << std::endl;
791  return false;
792  }
793  }
794  // return std::regex_match(number.substr(2), std::regex("^[0-90-9a-fA-F]+"));
795  }
796  else if(number[0] == 'b') // indicates binary
797  {
798  //__COUT__ << "b found" << std::endl;
799 
800  for(unsigned int i = 1; i < number.size(); ++i)
801  {
802  if(!((number[i] >= '0' && number[i] <= '1')))
803  {
804  //__COUT__ << "prob " << number[i] << std::endl;
805  return false;
806  }
807  }
808  }
809  else
810  {
811  //__COUT__ << "base 10 " << std::endl;
812  for(unsigned int i = 0; i < number.size(); ++i)
813  if(!((number[i] >= '0' && number[i] <= '9') || number[i] == '.' ||
814  number[i] == '+' || number[i] == '-'))
815  return false;
816  // Note: std::regex crashes in unresolvable ways (says Ryan.. also, stop using
817  // libraries) return std::regex_match(s,
818  // std::regex("^(\\-|\\+)?[0-9]*(\\.[0-9]+)?"));
819  }
820  }
821 
822  //__COUT__ << "yes " << std::endl;
823 
824  // all numbers are numbers
825  return true;
826 } // end isNumber()
827 
828 //==============================================================================
834 std::string StringMacros::getNumberType(const std::string& s)
835 {
836  // extract set of potential numbers and operators
837  std::vector<std::string> numbers;
838  std::vector<char> ops;
839 
840  bool hasDecimal = false;
841 
843  s,
844  numbers,
845  /*delimiter*/ std::set<char>({'+', '-', '*', '/'}),
846  /*whitespace*/ std::set<char>({' ', '\t', '\n', '\r'}),
847  &ops);
848 
849  //__COUTV__(StringMacros::vectorToString(numbers));
850  //__COUTV__(StringMacros::vectorToString(ops));
851 
852  for(const auto& number : numbers)
853  {
854  if(number.size() == 0)
855  continue; // skip empty numbers
856 
857  if(number.find("0x") == 0) // indicates hex
858  {
859  //__COUT__ << "0x found" << std::endl;
860  for(unsigned int i = 2; i < number.size(); ++i)
861  {
862  if(!((number[i] >= '0' && number[i] <= '9') ||
863  (number[i] >= 'A' && number[i] <= 'F') ||
864  (number[i] >= 'a' && number[i] <= 'f')))
865  {
866  //__COUT__ << "prob " << number[i] << std::endl;
867  return "nan";
868  }
869  }
870  // return std::regex_match(number.substr(2), std::regex("^[0-90-9a-fA-F]+"));
871  }
872  else if(number[0] == 'b') // indicates binary
873  {
874  //__COUT__ << "b found" << std::endl;
875 
876  for(unsigned int i = 1; i < number.size(); ++i)
877  {
878  if(!((number[i] >= '0' && number[i] <= '1')))
879  {
880  //__COUT__ << "prob " << number[i] << std::endl;
881  return "nan";
882  }
883  }
884  }
885  else
886  {
887  //__COUT__ << "base 10 " << std::endl;
888  for(unsigned int i = 0; i < number.size(); ++i)
889  if(!((number[i] >= '0' && number[i] <= '9') || number[i] == '.' ||
890  number[i] == '+' || number[i] == '-'))
891  return "nan";
892  else if(number[i] == '.')
893  hasDecimal = true;
894  // Note: std::regex crashes in unresolvable ways (says Ryan.. also, stop using
895  // libraries) return std::regex_match(s,
896  // std::regex("^(\\-|\\+)?[0-9]*(\\.[0-9]+)?"));
897  }
898  }
899 
900  //__COUT__ << "yes " << std::endl;
901 
902  // all numbers are numbers
903  if(hasDecimal)
904  return "double";
905  return "unsigned long long";
906 } // end getNumberType()
907 
908 //==============================================================================
909 // static template function
914 bool StringMacros::getNumber(const std::string& s, bool& retValue)
915 {
916  if(s.size() < 1)
917  {
918  __COUT_ERR__ << "Invalid empty bool string " << s << __E__;
919  return false;
920  }
921 
922  // check true case
923  if(s.find("1") != std::string::npos || s == "true" || s == "True" || s == "TRUE")
924  {
925  retValue = true;
926  return true;
927  }
928 
929  // check false case
930  if(s.find("0") != std::string::npos || s == "false" || s == "False" || s == "FALSE")
931  {
932  retValue = false;
933  return true;
934  }
935 
936  __COUT_ERR__ << "Invalid bool string " << s << __E__;
937  return false;
938 
939 } // end static getNumber<bool>
940 
941 //==============================================================================
945 std::string StringMacros::getTimestampString(const std::string& linuxTimeInSeconds)
946 {
947  time_t timestamp(strtol(linuxTimeInSeconds.c_str(), 0, 10));
948  return getTimestampString(timestamp);
949 } // end getTimestampString()
950 
951 //==============================================================================
955 std::string StringMacros::getTimestampString(const time_t linuxTimeInSeconds)
956 {
957  return ots::TimestampString().get(linuxTimeInSeconds);
958 } // end getTimestampString()
959 
960 //==============================================================================
964 {
965  //e.g., used by CoreSupervisorBase::getStatusProgressDetail(void)
966 
967  std::stringstream ss;
968  int days = t / 60 / 60 / 24;
969  if(days > 0)
970  {
971  ss << days << " day" << (days > 1 ? "s" : "") << ", ";
972  t -= days * 60 * 60 * 24;
973  }
974 
975  //HH:MM:SS
976  ss << std::setw(2) << std::setfill('0') << (t / 60 / 60) << ":" << std::setw(2)
977  << std::setfill('0') << ((t % (60 * 60)) / 60) << ":" << std::setw(2)
978  << std::setfill('0') << (t % 60);
979  return ss.str();
980 } //end getTimeDurationString()
981 
982 //==============================================================================
986  const std::string& value, bool doConvertEnvironmentVariables)
987 try
988 {
989  return doConvertEnvironmentVariables
991  : value;
992 }
993 catch(const std::runtime_error& e)
994 {
995  __SS__ << "Failed to validate value for default string data type. " << __E__
996  << e.what() << __E__;
997  __SS_THROW__;
998 }
999 
1000 //==============================================================================
1004 void StringMacros::getSetFromString(const std::string& inputString,
1005  std::set<std::string>& setToReturn,
1006  const std::set<char>& delimiter,
1007  const std::set<char>& whitespace)
1008 {
1009  unsigned int i = 0;
1010  unsigned int j = 0;
1011 
1012  // go through the full string extracting elements
1013  // add each found element to set
1014  for(; j < inputString.size(); ++j)
1015  if((whitespace.find(inputString[j]) !=
1016  whitespace.end() || // ignore leading white space or delimiter
1017  delimiter.find(inputString[j]) != delimiter.end()) &&
1018  i == j)
1019  ++i;
1020  else if((whitespace.find(inputString[j]) !=
1021  whitespace
1022  .end() || // trailing white space or delimiter indicates end
1023  delimiter.find(inputString[j]) != delimiter.end()) &&
1024  i != j) // assume end of element
1025  {
1026  //__COUT__ << "Set element found: " <<
1027  // inputString.substr(i,j-i) << std::endl;
1028 
1029  setToReturn.emplace(inputString.substr(i, j - i));
1030 
1031  // setup i and j for next find
1032  i = j + 1;
1033  }
1034 
1035  if(i != j) // last element check (for case when no concluding ' ' or delimiter)
1036  setToReturn.emplace(inputString.substr(i, j - i));
1037 } // end getSetFromString()
1038 
1039 //==============================================================================
1051 void StringMacros::getVectorFromString(const std::string& inputString,
1052  std::vector<std::string>& listToReturn,
1053  const std::set<char>& delimiter,
1054  const std::set<char>& whitespace,
1055  std::vector<char>* listOfDelimiters,
1056  bool decodeURIComponents)
1057 {
1058  unsigned int i = 0;
1059  unsigned int j = 0;
1060  unsigned int c = 0;
1061  std::set<char>::iterator delimeterSearchIt;
1062  char lastDelimiter = 0;
1063  bool isDelimiter;
1064  // bool foundLeadingDelimiter = false;
1065 
1066  //__COUT__ << inputString << __E__;
1067  //__COUTV__(inputString.length());
1068 
1069  // go through the full string extracting elements
1070  // add each found element to set
1071  for(; c < inputString.size(); ++c)
1072  {
1073  //__COUT__ << (char)inputString[c] << __E__;
1074 
1075  delimeterSearchIt = delimiter.find(inputString[c]);
1076  isDelimiter = delimeterSearchIt != delimiter.end();
1077 
1078  //__COUT__ << (char)inputString[c] << " " << isDelimiter <<
1079  //__E__;//char)lastDelimiter << __E__;
1080 
1081  if(whitespace.find(inputString[c]) !=
1082  whitespace.end() // ignore leading white space
1083  && i == j)
1084  {
1085  ++i;
1086  ++j;
1087  // if(isDelimiter)
1088  // foundLeadingDelimiter = true;
1089  }
1090  else if(whitespace.find(inputString[c]) != whitespace.end() &&
1091  i != j) // trailing white space, assume possible end of element
1092  {
1093  // do not change j or i
1094  }
1095  else if(isDelimiter) // delimiter is end of element
1096  {
1097  //__COUT__ << "Set element found: " <<
1098  // inputString.substr(i,j-i) << std::endl;
1099 
1100  if(listOfDelimiters && listToReturn.size()) // || foundLeadingDelimiter))
1101  // //accept leading delimiter
1102  // (especially for case of
1103  // leading negative in math
1104  // parsing)
1105  {
1106  //__COUTV__(lastDelimiter);
1107  listOfDelimiters->push_back(lastDelimiter);
1108  }
1109  listToReturn.push_back(decodeURIComponents ? StringMacros::decodeURIComponent(
1110  inputString.substr(i, j - i))
1111  : inputString.substr(i, j - i));
1112 
1113  // setup i and j for next find
1114  i = c + 1;
1115  j = c + 1;
1116  }
1117  else // part of element, so move j, not i
1118  j = c + 1;
1119 
1120  if(isDelimiter)
1121  lastDelimiter = *delimeterSearchIt;
1122  //__COUTV__(lastDelimiter);
1123  }
1124 
1125  if(1) // i != j) //last element check (for case when no concluding ' ' or delimiter)
1126  {
1127  //__COUT__ << "Last element found: " <<
1128  // inputString.substr(i,j-i) << std::endl;
1129 
1130  if(listOfDelimiters && listToReturn.size()) // || foundLeadingDelimiter))
1131  // //accept leading delimiter
1132  // (especially for case of leading
1133  // negative in math parsing)
1134  {
1135  //__COUTV__(lastDelimiter);
1136  listOfDelimiters->push_back(lastDelimiter);
1137  }
1138  listToReturn.push_back(decodeURIComponents ? StringMacros::decodeURIComponent(
1139  inputString.substr(i, j - i))
1140  : inputString.substr(i, j - i));
1141  }
1142 
1143  // assert that there is one less delimiter than values
1144  if(listOfDelimiters && listToReturn.size() - 1 != listOfDelimiters->size() &&
1145  listToReturn.size() != listOfDelimiters->size())
1146  {
1147  __SS__ << "There is a mismatch in delimiters to entries (should be equal or one "
1148  "less delimiter): "
1149  << listOfDelimiters->size() << " vs " << listToReturn.size() << __E__
1150  << "Entries: " << StringMacros::vectorToString(listToReturn) << __E__
1151  << "Delimiters: " << StringMacros::vectorToString(*listOfDelimiters)
1152  << __E__;
1153  __SS_THROW__;
1154  }
1155 
1156 } // end getVectorFromString()
1157 
1158 //==============================================================================
1170 std::vector<std::string> StringMacros::getVectorFromString(
1171  const std::string& inputString,
1172  const std::set<char>& delimiter,
1173  const std::set<char>& whitespace,
1174  std::vector<char>* listOfDelimiters,
1175  bool decodeURIComponents)
1176 {
1177  std::vector<std::string> listToReturn;
1178 
1180  listToReturn,
1181  delimiter,
1182  whitespace,
1183  listOfDelimiters,
1184  decodeURIComponents);
1185  return listToReturn;
1186 } // end getVectorFromString()
1187 
1188 //==============================================================================
1192 void StringMacros::getMapFromString(const std::string& inputString,
1193  std::map<std::string, std::string>& mapToReturn,
1194  const std::set<char>& pairPairDelimiter,
1195  const std::set<char>& nameValueDelimiter,
1196  const std::set<char>& whitespace)
1197 try
1198 {
1199  unsigned int i = 0;
1200  unsigned int j = 0;
1201  std::string name;
1202  bool needValue = false;
1203 
1204  // go through the full string extracting map pairs
1205  // add each found pair to map
1206  for(; j < inputString.size(); ++j)
1207  if(!needValue) // finding name
1208  {
1209  if((whitespace.find(inputString[j]) !=
1210  whitespace.end() || // ignore leading white space or delimiter
1211  pairPairDelimiter.find(inputString[j]) != pairPairDelimiter.end()) &&
1212  i == j)
1213  ++i;
1214  else if((whitespace.find(inputString[j]) !=
1215  whitespace
1216  .end() || // trailing white space or delimiter indicates end
1217  nameValueDelimiter.find(inputString[j]) !=
1218  nameValueDelimiter.end()) &&
1219  i != j) // assume end of map name
1220  {
1221  //__COUT__ << "Map name found: " <<
1222  // inputString.substr(i,j-i) << std::endl;
1223 
1224  name = inputString.substr(i, j - i); // save name, for concluding pair
1225 
1226  needValue = true; // need value now
1227 
1228  // setup i and j for next find
1229  i = j + 1;
1230  }
1231  }
1232  else // finding value
1233  {
1234  if((whitespace.find(inputString[j]) !=
1235  whitespace.end() || // ignore leading white space or delimiter
1236  nameValueDelimiter.find(inputString[j]) != nameValueDelimiter.end()) &&
1237  i == j)
1238  ++i;
1239  else if(whitespace.find(inputString[j]) !=
1240  whitespace
1241  .end() || // trailing white space or delimiter indicates end
1242  pairPairDelimiter.find(inputString[j]) !=
1243  pairPairDelimiter.end()) // &&
1244  // i != j) // assume end of value name
1245  {
1246  //__COUT__ << "Map value found: " <<
1247  // inputString.substr(i,j-i) << std::endl;
1248 
1249  auto /*pair<it,success>*/ emplaceReturn =
1250  mapToReturn.emplace(std::pair<std::string, std::string>(
1251  name,
1253  inputString.substr(i, j - i)) // value
1254  ));
1255 
1256  if(!emplaceReturn.second)
1257  {
1258  __COUT__ << "Ignoring repetitive value ('"
1259  << inputString.substr(i, j - i)
1260  << "') and keeping current value ('"
1261  << emplaceReturn.first->second << "'). " << __E__;
1262  }
1263 
1264  needValue = false; // need name now
1265 
1266  // setup i and j for next find
1267  i = j + 1;
1268  }
1269  }
1270 
1271  if(i != j) // last value (for case when no concluding ' ' or delimiter)
1272  {
1273  auto /*pair<it,success>*/ emplaceReturn =
1274  mapToReturn.emplace(std::pair<std::string, std::string>(
1275  name,
1277  inputString.substr(i, j - i)) // value
1278  ));
1279 
1280  if(!emplaceReturn.second)
1281  {
1282  __COUT__ << "Ignoring repetitive value ('" << inputString.substr(i, j - i)
1283  << "') and keeping current value ('" << emplaceReturn.first->second
1284  << "'). " << __E__;
1285  }
1286  }
1287 } // end getMapFromString()
1288 catch(const std::runtime_error& e)
1289 {
1290  __SS__ << "Error while extracting a map from the string '" << inputString
1291  << "'... is it a valid map?" << __E__ << e.what() << __E__;
1292  __SS_THROW__;
1293 }
1294 
1295 //==============================================================================
1297 std::string StringMacros::mapToString(const std::map<std::string, uint8_t>& mapToReturn,
1298  const std::string& primaryDelimeter,
1299  const std::string& secondaryDelimeter)
1300 {
1301  std::stringstream ss;
1302  bool first = true;
1303  for(auto& mapPair : mapToReturn)
1304  {
1305  if(first)
1306  first = false;
1307  else
1308  ss << primaryDelimeter;
1309  ss << mapPair.first << secondaryDelimeter << (unsigned int)mapPair.second;
1310  }
1311  return ss.str();
1312 } // end mapToString()
1313 
1314 //==============================================================================
1316 std::string StringMacros::setToString(const std::set<uint8_t>& setToReturn,
1317  const std::string& delimeter)
1318 {
1319  std::stringstream ss;
1320  bool first = true;
1321  for(auto& setValue : setToReturn)
1322  {
1323  if(first)
1324  first = false;
1325  else
1326  ss << delimeter;
1327  ss << (unsigned int)setValue;
1328  }
1329  return ss.str();
1330 } // end setToString()
1331 
1332 //==============================================================================
1334 std::string StringMacros::vectorToString(const std::vector<uint8_t>& setToReturn,
1335  const std::string& delimeter)
1336 {
1337  std::stringstream ss;
1338  bool first = true;
1339  if(delimeter == "\n")
1340  ss << "\n"; //add initial new line if new line delimiting
1341  for(auto& setValue : setToReturn)
1342  {
1343  if(first)
1344  first = false;
1345  else
1346  ss << delimeter;
1347  ss << (unsigned int)setValue;
1348  }
1349  return ss.str();
1350 } // end vectorToString()
1351 
1352 //==============================================================================
1362 bool StringMacros::extractCommonChunks(const std::vector<std::string>& haystack,
1363  std::vector<std::string>& commonChunksToReturn,
1364  std::vector<std::string>& wildcardStringsToReturn,
1365  unsigned int& fixedWildcardLength)
1366 {
1367  fixedWildcardLength = 0; // init to default
1368  __COUTTV__(StringMacros::vectorToString(haystack));
1369 
1370  // Steps:
1371  // - find start and end common chunks first in haystack strings
1372  // - use start and end to determine if there is more than one *
1373  // - decide if fixed width was specified (based on prepended 0s to numbers)
1374  // - search for more instances of * value
1375  //
1376  //
1377  // // Note: lambda recursive function to find chunks
1378  // std::function<void(
1379  // const std::vector<std::string>&,
1380  // const std::string&,
1381  // const unsigned int, const int)> localRecurse =
1382  // [&specialFolders, &specialMapTypes, &retMap, &localRecurse](
1383  // const std::vector<std::string>& haystack,
1384  // const std::string& offsetPath,
1385  // const unsigned int depth,
1386  // const int specialIndex)
1387  // {
1388  //
1389  // //__COUTV__(path);
1390  // //__COUTV__(depth);
1391  // }
1392  std::pair<unsigned int /*lo*/, unsigned int /*hi*/> wildcardBounds(
1393  std::make_pair(-1, 0)); // initialize to illegal wildcard
1394 
1395  // look for starting matching segment
1396  for(unsigned int n = 1; n < haystack.size(); ++n)
1397  for(unsigned int i = 0, j = 0;
1398  i < haystack[0].length() && j < haystack[n].length();
1399  ++i, ++j)
1400  {
1401  if(i < wildcardBounds.first)
1402  {
1403  if(haystack[0][i] != haystack[n][j])
1404  {
1405  wildcardBounds.first = i; // found lo side of wildcard
1406  break;
1407  }
1408  }
1409  else
1410  break;
1411  }
1412  __COUTS__(3) << "Low side = " << wildcardBounds.first << " "
1413  << haystack[0].substr(0, wildcardBounds.first) << __E__;
1414 
1415  // look for end matching segment
1416  for(unsigned int n = 1; n < haystack.size(); ++n)
1417  for(int i = haystack[0].length() - 1, j = haystack[n].length() - 1;
1418  i >= (int)wildcardBounds.first && j >= (int)wildcardBounds.first;
1419  --i, --j)
1420  {
1421  if(i > (int)wildcardBounds.second) // looking for hi side
1422  {
1423  if(haystack[0][i] != haystack[n][j])
1424  {
1425  wildcardBounds.second = i + 1; // found hi side of wildcard
1426  break;
1427  }
1428  }
1429  else
1430  break;
1431  }
1432 
1433  __COUTS__(3) << "High side = " << wildcardBounds.second << " "
1434  << haystack[0].substr(wildcardBounds.second) << __E__;
1435 
1436  // add first common chunk
1437  commonChunksToReturn.push_back(haystack[0].substr(0, wildcardBounds.first));
1438 
1439  if(wildcardBounds.first != (unsigned int)-1) // potentially more chunks if not end
1440  {
1441  // - use start and end to determine if there is more than one *
1442  for(int i = (wildcardBounds.first + wildcardBounds.second) / 2 + 1;
1443  i < (int)wildcardBounds.second;
1444  ++i)
1445  if(haystack[0][wildcardBounds.first] == haystack[0][i] &&
1446  haystack[0].substr(wildcardBounds.first, wildcardBounds.second - i) ==
1447  haystack[0].substr(i, wildcardBounds.second - i))
1448  {
1449  std::string multiWildcardString =
1450  haystack[0].substr(i, wildcardBounds.second - i);
1451  __COUT__ << "Potential multi-wildcard found: " << multiWildcardString
1452  << " at position i=" << i << __E__;
1453 
1454  std::vector<unsigned int /*lo index*/> wildCardInstances;
1455  // add front one now, and back one later
1456  wildCardInstances.push_back(wildcardBounds.first);
1457 
1458  unsigned int offset =
1459  wildCardInstances[0] + multiWildcardString.size() + 1;
1460  std::string middleString = haystack[0].substr(offset, (i - 1) - offset);
1461  __COUTV__(middleString);
1462 
1463  // search for more wildcard instances in new common area
1464  size_t k;
1465  while((k = middleString.find(multiWildcardString)) != std::string::npos)
1466  {
1467  __COUT__ << "Multi-wildcard found at " << k << __E__;
1468 
1469  wildCardInstances.push_back(offset + k);
1470 
1471  middleString =
1472  middleString.substr(k + multiWildcardString.size() + 1);
1473  offset += k + multiWildcardString.size() + 1;
1474  __COUTV__(middleString);
1475  }
1476 
1477  // add back one last
1478  wildCardInstances.push_back(i);
1479 
1480  for(unsigned int w = 0; w < wildCardInstances.size() - 1; ++w)
1481  {
1482  __COUTV__(wildCardInstances[w]);
1483  __COUTV__(wildCardInstances[w + 1]);
1484  __COUTV__(wildCardInstances.size());
1485  commonChunksToReturn.push_back(haystack[0].substr(
1486  wildCardInstances[w] + multiWildcardString.size(),
1487  wildCardInstances[w + 1] -
1488  (wildCardInstances[w] + multiWildcardString.size())));
1489  }
1490  }
1491 
1492  __COUTTV__(StringMacros::vectorToString(commonChunksToReturn));
1493  //confirm valid multi-commonChunksToReturn for all haystack entries (only first can be certain at this point)
1494  for(unsigned int c = 1; c < commonChunksToReturn.size(); ++c)
1495  {
1496  __COUT__ << "Checking [" << c << "]: " << commonChunksToReturn[c] << __E__;
1497  for(unsigned int n = 1; n < haystack.size(); ++n)
1498  {
1499  __COUT__ << "Checking chunks work with haystack [" << n
1500  << "]: " << haystack[n] << __E__;
1501  __COUTV__(commonChunksToReturn[0].size());
1502  std::string wildCardValue = haystack[n].substr(
1503  commonChunksToReturn[0].size(),
1504  haystack[n].find(commonChunksToReturn[1],
1505  commonChunksToReturn[0].size() + 1) -
1506  commonChunksToReturn[0].size());
1507  __COUTTV__(wildCardValue);
1508 
1509  std::string builtString = "";
1510  for(unsigned int cc = 0; cc < commonChunksToReturn.size(); ++cc)
1511  builtString += commonChunksToReturn[cc] + wildCardValue;
1512  __COUTTV__(builtString);
1513  __COUTTV__(wildCardValue);
1514 
1515  if(haystack[n].find(builtString) != 0)
1516  {
1517  __COUT__ << "Dropping common chunk " << commonChunksToReturn[c]
1518  << ", built '" << builtString << "' not found in "
1519  << haystack[n] << __E__;
1520  commonChunksToReturn.erase(commonChunksToReturn.begin() + c);
1521  --c; //rewind
1522  break; //check next chunk
1523  }
1524  else
1525  __COUTT__ << "Found built '" << builtString << "' in " << haystack[n]
1526  << __E__;
1527  } //end haystack loop
1528  } //end common chunk loop
1529 
1530  __COUTTV__(StringMacros::vectorToString(commonChunksToReturn));
1531  __COUTTV__(commonChunksToReturn[0].size());
1532 
1533  __COUTTV__(fixedWildcardLength);
1534  // check if all common chunks END in 0 to add fixed length
1535  for(unsigned int i = 0; i < commonChunksToReturn[0].size(); ++i)
1536  if(commonChunksToReturn[0][commonChunksToReturn[0].size() - 1 - i] == '0')
1537  {
1538  ++fixedWildcardLength;
1539  __COUTT__ << "Trying for added fixed length +1 to " << fixedWildcardLength
1540  << __E__;
1541  }
1542  else
1543  break;
1544 
1545  // bool allHave0 = true;
1546  for(unsigned int c = 0; c < commonChunksToReturn.size(); ++c)
1547  {
1548  unsigned int cnt = 0;
1549  for(unsigned int i = 0; i < commonChunksToReturn[c].size(); ++i)
1550  if(commonChunksToReturn[c][commonChunksToReturn[c].size() - 1 - i] == '0')
1551  ++cnt;
1552  else
1553  break;
1554 
1555  if(fixedWildcardLength < cnt)
1556  fixedWildcardLength = cnt;
1557  else if(fixedWildcardLength > cnt)
1558  {
1559  __SS__ << "Invalid fixed length found, please simplify indexing between "
1560  "these common chunks: "
1561  << StringMacros::vectorToString(commonChunksToReturn) << __E__;
1562  __SS_THROW__;
1563  }
1564  }
1565  __COUTTV__(fixedWildcardLength);
1566 
1567  if(fixedWildcardLength) // take trailing 0s out of common chunks
1568  for(unsigned int c = 0; c < commonChunksToReturn.size(); ++c)
1569  commonChunksToReturn[c] = commonChunksToReturn[c].substr(
1570  0, commonChunksToReturn[c].size() - fixedWildcardLength);
1571 
1572  // add last common chunk
1573  commonChunksToReturn.push_back(haystack[0].substr(wildcardBounds.second));
1574  } // end handling more chunks
1575  __COUTTV__(StringMacros::vectorToString(commonChunksToReturn));
1576 
1577  // now determine wildcard strings
1578  size_t k;
1579  unsigned int i;
1580  unsigned int ioff = fixedWildcardLength;
1581  bool wildcardsNeeded = false;
1582  bool someLeadingZeros = false;
1583  bool allWildcardsSameSize = true;
1584 
1585  for(unsigned int n = 0; n < haystack.size(); ++n)
1586  {
1587  std::string wildcard = "";
1588  k = 0;
1589  i = ioff + commonChunksToReturn[0].size();
1590 
1591  if(commonChunksToReturn.size() == 1) // just get end
1592  wildcard = haystack[n].substr(i);
1593  else
1594  for(unsigned int c = 1; c < commonChunksToReturn.size(); ++c)
1595  {
1596  if(c == commonChunksToReturn.size() - 1) // for last, do reverse find
1597  k = haystack[n].rfind(commonChunksToReturn[c]);
1598  else
1599  k = haystack[n].find(commonChunksToReturn[c], i + 1);
1600 
1601  if(wildcard == "")
1602  {
1603  // set wildcard for first time
1604  __COUTVS__(3, i);
1605  __COUTVS__(3, k);
1606  __COUTVS__(3, k - i);
1607 
1608  wildcard = haystack[n].substr(i, k - i);
1609  if(fixedWildcardLength && n == 0)
1610  fixedWildcardLength += wildcard.size();
1611 
1612  __COUTS__(3) << "name[" << n << "] = " << wildcard << " fixed @ "
1613  << fixedWildcardLength << __E__;
1614 
1615  break;
1616  }
1617  else if(0 /*skip validation in favor of speed*/ &&
1618  wildcard != haystack[n].substr(i, k - i))
1619  {
1620  __SS__ << "Invalid wildcard! for name[" << n << "] = " << haystack[n]
1621  << " - the extraction algorithm is confused, please simplify "
1622  "your naming convention."
1623  << __E__;
1624  __SS_THROW__;
1625  }
1626 
1627  i = k;
1628  } // end commonChunksToReturn loop
1629 
1630  if(wildcard.size())
1631  {
1632  wildcardsNeeded = true;
1633 
1634  //track if need for leading 0s in wildcards
1635  if(wildcard[0] == '0' && !fixedWildcardLength)
1636  {
1637  someLeadingZeros = true;
1638  if(wildcardStringsToReturn.size() &&
1639  wildcard.size() != wildcardStringsToReturn[0].size())
1640  allWildcardsSameSize = false;
1641  }
1642  }
1643  wildcardStringsToReturn.push_back(wildcard);
1644 
1645  } // end name loop
1646 
1647  __COUTTV__(StringMacros::vectorToString(commonChunksToReturn));
1648  __COUTTV__(StringMacros::vectorToString(wildcardStringsToReturn));
1649 
1650  if(someLeadingZeros && allWildcardsSameSize)
1651  {
1652  __COUTTV__(fixedWildcardLength); //should be 0 in this case
1653  fixedWildcardLength = wildcardStringsToReturn[0].size();
1654  __COUT__ << "Enforce wildcard size of " << fixedWildcardLength << __E__;
1655  }
1656 
1657  if(wildcardStringsToReturn.size() != haystack.size())
1658  {
1659  __SS__ << "There was a problem during common chunk extraction!" << __E__;
1660  __SS_THROW__;
1661  }
1662 
1663  return wildcardsNeeded;
1664 
1665 } // end extractCommonChunks()
1666 
1667 //==============================================================================
1672  const std::string& rhs) const
1673 {
1674  //__COUTV__(lhs);
1675  //__COUTV__(rhs);
1676  // return true if lhs < rhs (lhs will be ordered first)
1677 
1678  for(unsigned int i = 0; i < lhs.size() && i < rhs.size(); ++i)
1679  {
1680  //__COUT__ << i << "\t" << lhs[i] << "\t" << rhs[i] << __E__;
1681  if((lhs[i] >= 'A' && lhs[i] <= 'Z' && rhs[i] >= 'A' && rhs[i] <= 'Z') ||
1682  (lhs[i] >= 'a' && lhs[i] <= 'z' && rhs[i] >= 'a' && rhs[i] <= 'z'))
1683  { // same case
1684  if(lhs[i] == rhs[i])
1685  continue;
1686  return (lhs[i] < rhs[i]);
1687  //{ retVal = false; break;} //return false;
1688  }
1689  else if(lhs[i] >= 'A' && lhs[i] <= 'Z') // rhs lower case
1690  {
1691  if(lhs[i] + 32 == rhs[i]) // lower case is higher by 32
1692  return false; // in tie return lower case first
1693  return (lhs[i] + 32 < rhs[i]);
1694  }
1695  else if(rhs[i] >= 'A' && rhs[i] <= 'Z')
1696  {
1697  if(lhs[i] == rhs[i] + 32) // lower case is higher by 32
1698  return true; // in tie return lower case first
1699  return (lhs[i] < rhs[i] + 32);
1700  }
1701  else // not letters case (should only be for numbers)
1702  {
1703  if(lhs[i] == rhs[i])
1704  continue;
1705  return (lhs[i] < rhs[i]);
1706  }
1707  } // end case insensitive compare loop
1708 
1709  // lhs and rhs are equivalent to character[i], so return false if rhs.size() was the limit reached
1710  return lhs.size() < rhs.size();
1711 } // end IgnoreCaseCompareStruct::operator() comparison handler
1712 
1713 //==============================================================================
1716 std::string StringMacros::exec(const char* cmd)
1717 {
1718  __COUTTV__(cmd);
1719 
1720  std::array<char, 128> buffer;
1721  std::string result;
1722 
1723  // For capturing both stdout and stderr, we need to redirect stderr to stdout
1724  // This is done by appending " 2>&1" to the command
1725  std::string cmdWithRedirect = std::string(cmd) + " 2>&1";
1726  std::shared_ptr<FILE> pipe(popen(cmdWithRedirect.c_str(), "r"), pclose);
1727  if(!pipe)
1728  __THROW__("popen() failed!");
1729 
1730  // Read all output (both stdout and stderr)
1731  while(!feof(pipe.get()))
1732  {
1733  if(fgets(buffer.data(), 128, pipe.get()) != nullptr)
1734  result += buffer.data();
1735  }
1736 
1737  __COUTTV__(result);
1738  return result;
1739 } // end exec()
1740 
1741 // #include <iostream>
1742 #include <fstream> /* for ifstream */
1743 // #include <sstream>
1744 // #include <string>
1745 // #include <cstdlib>
1746 //==============================================================================
1747 uintptr_t find_library_base(const std::string& libname)
1748 {
1749  std::ifstream maps("/proc/self/maps");
1750  std::string line;
1751 
1752  while(std::getline(maps, line))
1753  {
1754  if(line.find(libname) != std::string::npos &&
1755  line.find("r-xp") != std::string::npos)
1756  {
1757  uintptr_t base;
1758  std::stringstream ss(line);
1759  ss >> std::hex >> base;
1760  return base;
1761  }
1762  }
1763  return 0;
1764 } //end find_library_base()
1765 
1766 //==============================================================================
1767 void resolve_stack_entry(const std::string& so_path,
1768  const std::string& real_name,
1769  const std::string& offset_begin, // e.g. "+0x249d"
1770  const std::string& offset_end // e.g. "[0x7f5518fa28fd]"
1771 )
1772 {
1773  // Extract runtime address from a string like "[0x....]".
1774  // Be defensive: validate delimiters before parsing to avoid exceptions.
1775  const std::size_t pos0x = offset_end.find("0x");
1776  if(pos0x == std::string::npos)
1777  {
1778  __COUTS__(52) << "resolve_stack_entry: could not find \"0x\" in offset_end: '"
1779  << offset_end << "'" << __E__;
1780  return;
1781  }
1782 
1783  const std::size_t posBracket = offset_end.find(']', pos0x);
1784  if(posBracket == std::string::npos || posBracket < pos0x + 3)
1785  {
1786  __COUTS__(52) << "resolve_stack_entry: could not find closing ']' with at least "
1787  "one hex digit after \"0x\" "
1788  "in offset_end: '"
1789  << offset_end << "'" << __E__;
1790  return;
1791  }
1792 
1793  const std::string addr_str = offset_end.substr(pos0x, posBracket - pos0x);
1794 
1795  uintptr_t runtime_addr = 0;
1796  try
1797  {
1798  runtime_addr = std::stoull(addr_str, nullptr, 16);
1799  }
1800  catch(const std::exception& e)
1801  {
1802  __COUTS__(52) << "resolve_stack_entry: failed to parse runtime address from '"
1803  << addr_str << "': " << e.what() << __E__;
1804  return;
1805  }
1806 
1807  std::string so_name = so_path.substr(so_path.find_last_of('/') + 1);
1808 
1809  uintptr_t base = find_library_base(so_name);
1810  if(!base)
1811  {
1812  std::cerr << "Could not find base for " << so_name << "\n";
1813  return;
1814  }
1815 
1816  uintptr_t file_addr = runtime_addr - base;
1817 
1818  std::ostringstream cmd;
1819  cmd << "addr2line -f -C -e " << so_path << " 0x" << std::hex << file_addr;
1820 
1821  __COUT__ << "\nResolving:\n"
1822  << so_path << " : " << real_name << offset_begin << " [" << std::hex
1823  << runtime_addr << "]\n\n";
1824 
1825  std::string result = StringMacros::exec(cmd.str().c_str());
1826  __COUTV__(result);
1827 } //end resolve_stack_entry()
1828 
1829 //==============================================================================
1833 #include <cxxabi.h> //for abi::__cxa_demangle
1834 #include <execinfo.h> //for back trace of stack
1835 // #include "TUnixSystem.h"
1837 {
1838  __SS__ << "ots::stackTrace:\n";
1839 
1840  void* array[10];
1841  size_t size;
1842 
1843  // get void*'s for all entries on the stack
1844  size = backtrace(array, 10);
1845  // backtrace_symbols_fd(array, size, STDERR_FILENO);
1846 
1847  // https://stackoverflow.com/questions/77005/how-to-automatically-generate-a-stacktrace-when-my-program-crashes
1848  char** messages = backtrace_symbols(array, size);
1849 
1850  // skip first stack frame (points here)
1851  // char syscom[256];
1852  for(unsigned int i = 1; i < size && messages != NULL; ++i)
1853  {
1854  // mangled name needs to be converted to get nice name and line number
1855  // line number not working... FIXME
1856 
1857  // sprintf(syscom,"addr2line %p -e %s",
1858  // array[i],
1859  // messages[i]); //last parameter is the name of this app
1860  // ss << StringMacros::exec(syscom) << __E__;
1861  // system(syscom);
1862 
1863  // continue;
1864 
1865  char *mangled_name = 0, *offset_begin = 0, *offset_end = 0;
1866 
1867  // find parentheses and +address offset surrounding mangled name
1868  for(char* p = messages[i]; *p; ++p)
1869  {
1870  if(*p == '(')
1871  {
1872  mangled_name = p;
1873  }
1874  else if(*p == '+')
1875  {
1876  offset_begin = p;
1877  }
1878  else if(*p == ')')
1879  {
1880  offset_end = p;
1881  break;
1882  }
1883  }
1884 
1885  // if the line could be processed, attempt to demangle the symbol
1886  if(mangled_name && offset_begin && offset_end && mangled_name < offset_begin)
1887  {
1888  *mangled_name++ = '\0';
1889  *offset_begin++ = '\0';
1890  *offset_end++ = '\0';
1891 
1892  int status;
1893  char* real_name = abi::__cxa_demangle(mangled_name, 0, 0, &status);
1894 
1895  // if demangling is successful, output the demangled function name
1896  if(status == 0)
1897  {
1898  ss << "[" << i << "] " << messages[i] << " : " << real_name << "+"
1899  << offset_begin << offset_end << std::endl;
1900  // Too slow to resolve lines (stackTrace getting called too much)!
1901  // resolve_stack_entry(messages[i],real_name,offset_begin,offset_end);
1902  }
1903  // otherwise, output the mangled function name
1904  else
1905  {
1906  ss << "[" << i << "] " << messages[i] << " : " << mangled_name << "+"
1907  << offset_begin << offset_end << std::endl;
1908  }
1909  free(real_name);
1910  }
1911  // otherwise, print the whole line
1912  else
1913  {
1914  ss << "[" << i << "] " << messages[i] << std::endl;
1915  }
1916  }
1917  ss << std::endl;
1918  ss << std::endl;
1919 
1920  free(messages);
1921 
1922  // call ROOT's stack trace to get line numbers of ALL threads
1923  // gSystem->StackTrace();
1924 
1925  return ss.str();
1926 } // end stackTrace
1927 
1928 //==============================================================================
1934  const std::string& location,
1935  const unsigned int& line)
1936 {
1937  char* environmentVariablePtr = getenv(name);
1938  if(!environmentVariablePtr)
1939  {
1940  __SS__ << "Environment variable '$" << name << "' not defined at " << location
1941  << ":" << line << __E__;
1942  ss << "\n\n" << StringMacros::stackTrace() << __E__;
1943  __SS_ONLY_THROW__;
1944  }
1945  return environmentVariablePtr;
1946 } // end otsGetEnvironmentVarable()
1947 
1948 //=========================================================================
1951 std::string StringMacros::extractXmlField(const std::string& xml,
1952  const std::string& field,
1953  uint32_t occurrence,
1954  size_t after,
1955  size_t* returnFindPos /* = nullptr */,
1956  const std::string& valueField /* = "value=" */,
1957  const std::string& quoteType /* = "'" */)
1958 {
1959  if(returnFindPos)
1960  *returnFindPos = std::string::npos;
1961 
1962  __COUTVS__(41, xml);
1963 
1964  size_t lo, findpos = after, hi;
1965  for(uint32_t i = 0; i <= occurrence; ++i)
1966  {
1967  bool anyFound = false;
1968  while((findpos =
1969  xml.find("<" + field, //allow for immediate closing of xml tag with >
1970  findpos)) != std::string::npos &&
1971  findpos + 1 + field.size() < xml.size())
1972  {
1973  __COUTS__(40) << "find: ---- '<" << field << " findpos=" << findpos
1974  << "findpos " << findpos << " " << xml[findpos] << " "
1975  << xml[findpos + 1 + field.size()] << " "
1976  << (int)xml[findpos + 1 + field.size()] << __E__;
1977 
1978  findpos +=
1979  1 +
1980  field
1981  .size(); //to point to closing white space and advance for next forward search
1982 
1983  //verify white space after the field
1984  if((quoteType == ">" && xml[findpos] == '>') || xml[findpos] == ' ' ||
1985  xml[findpos] == '\n' || xml[findpos] == '\t')
1986  {
1987  anyFound = true; //flag
1988  break;
1989  }
1990  }
1991 
1992  if(!anyFound)
1993  {
1994  __COUTS__(40) << "Field '" << field << "' not found" << __E__;
1995  return "";
1996  }
1997  }
1998 
1999  lo = xml.find(valueField + quoteType, findpos) + valueField.size() + quoteType.size();
2000 
2001  if(TTEST(40) && quoteType.size())
2002  {
2003  __COUTS__(40) << "Neighbors of field '" << field << "' and value '" << valueField
2004  << "' w/quote = " << quoteType << __E__;
2005  for(size_t i = lo - valueField.size(); i < lo + 10 && i < xml.size(); ++i)
2006  __COUTS__(40) << "xml[" << i << "] " << xml[i] << " vs " << quoteType << " ? "
2007  << (int)xml[i] << " vs " << (int)quoteType[0] << __E__;
2008  }
2009 
2010  if((hi = xml.find(
2011  quoteType == ">" ? "<" : quoteType, //if xml tag, change closing direction
2012  lo)) == std::string::npos)
2013  {
2014  __COUTS__(40) << "Value closing not found" << __E__;
2015  return "";
2016  }
2017 
2018  if(returnFindPos)
2019  *returnFindPos = findpos - (1 + field.size()); //remove offset that was added
2020 
2021  __COUTS__(40) << "after: " << after << ", findpos: " << findpos << ", hi/lo: " << hi
2022  << "/" << lo << ", size: " << xml.size() << __E__;
2023  __COUTVS__(40, xml.substr(lo, hi - lo));
2024  return xml.substr(lo, hi - lo);
2025 } //end extractXmlField()
2026 
2027 //=========================================================================
2030 std::string StringMacros::rextractXmlField(const std::string& xml,
2031  const std::string& field,
2032  uint32_t occurrence,
2033  size_t before,
2034  size_t* returnFindPos /* = nullptr */,
2035  const std::string& valueField /* = "value=" */,
2036  const std::string& quoteType /* = "'" */)
2037 {
2038  if(returnFindPos)
2039  *returnFindPos = std::string::npos;
2040 
2041  __COUTVS__(41, xml);
2042 
2043  size_t lo = 0, hi, findpos = before;
2044  for(uint32_t i = 0; i <= occurrence; ++i)
2045  {
2046  bool anyFound = false;
2047  while((findpos =
2048  xml.rfind("<" + field, //allow for immediate closing of xml tag with >
2049  findpos)) != std::string::npos &&
2050  findpos + 1 + field.size() < xml.size())
2051  {
2052  __COUTS__(40) << "rfind: ---- '<" << field << " findpos=" << findpos << " "
2053  << xml[findpos] << " " << xml[findpos + 1 + field.size()] << " "
2054  << (int)xml[findpos + 1 + field.size()] << __E__;
2055 
2056  findpos += 1 + field.size();
2057 
2058  //verify white space after the field
2059  if((quoteType == ">" && xml[findpos] == '>') || xml[findpos] == ' ' ||
2060  xml[findpos] == '\n' || xml[findpos] == '\t')
2061  {
2062  anyFound = true; //flag
2063  break;
2064  }
2065  else
2066  findpos -= 1 + field.size() + 1; //for next reverse search
2067  }
2068  if(!anyFound)
2069  {
2070  __COUTS__(40) << "Field '" << field << "' not found" << __E__;
2071  return "";
2072  }
2073  }
2074 
2075  lo = xml.find(valueField + quoteType, findpos) + valueField.size() + quoteType.size();
2076 
2077  if(TTEST(40) && quoteType.size())
2078  {
2079  __COUTS__(40) << "Neighbors?" << __E__;
2080  for(size_t i = findpos; i < lo + 10 && i < xml.size(); ++i)
2081  __COUTS__(40) << "xml[" << i << "] " << xml[i] << " vs " << quoteType << " ? "
2082  << (int)xml[i] << " vs " << (int)quoteType[0] << __E__;
2083  }
2084 
2085  if((hi = xml.find(
2086  quoteType == ">" ? "<" : quoteType, //if xml tag, change closing direction
2087  lo)) == std::string::npos)
2088  {
2089  __COUTS__(40) << "Value closing not found" << __E__;
2090  return "";
2091  }
2092 
2093  if(returnFindPos)
2094  *returnFindPos =
2095  findpos - (1 + field.size()); //return found position of "< + field"
2096 
2097  __COUTS__(40) << "before: " << before << ", findpos: " << findpos << ", hi/lo: " << hi
2098  << "/" << lo << ", size: " << xml.size() << __E__;
2099  __COUTVS__(40, xml.substr(lo, hi - lo));
2100  return xml.substr(lo, hi - lo);
2101 } //end rextractXmlField()
2102 
2103 //=========================================================================
2106 void StringMacros::coutSplit(const std::string& str,
2107  uint8_t lvl /* = 0 */,
2108  const std::set<char>& delimiter /* = {',', '\n', ';'} */)
2109 {
2110  auto splitArr =
2111  StringMacros::getVectorFromString(str, delimiter, {} /* whitespace */);
2112  __COUTV__(splitArr.size());
2113  __COUTVS__(lvl, splitArr.size());
2114  for(const auto& split : splitArr)
2115  __COUTS__(lvl) << split;
2116 } //end coutSplit()
2117 
2118 #ifdef __GNUG__
2119 #include <cxxabi.h>
2120 #include <cstdlib>
2121 #include <memory>
2122 
2123 //==============================================================================
2125 std::string StringMacros::demangleTypeName(const char* name)
2126 {
2127  int status = -4; // some arbitrary value to eliminate the compiler warning
2128 
2129  // enable c++11 by passing the flag -std=c++11 to g++
2130  std::unique_ptr<char, void (*)(void*)> res{
2131  abi::__cxa_demangle(name, NULL, NULL, &status), std::free};
2132 
2133  return (status == 0) ? res.get() : name;
2134 } // end demangleTypeName()
2135 
2136 #else // does nothing if not g++
2137 //==============================================================================
2140 std::string StringMacros::demangleTypeName(const char* name) { return name; }
2141 #endif
defines used also by OtsConfigurationWizardSupervisor
bool operator()(const std::string &lhs, const std::string &rhs) const
<get string in order ignoring letter case
static std::string getTimestampString(const std::string &linuxTimeInSeconds)
static const std::string & trim(std::string &s)
static std::string extractXmlField(const std::string &xml, const std::string &field, uint32_t occurrence, size_t after, size_t *returnFindPos=nullptr, const std::string &valueField="value=", const std::string &quoteType="'")
static void getVectorFromString(const std::string &inputString, std::vector< std::string > &listToReturn, const std::set< char > &delimiter={',', '|', '&'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'}, std::vector< char > *listOfDelimiters=0, bool decodeURIComponents=false)
static std::string exec(const char *cmd)
static void getSetFromString(const std::string &inputString, std::set< std::string > &setToReturn, const std::set< char > &delimiter={',', '|', '&'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'})
static std::string setToString(const std::set< T > &setToReturn, const std::string &delimeter=", ")
setToString ~
static std::string escapeString(std::string inString, bool allowWhiteSpace=false, bool forHtml=false)
static T validateValueForDefaultStringDataType(const std::string &value, bool doConvertEnvironmentVariables=true)
static char * otsGetEnvironmentVarable(const char *name, const std::string &location, const unsigned int &line)
static void sanitizeForSQL(std::string &data)
StringMacros::sanitizeForSQL.
static void coutSplit(const std::string &str, uint8_t traceLevel=TLVL_DEBUG, const std::set< char > &delimiter={',', '\n', ';'})
static std::string vectorToString(const std::vector< T > &setToReturn, const std::string &delimeter=", ")
vectorToString ~
static std::string convertEnvironmentVariables(const std::string &data)
static std::string getNumberType(const std::string &stringToCheck)
Note: before call consider use of stringToCheck = StringMacros::convertEnvironmentVariables(stringToC...
static std::string escapeJSONStringEntities(const std::string &str)
static std::string demangleTypeName(const char *name)
static std::string rextractXmlField(const std::string &xml, const std::string &field, uint32_t occurrence, size_t before, size_t *returnFindPos=nullptr, const std::string &valueField="value=", const std::string &quoteType="'")
static bool extractCommonChunks(const std::vector< std::string > &haystack, std::vector< std::string > &commonChunksToReturn, std::vector< std::string > &wildcardStrings, unsigned int &fixedWildcardLength)
static bool inWildCardSet(const std::string &needle, const std::set< std::string > &haystack)
static bool isNumber(const std::string &stringToCheck)
Note: before call consider use of stringToCheck = StringMacros::convertEnvironmentVariables(stringToC...
static std::string mapToString(const std::map< std::string, T > &mapToReturn, const std::string &primaryDelimeter=", ", const std::string &secondaryDelimeter=": ")
static void getMapFromString(const std::string &inputString, std::map< S, T > &mapToReturn, const std::set< char > &pairPairDelimiter={',', '|', '&'}, const std::set< char > &nameValueDelimiter={'=', ':'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'})
getMapFromString ~
static std::string restoreJSONStringEntities(const std::string &str)
static std::string getTimeDurationString(const time_t durationInSeconds=time(0))
static bool wildCardMatch(const std::string &needle, const std::string &haystack, unsigned int *priorityIndex=0)
Definition: StringMacros.cc:49
static std::string decodeURIComponent(const std::string &data)
static std::string stackTrace(void)
static bool getNumber(const std::string &s, T &retValue)