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