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