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