tdaq-develop-2025-02-12
StringMacros.cc
1 #include "otsdaq/Macros/StringMacros.h"
2 
3 #include <array>
4 
5 using namespace ots;
6 
7 //==============================================================================
18 bool StringMacros::wildCardMatch(const std::string& needle,
19  const std::string& haystack,
20  unsigned int* priorityIndex)
21 try
22 {
23  // __COUT__ << "\t\t wildCardMatch: " << needle <<
24  // " =in= " << haystack << " ??? " <<
25  // std::endl;
26 
27  // empty needle
28  if(needle.size() == 0)
29  {
30  if(priorityIndex)
31  *priorityIndex = 1; // consider an exact match, to stop higher level loops
32  return true; // if empty needle, always "found"
33  }
34 
35  // only wildcard
36  if(needle == "*")
37  {
38  if(priorityIndex)
39  *priorityIndex = 5; // only wildcard, is lowest priority
40  return true; // if empty needle, always "found"
41  }
42 
43  // no wildcards
44  if(needle == haystack)
45  {
46  if(priorityIndex)
47  *priorityIndex = 1; // an exact match
48  return true;
49  }
50 
51  // trailing wildcard
52  if(needle[needle.size() - 1] == '*' &&
53  needle.substr(0, needle.size() - 1) == haystack.substr(0, needle.size() - 1))
54  {
55  if(priorityIndex)
56  *priorityIndex = 2; // trailing wildcard match
57  return true;
58  }
59 
60  // leading wildcard
61  if(needle[0] == '*' &&
62  needle.substr(1) == haystack.substr(haystack.size() - (needle.size() - 1)))
63  {
64  if(priorityIndex)
65  *priorityIndex = 3; // leading wildcard match
66  return true;
67  }
68 
69  // leading wildcard and trailing wildcard
70  if(needle[0] == '*' && needle[needle.size() - 1] == '*' &&
71  std::string::npos != haystack.find(needle.substr(1, needle.size() - 2)))
72  {
73  if(priorityIndex)
74  *priorityIndex = 4; // leading and trailing wildcard match
75  return true;
76  }
77 
78  // else no match
79  if(priorityIndex)
80  *priorityIndex = 0; // no match
81  return false;
82 } //end wildCardMatch()
83 catch(...)
84 {
85  if(priorityIndex)
86  *priorityIndex = 0; // no match
87  return false; // if out of range
88 } //end wildCardMatch() catch
89 
90 //==============================================================================
94 bool StringMacros::inWildCardSet(const std::string& needle,
95  const std::set<std::string>& haystack)
96 {
97  for(const auto& haystackString : haystack)
98  {
99  // use wildcard match, flip needle parameter.. because we want haystack to have the wildcards
100  if(haystackString.size() && haystackString[0] == '!')
101  {
102  //treat as inverted
103  if(!StringMacros::wildCardMatch(haystackString.substr(1), needle))
104  return true;
105  }
106  else if(StringMacros::wildCardMatch(haystackString, needle))
107  return true;
108  }
109  return false;
110 }
111 
112 //==============================================================================
115 std::string StringMacros::decodeURIComponent(const std::string& data)
116 {
117  std::string decodeURIString(data.size(), 0); // init to same size
118  unsigned int j = 0;
119  for(unsigned int i = 0; i < data.size(); ++i, ++j)
120  {
121  if(data[i] == '%')
122  {
123  // high order hex nibble digit
124  if(data[i + 1] > '9') // then ABCDEF
125  decodeURIString[j] += (data[i + 1] - 55) * 16;
126  else
127  decodeURIString[j] += (data[i + 1] - 48) * 16;
128 
129  // low order hex nibble digit
130  if(data[i + 2] > '9') // then ABCDEF
131  decodeURIString[j] += (data[i + 2] - 55);
132  else
133  decodeURIString[j] += (data[i + 2] - 48);
134 
135  i += 2; // skip to next char
136  }
137  else
138  decodeURIString[j] = data[i];
139  }
140  decodeURIString.resize(j);
141  return decodeURIString;
142 } // end decodeURIComponent()
143 
144 //==============================================================================
145 std::string StringMacros::encodeURIComponent(const std::string& sourceStr)
146 {
147  std::string retStr = "";
148  char encodeStr[4];
149  for(const auto& c : sourceStr)
150  if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
151  retStr += c;
152  else
153  {
154  sprintf(encodeStr, "%%%2.2X", (uint8_t)c);
155  retStr += encodeStr;
156  }
157  return retStr;
158 } // end encodeURIComponent()
159 
160 //==============================================================================
162 void StringMacros::sanitizeForSQL(std::string& str)
163 {
164  std::map<char, std::string> replacements = {
165  {'\'', "''"}, // Single quote becomes two single quotes
166  {'\\', "\\\\"} //, // Backslash becomes double backslash
167  // {';', "\\;"}, // Semicolon can be escaped (optional)
168  // {'-', "\\-"}, // Dash for comments (optional, context-specific)
169  };
170 
171  size_t pos = 0;
172  while(pos < str.size())
173  {
174  auto it = replacements.find(str[pos]);
175  if(it != replacements.end())
176  {
177  str.replace(pos, 1, it->second);
178  pos += it->second.size(); // Advance past the replacement
179  }
180  else
181  {
182  ++pos;
183  }
184  }
185 } //end sanitizeForSQL
186 
187 //==============================================================================
196 std::string StringMacros::escapeString(std::string inString,
197  bool allowWhiteSpace /* = false */)
198 {
199  unsigned int ws = -1;
200  char htmlTmp[10];
201 
202  for(unsigned int i = 0; i < inString.length(); i++)
203  if(inString[i] != ' ')
204  {
205  __COUT_TYPE__(TLVL_DEBUG + 30) << __COUT_HDR__ << i << ". " << inString[i]
206  << ":" << (int)inString[i] << std::endl;
207 
208  // remove new lines and unprintable characters
209  if(inString[i] == '\r' || inString[i] == '\n' || // remove new line chars
210  inString[i] == '\t' || // remove tabs
211  inString[i] < 32 || // remove un-printable characters (they mess up xml
212  // interpretation)
213  (inString[i] > char(126) &&
214  inString[i] < char(161))) // this is aggravated by the bug in
215  // MFextensions (though Eric says he fixed on
216  // 8/24/2016) Note: greater than 255 should be
217  // impossible if by byte (but there are html
218  // chracters in 300s and 8000s)
219  {
220  //handle UTF-8 encoded characters
221  if(i + 2 < inString.size() && inString[i] == char(0xE2) &&
222  inString[i + 1] == char(0x80) &&
223  inString[i + 2] ==
224  char(0x93)) // longer dash endash is 3-bytes 0xE2 0x80 0x93
225  {
226  //encode "--" as &#8211;
227  inString.insert(i,
228  "&#82"); // insert HTML name before special character
229  inString.replace(
230  i + 4, 1, 1, '1'); // replace special character-0 with s
231  inString.replace(
232  i + 5, 1, 1, '1'); // replace special character-1 with h
233  inString.replace(
234  i + 6, 1, 1, ';'); // replace special character-2 with ;
235  i += 7; // skip to next char to check
236  ws = i; // last non white space char
237  --i;
238  continue;
239  }
240 
241  if( // maintain new lines and tabs
242  inString[i] == '\n')
243  {
244  if(allowWhiteSpace)
245  {
246  sprintf(htmlTmp, "&#%3.3d", inString[i]);
247  inString.insert(
248  i, std::string(htmlTmp)); // insert html str sequence
249  inString.replace(
250  i + 5, 1, 1, ';'); // replace special character with ;
251  i += 6; // skip to next char to check
252  --i;
253  }
254  else // translate to ' '
255  inString[i] = ' ';
256  }
257  else if( // maintain new lines and tabs
258  inString[i] == '\t')
259  {
260  if(allowWhiteSpace)
261  {
262  if(0)
263  {
264  // tab = 8 spaces
265  sprintf(htmlTmp,
266  "&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160");
267  inString.insert(
268  i, std::string(htmlTmp)); // insert html str sequence
269  inString.replace(
270  i + 47, 1, 1, ';'); // replace special character with ;
271  i += 48; // skip to next char to check
272  --i;
273  }
274  else // tab = 0x09
275  {
276  sprintf(htmlTmp, "&#009");
277  inString.insert(
278  i, std::string(htmlTmp)); // insert html str sequence
279  inString.replace(
280  i + 5, 1, 1, ';'); // replace special character with ;
281  i += 6; // skip to next char to check
282  --i;
283  }
284  }
285  else // translate to ' '
286  inString[i] = ' ';
287  }
288  else
289  {
290  inString.erase(i, 1); // erase character
291  --i; // step back so next char to check is correct
292  }
293  __COUT_TYPE__(TLVL_DEBUG + 31) << __COUT_HDR__ << inString << std::endl;
294  continue;
295  }
296 
297  __COUT_TYPE__(TLVL_DEBUG + 31) << __COUT_HDR__ << inString << std::endl;
298 
299  // replace special characters
300  if(inString[i] == '\"' || inString[i] == '\'')
301  {
302  inString.insert(i,
303  (inString[i] == '\'')
304  ? "&apos"
305  : "&quot"); // insert HTML name before quotes
306  inString.replace(i + 5, 1, 1, ';'); // replace special character with ;
307  i += 5; // skip to next char to check
308  //__COUT__ << inString << std::endl;
309  }
310  else if(inString[i] == '&')
311  {
312  inString.insert(i, "&amp"); // insert HTML name before special character
313  inString.replace(i + 4, 1, 1, ';'); // replace special character with ;
314  i += 4; // skip to next char to check
315  }
316  else if(inString[i] == '<' || inString[i] == '>')
317  {
318  inString.insert(
319  i,
320  (inString[i] == '<')
321  ? "&lt"
322  : "&gt"); // insert HTML name before special character
323  inString.replace(i + 3, 1, 1, ';'); // replace special character with ;
324  i += 3; // skip to next char to check
325  }
326  else if(inString[i] >= char(161) &&
327  inString[i] <= char(255)) // printable special characters
328  {
329  sprintf(htmlTmp, "&#%3.3d", inString[i]);
330  inString.insert(i, std::string(htmlTmp)); // insert html number sequence
331  inString.replace(i + 5, 1, 1, ';'); // replace special character with ;
332  i += 5; // skip to next char to check
333  }
334 
335  __COUT_TYPE__(TLVL_DEBUG + 30) << __COUT_HDR__ << inString << std::endl;
336 
337  ws = i; // last non white space char
338  }
339  else if(allowWhiteSpace) // keep white space if allowed
340  {
341  if(i - 1 == ws)
342  continue; // dont do anything for first white space
343 
344  // for second white space add 2, and 1 from then
345  if(0 && i - 2 == ws)
346  {
347  inString.insert(i, "&#160;"); // insert html space
348  i += 6; // skip to point at space again
349  }
350  inString.insert(i, "&#160"); // insert html space
351  inString.replace(i + 5, 1, 1, ';'); // replace special character with ;
352  i += 5; // skip to next char to check
353  // ws = i;
354  }
355 
356  __COUT_TYPE__(TLVL_DEBUG + 30)
357  << __COUT_HDR__ << inString.size() << " " << ws << std::endl;
358 
359  // inString.substr(0,ws+1);
360 
361  __COUT_TYPE__(TLVL_DEBUG + 30)
362  << __COUT_HDR__ << inString.size() << " " << inString << std::endl;
363 
364  if(allowWhiteSpace) // keep all white space
365  return inString;
366  // else trim trailing white space
367 
368  if(ws == (unsigned int)-1)
369  return ""; // empty std::string since all white space
370  return inString.substr(0, ws + 1); // trim right white space
371 } // end escapeString()
372 
373 //==============================================================================
378 std::string StringMacros::convertEnvironmentVariables(const std::string& data)
379 {
380  size_t begin = data.find("$");
381  if(begin != std::string::npos)
382  {
383  size_t end;
384  std::string envVariable;
385  std::string converted = data; // make copy to modify
386 
387  while(begin && begin != std::string::npos &&
388  converted[begin - 1] ==
389  '\\') //do not convert environment variables with escaped \$
390  {
391  converted.replace(begin - 1, 1, "");
392  begin = data.find("$", begin + 1); //find next
393  if(begin == std::string::npos)
394  {
395  __COUT_TYPE__(TLVL_DEBUG + 50)
396  << __COUT_HDR__
397  << "Only found escaped $'s that will not be converted: " << converted
398  << __E__;
399  return converted;
400  }
401  }
402 
403  if(data[begin + 1] == '{') // check if using ${NAME} syntax
404  {
405  end = data.find("}", begin + 2);
406  envVariable = data.substr(begin + 2, end - begin - 2);
407  ++end; // replace the closing } too!
408  }
409  else // else using $NAME syntax
410  {
411  // end is first non environment variable character
412  for(end = begin + 1; end < data.size(); ++end)
413  if(!((data[end] >= '0' && data[end] <= '9') ||
414  (data[end] >= 'A' && data[end] <= 'Z') ||
415  (data[end] >= 'a' && data[end] <= 'z') || data[end] == '-' ||
416  data[end] == '_' || data[end] == '.' || data[end] == ':'))
417  break; // found end
418  envVariable = data.substr(begin + 1, end - begin - 1);
419  }
420  __COUTVS__(50, data);
421  __COUTVS__(50, envVariable);
422  char* envResult = __ENV__(envVariable.c_str());
423 
424  if(envResult)
425  {
426  // proceed recursively
428  converted.replace(begin, end - begin, envResult));
429  }
430  else
431  {
432  __SS__ << ("The environmental variable '" + envVariable +
433  "' is not set! Please make sure you set it before continuing!")
434  << std::endl;
435  __SS_THROW__;
436  }
437  }
438  // else no environment variables found in string
439  __COUT_TYPE__(TLVL_DEBUG + 50) << __COUT_HDR__ << "Result: " << data << __E__;
440  return data;
441 } //end convertEnvironmentVariables()
442 
443 //==============================================================================
448 bool StringMacros::isNumber(const std::string& s)
449 {
450  // extract set of potential numbers and operators
451  std::vector<std::string> numbers;
452  std::vector<char> ops;
453 
454  if(!s.size())
455  return false;
456 
458  s,
459  numbers,
460  /*delimiter*/ std::set<char>({'+', '-', '*', '/'}),
461  /*whitespace*/ std::set<char>({' ', '\t', '\n', '\r'}),
462  &ops);
463 
464  //__COUTV__(StringMacros::vectorToString(numbers));
465  //__COUTV__(StringMacros::vectorToString(ops));
466 
467  for(const auto& number : numbers)
468  {
469  if(number.size() == 0)
470  continue; // skip empty numbers
471 
472  if(number.find("0x") == 0) // indicates hex
473  {
474  //__COUT__ << "0x found" << std::endl;
475  for(unsigned int i = 2; i < number.size(); ++i)
476  {
477  if(!((number[i] >= '0' && number[i] <= '9') ||
478  (number[i] >= 'A' && number[i] <= 'F') ||
479  (number[i] >= 'a' && number[i] <= 'f')))
480  {
481  //__COUT__ << "prob " << number[i] << std::endl;
482  return false;
483  }
484  }
485  // return std::regex_match(number.substr(2), std::regex("^[0-90-9a-fA-F]+"));
486  }
487  else if(number[0] == 'b') // indicates binary
488  {
489  //__COUT__ << "b found" << std::endl;
490 
491  for(unsigned int i = 1; i < number.size(); ++i)
492  {
493  if(!((number[i] >= '0' && number[i] <= '1')))
494  {
495  //__COUT__ << "prob " << number[i] << std::endl;
496  return false;
497  }
498  }
499  }
500  else
501  {
502  //__COUT__ << "base 10 " << std::endl;
503  for(unsigned int i = 0; i < number.size(); ++i)
504  if(!((number[i] >= '0' && number[i] <= '9') || number[i] == '.' ||
505  number[i] == '+' || number[i] == '-'))
506  return false;
507  // Note: std::regex crashes in unresolvable ways (says Ryan.. also, stop using
508  // libraries) return std::regex_match(s,
509  // std::regex("^(\\-|\\+)?[0-9]*(\\.[0-9]+)?"));
510  }
511  }
512 
513  //__COUT__ << "yes " << std::endl;
514 
515  // all numbers are numbers
516  return true;
517 } // end isNumber()
518 
519 //==============================================================================
525 std::string StringMacros::getNumberType(const std::string& s)
526 {
527  // extract set of potential numbers and operators
528  std::vector<std::string> numbers;
529  std::vector<char> ops;
530 
531  bool hasDecimal = false;
532 
534  s,
535  numbers,
536  /*delimiter*/ std::set<char>({'+', '-', '*', '/'}),
537  /*whitespace*/ std::set<char>({' ', '\t', '\n', '\r'}),
538  &ops);
539 
540  //__COUTV__(StringMacros::vectorToString(numbers));
541  //__COUTV__(StringMacros::vectorToString(ops));
542 
543  for(const auto& number : numbers)
544  {
545  if(number.size() == 0)
546  continue; // skip empty numbers
547 
548  if(number.find("0x") == 0) // indicates hex
549  {
550  //__COUT__ << "0x found" << std::endl;
551  for(unsigned int i = 2; i < number.size(); ++i)
552  {
553  if(!((number[i] >= '0' && number[i] <= '9') ||
554  (number[i] >= 'A' && number[i] <= 'F') ||
555  (number[i] >= 'a' && number[i] <= 'f')))
556  {
557  //__COUT__ << "prob " << number[i] << std::endl;
558  return "nan";
559  }
560  }
561  // return std::regex_match(number.substr(2), std::regex("^[0-90-9a-fA-F]+"));
562  }
563  else if(number[0] == 'b') // indicates binary
564  {
565  //__COUT__ << "b found" << std::endl;
566 
567  for(unsigned int i = 1; i < number.size(); ++i)
568  {
569  if(!((number[i] >= '0' && number[i] <= '1')))
570  {
571  //__COUT__ << "prob " << number[i] << std::endl;
572  return "nan";
573  }
574  }
575  }
576  else
577  {
578  //__COUT__ << "base 10 " << std::endl;
579  for(unsigned int i = 0; i < number.size(); ++i)
580  if(!((number[i] >= '0' && number[i] <= '9') || number[i] == '.' ||
581  number[i] == '+' || number[i] == '-'))
582  return "nan";
583  else if(number[i] == '.')
584  hasDecimal = true;
585  // Note: std::regex crashes in unresolvable ways (says Ryan.. also, stop using
586  // libraries) return std::regex_match(s,
587  // std::regex("^(\\-|\\+)?[0-9]*(\\.[0-9]+)?"));
588  }
589  }
590 
591  //__COUT__ << "yes " << std::endl;
592 
593  // all numbers are numbers
594  if(hasDecimal)
595  return "double";
596  return "unsigned long long";
597 } // end getNumberType()
598 
599 //==============================================================================
600 // static template function
605 bool StringMacros::getNumber(const std::string& s, bool& retValue)
606 {
607  if(s.size() < 1)
608  {
609  __COUT_ERR__ << "Invalid empty bool string " << s << __E__;
610  return false;
611  }
612 
613  // check true case
614  if(s.find("1") != std::string::npos || s == "true" || s == "True" || s == "TRUE")
615  {
616  retValue = true;
617  return true;
618  }
619 
620  // check false case
621  if(s.find("0") != std::string::npos || s == "false" || s == "False" || s == "FALSE")
622  {
623  retValue = false;
624  return true;
625  }
626 
627  __COUT_ERR__ << "Invalid bool string " << s << __E__;
628  return false;
629 
630 } // end static getNumber<bool>
631 
632 //==============================================================================
636 std::string StringMacros::getTimestampString(const std::string& linuxTimeInSeconds)
637 {
638  time_t timestamp(strtol(linuxTimeInSeconds.c_str(), 0, 10));
639  return getTimestampString(timestamp);
640 } // end getTimestampString()
641 
642 //==============================================================================
646 std::string StringMacros::getTimestampString(const time_t linuxTimeInSeconds)
647 {
648  std::string retValue(30, '\0'); // known fixed size: Thu Aug 23 14:55:02 2001 CST
649 
650  struct tm tmstruct;
651  ::localtime_r(&linuxTimeInSeconds, &tmstruct);
652  ::strftime(&retValue[0], 30, "%c %Z", &tmstruct);
653  retValue.resize(strlen(retValue.c_str()));
654 
655  return retValue;
656 } // end getTimestampString()
657 
658 //==============================================================================
662 {
663  //e.g., used by CoreSupervisorBase::getStatusProgressDetail(void)
664 
665  std::stringstream ss;
666  int days = t / 60 / 60 / 24;
667  if(days > 0)
668  {
669  ss << days << " day" << (days > 1 ? "s" : "") << ", ";
670  t -= days * 60 * 60 * 24;
671  }
672 
673  //HH:MM:SS
674  ss << std::setw(2) << std::setfill('0') << (t / 60 / 60) << ":" << std::setw(2)
675  << std::setfill('0') << ((t % (60 * 60)) / 60) << ":" << std::setw(2)
676  << std::setfill('0') << (t % 60);
677  return ss.str();
678 } //end getTimeDurationString()
679 
680 //==============================================================================
684  const std::string& value, bool doConvertEnvironmentVariables)
685 try
686 {
687  return doConvertEnvironmentVariables
689  : value;
690 }
691 catch(const std::runtime_error& e)
692 {
693  __SS__ << "Failed to validate value for default string data type. " << __E__
694  << e.what() << __E__;
695  __SS_THROW__;
696 }
697 
698 //==============================================================================
702 void StringMacros::getSetFromString(const std::string& inputString,
703  std::set<std::string>& setToReturn,
704  const std::set<char>& delimiter,
705  const std::set<char>& whitespace)
706 {
707  unsigned int i = 0;
708  unsigned int j = 0;
709 
710  // go through the full string extracting elements
711  // add each found element to set
712  for(; j < inputString.size(); ++j)
713  if((whitespace.find(inputString[j]) !=
714  whitespace.end() || // ignore leading white space or delimiter
715  delimiter.find(inputString[j]) != delimiter.end()) &&
716  i == j)
717  ++i;
718  else if((whitespace.find(inputString[j]) !=
719  whitespace
720  .end() || // trailing white space or delimiter indicates end
721  delimiter.find(inputString[j]) != delimiter.end()) &&
722  i != j) // assume end of element
723  {
724  //__COUT__ << "Set element found: " <<
725  // inputString.substr(i,j-i) << std::endl;
726 
727  setToReturn.emplace(inputString.substr(i, j - i));
728 
729  // setup i and j for next find
730  i = j + 1;
731  }
732 
733  if(i != j) // last element check (for case when no concluding ' ' or delimiter)
734  setToReturn.emplace(inputString.substr(i, j - i));
735 } // end getSetFromString()
736 
737 //==============================================================================
749 void StringMacros::getVectorFromString(const std::string& inputString,
750  std::vector<std::string>& listToReturn,
751  const std::set<char>& delimiter,
752  const std::set<char>& whitespace,
753  std::vector<char>* listOfDelimiters,
754  bool decodeURIComponents)
755 {
756  unsigned int i = 0;
757  unsigned int j = 0;
758  unsigned int c = 0;
759  std::set<char>::iterator delimeterSearchIt;
760  char lastDelimiter = 0;
761  bool isDelimiter;
762  // bool foundLeadingDelimiter = false;
763 
764  //__COUT__ << inputString << __E__;
765  //__COUTV__(inputString.length());
766 
767  // go through the full string extracting elements
768  // add each found element to set
769  for(; c < inputString.size(); ++c)
770  {
771  //__COUT__ << (char)inputString[c] << __E__;
772 
773  delimeterSearchIt = delimiter.find(inputString[c]);
774  isDelimiter = delimeterSearchIt != delimiter.end();
775 
776  //__COUT__ << (char)inputString[c] << " " << isDelimiter <<
777  //__E__;//char)lastDelimiter << __E__;
778 
779  if(whitespace.find(inputString[c]) !=
780  whitespace.end() // ignore leading white space
781  && i == j)
782  {
783  ++i;
784  ++j;
785  // if(isDelimiter)
786  // foundLeadingDelimiter = true;
787  }
788  else if(whitespace.find(inputString[c]) != whitespace.end() &&
789  i != j) // trailing white space, assume possible end of element
790  {
791  // do not change j or i
792  }
793  else if(isDelimiter) // delimiter is end of element
794  {
795  //__COUT__ << "Set element found: " <<
796  // inputString.substr(i,j-i) << std::endl;
797 
798  if(listOfDelimiters && listToReturn.size()) // || foundLeadingDelimiter))
799  // //accept leading delimiter
800  // (especially for case of
801  // leading negative in math
802  // parsing)
803  {
804  //__COUTV__(lastDelimiter);
805  listOfDelimiters->push_back(lastDelimiter);
806  }
807  listToReturn.push_back(decodeURIComponents ? StringMacros::decodeURIComponent(
808  inputString.substr(i, j - i))
809  : inputString.substr(i, j - i));
810 
811  // setup i and j for next find
812  i = c + 1;
813  j = c + 1;
814  }
815  else // part of element, so move j, not i
816  j = c + 1;
817 
818  if(isDelimiter)
819  lastDelimiter = *delimeterSearchIt;
820  //__COUTV__(lastDelimiter);
821  }
822 
823  if(1) // i != j) //last element check (for case when no concluding ' ' or delimiter)
824  {
825  //__COUT__ << "Last element found: " <<
826  // inputString.substr(i,j-i) << std::endl;
827 
828  if(listOfDelimiters && listToReturn.size()) // || foundLeadingDelimiter))
829  // //accept leading delimiter
830  // (especially for case of leading
831  // negative in math parsing)
832  {
833  //__COUTV__(lastDelimiter);
834  listOfDelimiters->push_back(lastDelimiter);
835  }
836  listToReturn.push_back(decodeURIComponents ? StringMacros::decodeURIComponent(
837  inputString.substr(i, j - i))
838  : inputString.substr(i, j - i));
839  }
840 
841  // assert that there is one less delimiter than values
842  if(listOfDelimiters && listToReturn.size() - 1 != listOfDelimiters->size() &&
843  listToReturn.size() != listOfDelimiters->size())
844  {
845  __SS__ << "There is a mismatch in delimiters to entries (should be equal or one "
846  "less delimiter): "
847  << listOfDelimiters->size() << " vs " << listToReturn.size() << __E__
848  << "Entries: " << StringMacros::vectorToString(listToReturn) << __E__
849  << "Delimiters: " << StringMacros::vectorToString(*listOfDelimiters)
850  << __E__;
851  __SS_THROW__;
852  }
853 
854 } // end getVectorFromString()
855 
856 //==============================================================================
868 std::vector<std::string> StringMacros::getVectorFromString(
869  const std::string& inputString,
870  const std::set<char>& delimiter,
871  const std::set<char>& whitespace,
872  std::vector<char>* listOfDelimiters,
873  bool decodeURIComponents)
874 {
875  std::vector<std::string> listToReturn;
876 
878  listToReturn,
879  delimiter,
880  whitespace,
881  listOfDelimiters,
882  decodeURIComponents);
883  return listToReturn;
884 } // end getVectorFromString()
885 
886 //==============================================================================
890 void StringMacros::getMapFromString(const std::string& inputString,
891  std::map<std::string, std::string>& mapToReturn,
892  const std::set<char>& pairPairDelimiter,
893  const std::set<char>& nameValueDelimiter,
894  const std::set<char>& whitespace)
895 try
896 {
897  unsigned int i = 0;
898  unsigned int j = 0;
899  std::string name;
900  bool needValue = false;
901 
902  // go through the full string extracting map pairs
903  // add each found pair to map
904  for(; j < inputString.size(); ++j)
905  if(!needValue) // finding name
906  {
907  if((whitespace.find(inputString[j]) !=
908  whitespace.end() || // ignore leading white space or delimiter
909  pairPairDelimiter.find(inputString[j]) != pairPairDelimiter.end()) &&
910  i == j)
911  ++i;
912  else if((whitespace.find(inputString[j]) !=
913  whitespace
914  .end() || // trailing white space or delimiter indicates end
915  nameValueDelimiter.find(inputString[j]) !=
916  nameValueDelimiter.end()) &&
917  i != j) // assume end of map name
918  {
919  //__COUT__ << "Map name found: " <<
920  // inputString.substr(i,j-i) << std::endl;
921 
922  name = inputString.substr(i, j - i); // save name, for concluding pair
923 
924  needValue = true; // need value now
925 
926  // setup i and j for next find
927  i = j + 1;
928  }
929  }
930  else // finding value
931  {
932  if((whitespace.find(inputString[j]) !=
933  whitespace.end() || // ignore leading white space or delimiter
934  nameValueDelimiter.find(inputString[j]) != nameValueDelimiter.end()) &&
935  i == j)
936  ++i;
937  else if(whitespace.find(inputString[j]) !=
938  whitespace
939  .end() || // trailing white space or delimiter indicates end
940  pairPairDelimiter.find(inputString[j]) !=
941  pairPairDelimiter.end()) // &&
942  // i != j) // assume end of value name
943  {
944  //__COUT__ << "Map value found: " <<
945  // inputString.substr(i,j-i) << std::endl;
946 
947  auto /*pair<it,success>*/ emplaceReturn =
948  mapToReturn.emplace(std::pair<std::string, std::string>(
949  name,
951  inputString.substr(i, j - i)) // value
952  ));
953 
954  if(!emplaceReturn.second)
955  {
956  __COUT__ << "Ignoring repetitive value ('"
957  << inputString.substr(i, j - i)
958  << "') and keeping current value ('"
959  << emplaceReturn.first->second << "'). " << __E__;
960  }
961 
962  needValue = false; // need name now
963 
964  // setup i and j for next find
965  i = j + 1;
966  }
967  }
968 
969  if(i != j) // last value (for case when no concluding ' ' or delimiter)
970  {
971  auto /*pair<it,success>*/ emplaceReturn =
972  mapToReturn.emplace(std::pair<std::string, std::string>(
973  name,
975  inputString.substr(i, j - i)) // value
976  ));
977 
978  if(!emplaceReturn.second)
979  {
980  __COUT__ << "Ignoring repetitive value ('" << inputString.substr(i, j - i)
981  << "') and keeping current value ('" << emplaceReturn.first->second
982  << "'). " << __E__;
983  }
984  }
985 } // end getMapFromString()
986 catch(const std::runtime_error& e)
987 {
988  __SS__ << "Error while extracting a map from the string '" << inputString
989  << "'... is it a valid map?" << __E__ << e.what() << __E__;
990  __SS_THROW__;
991 }
992 
993 //==============================================================================
995 std::string StringMacros::mapToString(const std::map<std::string, uint8_t>& mapToReturn,
996  const std::string& primaryDelimeter,
997  const std::string& secondaryDelimeter)
998 {
999  std::stringstream ss;
1000  bool first = true;
1001  for(auto& mapPair : mapToReturn)
1002  {
1003  if(first)
1004  first = false;
1005  else
1006  ss << primaryDelimeter;
1007  ss << mapPair.first << secondaryDelimeter << (unsigned int)mapPair.second;
1008  }
1009  return ss.str();
1010 } // end mapToString()
1011 
1012 //==============================================================================
1014 std::string StringMacros::setToString(const std::set<uint8_t>& setToReturn,
1015  const std::string& delimeter)
1016 {
1017  std::stringstream ss;
1018  bool first = true;
1019  for(auto& setValue : setToReturn)
1020  {
1021  if(first)
1022  first = false;
1023  else
1024  ss << delimeter;
1025  ss << (unsigned int)setValue;
1026  }
1027  return ss.str();
1028 } // end setToString()
1029 
1030 //==============================================================================
1032 std::string StringMacros::vectorToString(const std::vector<uint8_t>& setToReturn,
1033  const std::string& delimeter)
1034 {
1035  std::stringstream ss;
1036  bool first = true;
1037  if(delimeter == "\n")
1038  ss << "\n"; //add initial new line if new line delimiting
1039  for(auto& setValue : setToReturn)
1040  {
1041  if(first)
1042  first = false;
1043  else
1044  ss << delimeter;
1045  ss << (unsigned int)setValue;
1046  }
1047  return ss.str();
1048 } // end vectorToString()
1049 
1050 //==============================================================================
1060 bool StringMacros::extractCommonChunks(const std::vector<std::string>& haystack,
1061  std::vector<std::string>& commonChunksToReturn,
1062  std::vector<std::string>& wildcardStringsToReturn,
1063  unsigned int& fixedWildcardLength)
1064 {
1065  fixedWildcardLength = 0; // default
1066 
1067  // Steps:
1068  // - find start and end common chunks first in haystack strings
1069  // - use start and end to determine if there is more than one *
1070  // - decide if fixed width was specified (based on prepended 0s to numbers)
1071  // - search for more instances of * value
1072  //
1073  //
1074  // // Note: lambda recursive function to find chunks
1075  // std::function<void(
1076  // const std::vector<std::string>&,
1077  // const std::string&,
1078  // const unsigned int, const int)> localRecurse =
1079  // [&specialFolders, &specialMapTypes, &retMap, &localRecurse](
1080  // const std::vector<std::string>& haystack,
1081  // const std::string& offsetPath,
1082  // const unsigned int depth,
1083  // const int specialIndex)
1084  // {
1085  //
1086  // //__COUTV__(path);
1087  // //__COUTV__(depth);
1088  // }
1089  std::pair<unsigned int /*lo*/, unsigned int /*hi*/> wildcardBounds(
1090  std::make_pair(-1, 0)); // initialize to illegal wildcard
1091 
1092  // look for starting matching segment
1093  for(unsigned int n = 1; n < haystack.size(); ++n)
1094  for(unsigned int i = 0, j = 0;
1095  i < haystack[0].length() && j < haystack[n].length();
1096  ++i, ++j)
1097  {
1098  if(i < wildcardBounds.first)
1099  {
1100  if(haystack[0][i] != haystack[n][j])
1101  {
1102  wildcardBounds.first = i; // found lo side of wildcard
1103  break;
1104  }
1105  }
1106  else
1107  break;
1108  }
1109  // __COUT__ << "Low side = " << wildcardBounds.first << " " << haystack[0].substr(0, wildcardBounds.first) << __E__;
1110 
1111  // look for end matching segment
1112  for(unsigned int n = 1; n < haystack.size(); ++n)
1113  for(int i = haystack[0].length() - 1, j = haystack[n].length() - 1;
1114  i >= (int)wildcardBounds.first && j >= (int)wildcardBounds.first;
1115  --i, --j)
1116  {
1117  if(i > (int)wildcardBounds.second) // looking for hi side
1118  {
1119  if(haystack[0][i] != haystack[n][j])
1120  {
1121  wildcardBounds.second = i + 1; // found hi side of wildcard
1122  break;
1123  }
1124  }
1125  else
1126  break;
1127  }
1128 
1129  // __COUT__ << "High side = " << wildcardBounds.second << " " << haystack[0].substr(wildcardBounds.second) << __E__;
1130 
1131  // add first common chunk
1132  commonChunksToReturn.push_back(haystack[0].substr(0, wildcardBounds.first));
1133 
1134  if(wildcardBounds.first != (unsigned int)-1) // potentially more chunks if not end
1135  {
1136  // - use start and end to determine if there is more than one *
1137  for(int i = (wildcardBounds.first + wildcardBounds.second) / 2 + 1;
1138  i < (int)wildcardBounds.second;
1139  ++i)
1140  if(haystack[0][wildcardBounds.first] == haystack[0][i] &&
1141  haystack[0].substr(wildcardBounds.first, wildcardBounds.second - i) ==
1142  haystack[0].substr(i, wildcardBounds.second - i))
1143  {
1144  std::string multiWildcardString =
1145  haystack[0].substr(i, wildcardBounds.second - i);
1146  __COUT__ << "Multi-wildcard found: " << multiWildcardString << __E__;
1147 
1148  std::vector<unsigned int /*lo index*/> wildCardInstances;
1149  // add front one now, and back one later
1150  wildCardInstances.push_back(wildcardBounds.first);
1151 
1152  unsigned int offset =
1153  wildCardInstances[0] + multiWildcardString.size() + 1;
1154  std::string middleString = haystack[0].substr(offset, (i - 1) - offset);
1155  __COUTV__(middleString);
1156 
1157  // search for more wildcard instances in new common area
1158  size_t k;
1159  while((k = middleString.find(multiWildcardString)) != std::string::npos)
1160  {
1161  __COUT__ << "Multi-wildcard found at " << k << __E__;
1162 
1163  wildCardInstances.push_back(offset + k);
1164 
1165  middleString =
1166  middleString.substr(k + multiWildcardString.size() + 1);
1167  offset += k + multiWildcardString.size() + 1;
1168  __COUTV__(middleString);
1169  }
1170 
1171  // add back one last
1172  wildCardInstances.push_back(i);
1173 
1174  for(unsigned int w = 0; w < wildCardInstances.size() - 1; ++w)
1175  {
1176  commonChunksToReturn.push_back(haystack[0].substr(
1177  wildCardInstances[w] + wildCardInstances.size(),
1178  wildCardInstances[w + 1] -
1179  (wildCardInstances[w] + wildCardInstances.size())));
1180  }
1181  }
1182 
1183  // check if all common chunks end in 0 to add fixed length
1184 
1185  for(unsigned int i = 0; i < commonChunksToReturn[0].size(); ++i)
1186  if(commonChunksToReturn[0][commonChunksToReturn[0].size() - 1 - i] == '0')
1187  ++fixedWildcardLength;
1188  else
1189  break;
1190 
1191  // bool allHave0 = true;
1192  for(unsigned int c = 0; c < commonChunksToReturn.size(); ++c)
1193  {
1194  unsigned int cnt = 0;
1195  for(unsigned int i = 0; i < commonChunksToReturn[c].size(); ++i)
1196  if(commonChunksToReturn[c][commonChunksToReturn[c].size() - 1 - i] == '0')
1197  ++cnt;
1198  else
1199  break;
1200 
1201  if(fixedWildcardLength < cnt)
1202  fixedWildcardLength = cnt;
1203  else if(fixedWildcardLength > cnt)
1204  {
1205  __SS__ << "Invalid fixed length found, please simplify indexing between "
1206  "these common chunks: "
1207  << StringMacros::vectorToString(commonChunksToReturn) << __E__;
1208  __SS_THROW__;
1209  }
1210  }
1211  // __COUTV__(fixedWildcardLength);
1212 
1213  if(fixedWildcardLength) // take trailing 0s out of common chunks
1214  for(unsigned int c = 0; c < commonChunksToReturn.size(); ++c)
1215  commonChunksToReturn[c] = commonChunksToReturn[c].substr(
1216  0, commonChunksToReturn[c].size() - fixedWildcardLength);
1217 
1218  // add last common chunk
1219  commonChunksToReturn.push_back(haystack[0].substr(wildcardBounds.second));
1220  } // end handling more chunks
1221 
1222  // now determine wildcard strings
1223  size_t k;
1224  unsigned int i;
1225  unsigned int ioff = fixedWildcardLength;
1226  bool wildcardsNeeded = false;
1227 
1228  for(unsigned int n = 0; n < haystack.size(); ++n)
1229  {
1230  std::string wildcard = "";
1231  k = 0;
1232  i = ioff + commonChunksToReturn[0].size();
1233 
1234  if(commonChunksToReturn.size() == 1) // just get end
1235  wildcard = haystack[n].substr(i);
1236  else
1237  for(unsigned int c = 1; c < commonChunksToReturn.size(); ++c)
1238  {
1239  if(c == commonChunksToReturn.size() - 1) // for last, do reverse find
1240  k = haystack[n].rfind(commonChunksToReturn[c]);
1241  else
1242  k = haystack[n].find(commonChunksToReturn[c], i + 1);
1243 
1244  if(wildcard == "")
1245  {
1246  // set wildcard for first time
1247  // __COUTV__(i);
1248  // __COUTV__(k);
1249  // __COUTV__(k - i);
1250 
1251  wildcard = haystack[n].substr(i, k - i);
1252  if(fixedWildcardLength && n == 0)
1253  fixedWildcardLength += wildcard.size();
1254 
1255  // __COUT__ << "name[" << n << "] = " << wildcard << " fixed @ " << fixedWildcardLength << __E__;
1256 
1257  break;
1258  }
1259  else if(0 /*skip validation in favor of speed*/ &&
1260  wildcard != haystack[n].substr(i, k - i))
1261  {
1262  __SS__ << "Invalid wildcard! for name[" << n << "] = " << haystack[n]
1263  << " - the extraction algorithm is confused, please simplify "
1264  "your naming convention."
1265  << __E__;
1266  __SS_THROW__;
1267  }
1268 
1269  i = k;
1270  } // end commonChunksToReturn loop
1271 
1272  if(wildcard.size())
1273  wildcardsNeeded = true;
1274  wildcardStringsToReturn.push_back(wildcard);
1275 
1276  } // end name loop
1277 
1278  // __COUTV__(StringMacros::vectorToString(commonChunksToReturn));
1279  // __COUTV__(StringMacros::vectorToString(wildcardStringsToReturn));
1280 
1281  if(wildcardStringsToReturn.size() != haystack.size())
1282  {
1283  __SS__ << "There was a problem during common chunk extraction!" << __E__;
1284  __SS_THROW__;
1285  }
1286 
1287  return wildcardsNeeded;
1288 
1289 } // end extractCommonChunks()
1290 
1291 //==============================================================================
1296  const std::string& rhs) const
1297 {
1298  //__COUTV__(lhs);
1299  //__COUTV__(rhs);
1300  // return true if lhs < rhs (lhs will be ordered first)
1301 
1302  for(unsigned int i = 0; i < lhs.size() && i < rhs.size(); ++i)
1303  {
1304  //__COUT__ << i << "\t" << lhs[i] << "\t" << rhs[i] << __E__;
1305  if((lhs[i] >= 'A' && lhs[i] <= 'Z' && rhs[i] >= 'A' && rhs[i] <= 'Z') ||
1306  (lhs[i] >= 'a' && lhs[i] <= 'z' && rhs[i] >= 'a' && rhs[i] <= 'z'))
1307  { // same case
1308  if(lhs[i] == rhs[i])
1309  continue;
1310  return (lhs[i] < rhs[i]);
1311  //{ retVal = false; break;} //return false;
1312  }
1313  else if(lhs[i] >= 'A' && lhs[i] <= 'Z') // rhs lower case
1314  {
1315  if(lhs[i] + 32 == rhs[i]) // lower case is higher by 32
1316  return false; // in tie return lower case first
1317  return (lhs[i] + 32 < rhs[i]);
1318  }
1319  else if(rhs[i] >= 'A' && rhs[i] <= 'Z')
1320  {
1321  if(lhs[i] == rhs[i] + 32) // lower case is higher by 32
1322  return true; // in tie return lower case first
1323  return (lhs[i] < rhs[i] + 32);
1324  }
1325  else // not letters case (should only be for numbers)
1326  {
1327  if(lhs[i] == rhs[i])
1328  continue;
1329  return (lhs[i] < rhs[i]);
1330  }
1331  } // end case insensitive compare loop
1332 
1333  // lhs and rhs are equivalent to character[i], so return false if rhs.size() was the limit reached
1334  return lhs.size() < rhs.size();
1335 } // end IgnoreCaseCompareStruct::operator() comparison handler
1336 
1337 //==============================================================================
1340 std::string StringMacros::exec(const char* cmd)
1341 {
1342  __COUTV__(cmd);
1343 
1344  std::array<char, 128> buffer;
1345  std::string result;
1346  std::shared_ptr<FILE> pipe(popen(cmd, "r"), pclose);
1347  if(!pipe)
1348  __THROW__("popen() failed!");
1349  while(!feof(pipe.get()))
1350  {
1351  if(fgets(buffer.data(), 128, pipe.get()) != nullptr)
1352  result += buffer.data();
1353  }
1354  //__COUTV__(result);
1355  return result;
1356 } // end exec()
1357 
1358 //==============================================================================
1362 #include <cxxabi.h> //for abi::__cxa_demangle
1363 #include <execinfo.h> //for back trace of stack
1364 // #include "TUnixSystem.h"
1366 {
1367  __SS__ << "ots::stackTrace:\n";
1368 
1369  void* array[10];
1370  size_t size;
1371 
1372  // get void*'s for all entries on the stack
1373  size = backtrace(array, 10);
1374  // backtrace_symbols_fd(array, size, STDERR_FILENO);
1375 
1376  // https://stackoverflow.com/questions/77005/how-to-automatically-generate-a-stacktrace-when-my-program-crashes
1377  char** messages = backtrace_symbols(array, size);
1378 
1379  // skip first stack frame (points here)
1380  // char syscom[256];
1381  for(unsigned int i = 1; i < size && messages != NULL; ++i)
1382  {
1383  // mangled name needs to be converted to get nice name and line number
1384  // line number not working... FIXME
1385 
1386  // sprintf(syscom,"addr2line %p -e %s",
1387  // array[i],
1388  // messages[i]); //last parameter is the name of this app
1389  // ss << StringMacros::exec(syscom) << __E__;
1390  // system(syscom);
1391 
1392  // continue;
1393 
1394  char *mangled_name = 0, *offset_begin = 0, *offset_end = 0;
1395 
1396  // find parentheses and +address offset surrounding mangled name
1397  for(char* p = messages[i]; *p; ++p)
1398  {
1399  if(*p == '(')
1400  {
1401  mangled_name = p;
1402  }
1403  else if(*p == '+')
1404  {
1405  offset_begin = p;
1406  }
1407  else if(*p == ')')
1408  {
1409  offset_end = p;
1410  break;
1411  }
1412  }
1413 
1414  // if the line could be processed, attempt to demangle the symbol
1415  if(mangled_name && offset_begin && offset_end && mangled_name < offset_begin)
1416  {
1417  *mangled_name++ = '\0';
1418  *offset_begin++ = '\0';
1419  *offset_end++ = '\0';
1420 
1421  int status;
1422  char* real_name = abi::__cxa_demangle(mangled_name, 0, 0, &status);
1423 
1424  // if demangling is successful, output the demangled function name
1425  if(status == 0)
1426  {
1427  ss << "[" << i << "] " << messages[i] << " : " << real_name << "+"
1428  << offset_begin << offset_end << std::endl;
1429  }
1430  // otherwise, output the mangled function name
1431  else
1432  {
1433  ss << "[" << i << "] " << messages[i] << " : " << mangled_name << "+"
1434  << offset_begin << offset_end << std::endl;
1435  }
1436  free(real_name);
1437  }
1438  // otherwise, print the whole line
1439  else
1440  {
1441  ss << "[" << i << "] " << messages[i] << std::endl;
1442  }
1443  }
1444  ss << std::endl;
1445 
1446  free(messages);
1447 
1448  // call ROOT's stack trace to get line numbers of ALL threads
1449  // gSystem->StackTrace();
1450 
1451  return ss.str();
1452 } // end stackTrace
1453 
1454 //==============================================================================
1460  const std::string& location,
1461  const unsigned int& line)
1462 {
1463  char* environmentVariablePtr = getenv(name);
1464  if(!environmentVariablePtr)
1465  {
1466  __SS__ << "Environment variable '$" << name << "' not defined at " << location
1467  << ":" << line << __E__;
1468  ss << "\n\n" << StringMacros::stackTrace() << __E__;
1469  __SS_ONLY_THROW__;
1470  }
1471  return environmentVariablePtr;
1472 } // end otsGetEnvironmentVarable()
1473 
1474 //=========================================================================
1477 std::string StringMacros::extractXmlField(const std::string& xml,
1478  const std::string& field,
1479  uint32_t occurrence,
1480  size_t after,
1481  size_t* returnFindPos /* = nullptr */,
1482  const std::string& valueField /* = "value=" */,
1483  const std::string& quoteType /* = "'" */)
1484 {
1485  if(returnFindPos)
1486  *returnFindPos = std::string::npos;
1487 
1488  __COUTVS__(41, xml);
1489 
1490  size_t lo, findpos = after, hi;
1491  for(uint32_t i = 0; i <= occurrence; ++i)
1492  {
1493  bool anyFound = false;
1494  while((findpos =
1495  xml.find("<" + field, //allow for immediate closing of xml tag with >
1496  findpos)) != std::string::npos &&
1497  findpos + 1 + field.size() < xml.size())
1498  {
1499  __COUT_TYPE__(TLVL_DEBUG + 40)
1500  << __COUT_HDR__ << "find: ---- '<" << field << " findpos=" << findpos
1501  << "findpos " << findpos << " " << xml[findpos] << " "
1502  << xml[findpos + 1 + field.size()] << " "
1503  << (int)xml[findpos + 1 + field.size()] << __E__;
1504 
1505  findpos +=
1506  1 +
1507  field
1508  .size(); //to point to closing white space and advance for next forward search
1509 
1510  //verify white space after the field
1511  if((quoteType == ">" && xml[findpos] == '>') || xml[findpos] == ' ' ||
1512  xml[findpos] == '\n' || xml[findpos] == '\t')
1513  {
1514  anyFound = true; //flag
1515  break;
1516  }
1517  }
1518 
1519  if(!anyFound)
1520  {
1521  __COUT_TYPE__(TLVL_DEBUG + 40)
1522  << __COUT_HDR__ << "Field '" << field << "' not found" << __E__;
1523  return "";
1524  }
1525  }
1526 
1527  lo = xml.find(valueField + quoteType, findpos) + valueField.size() + quoteType.size();
1528 
1529  if(TTEST(40) && quoteType.size())
1530  {
1531  __COUT_TYPE__(TLVL_DEBUG + 40)
1532  << __COUT_HDR__ << "Neighbors of field '" << field << "' and value '"
1533  << valueField << "' w/quote = " << quoteType << __E__;
1534  for(size_t i = lo - valueField.size(); i < lo + 10 && i < xml.size(); ++i)
1535  __COUT_TYPE__(TLVL_DEBUG + 40)
1536  << __COUT_HDR__ << "xml[" << i << "] " << xml[i] << " vs " << quoteType
1537  << " ? " << (int)xml[i] << " vs " << (int)quoteType[0] << __E__;
1538  }
1539 
1540  if((hi = xml.find(
1541  quoteType == ">" ? "<" : quoteType, //if xml tag, change closing direction
1542  lo)) == std::string::npos)
1543  {
1544  __COUT_TYPE__(TLVL_DEBUG + 40)
1545  << __COUT_HDR__ << "Value closing not found" << __E__;
1546  return "";
1547  }
1548 
1549  if(returnFindPos)
1550  *returnFindPos = findpos - (1 + field.size()); //remove offset that was added
1551 
1552  __COUT_TYPE__(TLVL_DEBUG + 40)
1553  << __COUT_HDR__ << "after: " << after << ", findpos: " << findpos
1554  << ", hi/lo: " << hi << "/" << lo << ", size: " << xml.size() << __E__;
1555  __COUTVS__(40, xml.substr(lo, hi - lo));
1556  return xml.substr(lo, hi - lo);
1557 } //end extractXmlField()
1558 
1559 //=========================================================================
1562 std::string StringMacros::rextractXmlField(const std::string& xml,
1563  const std::string& field,
1564  uint32_t occurrence,
1565  size_t before,
1566  size_t* returnFindPos /* = nullptr */,
1567  const std::string& valueField /* = "value=" */,
1568  const std::string& quoteType /* = "'" */)
1569 {
1570  if(returnFindPos)
1571  *returnFindPos = std::string::npos;
1572 
1573  __COUTVS__(41, xml);
1574 
1575  size_t lo = 0, hi, findpos = before;
1576  for(uint32_t i = 0; i <= occurrence; ++i)
1577  {
1578  bool anyFound = false;
1579  while((findpos =
1580  xml.rfind("<" + field, //allow for immediate closing of xml tag with >
1581  findpos)) != std::string::npos &&
1582  findpos + 1 + field.size() < xml.size())
1583  {
1584  __COUT_TYPE__(TLVL_DEBUG + 40)
1585  << __COUT_HDR__ << "rfind: ---- '<" << field << " findpos=" << findpos
1586  << " " << xml[findpos] << " " << xml[findpos + 1 + field.size()] << " "
1587  << (int)xml[findpos + 1 + field.size()] << __E__;
1588 
1589  findpos += 1 + field.size();
1590 
1591  //verify white space after the field
1592  if((quoteType == ">" && xml[findpos] == '>') || xml[findpos] == ' ' ||
1593  xml[findpos] == '\n' || xml[findpos] == '\t')
1594  {
1595  anyFound = true; //flag
1596  break;
1597  }
1598  else
1599  findpos -= 1 + field.size() + 1; //for next reverse search
1600  }
1601  if(!anyFound)
1602  {
1603  __COUT_TYPE__(TLVL_DEBUG + 40)
1604  << __COUT_HDR__ << "Field '" << field << "' not found" << __E__;
1605  return "";
1606  }
1607  }
1608 
1609  lo = xml.find(valueField + quoteType, findpos) + valueField.size() + quoteType.size();
1610 
1611  if(TTEST(40) && quoteType.size())
1612  {
1613  __COUT_TYPE__(TLVL_DEBUG + 40) << __COUT_HDR__ << "Neighbors?" << __E__;
1614  for(size_t i = findpos; i < lo + 10 && i < xml.size(); ++i)
1615  __COUT_TYPE__(TLVL_DEBUG + 40)
1616  << __COUT_HDR__ << "xml[" << i << "] " << xml[i] << " vs " << quoteType
1617  << " ? " << (int)xml[i] << " vs " << (int)quoteType[0] << __E__;
1618  }
1619 
1620  if((hi = xml.find(
1621  quoteType == ">" ? "<" : quoteType, //if xml tag, change closing direction
1622  lo)) == std::string::npos)
1623  {
1624  __COUT_TYPE__(TLVL_DEBUG + 40)
1625  << __COUT_HDR__ << "Value closing not found" << __E__;
1626  return "";
1627  }
1628 
1629  if(returnFindPos)
1630  *returnFindPos =
1631  findpos - (1 + field.size()); //return found position of "< + field"
1632 
1633  __COUT_TYPE__(TLVL_DEBUG + 40)
1634  << __COUT_HDR__ << "before: " << before << ", findpos: " << findpos
1635  << ", hi/lo: " << hi << "/" << lo << ", size: " << xml.size() << __E__;
1636  __COUTVS__(40, xml.substr(lo, hi - lo));
1637  return xml.substr(lo, hi - lo);
1638 } //end rextractXmlField()
1639 
1640 #ifdef __GNUG__
1641 #include <cxxabi.h>
1642 #include <cstdlib>
1643 #include <memory>
1644 
1645 //==============================================================================
1647 std::string StringMacros::demangleTypeName(const char* name)
1648 {
1649  int status = -4; // some arbitrary value to eliminate the compiler warning
1650 
1651  // enable c++11 by passing the flag -std=c++11 to g++
1652  std::unique_ptr<char, void (*)(void*)> res{
1653  abi::__cxa_demangle(name, NULL, NULL, &status), std::free};
1654 
1655  return (status == 0) ? res.get() : name;
1656 } // end demangleTypeName()
1657 
1658 #else // does nothing if not g++
1659 //==============================================================================
1662 std::string StringMacros::demangleTypeName(const char* name) { return name; }
1663 #endif
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 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 T validateValueForDefaultStringDataType(const std::string &value, bool doConvertEnvironmentVariables=true)
static char * otsGetEnvironmentVarable(const char *name, const std::string &location, const unsigned int &line)
static std::string escapeString(std::string inString, bool allowWhiteSpace=false)
static void sanitizeForSQL(std::string &data)
StringMacros::sanitizeForSQL.
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 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)
Definition: StringMacros.cc:94
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 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:18
static std::string decodeURIComponent(const std::string &data)
static std::string stackTrace(void)
static bool getNumber(const std::string &s, T &retValue)