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