otsdaq  v2_04_02
StringMacros.cc
1 #include "otsdaq/Macros/StringMacros.h"
2 
3 using namespace ots;
4 
5 //==============================================================================
6 // wildCardMatch
7 // find needle in haystack
8 // allow needle to have leading and/or trailing wildcard '*'
9 // consider priority in matching, no matter the order in the haystack:
10 // - 0: no match!
11 // - 1: highest priority is exact match
12 // - 2: next highest is partial TRAILING-wildcard match
13 // - 3: next highest is partial LEADING-wildcard match
14 // - 4: lowest priority is partial full-wildcard match
15 // return priority found by reference
16 bool StringMacros::wildCardMatch(const std::string& needle,
17  const std::string& haystack,
18  unsigned int* priorityIndex) try
19 {
20  // __COUT__ << "\t\t wildCardMatch: " << needle <<
21  // " =in= " << haystack << " ??? " <<
22  // std::endl;
23 
24  // empty needle
25  if(needle.size() == 0)
26  {
27  if(priorityIndex)
28  *priorityIndex = 1; // consider an exact match, to stop higher level loops
29  return true; // if empty needle, always "found"
30  }
31 
32  // only wildcard
33  if(needle == "*")
34  {
35  if(priorityIndex)
36  *priorityIndex = 5; // only wildcard, is lowest priority
37  return true; // if empty needle, always "found"
38  }
39 
40  // no wildcards
41  if(needle == haystack)
42  {
43  if(priorityIndex)
44  *priorityIndex = 1; // an exact match
45  return true;
46  }
47 
48  // trailing wildcard
49  if(needle[needle.size() - 1] == '*' &&
50  needle.substr(0, needle.size() - 1) == haystack.substr(0, needle.size() - 1))
51  {
52  if(priorityIndex)
53  *priorityIndex = 2; // trailing wildcard match
54  return true;
55  }
56 
57  // leading wildcard
58  if(needle[0] == '*' &&
59  needle.substr(1) == haystack.substr(haystack.size() - (needle.size() - 1)))
60  {
61  if(priorityIndex)
62  *priorityIndex = 3; // leading wildcard match
63  return true;
64  }
65 
66  // leading wildcard and trailing wildcard
67  if(needle[0] == '*' && needle[needle.size() - 1] == '*' &&
68  std::string::npos != haystack.find(needle.substr(1, needle.size() - 2)))
69  {
70  if(priorityIndex)
71  *priorityIndex = 4; // leading and trailing wildcard match
72  return true;
73  }
74 
75  // else no match
76  if(priorityIndex)
77  *priorityIndex = 0; // no match
78  return false;
79 }
80 catch(...)
81 {
82  if(priorityIndex)
83  *priorityIndex = 0; // no match
84  return false; // if out of range
85 }
86 
87 //========================================================================================================================
88 // inWildCardSet ~
89 // returns true if needle is in haystack (considering wildcards)
90 bool StringMacros::inWildCardSet(const std::string& needle,
91  const std::set<std::string>& haystack)
92 {
93  for(const auto& haystackString : haystack)
94  // use wildcard match, flip needle parameter.. because we want haystack to have
95  // the wildcards
96  if(StringMacros::wildCardMatch(haystackString, needle))
97  return true;
98  return false;
99 }
100 
101 //==============================================================================
102 // decodeURIComponent
103 // converts all %## to the ascii character
104 std::string StringMacros::decodeURIComponent(const std::string& data)
105 {
106  std::string decodeURIString(data.size(), 0); // init to same size
107  unsigned int j = 0;
108  for(unsigned int i = 0; i < data.size(); ++i, ++j)
109  {
110  if(data[i] == '%')
111  {
112  // high order hex nibble digit
113  if(data[i + 1] > '9') // then ABCDEF
114  decodeURIString[j] += (data[i + 1] - 55) * 16;
115  else
116  decodeURIString[j] += (data[i + 1] - 48) * 16;
117 
118  // low order hex nibble digit
119  if(data[i + 2] > '9') // then ABCDEF
120  decodeURIString[j] += (data[i + 2] - 55);
121  else
122  decodeURIString[j] += (data[i + 2] - 48);
123 
124  i += 2; // skip to next char
125  }
126  else
127  decodeURIString[j] = data[i];
128  }
129  decodeURIString.resize(j);
130  return decodeURIString;
131 } // end decodeURIComponent()
132 
133 //==============================================================================
134 std::string StringMacros::encodeURIComponent(const std::string& sourceStr)
135 {
136  std::string retStr = "";
137  char encodeStr[4];
138  for(const auto& c : sourceStr)
139  if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
140  retStr += c;
141  else
142  {
143  sprintf(encodeStr, "%%%2.2X", c);
144  retStr += encodeStr;
145  }
146  return retStr;
147 } // end encodeURIComponent()
148 
149 //==============================================================================
150 // convertEnvironmentVariables ~
151 // static recursive function
152 //
153 // allows environment variables entered as $NAME or ${NAME}
154 std::string StringMacros::convertEnvironmentVariables(const std::string& data)
155 {
156  size_t begin = data.find("$");
157  if(begin != std::string::npos)
158  {
159  size_t end;
160  std::string envVariable;
161  std::string converted = data; // make copy to modify
162 
163  if(data[begin + 1] == '{') // check if using ${NAME} syntax
164  {
165  end = data.find("}", begin + 2);
166  envVariable = data.substr(begin + 2, end - begin - 2);
167  ++end; // replace the closing } too!
168  }
169  else // else using $NAME syntax
170  {
171  // end is first non environment variable character
172  for(end = begin + 1; end < data.size(); ++end)
173  if(!((data[end] >= '0' && data[end] <= '9') ||
174  (data[end] >= 'A' && data[end] <= 'Z') ||
175  (data[end] >= 'a' && data[end] <= 'z') || data[end] == '-' ||
176  data[end] == '_' || data[end] == '.' || data[end] == ':'))
177  break; // found end
178  envVariable = data.substr(begin + 1, end - begin - 1);
179  }
180  //__COUTV__(data);
181  //__COUTV__(envVariable);
182  char* envResult = __ENV__(envVariable.c_str());
183 
184  if(envResult)
185  {
186  // proceed recursively
187  return convertEnvironmentVariables(
188  converted.replace(begin, end - begin, envResult));
189  }
190  else
191  {
192  __SS__ << ("The environmental variable '" + envVariable +
193  "' is not set! Please make sure you set it before continuing!")
194  << std::endl;
195  __SS_THROW__;
196  }
197  }
198  // else no environment variables found in string
199  //__COUT__ << "Result: " << data << __E__;
200  return data;
201 }
202 
203 //==============================================================================
204 // isNumber ~~
205 // returns true if one or many numbers separated by operations (+,-,/,*) is
206 // present in the string.
207 // Numbers can be hex ("0x.."), binary("b..."), or base10.
208 bool StringMacros::isNumber(const std::string& s)
209 {
210  // extract set of potential numbers and operators
211  std::vector<std::string> numbers;
212  std::vector<char> ops;
213 
214  StringMacros::getVectorFromString(
215  s,
216  numbers,
217  /*delimiter*/ std::set<char>({'+', '-', '*', '/'}),
218  /*whitespace*/ std::set<char>({' ', '\t', '\n', '\r'}),
219  &ops);
220 
221  //__COUTV__(StringMacros::vectorToString(numbers));
222  //__COUTV__(StringMacros::vectorToString(ops));
223 
224  for(const auto& number : numbers)
225  {
226  if(number.size() == 0)
227  continue; // skip empty numbers
228 
229  if(number.find("0x") == 0) // indicates hex
230  {
231  //__COUT__ << "0x found" << std::endl;
232  for(unsigned int i = 2; i < number.size(); ++i)
233  {
234  if(!((number[i] >= '0' && number[i] <= '9') ||
235  (number[i] >= 'A' && number[i] <= 'F') ||
236  (number[i] >= 'a' && number[i] <= 'f')))
237  {
238  //__COUT__ << "prob " << number[i] << std::endl;
239  return false;
240  }
241  }
242  // return std::regex_match(number.substr(2), std::regex("^[0-90-9a-fA-F]+"));
243  }
244  else if(number[0] == 'b') // indicates binary
245  {
246  //__COUT__ << "b found" << std::endl;
247 
248  for(unsigned int i = 1; i < number.size(); ++i)
249  {
250  if(!((number[i] >= '0' && number[i] <= '1')))
251  {
252  //__COUT__ << "prob " << number[i] << std::endl;
253  return false;
254  }
255  }
256  }
257  else
258  {
259  //__COUT__ << "base 10 " << std::endl;
260  for(unsigned int i = 0; i < number.size(); ++i)
261  if(!((number[i] >= '0' && number[i] <= '9') || number[i] == '.' ||
262  number[i] == '+' || number[i] == '-'))
263  return false;
264  // Note: std::regex crashes in unresolvable ways (says Ryan.. also, stop using
265  // libraries) return std::regex_match(s,
266  // std::regex("^(\\-|\\+)?[0-9]*(\\.[0-9]+)?"));
267  }
268  }
269 
270  //__COUT__ << "yes " << std::endl;
271 
272  // all numbers are numbers
273  return true;
274 } // end isNumber()
275 
276 //==============================================================================
277 // getNumberType ~~
278 // returns string of number type: "unsigned long long", "double"
279 // or else "nan" for not-a-number
280 //
281 // Numbers can be hex ("0x.."), binary("b..."), or base10.
282 std::string StringMacros::getNumberType(const std::string& s)
283 {
284  // extract set of potential numbers and operators
285  std::vector<std::string> numbers;
286  std::vector<char> ops;
287 
288  bool hasDecimal = false;
289 
290  StringMacros::getVectorFromString(
291  s,
292  numbers,
293  /*delimiter*/ std::set<char>({'+', '-', '*', '/'}),
294  /*whitespace*/ std::set<char>({' ', '\t', '\n', '\r'}),
295  &ops);
296 
297  //__COUTV__(StringMacros::vectorToString(numbers));
298  //__COUTV__(StringMacros::vectorToString(ops));
299 
300  for(const auto& number : numbers)
301  {
302  if(number.size() == 0)
303  continue; // skip empty numbers
304 
305  if(number.find("0x") == 0) // indicates hex
306  {
307  //__COUT__ << "0x found" << std::endl;
308  for(unsigned int i = 2; i < number.size(); ++i)
309  {
310  if(!((number[i] >= '0' && number[i] <= '9') ||
311  (number[i] >= 'A' && number[i] <= 'F') ||
312  (number[i] >= 'a' && number[i] <= 'f')))
313  {
314  //__COUT__ << "prob " << number[i] << std::endl;
315  return "nan";
316  }
317  }
318  // return std::regex_match(number.substr(2), std::regex("^[0-90-9a-fA-F]+"));
319  }
320  else if(number[0] == 'b') // indicates binary
321  {
322  //__COUT__ << "b found" << std::endl;
323 
324  for(unsigned int i = 1; i < number.size(); ++i)
325  {
326  if(!((number[i] >= '0' && number[i] <= '1')))
327  {
328  //__COUT__ << "prob " << number[i] << std::endl;
329  return "nan";
330  }
331  }
332  }
333  else
334  {
335  //__COUT__ << "base 10 " << std::endl;
336  for(unsigned int i = 0; i < number.size(); ++i)
337  if(!((number[i] >= '0' && number[i] <= '9') || number[i] == '.' ||
338  number[i] == '+' || number[i] == '-'))
339  return "nan";
340  else if(number[i] == '.')
341  hasDecimal = true;
342  // Note: std::regex crashes in unresolvable ways (says Ryan.. also, stop using
343  // libraries) return std::regex_match(s,
344  // std::regex("^(\\-|\\+)?[0-9]*(\\.[0-9]+)?"));
345  }
346  }
347 
348  //__COUT__ << "yes " << std::endl;
349 
350  // all numbers are numbers
351  if(hasDecimal)
352  return "double";
353  return "unsigned long long";
354 } // end getNumberType()
355 
356 //==============================================================================
357 // static template function
358 // for bool, but not all other number types
359 // return false if string is not a bool
360 // template<>
361 // inline bool StringMacros::getNumber<bool>(const std::string& s, bool& retValue)
362 bool StringMacros::getNumber(const std::string& s, bool& retValue)
363 {
364  if(s.size() < 1)
365  {
366  __COUT_ERR__ << "Invalid empty bool string " << s << __E__;
367  return false;
368  }
369 
370  // check true case
371  if(s.find("1") != std::string::npos || s == "true" || s == "True" || s == "TRUE")
372  {
373  retValue = true;
374  return true;
375  }
376 
377  // check false case
378  if(s.find("0") != std::string::npos || s == "false" || s == "False" || s == "FALSE")
379  {
380  retValue = false;
381  return true;
382  }
383 
384  __COUT_ERR__ << "Invalid bool string " << s << __E__;
385  return false;
386 
387 } // end static getNumber<bool>
388 
389 //==============================================================================
390 // getTimestampString ~~
391 // returns ots style timestamp string
392 // of known fixed size: Thu Aug 23 14:55:02 2001 CST
393 std::string StringMacros::getTimestampString(const std::string& linuxTimeInSeconds)
394 {
395  time_t timestamp(strtol(linuxTimeInSeconds.c_str(), 0, 10));
396  return getTimestampString(timestamp);
397 } // end getTimestampString()
398 
399 //==============================================================================
400 // getTimestampString ~~
401 // returns ots style timestamp string
402 // of known fixed size: Thu Aug 23 14:55:02 2001 CST
403 std::string StringMacros::getTimestampString(const time_t& linuxTimeInSeconds)
404 {
405  std::string retValue(30, '\0'); // known fixed size: Thu Aug 23 14:55:02 2001 CST
406 
407  struct tm tmstruct;
408  ::localtime_r(&linuxTimeInSeconds, &tmstruct);
409  ::strftime(&retValue[0], 30, "%c %Z", &tmstruct);
410  retValue.resize(strlen(retValue.c_str()));
411 
412  return retValue;
413 } // end getTimestampString()
414 
415 //==============================================================================
416 // validateValueForDefaultStringDataType
417 //
418 std::string StringMacros::validateValueForDefaultStringDataType(
419  const std::string& value, bool doConvertEnvironmentVariables) try
420 {
421  return doConvertEnvironmentVariables
422  ? StringMacros::convertEnvironmentVariables(value)
423  : value;
424 }
425 catch(const std::runtime_error& e)
426 {
427  __SS__ << "Failed to validate value for default string data type. " << __E__
428  << e.what() << __E__;
429  __SS_THROW__;
430 }
431 
432 //==============================================================================
433 // getSetFromString
434 // extracts the set of elements from string that uses a delimiter
435 // ignoring whitespace
436 void StringMacros::getSetFromString(const std::string& inputString,
437  std::set<std::string>& setToReturn,
438  const std::set<char>& delimiter,
439  const std::set<char>& whitespace)
440 {
441  unsigned int i = 0;
442  unsigned int j = 0;
443 
444  // go through the full string extracting elements
445  // add each found element to set
446  for(; j < inputString.size(); ++j)
447  if((whitespace.find(inputString[j]) !=
448  whitespace.end() || // ignore leading white space or delimiter
449  delimiter.find(inputString[j]) != delimiter.end()) &&
450  i == j)
451  ++i;
452  else if((whitespace.find(inputString[j]) !=
453  whitespace
454  .end() || // trailing white space or delimiter indicates end
455  delimiter.find(inputString[j]) != delimiter.end()) &&
456  i != j) // assume end of element
457  {
458  //__COUT__ << "Set element found: " <<
459  // inputString.substr(i,j-i) << std::endl;
460 
461  setToReturn.emplace(inputString.substr(i, j - i));
462 
463  // setup i and j for next find
464  i = j + 1;
465  }
466 
467  if(i != j) // last element check (for case when no concluding ' ' or delimiter)
468  setToReturn.emplace(inputString.substr(i, j - i));
469 } // end getSetFromString()
470 
471 //==============================================================================
472 // getVectorFromString
473 // extracts the list of elements from string that uses a delimiter
474 // ignoring whitespace
475 // optionally returns the list of delimiters encountered, which may be useful
476 // for extracting which operator was used.
477 //
478 //
479 // Note: lists are returned as vectors
480 // Note: the size() of delimiters will be one less than the size() of the returned values
481 // unless there is a leading delimiter, in which case vectors will have the same
482 // size.
483 void StringMacros::getVectorFromString(const std::string& inputString,
484  std::vector<std::string>& listToReturn,
485  const std::set<char>& delimiter,
486  const std::set<char>& whitespace,
487  std::vector<char>* listOfDelimiters)
488 {
489  unsigned int i = 0;
490  unsigned int j = 0;
491  unsigned int c = 0;
492  std::set<char>::iterator delimeterSearchIt;
493  char lastDelimiter;
494  bool isDelimiter;
495  // bool foundLeadingDelimiter = false;
496 
497  //__COUT__ << inputString << __E__;
498  //__COUTV__(inputString.length());
499 
500  // go through the full string extracting elements
501  // add each found element to set
502  for(; c < inputString.size(); ++c)
503  {
504  //__COUT__ << (char)inputString[c] << __E__;
505 
506  delimeterSearchIt = delimiter.find(inputString[c]);
507  isDelimiter = delimeterSearchIt != delimiter.end();
508 
509  //__COUT__ << (char)inputString[c] << " " << isDelimiter <<
510  //__E__;//char)lastDelimiter << __E__;
511 
512  if(whitespace.find(inputString[c]) !=
513  whitespace.end() // ignore leading white space
514  && i == j)
515  {
516  ++i;
517  ++j;
518  // if(isDelimiter)
519  // foundLeadingDelimiter = true;
520  }
521  else if(whitespace.find(inputString[c]) != whitespace.end() &&
522  i != j) // trailing white space, assume possible end of element
523  {
524  // do not change j or i
525  }
526  else if(isDelimiter) // delimiter is end of element
527  {
528  //__COUT__ << "Set element found: " <<
529  // inputString.substr(i,j-i) << std::endl;
530 
531  if(listOfDelimiters && listToReturn.size()) // || foundLeadingDelimiter))
532  // //accept leading delimiter
533  // (especially for case of
534  // leading negative in math
535  // parsing)
536  {
537  //__COUTV__(lastDelimiter);
538  listOfDelimiters->push_back(lastDelimiter);
539  }
540  listToReturn.push_back(inputString.substr(i, j - i));
541 
542  // setup i and j for next find
543  i = c + 1;
544  j = c + 1;
545  }
546  else // part of element, so move j, not i
547  j = c + 1;
548 
549  if(isDelimiter)
550  lastDelimiter = *delimeterSearchIt;
551  //__COUTV__(lastDelimiter);
552  }
553 
554  if(1) // i != j) //last element check (for case when no concluding ' ' or delimiter)
555  {
556  //__COUT__ << "Last element found: " <<
557  // inputString.substr(i,j-i) << std::endl;
558 
559  if(listOfDelimiters && listToReturn.size()) // || foundLeadingDelimiter))
560  // //accept leading delimiter
561  // (especially for case of leading
562  // negative in math parsing)
563  {
564  //__COUTV__(lastDelimiter);
565  listOfDelimiters->push_back(lastDelimiter);
566  }
567  listToReturn.push_back(inputString.substr(i, j - i));
568  }
569 
570  // assert that there is one less delimiter than values
571  if(listOfDelimiters && listToReturn.size() - 1 != listOfDelimiters->size() &&
572  listToReturn.size() != listOfDelimiters->size())
573  {
574  __SS__ << "There is a mismatch in delimiters to entries (should be equal or one "
575  "less delimiter): "
576  << listOfDelimiters->size() << " vs " << listToReturn.size() << __E__
577  << "Entries: " << StringMacros::vectorToString(listToReturn) << __E__
578  << "Delimiters: " << StringMacros::vectorToString(*listOfDelimiters)
579  << __E__;
580  __SS_THROW__;
581  }
582 
583 } // end getVectorFromString()
584 
585 //==============================================================================
586 // getVectorFromString
587 // extracts the list of elements from string that uses a delimiter
588 // ignoring whitespace
589 // optionally returns the list of delimiters encountered, which may be useful
590 // for extracting which operator was used.
591 //
592 //
593 // Note: lists are returned as vectors
594 // Note: the size() of delimiters will be one less than the size() of the returned values
595 // unless there is a leading delimiter, in which case vectors will have the same
596 // size.
597 std::vector<std::string> StringMacros::getVectorFromString(
598  const std::string& inputString,
599  const std::set<char>& delimiter,
600  const std::set<char>& whitespace,
601  std::vector<char>* listOfDelimiters)
602 {
603  std::vector<std::string> listToReturn;
604 
605  StringMacros::getVectorFromString(
606  inputString, listToReturn, delimiter, whitespace, listOfDelimiters);
607  return listToReturn;
608 } // end getVectorFromString()
609 
610 //==============================================================================
611 // getMapFromString
612 // extracts the map of name-value pairs from string that uses two s
613 // ignoring whitespace
614 void StringMacros::getMapFromString(const std::string& inputString,
615  std::map<std::string, std::string>& mapToReturn,
616  const std::set<char>& pairPairDelimiter,
617  const std::set<char>& nameValueDelimiter,
618  const std::set<char>& whitespace) try
619 {
620  unsigned int i = 0;
621  unsigned int j = 0;
622  std::string name;
623  bool needValue = false;
624 
625  // go through the full string extracting map pairs
626  // add each found pair to map
627  for(; j < inputString.size(); ++j)
628  if(!needValue) // finding name
629  {
630  if((whitespace.find(inputString[j]) !=
631  whitespace.end() || // ignore leading white space or delimiter
632  pairPairDelimiter.find(inputString[j]) != pairPairDelimiter.end()) &&
633  i == j)
634  ++i;
635  else if((whitespace.find(inputString[j]) !=
636  whitespace
637  .end() || // trailing white space or delimiter indicates end
638  nameValueDelimiter.find(inputString[j]) !=
639  nameValueDelimiter.end()) &&
640  i != j) // assume end of map name
641  {
642  //__COUT__ << "Map name found: " <<
643  // inputString.substr(i,j-i) << std::endl;
644 
645  name = inputString.substr(i, j - i); // save name, for concluding pair
646 
647  needValue = true; // need value now
648 
649  // setup i and j for next find
650  i = j + 1;
651  }
652  }
653  else // finding value
654  {
655  if((whitespace.find(inputString[j]) !=
656  whitespace.end() || // ignore leading white space or delimiter
657  nameValueDelimiter.find(inputString[j]) != nameValueDelimiter.end()) &&
658  i == j)
659  ++i;
660  else if((whitespace.find(inputString[j]) !=
661  whitespace
662  .end() || // trailing white space or delimiter indicates end
663  pairPairDelimiter.find(inputString[j]) != pairPairDelimiter.end()) &&
664  i != j) // assume end of value name
665  {
666  //__COUT__ << "Map value found: " <<
667  // inputString.substr(i,j-i) << std::endl;
668 
669  auto /*pair<it,success>*/ emplaceReturn =
670  mapToReturn.emplace(std::pair<std::string, std::string>(
671  name,
672  validateValueForDefaultStringDataType(
673  inputString.substr(i, j - i)) // value
674  ));
675 
676  if(!emplaceReturn.second)
677  {
678  __COUT__ << "Ignoring repetitive value ('"
679  << inputString.substr(i, j - i)
680  << "') and keeping current value ('"
681  << emplaceReturn.first->second << "'). " << __E__;
682  }
683 
684  needValue = false; // need name now
685 
686  // setup i and j for next find
687  i = j + 1;
688  }
689  }
690 
691  if(i != j) // last value (for case when no concluding ' ' or delimiter)
692  {
693  auto /*pair<it,success>*/ emplaceReturn =
694  mapToReturn.emplace(std::pair<std::string, std::string>(
695  name,
696  validateValueForDefaultStringDataType(
697  inputString.substr(i, j - i)) // value
698  ));
699 
700  if(!emplaceReturn.second)
701  {
702  __COUT__ << "Ignoring repetitive value ('" << inputString.substr(i, j - i)
703  << "') and keeping current value ('" << emplaceReturn.first->second
704  << "'). " << __E__;
705  }
706  }
707 }
708 catch(const std::runtime_error& e)
709 {
710  __SS__ << "Error while extracting a map from the string '" << inputString
711  << "'... is it a valid map?" << __E__ << e.what() << __E__;
712  __SS_THROW__;
713 }
714 
715 //==============================================================================
716 // mapToString
717 std::string StringMacros::mapToString(const std::map<std::string, uint8_t>& mapToReturn,
718  const std::string& primaryDelimeter,
719  const std::string& secondaryDelimeter)
720 {
721  std::stringstream ss;
722  bool first = true;
723  for(auto& mapPair : mapToReturn)
724  {
725  if(first)
726  first = false;
727  else
728  ss << primaryDelimeter;
729  ss << mapPair.first << secondaryDelimeter << (unsigned int)mapPair.second;
730  }
731  return ss.str();
732 }
733 
734 //==============================================================================
735 // setToString
736 std::string StringMacros::setToString(const std::set<uint8_t>& setToReturn,
737  const std::string& delimeter)
738 {
739  std::stringstream ss;
740  bool first = true;
741  for(auto& setValue : setToReturn)
742  {
743  if(first)
744  first = false;
745  else
746  ss << delimeter;
747  ss << (unsigned int)setValue;
748  }
749  return ss.str();
750 }
751 
752 //==============================================================================
753 // vectorToString
754 std::string StringMacros::vectorToString(const std::vector<uint8_t>& setToReturn,
755  const std::string& delimeter)
756 {
757  std::stringstream ss;
758  bool first = true;
759  for(auto& setValue : setToReturn)
760  {
761  if(first)
762  first = false;
763  else
764  ss << delimeter;
765  ss << (unsigned int)setValue;
766  }
767  return ss.str();
768 }
769 
770 //========================================================================================================================
771 // exec
772 // run linux command and get result back in string
773 std::string StringMacros::exec(const char* cmd)
774 {
775  __COUTV__(cmd);
776 
777  std::array<char, 128> buffer;
778  std::string result;
779  std::shared_ptr<FILE> pipe(popen(cmd, "r"), pclose);
780  if(!pipe)
781  __THROW__("popen() failed!");
782  while(!feof(pipe.get()))
783  {
784  if(fgets(buffer.data(), 128, pipe.get()) != nullptr)
785  result += buffer.data();
786  }
787  return result;
788 } // end exec
789 
790 //==============================================================================
791 // stackTrace
792 // static function
793 // https://gist.github.com/fmela/591333/c64f4eb86037bb237862a8283df70cdfc25f01d3
794 #include <cxxabi.h> //for abi::__cxa_demangle
795 #include <execinfo.h> //for back trace of stack
796 //#include "TUnixSystem.h"
797 std::string StringMacros::stackTrace()
798 {
799  __SS__ << "ots::stackTrace:\n";
800 
801  void* array[10];
802  size_t size;
803 
804  // get void*'s for all entries on the stack
805  size = backtrace(array, 10);
806  // backtrace_symbols_fd(array, size, STDERR_FILENO);
807 
808  // https://stackoverflow.com/questions/77005/how-to-automatically-generate-a-stacktrace-when-my-program-crashes
809  char** messages = backtrace_symbols(array, size);
810 
811  // skip first stack frame (points here)
812  char syscom[256];
813  for(unsigned int i = 1; i < size && messages != NULL; ++i)
814  {
815  // mangled name needs to be converted to get nice name and line number
816  // line number not working... FIXME
817 
818  // sprintf(syscom,"addr2line %p -e %s",
819  // array[i],
820  // messages[i]); //last parameter is the name of this app
821  // ss << StringMacros::exec(syscom) << __E__;
822  // system(syscom);
823 
824  // continue;
825 
826  char *mangled_name = 0, *offset_begin = 0, *offset_end = 0;
827 
828  // find parentheses and +address offset surrounding mangled name
829  for(char* p = messages[i]; *p; ++p)
830  {
831  if(*p == '(')
832  {
833  mangled_name = p;
834  }
835  else if(*p == '+')
836  {
837  offset_begin = p;
838  }
839  else if(*p == ')')
840  {
841  offset_end = p;
842  break;
843  }
844  }
845 
846  // if the line could be processed, attempt to demangle the symbol
847  if(mangled_name && offset_begin && offset_end && mangled_name < offset_begin)
848  {
849  *mangled_name++ = '\0';
850  *offset_begin++ = '\0';
851  *offset_end++ = '\0';
852 
853  int status;
854  char* real_name = abi::__cxa_demangle(mangled_name, 0, 0, &status);
855 
856  // if demangling is successful, output the demangled function name
857  if(status == 0)
858  {
859  ss << "[" << i << "] " << messages[i] << " : " << real_name << "+"
860  << offset_begin << offset_end << std::endl;
861  }
862  // otherwise, output the mangled function name
863  else
864  {
865  ss << "[" << i << "] " << messages[i] << " : " << mangled_name << "+"
866  << offset_begin << offset_end << std::endl;
867  }
868  free(real_name);
869  }
870  // otherwise, print the whole line
871  else
872  {
873  ss << "[" << i << "] " << messages[i] << std::endl;
874  }
875  }
876  ss << std::endl;
877 
878  free(messages);
879 
880  // call ROOT's stack trace to get line numbers of ALL threads
881  // gSystem->StackTrace();
882 
883  return ss.str();
884 } // end stackTrace
885 
886 //==============================================================================
887 // otsGetEnvironmentVarable
888 // declare special ots environment variable get,
889 // that throws exception instead of causing crashes with null pointer.
890 // Note: usually called with __ENV__(X) in CoutMacros.h
891 char* StringMacros::otsGetEnvironmentVarable(const char* name,
892  const std::string& location,
893  const unsigned int& line)
894 {
895  char* environmentVariablePtr = getenv(name);
896  if(!environmentVariablePtr)
897  {
898  __SS__ << "Environment variable '" << name << "' not defined at " << location
899  << "[" << line << "]" << __E__;
900  ss << "\n\n" << StringMacros::stackTrace() << __E__;
901  __SS_THROW__;
902  }
903  return environmentVariablePtr;
904 } // end otsGetEnvironmentVarable()
905 
906 #ifdef __GNUG__
907 #include <cxxabi.h>
908 #include <cstdlib>
909 #include <memory>
910 
911 //==============================================================================
912 // demangleTypeName
913 std::string StringMacros::demangleTypeName(const char* name)
914 {
915  int status = -4; // some arbitrary value to eliminate the compiler warning
916 
917  // enable c++11 by passing the flag -std=c++11 to g++
918  std::unique_ptr<char, void (*)(void*)> res{
919  abi::__cxa_demangle(name, NULL, NULL, &status), std::free};
920 
921  return (status == 0) ? res.get() : name;
922 } // end demangleTypeName()
923 
924 #else // does nothing if not g++
925 //==============================================================================
926 // demangleTypeName
927 //
928 std::string StringMacros::demangleTypeName(const char* name) { return name; }
929 #endif