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-2013 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  "stringOps::getVariable\n"
292  "(\n"
293  " const word&,\n"
294  " const dictionary&,\n"
295  " const bool,\n"
296  " const bool\n"
297  ")\n",
298  dict
299  ) << "Cannot find dictionary or environment variable "
300  << name << exit(FatalIOError);
301  }
302  }
303  else
304  {
306  (
307  "stringOps::getVariable\n"
308  "(\n"
309  " const word&,\n"
310  " const dictionary&,\n"
311  " const bool,\n"
312  " const bool\n"
313  ")\n",
314  dict
315  ) << "Cannot find dictionary variable "
316  << name << exit(FatalIOError);
317  }
318 
319  return value;
320 }
321 
322 
324 (
325  const string& s,
326  string::size_type& index,
327  const dictionary& dict,
328  const bool allowEnvVars,
329  const bool allowEmpty
330 )
331 {
332  string newString;
333 
334  while (index < s.size())
335  {
336  if (s[index] == '$' && s[index+1] == '{')
337  {
338  // Recurse to parse variable name
339  index += 2;
340  string val = expand(s, index, dict, allowEnvVars, allowEmpty);
341  newString.append(val);
342  }
343  else if (s[index] == '}')
344  {
345  return getVariable(newString, dict, allowEnvVars, allowEmpty);
346  }
347  else
348  {
349  newString.append(string(s[index]));
350  }
351  index++;
352  }
353  return newString;
354 }
355 
356 
358 (
359  string& s,
360  const dictionary& dict,
361  const bool allowEnvVars,
362  const bool allowEmpty,
363  const char sigil
364 )
365 {
366  string::size_type begVar = 0;
367 
368  // Expand $VAR or ${VAR}
369  // Repeat until nothing more is found
370  while
371  (
372  (begVar = s.find(sigil, begVar)) != string::npos
373  && begVar < s.size()-1
374  )
375  {
376  if (begVar == 0 || s[begVar-1] != '\\')
377  {
378  if (s[begVar+1] == '{')
379  {
380  // Recursive variable expansion mode
381  label stringStart = begVar;
382  begVar += 2;
383  string varValue
384  (
385  expand
386  (
387  s,
388  begVar,
389  dict,
390  allowEnvVars,
391  allowEmpty
392  )
393  );
394 
395  s.std::string::replace
396  (
397  stringStart,
398  begVar - stringStart + 1,
399  varValue
400  );
401 
402  begVar = stringStart+varValue.size();
403  }
404  else
405  {
406  string::iterator iter = s.begin() + begVar + 1;
407 
408  // more generous in accepting keywords than for env variables
409  string::size_type endVar = begVar;
410  while
411  (
412  iter != s.end()
413  &&
414  (
415  isalnum(*iter)
416  || *iter == '.'
417  || *iter == ':'
418  || *iter == '_'
419  )
420  )
421  {
422  ++iter;
423  ++endVar;
424  }
425 
426  const word varName
427  (
428  s.substr
429  (
430  begVar + 1,
431  endVar - begVar
432  ),
433  false
434  );
435 
436  string varValue
437  (
439  (
440  varName,
441  dict,
442  allowEnvVars,
443  allowEmpty
444  )
445  );
446 
447  s.std::string::replace
448  (
449  begVar,
450  varName.size()+1,
451  varValue
452  );
453  begVar += varValue.size();
454  }
455  }
456  else
457  {
458  ++begVar;
459  }
460  }
461 
462  if (!s.empty())
463  {
464  if (s[0] == '~')
465  {
466  // Expand initial ~
467  // ~/ => home directory
468  // ~OpenFOAM => site/user OpenFOAM configuration directory
469  // ~user => home directory for specified user
470 
471  string user;
472  fileName file;
473 
474  if ((begVar = s.find('/')) != string::npos)
475  {
476  user = s.substr(1, begVar - 1);
477  file = s.substr(begVar + 1);
478  }
479  else
480  {
481  user = s.substr(1);
482  }
483 
484  // NB: be a bit lazy and expand ~unknownUser as an
485  // empty string rather than leaving it untouched.
486  // otherwise add extra test
487  if (user == "OpenFOAM")
488  {
489  s = findEtcFile(file);
490  }
491  else
492  {
493  s = home(user)/file;
494  }
495  }
496  else if (s[0] == '.')
497  {
498  // Expand a lone '.' and an initial './' into cwd
499  if (s.size() == 1)
500  {
501  s = cwd();
502  }
503  else if (s[1] == '/')
504  {
505  s.std::string::replace(0, 1, cwd());
506  }
507  }
508  }
509 
510  return s;
511 }
512 
513 
515 (
516  string& s,
517  const dictionary& dict,
518  const char sigil
519 )
520 {
521  string::size_type begVar = 0;
522 
523  // Expand $VAR or ${VAR}
524  // Repeat until nothing more is found
525  while
526  (
527  (begVar = s.find(sigil, begVar)) != string::npos
528  && begVar < s.size()-1
529  )
530  {
531  if (begVar == 0 || s[begVar-1] != '\\')
532  {
533  // Find end of first occurrence
534  string::size_type endVar = begVar;
535  string::size_type delim = 0;
536 
537  if (s[begVar+1] == '{')
538  {
539  endVar = s.find('}', begVar);
540  delim = 1;
541  }
542  else
543  {
544  string::iterator iter = s.begin() + begVar + 1;
545 
546  // more generous in accepting keywords than for env variables
547  while
548  (
549  iter != s.end()
550  &&
551  (
552  isalnum(*iter)
553  || *iter == '.'
554  || *iter == ':'
555  || *iter == '_'
556  )
557  )
558  {
559  ++iter;
560  ++endVar;
561  }
562  }
563 
564  if (endVar == string::npos)
565  {
566  // likely parsed '${...' without closing '}' - abort
567  break;
568  }
569  else if (endVar == begVar)
570  {
571  // parsed '${}' or $badChar - skip over
572  begVar = endVar + 1;
573  }
574  else
575  {
576  const word varName
577  (
578  s.substr
579  (
580  begVar + 1 + delim,
581  endVar - begVar - 2*delim
582  ),
583  false
584  );
585 
586 
587  // lookup in the dictionary
588  const entry* ePtr = dict.lookupScopedEntryPtr
589  (
590  varName,
591  true,
592  false // wildcards disabled. See primitiveEntry
593  );
594 
595  // if defined - copy its entries
596  if (ePtr)
597  {
598  OStringStream buf;
599  // Force floating point numbers to be printed with at least
600  // some decimal digits.
601  buf << fixed;
602  buf.precision(IOstream::defaultPrecision());
603  if (ePtr->isDict())
604  {
605  ePtr->dict().write(buf, false);
606  }
607  else
608  {
609  // fail for other types
610  dynamicCast<const primitiveEntry>
611  (
612  *ePtr
613  ).write(buf, true);
614  }
615 
616  s.std::string::replace
617  (
618  begVar,
619  endVar - begVar + 1,
620  buf.str()
621  );
622  begVar += buf.str().size();
623  }
624  else
625  {
626  // not defined - leave original string untouched
627  begVar = endVar + 1;
628  }
629  }
630  }
631  else
632  {
633  ++begVar;
634  }
635  }
636 
637  return s;
638 }
639 
640 
642 (
643  const string& original,
644  const bool allowEmpty
645 )
646 {
647  string s(original);
648  return inplaceExpand(s, allowEmpty);
649 }
650 
651 
653 (
654  string& s,
655  const bool allowEmpty
656 )
657 {
658  string::size_type begVar = 0;
659 
660  // Expand $VARS
661  // Repeat until nothing more is found
662  while
663  (
664  (begVar = s.find('$', begVar)) != string::npos
665  && begVar < s.size()-1
666  )
667  {
668  if (begVar == 0 || s[begVar-1] != '\\')
669  {
670  // Find end of first occurrence
671  string::size_type endVar = begVar;
672  string::size_type delim = 0;
673 
674  // The type/position of the ":-" or ":+" alternative values
675  int altType = 0;
676  string::size_type altPos = string::npos;
677 
678  if (s[begVar+1] == '{')
679  {
680  endVar = s.find('}', begVar);
681  delim = 1;
682 
683  // check for ${parameter:-word} or ${parameter:+word}
684  if (endVar != string::npos)
685  {
686  altPos = begVar;
687  altType = findParameterAlternative(s, altPos, endVar);
688  }
689  }
690  else
691  {
692  string::iterator iter = s.begin() + begVar + 1;
693 
694  while
695  (
696  iter != s.end()
697  && (isalnum(*iter) || *iter == '_')
698  )
699  {
700  ++iter;
701  ++endVar;
702  }
703  }
704 
705 
706  if (endVar == string::npos)
707  {
708  // likely parsed '${...' without closing '}' - abort
709  break;
710  }
711  else if (endVar == begVar)
712  {
713  // parsed '${}' or $badChar - skip over
714  begVar = endVar + 1;
715  }
716  else
717  {
718  const word varName
719  (
720  s.substr
721  (
722  begVar + 1 + delim,
723  (
724  (altPos == string::npos ? endVar : altPos)
725  - begVar - 2*delim
726  )
727  ),
728  false
729  );
730 
731  std::string altValue;
732  if (altPos != string::npos)
733  {
734  // had ":-" or ":+" alternative value
735  altValue = s.substr
736  (
737  altPos + 2,
738  endVar - altPos - 2*delim
739  );
740  }
741 
742  const string varValue = getEnv(varName);
743  if (varValue.size())
744  {
745  if (altPos != string::npos && altType == '+')
746  {
747  // was found, use ":+" alternative
748  s.std::string::replace
749  (
750  begVar,
751  endVar - begVar + 1,
752  altValue
753  );
754  begVar += altValue.size();
755  }
756  else
757  {
758  // was found, use value
759  s.std::string::replace
760  (
761  begVar,
762  endVar - begVar + 1,
763  varValue
764  );
765  begVar += varValue.size();
766  }
767  }
768  else if (altPos != string::npos)
769  {
770  // use ":-" or ":+" alternative values
771  if (altType == '-')
772  {
773  // was not found, use ":-" alternative
774  s.std::string::replace
775  (
776  begVar,
777  endVar - begVar + 1,
778  altValue
779  );
780  begVar += altValue.size();
781  }
782  else
783  {
784  // was not found, ":+" alternative implies
785  // substitute with nothing
786  s.std::string::erase(begVar, endVar - begVar + 1);
787  }
788  }
789  else if (allowEmpty)
790  {
791  s.std::string::erase(begVar, endVar - begVar + 1);
792  }
793  else
794  {
796  (
797  "stringOps::inplaceExpand(string&, const bool)"
798  ) << "Unknown variable name '" << varName << "'"
799  << exit(FatalError);
800  }
801  }
802  }
803  else
804  {
805  ++begVar;
806  }
807  }
808 
809  if (!s.empty())
810  {
811  if (s[0] == '~')
812  {
813  // Expand initial ~
814  // ~/ => home directory
815  // ~OpenFOAM => site/user OpenFOAM configuration directory
816  // ~user => home directory for specified user
817 
818  string user;
819  fileName file;
820 
821  if ((begVar = s.find('/')) != string::npos)
822  {
823  user = s.substr(1, begVar - 1);
824  file = s.substr(begVar + 1);
825  }
826  else
827  {
828  user = s.substr(1);
829  }
830 
831  // NB: be a bit lazy and expand ~unknownUser as an
832  // empty string rather than leaving it untouched.
833  // otherwise add extra test
834  if (user == "OpenFOAM")
835  {
836  s = findEtcFile(file);
837  }
838  else
839  {
840  s = home(user)/file;
841  }
842  }
843  else if (s[0] == '.')
844  {
845  // Expand a lone '.' and an initial './' into cwd
846  if (s.size() == 1)
847  {
848  s = cwd();
849  }
850  else if (s[1] == '/')
851  {
852  s.std::string::replace(0, 1, cwd());
853  }
854  }
855  }
856 
857  return s;
858 }
859 
860 
862 {
863  if (!s.empty())
864  {
865  string::size_type beg = 0;
866  while (beg < s.size() && isspace(s[beg]))
867  {
868  ++beg;
869  }
870 
871  if (beg)
872  {
873  return s.substr(beg);
874  }
875  }
876 
877  return s;
878 }
879 
880 
882 {
883  if (!s.empty())
884  {
885  string::size_type beg = 0;
886  while (beg < s.size() && isspace(s[beg]))
887  {
888  ++beg;
889  }
890 
891  if (beg)
892  {
893  s.erase(0, beg);
894  }
895  }
896 
897  return s;
898 }
899 
900 
902 {
903  if (!s.empty())
904  {
905  string::size_type sz = s.size();
906  while (sz && isspace(s[sz-1]))
907  {
908  --sz;
909  }
910 
911  if (sz < s.size())
912  {
913  return s.substr(0, sz);
914  }
915  }
916 
917  return s;
918 }
919 
920 
922 {
923  if (!s.empty())
924  {
925  string::size_type sz = s.size();
926  while (sz && isspace(s[sz-1]))
927  {
928  --sz;
929  }
930 
931  s.resize(sz);
932  }
933 
934  return s;
935 }
936 
937 
938 Foam::string Foam::stringOps::trim(const string& original)
939 {
940  return trimLeft(trimRight(original));
941 }
942 
943 
945 {
946  inplaceTrimRight(s);
947  inplaceTrimLeft(s);
948 
949  return s;
950 }
951 
952 
953 // ************************************************************************* //
string & inplaceTrimRight(string &)
Trim trailing whitespace inplace.
Definition: stringOps.C:921
fileName cwd()
Return current working directory path name.
Definition: POSIX.C:247
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 ))
string & inplaceTrim(string &)
Trim leading and trailing whitespace inplace.
Definition: stringOps.C:944
An STL-conforming const_iterator.
Definition: HashTable.H:470
An STL-conforming hash table.
Definition: HashTable.H:61
const entry * lookupScopedEntryPtr(const word &, bool recursive, bool patternMatch) const
Find and return an entry data stream pointer if present.
Definition: dictionary.C:463
string getEnv(const word &)
Return environment variable of given name.
Definition: POSIX.C:102
string str() const
Return the string.
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
A class for handling words, derived from string.
Definition: word.H:59
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
A class for handling character strings derived from std::string.
Definition: string.H:74
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
A list of keyword definitions, which are a keyword followed by any number of values (e...
Definition: dictionary.H:137
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
string trimLeft(const string &)
Return string trimmed of leading whitespace.
Definition: stringOps.C:861
runTime write()
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
graph_traits< Graph >::vertices_size_type size_type
Definition: SloanRenumber.C:73
IOerror FatalIOError
virtual int precision() const
Get precision of output field.
Definition: OSstream.C:290
string trimRight(const string &)
Return string trimmed of trailing whitespace.
Definition: stringOps.C:901
Output to memory buffer stream.
Definition: OStringStream.H:49
fileName findEtcFile(const fileName &, bool mandatory=false)
Search for a file using findEtcFiles.
Definition: POSIX.C:405
string & inplaceTrimLeft(string &)
Trim leading whitespace inplace.
Definition: stringOps.C:881
#define FatalErrorIn(functionName)
Report an error message using Foam::FatalError.
Definition: error.H:314
A keyword and a list of tokens is an &#39;entry&#39;.
Definition: entry.H:65
fileName home()
Return home directory path name for the current user.
Definition: POSIX.C:192
error FatalError
string trim(const string &)
Return string trimmed of leading and trailing whitespace.
Definition: stringOps.C:938
A class for handling file names.
Definition: fileName.H:69
dimensionedScalar pos(const dimensionedScalar &ds)
iterator find(const Key &)
Find and return an iterator set at the hashedEntry.
Definition: HashTable.C:139
bool isspace(char c)
Definition: char.H:53
#define FatalIOErrorIn(functionName, ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:325
IOstream & fixed(IOstream &io)
Definition: IOstream.H:576
string getVariable(const word &name, const dictionary &dict, const bool allowEnvVars, const bool allowEmpty)
Get dictionary or (optionally) environment variable.
Definition: stringOps.C:252