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