stringOps.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2011-2022 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "stringOps.H"
27 #include "OSspecific.H"
28 #include "etcFiles.H"
29 
30 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
31 
32 // Find the type/position of the ":-" or ":+" alternative values
33 static inline int findParameterAlternative
34 (
35  const std::string& s,
38 )
39 {
40  while (pos != std::string::npos)
41  {
42  pos = s.find(':', pos);
43  if (pos != std::string::npos)
44  {
45  if (pos < endPos)
46  {
47  // Nn-range: check for '+' or '-' following the ':'
48  const int altType = s[pos+1];
49  if (altType == '+' || altType == '-')
50  {
51  return altType;
52  }
53 
54  ++pos; // Unknown/unsupported - continue at next position
55  }
56  else
57  {
58  // Out-of-range: abort
59  pos = std::string::npos;
60  }
61  }
62  }
63 
64  return 0;
65 }
66 
67 
69 (
70  const string& original,
72  const char sigil
73 )
74 {
75  string s(original);
76  return inplaceExpand(s, mapping);
77 }
78 
79 
81 (
82  string& s,
84  const char sigil
85 )
86 {
87  string::size_type begVar = 0;
88 
89  // Expand $VAR or ${VAR}
90  // Repeat until nothing more is found
91  while
92  (
93  (begVar = s.find(sigil, begVar)) != string::npos
94  && begVar < s.size()-1
95  )
96  {
97  if (begVar == 0 || s[begVar-1] != '\\')
98  {
99  // Find end of first occurrence
100  string::size_type endVar = begVar;
101  string::size_type delim = 0;
102 
103  // The type/position of the ":-" or ":+" alternative values
104  int altType = 0;
105  string::size_type altPos = string::npos;
106 
107  if (s[begVar+1] == '{')
108  {
109  endVar = s.find('}', begVar);
110  delim = 1;
111 
112  // Check for ${parameter:-word} or ${parameter:+word}
113  if (endVar != string::npos)
114  {
115  altPos = begVar;
116  altType = findParameterAlternative(s, altPos, endVar);
117  }
118  }
119  else
120  {
121  string::iterator iter = s.begin() + begVar + 1;
122 
123  // Accept all dictionary and environment variable characters
124  while
125  (
126  iter != s.end()
127  &&
128  (
129  isalnum(*iter)
130  || *iter == '/' // For dictionary slash syntax
131  || *iter == '!' // For dictionary slash syntax
132  || *iter == '.' // For dictionary dot syntax
133  || *iter == ':' // For dictionary dot syntax
134  || *iter == '_'
135  )
136  )
137  {
138  ++iter;
139  ++endVar;
140  }
141  }
142 
143  if (endVar == string::npos)
144  {
145  // Likely parsed '${...' without closing '}' - abort
146  break;
147  }
148  else if (endVar == begVar)
149  {
150  // Parsed '${}' or $badChar - skip over
151  begVar = endVar + 1;
152  }
153  else
154  {
155  const word varName
156  (
157  s.substr
158  (
159  begVar + 1 + delim,
160  (
161  (altPos == string::npos ? endVar : altPos)
162  - begVar - 2*delim
163  )
164  ),
165  false
166  );
167 
168  std::string altValue;
169  if (altPos != string::npos)
170  {
171  // Had ":-" or ":+" alternative value
172  altValue = s.substr
173  (
174  altPos + 2,
175  endVar - altPos - 2*delim
176  );
177  }
178 
179 
181  mapping.find(varName);
182 
184  {
185  if (altPos != string::npos && altType == '+')
186  {
187  // Was found, use ":+" alternative
188  s.std::string::replace
189  (
190  begVar,
191  endVar - begVar + 1,
192  altValue
193  );
194  begVar += altValue.size();
195  }
196  else
197  {
198  // Was found, use value
199  s.std::string::replace
200  (
201  begVar,
202  endVar - begVar + 1,
203  *fnd
204  );
205  begVar += (*fnd).size();
206  }
207  }
208  else if (altPos != string::npos && altType == '-')
209  {
210  // Was not found, use ":-" alternative
211  s.std::string::replace
212  (
213  begVar,
214  endVar - begVar + 1,
215  altValue
216  );
217  begVar += altValue.size();
218  }
219  else
220  {
221  // Substitute with nothing, also for ":+" alternative
222  s.std::string::erase(begVar, endVar - begVar + 1);
223  }
224  }
225  }
226  else
227  {
228  ++begVar;
229  }
230  }
231 
232  return s;
233 }
234 
235 
237 (
238  const string& original,
239  const dictionary& dict,
240  const char sigil
241 )
242 {
243  string s(original);
244  return inplaceExpand(s, dict, sigil);
245 }
246 
247 
249 (
250  const word& name,
251  const dictionary& dict,
252  const bool allowEnvVars,
253  const bool allowEmpty
254 )
255 {
256  const entry* ePtr = dict.lookupScopedEntryPtr(name, true, false);
257 
258  if (ePtr)
259  {
260  OStringStream buf;
261 
262  // Force floating point numbers to be printed with at least
263  // some decimal digits.
264  buf << scientific;
265 
266  buf.precision(IOstream::defaultPrecision());
267 
268  // Fail for non-primitiveEntry
269  dynamicCast<const primitiveEntry>(*ePtr).write(buf, true);
270 
271  return buf.str();
272  }
273  else if (allowEnvVars)
274  {
275  string::const_iterator iter = name.begin();
276 
277  // Search for the sub-set of characters in name which are allowed
278  // in environment variables
279  string::size_type begVar = 0;
280  string::size_type endVar = begVar;
281  while (iter != name.end() && (isalnum(*iter) || *iter == '_'))
282  {
283  ++iter;
284  ++endVar;
285  }
286 
287  const word varName(name.substr(begVar, endVar - begVar), false);
288 
289  string varValue = getEnv(varName);
290 
291  if (!allowEmpty && varValue.empty())
292  {
294  (
295  dict
296  ) << "Cannot find dictionary or environment variable "
297  << name << exit(FatalIOError);
298  }
299 
300  varValue += name.substr(endVar, name.size() - endVar);
301 
302  return varValue;
303  }
304  else
305  {
307  (
308  dict
309  ) << "Cannot find dictionary variable "
310  << name << exit(FatalIOError);
311 
312  return string::null;
313  }
314 }
315 
316 
318 (
319  const string& s,
320  string::size_type& index,
321  const dictionary& dict,
322  const bool allowEnvVars,
323  const bool allowEmpty
324 )
325 {
326  string newString;
327 
328  while (index < s.size())
329  {
330  if (s[index] == '$' && s[index+1] == '{')
331  {
332  // Recurse to parse variable name
333  index += 2;
334  string val = expand(s, index, dict, allowEnvVars, allowEmpty);
335  newString.append(val);
336  }
337  else if (s[index] == '}')
338  {
339  return getVariable(newString, dict, allowEnvVars, allowEmpty);
340  }
341  else
342  {
343  newString.append(string(s[index]));
344  }
345  index++;
346  }
347  return newString;
348 }
349 
350 
352 (
353  string& s,
354  const dictionary& dict,
355  const bool allowEnvVars,
356  const bool allowEmpty,
357  const char sigil
358 )
359 {
360  string::size_type begVar = 0;
361 
362  // Expand $VAR or ${VAR}
363  // Repeat until nothing more is found
364  while
365  (
366  (begVar = s.find(sigil, begVar)) != string::npos
367  && begVar < s.size()-1
368  )
369  {
370  if (begVar == 0 || s[begVar-1] != '\\')
371  {
372  if (s[begVar+1] == '{')
373  {
374  // Recursive variable expansion mode
375  label stringStart = begVar;
376  begVar += 2;
377  string varValue
378  (
379  expand
380  (
381  s,
382  begVar,
383  dict,
384  allowEnvVars,
385  allowEmpty
386  )
387  );
388 
389  s.std::string::replace
390  (
391  stringStart,
392  begVar - stringStart + 1,
393  varValue
394  );
395 
396  begVar = stringStart+varValue.size();
397  }
398  else
399  {
400  string::iterator iter = s.begin() + begVar + 1;
401 
402  // Accept all dictionary and environment variable characters
403  string::size_type endVar = begVar;
404  while
405  (
406  iter != s.end()
407  &&
408  (
409  isalnum(*iter)
410  || *iter == '/' // For dictionary slash syntax
411  || *iter == '!' // For dictionary slash syntax
412  || *iter == '.' // For dictionary dot syntax
413  || *iter == ':' // For dictionary dot syntax
414  || *iter == '_'
415  )
416  )
417  {
418  ++iter;
419  ++endVar;
420  }
421 
422  const word varName
423  (
424  s.substr
425  (
426  begVar + 1,
427  endVar - begVar
428  ),
429  false
430  );
431 
432  string varValue
433  (
435  (
436  varName,
437  dict,
438  allowEnvVars,
439  allowEmpty
440  )
441  );
442 
443  s.std::string::replace
444  (
445  begVar,
446  varName.size()+1,
447  varValue
448  );
449  begVar += varValue.size();
450  }
451  }
452  else
453  {
454  ++begVar;
455  }
456  }
457 
458  if (!s.empty())
459  {
460  if (s[0] == '~')
461  {
462  // Expand initial ~
463  // ~/ => home directory
464  // ~OpenFOAM => site/user OpenFOAM configuration directory
465  // ~user => home directory for specified user
466 
467  string user;
468  fileName file;
469 
470  if ((begVar = s.find('/')) != string::npos)
471  {
472  user = s.substr(1, begVar - 1);
473  file = s.substr(begVar + 1);
474  }
475  else
476  {
477  user = s.substr(1);
478  }
479 
480  // NB: be a bit lazy and expand ~unknownUser as an
481  // empty string rather than leaving it untouched.
482  // otherwise add extra test
483  if (user == "OpenFOAM")
484  {
485  s = findEtcFile(file);
486  }
487  else
488  {
489  s = home(user)/file;
490  }
491  }
492  else if (s[0] == '.')
493  {
494  // Expand a lone '.' and an initial './' into cwd
495  if (s.size() == 1)
496  {
497  s = cwd();
498  }
499  else if (s[1] == '/')
500  {
501  s.std::string::replace(0, 1, cwd());
502  }
503  }
504  }
505 
506  return s;
507 }
508 
509 
511 (
512  string& s,
513  const dictionary& dict,
514  const char sigil
515 )
516 {
517  string::size_type begVar = 0;
518 
519  // Expand $VAR or ${VAR}
520  // Repeat until nothing more is found
521  while
522  (
523  (begVar = s.find(sigil, begVar)) != string::npos
524  && begVar < s.size()-1
525  )
526  {
527  if (begVar == 0 || s[begVar-1] != '\\')
528  {
529  // Find end of first occurrence
530  string::size_type endVar = begVar;
531  string::size_type delim = 0;
532 
533  if (s[begVar+1] == '{')
534  {
535  endVar = s.find('}', begVar);
536  delim = 1;
537  }
538  else
539  {
540  string::iterator iter = s.begin() + begVar + 1;
541 
542  // Accept all dictionary and environment variable characters
543  while
544  (
545  iter != s.end()
546  &&
547  (
548  isalnum(*iter)
549  || *iter == '/' // For dictionary slash syntax
550  || *iter == '!' // For dictionary slash syntax
551  || *iter == '.' // For dictionary dot syntax
552  || *iter == ':' // For dictionary dot syntax
553  || *iter == '_'
554  )
555  )
556  {
557  ++iter;
558  ++endVar;
559  }
560  }
561 
562  if (endVar == string::npos)
563  {
564  // Likely parsed '${...' without closing '}' - abort
565  break;
566  }
567  else if (endVar == begVar)
568  {
569  // Parsed '${}' or $badChar - skip over
570  begVar = endVar + 1;
571  }
572  else
573  {
574  const word varName
575  (
576  s.substr
577  (
578  begVar + 1 + delim,
579  endVar - begVar - 2*delim
580  ),
581  false
582  );
583 
584 
585  // Lookup in the dictionary
586  const entry* ePtr = dict.lookupScopedEntryPtr
587  (
588  varName,
589  true,
590  false // Wildcards disabled. See primitiveEntry
591  );
592 
593  // If defined - copy its entries
594  if (ePtr)
595  {
596  OStringStream buf;
597  // Force floating point numbers to be printed with at least
598  // some decimal digits.
599  buf << scientific;
600  buf.precision(IOstream::defaultPrecision());
601  if (ePtr->isDict())
602  {
603  ePtr->dict().write(buf, false);
604  }
605  else
606  {
607  // Fail for other types
608  dynamicCast<const primitiveEntry>
609  (
610  *ePtr
611  ).write(buf, true);
612  }
613 
614  s.std::string::replace
615  (
616  begVar,
617  endVar - begVar + 1,
618  buf.str()
619  );
620  begVar += buf.str().size();
621  }
622  else
623  {
624  // Not defined - leave original string untouched
625  begVar = endVar + 1;
626  }
627  }
628  }
629  else
630  {
631  ++begVar;
632  }
633  }
634 
635  return s;
636 }
637 
638 
640 (
641  const string& original,
642  const bool allowEmpty
643 )
644 {
645  string s(original);
646  return inplaceExpand(s, allowEmpty);
647 }
648 
649 
651 (
652  string& s,
653  const bool allowEmpty
654 )
655 {
656  string::size_type begVar = 0;
657 
658  // Expand $VARS
659  // Repeat until nothing more is found
660  while
661  (
662  (begVar = s.find('$', begVar)) != string::npos
663  && begVar < s.size()-1
664  )
665  {
666  if (begVar == 0 || s[begVar-1] != '\\')
667  {
668  // Find end of first occurrence
669  string::size_type endVar = begVar;
670  string::size_type delim = 0;
671 
672  // The type/position of the ":-" or ":+" alternative values
673  int altType = 0;
674  string::size_type altPos = string::npos;
675 
676  if (s[begVar+1] == '{')
677  {
678  endVar = s.find('}', begVar);
679  delim = 1;
680 
681  // Check for ${parameter:-word} or ${parameter:+word}
682  if (endVar != string::npos)
683  {
684  altPos = begVar;
685  altType = findParameterAlternative(s, altPos, endVar);
686  }
687  }
688  else
689  {
690  string::iterator iter = s.begin() + begVar + 1;
691 
692  while
693  (
694  iter != s.end()
695  && (isalnum(*iter) || *iter == '_')
696  )
697  {
698  ++iter;
699  ++endVar;
700  }
701  }
702 
703 
704  if (endVar == string::npos)
705  {
706  // Likely parsed '${...' without closing '}' - abort
707  break;
708  }
709  else if (endVar == begVar)
710  {
711  // Parsed '${}' or $badChar - skip over
712  begVar = endVar + 1;
713  }
714  else
715  {
716  const word varName
717  (
718  s.substr
719  (
720  begVar + 1 + delim,
721  (
722  (altPos == string::npos ? endVar : altPos)
723  - begVar - 2*delim
724  )
725  ),
726  false
727  );
728 
729  std::string altValue;
730  if (altPos != string::npos)
731  {
732  // Had ":-" or ":+" alternative value
733  altValue = s.substr
734  (
735  altPos + 2,
736  endVar - altPos - 2*delim
737  );
738  }
739 
740  const string varValue = getEnv(varName);
741  if (varValue.size())
742  {
743  if (altPos != string::npos && altType == '+')
744  {
745  // Was found, use ":+" alternative
746  s.std::string::replace
747  (
748  begVar,
749  endVar - begVar + 1,
750  altValue
751  );
752  begVar += altValue.size();
753  }
754  else
755  {
756  // Was found, use value
757  s.std::string::replace
758  (
759  begVar,
760  endVar - begVar + 1,
761  varValue
762  );
763  begVar += varValue.size();
764  }
765  }
766  else if (altPos != string::npos)
767  {
768  // Use ":-" or ":+" alternative values
769  if (altType == '-')
770  {
771  // Was not found, use ":-" alternative
772  s.std::string::replace
773  (
774  begVar,
775  endVar - begVar + 1,
776  altValue
777  );
778  begVar += altValue.size();
779  }
780  else
781  {
782  // Was not found, ":+" alternative implies
783  // substitute with nothing
784  s.std::string::erase(begVar, endVar - begVar + 1);
785  }
786  }
787  else if (allowEmpty)
788  {
789  s.std::string::erase(begVar, endVar - begVar + 1);
790  }
791  else
792  {
794  << "Unknown variable name '" << varName << "'"
795  << exit(FatalError);
796  }
797  }
798  }
799  else
800  {
801  ++begVar;
802  }
803  }
804 
805  if (!s.empty())
806  {
807  if (s[0] == '~')
808  {
809  // Expand initial ~
810  // ~/ => home directory
811  // ~OpenFOAM => site/user OpenFOAM configuration directory
812  // ~user => home directory for specified user
813 
814  string user;
815  fileName file;
816 
817  if ((begVar = s.find('/')) != string::npos)
818  {
819  user = s.substr(1, begVar - 1);
820  file = s.substr(begVar + 1);
821  }
822  else
823  {
824  user = s.substr(1);
825  }
826 
827  // NB: be a bit lazy and expand ~unknownUser as an
828  // empty string rather than leaving it untouched.
829  // otherwise add extra test
830  if (user == "OpenFOAM")
831  {
832  s = findEtcFile(file);
833  }
834  else
835  {
836  s = home(user)/file;
837  }
838  }
839  else if (s[0] == '.')
840  {
841  // Expand a lone '.' and an initial './' into cwd
842  if (s.size() == 1)
843  {
844  s = cwd();
845  }
846  else if (s[1] == '/')
847  {
848  s.std::string::replace(0, 1, cwd());
849  }
850  }
851  }
852 
853  return s;
854 }
855 
856 
858 {
859  if (!s.empty())
860  {
861  string::size_type beg = 0;
862  while (beg < s.size() && isspace(s[beg]))
863  {
864  ++beg;
865  }
866 
867  if (beg)
868  {
869  return s.substr(beg);
870  }
871  }
872 
873  return s;
874 }
875 
876 
878 {
879  if (!s.empty())
880  {
881  string::size_type beg = 0;
882  while (beg < s.size() && isspace(s[beg]))
883  {
884  ++beg;
885  }
886 
887  if (beg)
888  {
889  s.erase(0, beg);
890  }
891  }
892 
893  return s;
894 }
895 
896 
898 {
899  if (!s.empty())
900  {
901  string::size_type sz = s.size();
902  while (sz && isspace(s[sz-1]))
903  {
904  --sz;
905  }
906 
907  if (sz < s.size())
908  {
909  return s.substr(0, sz);
910  }
911  }
912 
913  return s;
914 }
915 
916 
918 {
919  if (!s.empty())
920  {
921  string::size_type sz = s.size();
922  while (sz && isspace(s[sz-1]))
923  {
924  --sz;
925  }
926 
927  s.resize(sz);
928  }
929 
930  return s;
931 }
932 
933 
934 Foam::string Foam::stringOps::trim(const string& original)
935 {
936  return trimLeft(trimRight(original));
937 }
938 
939 
941 {
942  inplaceTrimRight(s);
943  inplaceTrimLeft(s);
944 
945  return s;
946 }
947 
948 
949 // ************************************************************************* //
string getEnv(const word &)
Return environment variable of given name.
Definition: POSIX.C:97
string & inplaceTrim(string &)
Trim leading and trailing whitespace inplace.
Definition: stringOps.C:940
string expand(const string &, const HashTable< string, word, string::hash > &mapping, const char sigil='$')
Expand occurrences of variables according to the mapping.
Definition: stringOps.C:69
string getVariable(const word &name, const dictionary &dict, const bool allowEnvVars, const bool allowEmpty)
Get dictionary or (optionally) environment variable.
Definition: stringOps.C:249
A class for handling file names.
Definition: fileName.H:79
An STL-conforming const_iterator.
Definition: HashTable.H:481
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
virtual int precision() const
Get precision of output field.
Definition: OSstream.C:246
error FatalError
A list of keyword definitions, which are a keyword followed by any number of values (e...
Definition: dictionary.H:156
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:306
fileName home()
Return home directory path name for the current user.
Definition: POSIX.C:186
const entry * lookupScopedEntryPtr(const word &, bool recursive, bool patternMatch) const
Find and return an entry data stream pointer if present.
Definition: dictionary.C:891
string trim(const string &)
Return string trimmed of leading and trailing whitespace.
Definition: stringOps.C:934
iterator find(const Key &)
Find and return an iterator set at the hashedEntry.
Definition: HashTable.C:142
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
dimensionedScalar pos(const dimensionedScalar &ds)
gmvFile<< "tracers "<< particles.size()<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().x()<< " ";}gmvFile<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().y()<< " ";}gmvFile<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
A class for handling words, derived from string.
Definition: word.H:59
Functions to search &#39;etc&#39; directories for configuration files etc.
void write(std::ostream &os, const bool binary, List< floatScalar > &fField)
Write floats ascii or binary.
An STL-conforming hash table.
Definition: HashTable.H:61
graph_traits< Graph >::vertices_size_type size_type
Definition: SloanRenumber.C:73
fileName findEtcFile(const fileName &, bool mandatory=false)
Search for a file using findEtcFiles.
Definition: etcFiles.C:252
string & inplaceTrimRight(string &)
Trim trailing whitespace inplace.
Definition: stringOps.C:917
bool isspace(char c)
Definition: char.H:53
string trimRight(const string &)
Return string trimmed of trailing whitespace.
Definition: stringOps.C:897
string & inplaceExpand(string &, const HashTable< string, word, string::hash > &mapping, const char sigil='$')
Inplace expand occurrences of variables according to the mapping.
Definition: stringOps.C:81
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:318
string str() const
Return the string.
fileName cwd()
Return current working directory path name.
Definition: POSIX.C:241
string & inplaceTrimLeft(string &)
Trim leading whitespace inplace.
Definition: stringOps.C:877
A class for handling character strings derived from std::string.
Definition: string.H:76
string trimLeft(const string &)
Return string trimmed of leading whitespace.
Definition: stringOps.C:857
Output to memory buffer stream.
Definition: OStringStream.H:49
static int findParameterAlternative(const std::string &s, std::string::size_type &pos, std::string::size_type endPos)
Definition: stringOps.C:34
IOstream & scientific(IOstream &io)
Definition: IOstream.H:579
A keyword and a list of tokens is an &#39;entry&#39;.
Definition: entry.H:65
IOerror FatalIOError