otsdaq  3.11.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 //==============================================================================
1242 bool StringMacros::splitMacroArgTriple(const std::string& inputString,
1243  std::string& name,
1244  std::string& initValue,
1245  std::string& stepValue,
1246  char delimiter /* = ':' */)
1247 {
1248  size_t stepPos = inputString.rfind(delimiter);
1249  if(stepPos == std::string::npos || stepPos == 0)
1250  return false;
1251  size_t initPos = inputString.rfind(delimiter, stepPos - 1);
1252  if(initPos == std::string::npos)
1253  return false;
1254 
1255  auto trim = [](std::string s) {
1256  size_t b = s.find_first_not_of(" \t\n\r");
1257  size_t e = s.find_last_not_of(" \t\n\r");
1258  return b == std::string::npos ? std::string() : s.substr(b, e - b + 1);
1259  };
1260  name = trim(inputString.substr(0, initPos));
1261  initValue = trim(inputString.substr(initPos + 1, stepPos - initPos - 1));
1262  stepValue = trim(inputString.substr(stepPos + 1));
1263  return name.size() > 0;
1264 } // end splitMacroArgTriple()
1265 
1266 //==============================================================================
1270 void StringMacros::getMapFromString(const std::string& inputString,
1271  std::map<std::string, std::string>& mapToReturn,
1272  const std::set<char>& pairPairDelimiter,
1273  const std::set<char>& nameValueDelimiter,
1274  const std::set<char>& whitespace)
1275 try
1276 {
1277  unsigned int i = 0;
1278  unsigned int j = 0;
1279  std::string name;
1280  bool needValue = false;
1281 
1282  // go through the full string extracting map pairs
1283  // add each found pair to map
1284  for(; j < inputString.size(); ++j)
1285  if(!needValue) // finding name
1286  {
1287  if((whitespace.find(inputString[j]) !=
1288  whitespace.end() || // ignore leading white space or delimiter
1289  pairPairDelimiter.find(inputString[j]) != pairPairDelimiter.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  nameValueDelimiter.find(inputString[j]) !=
1296  nameValueDelimiter.end()) &&
1297  i != j) // assume end of map name
1298  {
1299  //__COUT__ << "Map name found: " <<
1300  // inputString.substr(i,j-i) << std::endl;
1301 
1302  name = inputString.substr(i, j - i); // save name, for concluding pair
1303 
1304  needValue = true; // need value now
1305 
1306  // setup i and j for next find
1307  i = j + 1;
1308  }
1309  }
1310  else // finding value
1311  {
1312  if((whitespace.find(inputString[j]) !=
1313  whitespace.end() || // ignore leading white space or delimiter
1314  nameValueDelimiter.find(inputString[j]) != nameValueDelimiter.end()) &&
1315  i == j)
1316  ++i;
1317  else if(whitespace.find(inputString[j]) !=
1318  whitespace
1319  .end() || // trailing white space or delimiter indicates end
1320  pairPairDelimiter.find(inputString[j]) !=
1321  pairPairDelimiter.end()) // &&
1322  // i != j) // assume end of value name
1323  {
1324  //__COUT__ << "Map value found: " <<
1325  // inputString.substr(i,j-i) << std::endl;
1326 
1327  auto /*pair<it,success>*/ emplaceReturn =
1328  mapToReturn.emplace(std::pair<std::string, std::string>(
1329  name,
1331  inputString.substr(i, j - i)) // value
1332  ));
1333 
1334  if(!emplaceReturn.second)
1335  {
1336  __COUT__ << "Ignoring repetitive value ('"
1337  << inputString.substr(i, j - i)
1338  << "') and keeping current value ('"
1339  << emplaceReturn.first->second << "'). " << __E__;
1340  }
1341 
1342  needValue = false; // need name now
1343 
1344  // setup i and j for next find
1345  i = j + 1;
1346  }
1347  }
1348 
1349  if(i != j) // last value (for case when no concluding ' ' or delimiter)
1350  {
1351  auto /*pair<it,success>*/ emplaceReturn =
1352  mapToReturn.emplace(std::pair<std::string, std::string>(
1353  name,
1355  inputString.substr(i, j - i)) // value
1356  ));
1357 
1358  if(!emplaceReturn.second)
1359  {
1360  __COUT__ << "Ignoring repetitive value ('" << inputString.substr(i, j - i)
1361  << "') and keeping current value ('" << emplaceReturn.first->second
1362  << "'). " << __E__;
1363  }
1364  }
1365 } // end getMapFromString()
1366 catch(const std::runtime_error& e)
1367 {
1368  __SS__ << "Error while extracting a map from the string '" << inputString
1369  << "'... is it a valid map?" << __E__ << e.what() << __E__;
1370  __SS_THROW__;
1371 }
1372 
1373 //==============================================================================
1375 std::string StringMacros::mapToString(const std::map<std::string, uint8_t>& mapToReturn,
1376  const std::string& primaryDelimeter,
1377  const std::string& secondaryDelimeter)
1378 {
1379  std::stringstream ss;
1380  bool first = true;
1381  for(auto& mapPair : mapToReturn)
1382  {
1383  if(first)
1384  first = false;
1385  else
1386  ss << primaryDelimeter;
1387  ss << mapPair.first << secondaryDelimeter << (unsigned int)mapPair.second;
1388  }
1389  return ss.str();
1390 } // end mapToString()
1391 
1392 //==============================================================================
1394 std::string StringMacros::setToString(const std::set<uint8_t>& setToReturn,
1395  const std::string& delimeter)
1396 {
1397  std::stringstream ss;
1398  bool first = true;
1399  for(auto& setValue : setToReturn)
1400  {
1401  if(first)
1402  first = false;
1403  else
1404  ss << delimeter;
1405  ss << (unsigned int)setValue;
1406  }
1407  return ss.str();
1408 } // end setToString()
1409 
1410 //==============================================================================
1412 std::string StringMacros::vectorToString(const std::vector<uint8_t>& setToReturn,
1413  const std::string& delimeter)
1414 {
1415  std::stringstream ss;
1416  bool first = true;
1417  if(delimeter == "\n")
1418  ss << "\n"; //add initial new line if new line delimiting
1419  for(auto& setValue : setToReturn)
1420  {
1421  if(first)
1422  first = false;
1423  else
1424  ss << delimeter;
1425  ss << (unsigned int)setValue;
1426  }
1427  return ss.str();
1428 } // end vectorToString()
1429 
1430 //==============================================================================
1440 bool StringMacros::extractCommonChunks(const std::vector<std::string>& haystack,
1441  std::vector<std::string>& commonChunksToReturn,
1442  std::vector<std::string>& wildcardStringsToReturn,
1443  unsigned int& fixedWildcardLength)
1444 {
1445  fixedWildcardLength = 0; // init to default
1446  __COUTTV__(StringMacros::vectorToString(haystack));
1447 
1448  // Steps:
1449  // - find start and end common chunks first in haystack strings
1450  // - use start and end to determine if there is more than one *
1451  // - decide if fixed width was specified (based on prepended 0s to numbers)
1452  // - search for more instances of * value
1453  //
1454  //
1455  // // Note: lambda recursive function to find chunks
1456  // std::function<void(
1457  // const std::vector<std::string>&,
1458  // const std::string&,
1459  // const unsigned int, const int)> localRecurse =
1460  // [&specialFolders, &specialMapTypes, &retMap, &localRecurse](
1461  // const std::vector<std::string>& haystack,
1462  // const std::string& offsetPath,
1463  // const unsigned int depth,
1464  // const int specialIndex)
1465  // {
1466  //
1467  // //__COUTV__(path);
1468  // //__COUTV__(depth);
1469  // }
1470  std::pair<unsigned int /*lo*/, unsigned int /*hi*/> wildcardBounds(
1471  std::make_pair(-1, 0)); // initialize to illegal wildcard
1472 
1473  // look for starting matching segment
1474  for(unsigned int n = 1; n < haystack.size(); ++n)
1475  for(unsigned int i = 0, j = 0;
1476  i < haystack[0].length() && j < haystack[n].length();
1477  ++i, ++j)
1478  {
1479  if(i < wildcardBounds.first)
1480  {
1481  if(haystack[0][i] != haystack[n][j])
1482  {
1483  wildcardBounds.first = i; // found lo side of wildcard
1484  break;
1485  }
1486  }
1487  else
1488  break;
1489  }
1490  __COUTS__(3) << "Low side = " << wildcardBounds.first << " "
1491  << haystack[0].substr(0, wildcardBounds.first) << __E__;
1492 
1493  // look for end matching segment
1494  for(unsigned int n = 1; n < haystack.size(); ++n)
1495  for(int i = haystack[0].length() - 1, j = haystack[n].length() - 1;
1496  i >= (int)wildcardBounds.first && j >= (int)wildcardBounds.first;
1497  --i, --j)
1498  {
1499  if(i > (int)wildcardBounds.second) // looking for hi side
1500  {
1501  if(haystack[0][i] != haystack[n][j])
1502  {
1503  wildcardBounds.second = i + 1; // found hi side of wildcard
1504  break;
1505  }
1506  }
1507  else
1508  break;
1509  }
1510 
1511  __COUTS__(3) << "High side = " << wildcardBounds.second << " "
1512  << haystack[0].substr(wildcardBounds.second) << __E__;
1513 
1514  // add first common chunk
1515  commonChunksToReturn.push_back(haystack[0].substr(0, wildcardBounds.first));
1516 
1517  if(wildcardBounds.first != (unsigned int)-1) // potentially more chunks if not end
1518  {
1519  // - use start and end to determine if there is more than one *
1520  for(int i = (wildcardBounds.first + wildcardBounds.second) / 2 + 1;
1521  i < (int)wildcardBounds.second;
1522  ++i)
1523  if(haystack[0][wildcardBounds.first] == haystack[0][i] &&
1524  haystack[0].substr(wildcardBounds.first, wildcardBounds.second - i) ==
1525  haystack[0].substr(i, wildcardBounds.second - i))
1526  {
1527  std::string multiWildcardString =
1528  haystack[0].substr(i, wildcardBounds.second - i);
1529  __COUT__ << "Potential multi-wildcard found: " << multiWildcardString
1530  << " at position i=" << i << __E__;
1531 
1532  std::vector<unsigned int /*lo index*/> wildCardInstances;
1533  // add front one now, and back one later
1534  wildCardInstances.push_back(wildcardBounds.first);
1535 
1536  unsigned int offset =
1537  wildCardInstances[0] + multiWildcardString.size() + 1;
1538  std::string middleString = haystack[0].substr(offset, (i - 1) - offset);
1539  __COUTV__(middleString);
1540 
1541  // search for more wildcard instances in new common area
1542  size_t k;
1543  while((k = middleString.find(multiWildcardString)) != std::string::npos)
1544  {
1545  __COUT__ << "Multi-wildcard found at " << k << __E__;
1546 
1547  wildCardInstances.push_back(offset + k);
1548 
1549  middleString =
1550  middleString.substr(k + multiWildcardString.size() + 1);
1551  offset += k + multiWildcardString.size() + 1;
1552  __COUTV__(middleString);
1553  }
1554 
1555  // add back one last
1556  wildCardInstances.push_back(i);
1557 
1558  for(unsigned int w = 0; w < wildCardInstances.size() - 1; ++w)
1559  {
1560  __COUTV__(wildCardInstances[w]);
1561  __COUTV__(wildCardInstances[w + 1]);
1562  __COUTV__(wildCardInstances.size());
1563  commonChunksToReturn.push_back(haystack[0].substr(
1564  wildCardInstances[w] + multiWildcardString.size(),
1565  wildCardInstances[w + 1] -
1566  (wildCardInstances[w] + multiWildcardString.size())));
1567  }
1568  }
1569 
1570  __COUTTV__(StringMacros::vectorToString(commonChunksToReturn));
1571  //confirm valid multi-commonChunksToReturn for all haystack entries (only first can be certain at this point)
1572  for(unsigned int c = 1; c < commonChunksToReturn.size(); ++c)
1573  {
1574  __COUT__ << "Checking [" << c << "]: " << commonChunksToReturn[c] << __E__;
1575  for(unsigned int n = 1; n < haystack.size(); ++n)
1576  {
1577  __COUT__ << "Checking chunks work with haystack [" << n
1578  << "]: " << haystack[n] << __E__;
1579  __COUTV__(commonChunksToReturn[0].size());
1580  std::string wildCardValue = haystack[n].substr(
1581  commonChunksToReturn[0].size(),
1582  haystack[n].find(commonChunksToReturn[1],
1583  commonChunksToReturn[0].size() + 1) -
1584  commonChunksToReturn[0].size());
1585  __COUTTV__(wildCardValue);
1586 
1587  std::string builtString = "";
1588  for(unsigned int cc = 0; cc < commonChunksToReturn.size(); ++cc)
1589  builtString += commonChunksToReturn[cc] + wildCardValue;
1590  __COUTTV__(builtString);
1591  __COUTTV__(wildCardValue);
1592 
1593  if(haystack[n].find(builtString) != 0)
1594  {
1595  __COUT__ << "Dropping common chunk " << commonChunksToReturn[c]
1596  << ", built '" << builtString << "' not found in "
1597  << haystack[n] << __E__;
1598  commonChunksToReturn.erase(commonChunksToReturn.begin() + c);
1599  --c; //rewind
1600  break; //check next chunk
1601  }
1602  else
1603  __COUTT__ << "Found built '" << builtString << "' in " << haystack[n]
1604  << __E__;
1605  } //end haystack loop
1606  } //end common chunk loop
1607 
1608  __COUTTV__(StringMacros::vectorToString(commonChunksToReturn));
1609  __COUTTV__(commonChunksToReturn[0].size());
1610 
1611  __COUTTV__(fixedWildcardLength);
1612  // check if all common chunks END in 0 to add fixed length
1613  for(unsigned int i = 0; i < commonChunksToReturn[0].size(); ++i)
1614  if(commonChunksToReturn[0][commonChunksToReturn[0].size() - 1 - i] == '0')
1615  {
1616  ++fixedWildcardLength;
1617  __COUTT__ << "Trying for added fixed length +1 to " << fixedWildcardLength
1618  << __E__;
1619  }
1620  else
1621  break;
1622 
1623  // bool allHave0 = true;
1624  for(unsigned int c = 0; c < commonChunksToReturn.size(); ++c)
1625  {
1626  unsigned int cnt = 0;
1627  for(unsigned int i = 0; i < commonChunksToReturn[c].size(); ++i)
1628  if(commonChunksToReturn[c][commonChunksToReturn[c].size() - 1 - i] == '0')
1629  ++cnt;
1630  else
1631  break;
1632 
1633  if(fixedWildcardLength < cnt)
1634  fixedWildcardLength = cnt;
1635  else if(fixedWildcardLength > cnt)
1636  {
1637  __SS__ << "Invalid fixed length found, please simplify indexing between "
1638  "these common chunks: "
1639  << StringMacros::vectorToString(commonChunksToReturn) << __E__;
1640  __SS_THROW__;
1641  }
1642  }
1643  __COUTTV__(fixedWildcardLength);
1644 
1645  if(fixedWildcardLength) // take trailing 0s out of common chunks
1646  for(unsigned int c = 0; c < commonChunksToReturn.size(); ++c)
1647  commonChunksToReturn[c] = commonChunksToReturn[c].substr(
1648  0, commonChunksToReturn[c].size() - fixedWildcardLength);
1649 
1650  // add last common chunk
1651  commonChunksToReturn.push_back(haystack[0].substr(wildcardBounds.second));
1652  } // end handling more chunks
1653  __COUTTV__(StringMacros::vectorToString(commonChunksToReturn));
1654 
1655  // now determine wildcard strings
1656  size_t k;
1657  unsigned int i;
1658  unsigned int ioff = fixedWildcardLength;
1659  bool wildcardsNeeded = false;
1660  bool someLeadingZeros = false;
1661  bool allWildcardsSameSize = true;
1662 
1663  for(unsigned int n = 0; n < haystack.size(); ++n)
1664  {
1665  std::string wildcard = "";
1666  k = 0;
1667  i = ioff + commonChunksToReturn[0].size();
1668 
1669  if(commonChunksToReturn.size() == 1) // just get end
1670  wildcard = haystack[n].substr(i);
1671  else
1672  for(unsigned int c = 1; c < commonChunksToReturn.size(); ++c)
1673  {
1674  if(c == commonChunksToReturn.size() - 1) // for last, do reverse find
1675  k = haystack[n].rfind(commonChunksToReturn[c]);
1676  else
1677  k = haystack[n].find(commonChunksToReturn[c], i + 1);
1678 
1679  if(wildcard == "")
1680  {
1681  // set wildcard for first time
1682  __COUTVS__(3, i);
1683  __COUTVS__(3, k);
1684  __COUTVS__(3, k - i);
1685 
1686  wildcard = haystack[n].substr(i, k - i);
1687  if(fixedWildcardLength && n == 0)
1688  fixedWildcardLength += wildcard.size();
1689 
1690  __COUTS__(3) << "name[" << n << "] = " << wildcard << " fixed @ "
1691  << fixedWildcardLength << __E__;
1692 
1693  break;
1694  }
1695  else if(0 /*skip validation in favor of speed*/ &&
1696  wildcard != haystack[n].substr(i, k - i))
1697  {
1698  __SS__ << "Invalid wildcard! for name[" << n << "] = " << haystack[n]
1699  << " - the extraction algorithm is confused, please simplify "
1700  "your naming convention."
1701  << __E__;
1702  __SS_THROW__;
1703  }
1704 
1705  i = k;
1706  } // end commonChunksToReturn loop
1707 
1708  if(wildcard.size())
1709  {
1710  wildcardsNeeded = true;
1711 
1712  //track if need for leading 0s in wildcards
1713  if(wildcard[0] == '0' && !fixedWildcardLength)
1714  {
1715  someLeadingZeros = true;
1716  if(wildcardStringsToReturn.size() &&
1717  wildcard.size() != wildcardStringsToReturn[0].size())
1718  allWildcardsSameSize = false;
1719  }
1720  }
1721  wildcardStringsToReturn.push_back(wildcard);
1722 
1723  } // end name loop
1724 
1725  __COUTTV__(StringMacros::vectorToString(commonChunksToReturn));
1726  __COUTTV__(StringMacros::vectorToString(wildcardStringsToReturn));
1727 
1728  if(someLeadingZeros && allWildcardsSameSize)
1729  {
1730  __COUTTV__(fixedWildcardLength); //should be 0 in this case
1731  fixedWildcardLength = wildcardStringsToReturn[0].size();
1732  __COUT__ << "Enforce wildcard size of " << fixedWildcardLength << __E__;
1733  }
1734 
1735  if(wildcardStringsToReturn.size() != haystack.size())
1736  {
1737  __SS__ << "There was a problem during common chunk extraction!" << __E__;
1738  __SS_THROW__;
1739  }
1740 
1741  return wildcardsNeeded;
1742 
1743 } // end extractCommonChunks()
1744 
1745 //==============================================================================
1750  const std::string& rhs) const
1751 {
1752  //__COUTV__(lhs);
1753  //__COUTV__(rhs);
1754  // return true if lhs < rhs (lhs will be ordered first)
1755 
1756  for(unsigned int i = 0; i < lhs.size() && i < rhs.size(); ++i)
1757  {
1758  //__COUT__ << i << "\t" << lhs[i] << "\t" << rhs[i] << __E__;
1759  if((lhs[i] >= 'A' && lhs[i] <= 'Z' && rhs[i] >= 'A' && rhs[i] <= 'Z') ||
1760  (lhs[i] >= 'a' && lhs[i] <= 'z' && rhs[i] >= 'a' && rhs[i] <= 'z'))
1761  { // same case
1762  if(lhs[i] == rhs[i])
1763  continue;
1764  return (lhs[i] < rhs[i]);
1765  //{ retVal = false; break;} //return false;
1766  }
1767  else if(lhs[i] >= 'A' && lhs[i] <= 'Z') // rhs lower case
1768  {
1769  if(lhs[i] + 32 == rhs[i]) // lower case is higher by 32
1770  return false; // in tie return lower case first
1771  return (lhs[i] + 32 < rhs[i]);
1772  }
1773  else if(rhs[i] >= 'A' && rhs[i] <= 'Z')
1774  {
1775  if(lhs[i] == rhs[i] + 32) // lower case is higher by 32
1776  return true; // in tie return lower case first
1777  return (lhs[i] < rhs[i] + 32);
1778  }
1779  else // not letters case (should only be for numbers)
1780  {
1781  if(lhs[i] == rhs[i])
1782  continue;
1783  return (lhs[i] < rhs[i]);
1784  }
1785  } // end case insensitive compare loop
1786 
1787  // lhs and rhs are equivalent to character[i], so return false if rhs.size() was the limit reached
1788  return lhs.size() < rhs.size();
1789 } // end IgnoreCaseCompareStruct::operator() comparison handler
1790 
1791 //==============================================================================
1794 std::string StringMacros::exec(const char* cmd)
1795 {
1796  __COUTTV__(cmd);
1797 
1798  std::array<char, 128> buffer;
1799  std::string result;
1800 
1801  // For capturing both stdout and stderr, we need to redirect stderr to stdout
1802  // This is done by appending " 2>&1" to the command
1803  std::string cmdWithRedirect = std::string(cmd) + " 2>&1";
1804  FILE* rawPipe = popen(cmdWithRedirect.c_str(), "r");
1805  if(!rawPipe)
1806  __THROW__("popen() failed!");
1807 
1808  // Own the pipe so that an exception while accumulating output still closes it
1809  // and reaps the child process. Released below so the success path can check
1810  // the pclose() status itself.
1811  std::unique_ptr<FILE, int (*)(FILE*)> pipe(rawPipe, pclose);
1812 
1813  while(fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr)
1814  result += buffer.data();
1815 
1816  int status = pclose(pipe.release());
1817  if(status == -1)
1818  __COUT_WARN__ << "pclose() failed for command: " << cmd << __E__;
1819 
1820  __COUTTV__(result);
1821  return result;
1822 } // end exec()
1823 
1824 // #include <iostream>
1825 #include <fstream> /* for ifstream */
1826 // #include <sstream>
1827 // #include <string>
1828 // #include <cstdlib>
1829 //==============================================================================
1830 uintptr_t find_library_base(const std::string& libname)
1831 {
1832  std::ifstream maps("/proc/self/maps");
1833  std::string line;
1834 
1835  while(std::getline(maps, line))
1836  {
1837  if(line.find(libname) != std::string::npos &&
1838  line.find("r-xp") != std::string::npos)
1839  {
1840  uintptr_t base;
1841  std::stringstream ss(line);
1842  ss >> std::hex >> base;
1843  return base;
1844  }
1845  }
1846  return 0;
1847 } //end find_library_base()
1848 
1849 //==============================================================================
1850 void resolve_stack_entry(const std::string& so_path,
1851  const std::string& real_name,
1852  const std::string& offset_begin, // e.g. "+0x249d"
1853  const std::string& offset_end // e.g. "[0x7f5518fa28fd]"
1854 )
1855 {
1856  // Extract runtime address from a string like "[0x....]".
1857  // Be defensive: validate delimiters before parsing to avoid exceptions.
1858  const std::size_t pos0x = offset_end.find("0x");
1859  if(pos0x == std::string::npos)
1860  {
1861  __COUTS__(52) << "resolve_stack_entry: could not find \"0x\" in offset_end: '"
1862  << offset_end << "'" << __E__;
1863  return;
1864  }
1865 
1866  const std::size_t posBracket = offset_end.find(']', pos0x);
1867  if(posBracket == std::string::npos || posBracket < pos0x + 3)
1868  {
1869  __COUTS__(52) << "resolve_stack_entry: could not find closing ']' with at least "
1870  "one hex digit after \"0x\" "
1871  "in offset_end: '"
1872  << offset_end << "'" << __E__;
1873  return;
1874  }
1875 
1876  const std::string addr_str = offset_end.substr(pos0x, posBracket - pos0x);
1877 
1878  uintptr_t runtime_addr = 0;
1879  try
1880  {
1881  runtime_addr = std::stoull(addr_str, nullptr, 16);
1882  }
1883  catch(const std::exception& e)
1884  {
1885  __COUTS__(52) << "resolve_stack_entry: failed to parse runtime address from '"
1886  << addr_str << "': " << e.what() << __E__;
1887  return;
1888  }
1889 
1890  std::string so_name = so_path.substr(so_path.find_last_of('/') + 1);
1891 
1892  uintptr_t base = find_library_base(so_name);
1893  if(!base)
1894  {
1895  std::cerr << "Could not find base for " << so_name << "\n";
1896  return;
1897  }
1898 
1899  uintptr_t file_addr = runtime_addr - base;
1900 
1901  std::ostringstream cmd;
1902  cmd << "addr2line -f -C -e " << so_path << " 0x" << std::hex << file_addr;
1903 
1904  __COUT__ << "\nResolving:\n"
1905  << so_path << " : " << real_name << offset_begin << " [" << std::hex
1906  << runtime_addr << "]\n\n";
1907 
1908  std::string result = StringMacros::exec(cmd.str().c_str());
1909  __COUTV__(result);
1910 } //end resolve_stack_entry()
1911 
1912 //==============================================================================
1916 #include <cxxabi.h> //for abi::__cxa_demangle
1917 #include <execinfo.h> //for back trace of stack
1918 // #include "TUnixSystem.h"
1920 {
1921  __SS__ << "ots::stackTrace:\n";
1922 
1923  void* array[10];
1924  size_t size;
1925 
1926  // get void*'s for all entries on the stack
1927  size = backtrace(array, 10);
1928  // backtrace_symbols_fd(array, size, STDERR_FILENO);
1929 
1930  // https://stackoverflow.com/questions/77005/how-to-automatically-generate-a-stacktrace-when-my-program-crashes
1931  char** messages = backtrace_symbols(array, size);
1932 
1933  // skip first stack frame (points here)
1934  // char syscom[256];
1935  for(unsigned int i = 1; i < size && messages != NULL; ++i)
1936  {
1937  // mangled name needs to be converted to get nice name and line number
1938  // line number not working... FIXME
1939 
1940  // sprintf(syscom,"addr2line %p -e %s",
1941  // array[i],
1942  // messages[i]); //last parameter is the name of this app
1943  // ss << StringMacros::exec(syscom) << __E__;
1944  // system(syscom);
1945 
1946  // continue;
1947 
1948  char *mangled_name = 0, *offset_begin = 0, *offset_end = 0;
1949 
1950  // find parentheses and +address offset surrounding mangled name
1951  for(char* p = messages[i]; *p; ++p)
1952  {
1953  if(*p == '(')
1954  {
1955  mangled_name = p;
1956  }
1957  else if(*p == '+')
1958  {
1959  offset_begin = p;
1960  }
1961  else if(*p == ')')
1962  {
1963  offset_end = p;
1964  break;
1965  }
1966  }
1967 
1968  // if the line could be processed, attempt to demangle the symbol
1969  if(mangled_name && offset_begin && offset_end && mangled_name < offset_begin)
1970  {
1971  *mangled_name++ = '\0';
1972  *offset_begin++ = '\0';
1973  *offset_end++ = '\0';
1974 
1975  int status;
1976  char* real_name = abi::__cxa_demangle(mangled_name, 0, 0, &status);
1977 
1978  // if demangling is successful, output the demangled function name
1979  if(status == 0)
1980  {
1981  ss << "[" << i << "] " << messages[i] << " : " << real_name << "+"
1982  << offset_begin << offset_end << std::endl;
1983  // Too slow to resolve lines (stackTrace getting called too much)!
1984  // resolve_stack_entry(messages[i],real_name,offset_begin,offset_end);
1985  }
1986  // otherwise, output the mangled function name
1987  else
1988  {
1989  ss << "[" << i << "] " << messages[i] << " : " << mangled_name << "+"
1990  << offset_begin << offset_end << std::endl;
1991  }
1992  free(real_name);
1993  }
1994  // otherwise, print the whole line
1995  else
1996  {
1997  ss << "[" << i << "] " << messages[i] << std::endl;
1998  }
1999  }
2000  ss << std::endl;
2001  ss << std::endl;
2002 
2003  free(messages);
2004 
2005  // call ROOT's stack trace to get line numbers of ALL threads
2006  // gSystem->StackTrace();
2007 
2008  return ss.str();
2009 } // end stackTrace
2010 
2011 //==============================================================================
2017  const std::string& location,
2018  const unsigned int& line)
2019 {
2020  char* environmentVariablePtr = getenv(name);
2021  if(!environmentVariablePtr)
2022  {
2023  __SS__ << "Environment variable '$" << name << "' not defined at " << location
2024  << ":" << line << __E__;
2025  ss << "\n\n" << StringMacros::stackTrace() << __E__;
2026  __SS_ONLY_THROW__;
2027  }
2028  return environmentVariablePtr;
2029 } // end otsGetEnvironmentVarable()
2030 
2031 //=========================================================================
2034 std::string StringMacros::extractXmlField(const std::string& xml,
2035  const std::string& field,
2036  uint32_t occurrence,
2037  size_t after,
2038  size_t* returnFindPos /* = nullptr */,
2039  const std::string& valueField /* = "value=" */,
2040  const std::string& quoteType /* = "'" */)
2041 {
2042  if(returnFindPos)
2043  *returnFindPos = std::string::npos;
2044 
2045  __COUTVS__(41, xml);
2046 
2047  size_t lo, findpos = after, hi;
2048  for(uint32_t i = 0; i <= occurrence; ++i)
2049  {
2050  bool anyFound = false;
2051  while((findpos =
2052  xml.find("<" + field, //allow for immediate closing of xml tag with >
2053  findpos)) != std::string::npos &&
2054  findpos + 1 + field.size() < xml.size())
2055  {
2056  __COUTS__(40) << "find: ---- '<" << field << " findpos=" << findpos
2057  << "findpos " << findpos << " " << xml[findpos] << " "
2058  << xml[findpos + 1 + field.size()] << " "
2059  << (int)xml[findpos + 1 + field.size()] << __E__;
2060 
2061  findpos +=
2062  1 +
2063  field
2064  .size(); //to point to closing white space and advance for next forward search
2065 
2066  //verify white space after the field
2067  if((quoteType == ">" && xml[findpos] == '>') || xml[findpos] == ' ' ||
2068  xml[findpos] == '\n' || xml[findpos] == '\t')
2069  {
2070  anyFound = true; //flag
2071  break;
2072  }
2073  }
2074 
2075  if(!anyFound)
2076  {
2077  __COUTS__(40) << "Field '" << field << "' not found" << __E__;
2078  return "";
2079  }
2080  }
2081 
2082  lo = xml.find(valueField + quoteType, findpos) + valueField.size() + quoteType.size();
2083 
2084  if(TTEST(40) && quoteType.size())
2085  {
2086  __COUTS__(40) << "Neighbors of field '" << field << "' and value '" << valueField
2087  << "' w/quote = " << quoteType << __E__;
2088  for(size_t i = lo - valueField.size(); i < lo + 10 && i < xml.size(); ++i)
2089  __COUTS__(40) << "xml[" << i << "] " << xml[i] << " vs " << quoteType << " ? "
2090  << (int)xml[i] << " vs " << (int)quoteType[0] << __E__;
2091  }
2092 
2093  if((hi = xml.find(
2094  quoteType == ">" ? "<" : quoteType, //if xml tag, change closing direction
2095  lo)) == std::string::npos)
2096  {
2097  __COUTS__(40) << "Value closing not found" << __E__;
2098  return "";
2099  }
2100 
2101  if(returnFindPos)
2102  *returnFindPos = findpos - (1 + field.size()); //remove offset that was added
2103 
2104  __COUTS__(40) << "after: " << after << ", findpos: " << findpos << ", hi/lo: " << hi
2105  << "/" << lo << ", size: " << xml.size() << __E__;
2106  __COUTVS__(40, xml.substr(lo, hi - lo));
2107  return xml.substr(lo, hi - lo);
2108 } //end extractXmlField()
2109 
2110 //=========================================================================
2113 std::string StringMacros::rextractXmlField(const std::string& xml,
2114  const std::string& field,
2115  uint32_t occurrence,
2116  size_t before,
2117  size_t* returnFindPos /* = nullptr */,
2118  const std::string& valueField /* = "value=" */,
2119  const std::string& quoteType /* = "'" */)
2120 {
2121  if(returnFindPos)
2122  *returnFindPos = std::string::npos;
2123 
2124  __COUTVS__(41, xml);
2125 
2126  size_t lo = 0, hi, findpos = before;
2127  for(uint32_t i = 0; i <= occurrence; ++i)
2128  {
2129  bool anyFound = false;
2130  while((findpos =
2131  xml.rfind("<" + field, //allow for immediate closing of xml tag with >
2132  findpos)) != std::string::npos &&
2133  findpos + 1 + field.size() < xml.size())
2134  {
2135  __COUTS__(40) << "rfind: ---- '<" << field << " findpos=" << findpos << " "
2136  << xml[findpos] << " " << xml[findpos + 1 + field.size()] << " "
2137  << (int)xml[findpos + 1 + field.size()] << __E__;
2138 
2139  findpos += 1 + field.size();
2140 
2141  //verify white space after the field
2142  if((quoteType == ">" && xml[findpos] == '>') || xml[findpos] == ' ' ||
2143  xml[findpos] == '\n' || xml[findpos] == '\t')
2144  {
2145  anyFound = true; //flag
2146  break;
2147  }
2148  else
2149  findpos -= 1 + field.size() + 1; //for next reverse search
2150  }
2151  if(!anyFound)
2152  {
2153  __COUTS__(40) << "Field '" << field << "' not found" << __E__;
2154  return "";
2155  }
2156  }
2157 
2158  lo = xml.find(valueField + quoteType, findpos) + valueField.size() + quoteType.size();
2159 
2160  if(TTEST(40) && quoteType.size())
2161  {
2162  __COUTS__(40) << "Neighbors?" << __E__;
2163  for(size_t i = findpos; i < lo + 10 && i < xml.size(); ++i)
2164  __COUTS__(40) << "xml[" << i << "] " << xml[i] << " vs " << quoteType << " ? "
2165  << (int)xml[i] << " vs " << (int)quoteType[0] << __E__;
2166  }
2167 
2168  if((hi = xml.find(
2169  quoteType == ">" ? "<" : quoteType, //if xml tag, change closing direction
2170  lo)) == std::string::npos)
2171  {
2172  __COUTS__(40) << "Value closing not found" << __E__;
2173  return "";
2174  }
2175 
2176  if(returnFindPos)
2177  *returnFindPos =
2178  findpos - (1 + field.size()); //return found position of "< + field"
2179 
2180  __COUTS__(40) << "before: " << before << ", findpos: " << findpos << ", hi/lo: " << hi
2181  << "/" << lo << ", size: " << xml.size() << __E__;
2182  __COUTVS__(40, xml.substr(lo, hi - lo));
2183  return xml.substr(lo, hi - lo);
2184 } //end rextractXmlField()
2185 
2186 //=========================================================================
2189 void StringMacros::coutSplit(const std::string& str,
2190  uint8_t lvl /* = 0 */,
2191  const std::set<char>& delimiter /* = {',', '\n', ';'} */)
2192 {
2193  auto splitArr =
2194  StringMacros::getVectorFromString(str, delimiter, {} /* whitespace */);
2195  __COUTV__(splitArr.size());
2196  __COUTVS__(lvl, splitArr.size());
2197  for(const auto& split : splitArr)
2198  __COUTS__(lvl) << split;
2199 } //end coutSplit()
2200 
2201 #ifdef __GNUG__
2202 #include <cxxabi.h>
2203 #include <cstdlib>
2204 #include <memory>
2205 
2206 //==============================================================================
2208 std::string StringMacros::demangleTypeName(const char* name)
2209 {
2210  int status = -4; // some arbitrary value to eliminate the compiler warning
2211 
2212  // enable c++11 by passing the flag -std=c++11 to g++
2213  std::unique_ptr<char, void (*)(void*)> res{
2214  abi::__cxa_demangle(name, NULL, NULL, &status), std::free};
2215 
2216  return (status == 0) ? res.get() : name;
2217 } // end demangleTypeName()
2218 
2219 #else // does nothing if not g++
2220 //==============================================================================
2223 std::string StringMacros::demangleTypeName(const char* name) { return name; }
2224 #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 bool splitMacroArgTriple(const std::string &inputString, std::string &name, std::string &initValue, std::string &stepValue, char delimiter=':')
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)