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 | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-2016 OpenFOAM Foundation
9  Copyright (C) 2017-2021 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "stringOps.H"
30 #include "typeInfo.H"
31 #include "etcFiles.H"
32 #include "UPstream.H"
33 #include "StringStream.H"
34 #include "OSstream.H"
35 #include "OSspecific.H"
36 #include <cctype>
37 
38 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
39 
40 namespace Foam
41 {
42 
43 // Return the file location mode (string) as a numerical value.
44 //
45 // - u : location mask 0700
46 // - g : location mask 0070
47 // - o : location mask 0007
48 // - a : location mask 0777
49 //
50 static inline unsigned short modeToLocation
51 (
52  const std::string& mode,
53  std::size_t pos = 0
54 )
55 {
56  unsigned short where(0);
57 
58  if (std::string::npos != mode.find('u', pos)) { where |= 0700; } // User
59  if (std::string::npos != mode.find('g', pos)) { where |= 0070; } // Group
60  if (std::string::npos != mode.find('o', pos)) { where |= 0007; } // Other
61  if (std::string::npos != mode.find('a', pos)) { where |= 0777; } // All
62 
63  return where;
64 }
65 
66 
67 // Expand a leading <tag>/
68 // Convenient for frequently used directories
69 //
70 // <etc>/ => user/group/other etc - findEtcEntry()
71 // <etc(:[ugoa]+)?>/ => user/group/other etc - findEtcEntry()
72 // <case>/ => FOAM_CASE directory
73 // <constant>/ => FOAM_CASE/constant directory
74 // <system>/ => FOAM_CASE/system directory
75 static void expandLeadingTag(std::string& s, const char b, const char e)
76 {
77  if (s[0] != b)
78  {
79  return;
80  }
81 
82  auto delim = s.find(e);
83  if (std::string::npos == delim)
84  {
85  return; // Error: no closing delim - ignore expansion
86  }
87 
88  fileName file;
89 
90  const char nextC = s[++delim];
91 
92  // Require the following character to be '/' or the end of string.
93  if (nextC)
94  {
95  if (nextC != '/')
96  {
97  return;
98  }
99 
100  file.assign(s.substr(delim + 1));
101  }
102 
103  const std::string tag(s, 1, delim-2);
104  const auto tagLen = tag.length();
105 
106  // Note that file is also allowed to be an empty string.
107 
108  if (tag == "etc")
109  {
110  s = findEtcEntry(file);
111  }
112  else if (tag == "case")
113  {
114  s = fileName(Foam::getEnv("FOAM_CASE"))/file;
115  }
116  else if (tag == "constant" || tag == "system")
117  {
118  s = fileName(Foam::getEnv("FOAM_CASE"))/tag/file;
119  }
120  else if (tagLen >= 4 && tag.compare(0, 4, "etc:") == 0)
121  {
122  // <etc:[ugoa]+> type of tag - convert "ugo" to numeric
123 
124  s = findEtcEntry(file, modeToLocation(tag,4));
125  }
126 }
127 
128 
129 // Expand a leading tilde
130 // ~/ => home directory
131 // ~user => home directory for specified user
132 // Deprecated ~OpenFOAM => <etc> instead
133 static void expandLeadingTilde(std::string& s)
134 {
135  if (s[0] != '~')
136  {
137  return;
138  }
139 
140  std::string user;
141  fileName file;
142 
143  const auto slash = s.find('/');
144  if (slash == std::string::npos)
145  {
146  user = s.substr(1);
147  }
148  else
149  {
150  user = s.substr(1, slash - 1);
151  file = s.substr(slash + 1);
152  }
153 
154  // NB: be a bit lazy and expand ~unknownUser as an
155  // empty string rather than leaving it untouched.
156  // otherwise add extra test
157 
158  if (user == "OpenFOAM")
159  {
160  // Compat Warning
161  const int version(1806);
162 
163  if (error::master())
164  {
165  std::cerr
166  << nl
167  << "--> FOAM Warning :" << nl
168  << " Found [v" << version << "] '"
169  << "~OpenFOAM" << "' string expansion instead of '"
170  << "<etc>" << "' in string\n\"" << s << "\"\n" << nl
171  << std::endl;
172 
173  error::warnAboutAge("expansion", version);
174  }
175 
176  s = findEtcFile(file);
177  }
178  else
179  {
180  s = home(user)/file;
181  }
182 }
183 
184 
185 // Expand leading contents: "./", "~..", "<tag>/"
186 static void expandLeading(std::string& s)
187 {
188  if (s.empty())
189  {
190  return;
191  }
192 
193  switch (s[0])
194  {
195  case '.':
196  {
197  // Expand a lone '.' and an initial './' into cwd
198  if (s.size() == 1)
199  {
200  s = cwd();
201  }
202  else if (s[1] == '/')
203  {
204  s.replace(0, 1, cwd());
205  }
206  break;
207  }
208  case '<':
209  {
210  expandLeadingTag(s, '<', '>');
211  break;
212  }
213  case '~':
214  {
216  break;
217  }
218  }
219 }
220 
221 
222 // Serialize an entry (primitive or dictionary) with special treatment
223 // for primitive entries that are already a string-type.
224 static inline std::string entryToString
225 (
226  const entry* eptr,
227  const bool allowSubDict
228 )
229 {
230  std::string str;
231 
232  if (eptr)
233  {
234  OStringStream buf;
235  // Force floating point numbers to be printed with at least
236  // some decimal digits.
237  buf << fixed;
238  buf.precision(IOstream::defaultPrecision());
239 
240  if (allowSubDict && eptr->isDict())
241  {
242  eptr->dict().write(buf, false);
243  str = buf.str();
244  }
245  else
246  {
247  // Fail for non-primitiveEntry
248  const auto& pe = dynamicCast<const primitiveEntry>(*eptr);
249 
250  if (pe.size() == 1 && pe[0].isStringType())
251  {
252  // Already a string-type (WORD, STRING, ...). Just copy.
253  str = pe[0].stringToken();
254  }
255  else
256  {
257  pe.write(buf, true);
258  str = buf.str();
259  }
260  }
261  }
262 
263  return str;
264 }
265 
266 } // End namespace Foam
267 
268 
269 // Details for handling dictionary expansion
270 
271 namespace
272 {
273 
274 // Acceptable values for $variable names.
275 //
276 // Similar to word::valid(), except we don't have the benefit of a parser
277 // to filter out other unacceptable entries for us.
278 //
279 // Does not currently accept '/' in a variable name.
280 // We would like "$file/$name" to expand as two variables.
281 static inline bool validVariableChar(char c)
282 {
283  return
284  (
285  std::isalnum(c)
286  || c == '.'
287  || c == ':'
288  || c == '_'
289  );
290 }
291 
292 
293 // Find the type/position of the ":-" or ":+" alternative values
294 // Returns 0, '-', '+' corresponding to not-found or ':-' or ':+'
295 static inline int findParameterAlternative
296 (
297  const std::string& s,
300 )
301 {
302  while (pos != std::string::npos)
303  {
304  pos = s.find(':', pos);
305  if (pos != std::string::npos)
306  {
307  if (pos < endPos)
308  {
309  // in-range: check for '+' or '-' following the ':'
310  const int altType = s[pos+1];
311  if (altType == '+' || altType == '-')
312  {
313  return altType;
314  }
315 
316  ++pos; // unknown/unsupported - continue at next position
317  }
318  else
319  {
320  // out-of-range: abort
321  pos = std::string::npos;
322  }
323  }
324  }
325 
326  return 0;
327 }
328 
329 
330 // For input string of "$variable with other" return the length of
331 // the variable.
332 //
333 // Intentionally will not capture ':+', ':-' alterations. Use ${ .. } for that
334 static inline std::string::size_type findVariableLen
335 (
336  const std::string& s,
338  const char sigil = '$'
339 )
340 {
341  std::string::size_type len = 0;
342 
343  if (pos < s.length())
344  {
345  if (s[pos] == sigil)
346  {
347  // Skip leading '$' in the count!
348  ++pos;
349  }
350 
351  for
352  (
353  auto iter = s.cbegin() + pos;
354  iter != s.cend() && validVariableChar(*iter);
355  ++iter
356  )
357  {
358  ++len;
359  }
360  }
361 
362  return len;
363 }
364 
365 } // End namespace anonymous
366 
367 
368 namespace Foam
369 {
370 
371 // Get dictionary or (optionally) environment variable
372 //
373 // Handles default and alternative values as per the POSIX shell.
374 // \code
375 // ${parameter:-defValue}
376 // ${parameter:+altValue}
377 // \endcode
379 (
380  const word& name,
381  const dictionary* dictptr,
382  const bool allowEnv,
383  const bool allowEmpty,
384  const bool allowSubDict
385 )
386 {
387  // The type/position of the ":-" or ":+" alternative values
388  std::string::size_type altPos = 0;
389 
390  // Check for parameter:-word or parameter:+word
391  const int altType =
392  findParameterAlternative(name, altPos, name.size()-1);
393 
394  const word lookupName =
395  (altType ? word(name.substr(0,altPos), false) : name);
396 
397  const entry* eptr =
398  (
399  (dictptr != nullptr)
400  ? dictptr->findScoped(lookupName, keyType::LITERAL_RECURSIVE)
401  : nullptr
402  );
403 
404  string value;
405  if (eptr)
406  {
407  value = entryToString(eptr, allowSubDict);
408  }
409  else if (allowEnv || dictptr == nullptr)
410  {
411  value = Foam::getEnv(lookupName);
412  }
413 
414  if (value.empty() ? (altType == '-') : (altType == '+'))
415  {
416  // Not found or empty: use ":-" alternative value
417  // Found and not empty: use ":+" alternative value
418  value = name.substr(altPos + 2);
419  }
420 
421  if (!allowEmpty && value.empty())
422  {
423  if (dictptr != nullptr)
424  {
425  auto& err =
426  FatalIOErrorInFunction(*dictptr)
427  << "Cannot find dictionary entry ";
428 
429  if (allowEnv)
430  {
431  err << "or environment ";
432  }
433 
434  err << "variable '" << lookupName << "'" << nl
435  << exit(FatalIOError);
436  }
437  else
438  {
440  << "Unknown variable '" << lookupName << "'" << nl
441  << exit(FatalError);
442  }
443  }
444 
445  return value;
446 }
447 
448 
449 // Recursively expands (dictionary or environment) variable
450 // starting at index in string. Updates index.
451 //
452 // String: "abc ${var} def",
453 // Receive: "var} def"
454 //
455 // String: "abc ${{expr}} def"
456 // Receive: "{expr}} def"
457 //
458 // On return, the index will be adjust to be AFTER the closing '}'
460 (
461  const std::string& s,
462  std::string::size_type& index,
463  const dictionary* dictptr,
464  const bool allowEnv,
465  const bool allowEmpty,
466  const bool allowSubDict
467 )
468 {
470 
471  // Track ${{ expr }} expressions
472  const bool isExpr = (index < s.size() && s[index] == '{');
473 
474  if (isExpr)
475  {
476  ++index;
477  }
478 
479  // Initially called for a ${variable}, not ${{expr}}
480  bool isVar = !isExpr;
481 
482  string out;
483 
484  for (/*nil*/; index < s.size(); ++index)
485  {
487  if (s[index] == '$')
488  {
489  if (s[index+1] == '{')
490  {
491  // Recurse to parse variable name
492  index += 2;
493 
494  string val =
496  (
497  s,
498  index,
499  dictptr,
500  allowEnv,
501  allowEmpty,
502  allowSubDict
503  );
504 
505  out.append(val); // Append content
506 
508 
509  // Already skipped past '}' terminator?
510  if (s[index-1] == '}')
511  {
512  --index;
513  }
514  }
515  else if (validVariableChar(s[index+1]))
516  {
517  // A regular $var expansion without a surrounding {}.
518 
519  const auto varLen = findVariableLen(s, index);
520  const word varName(s.substr(index+1, varLen), false);
521  index += varLen;
522 
523  string val =
525  (
526  varName,
527  dictptr,
528  allowEnv,
529  allowEmpty,
530  allowSubDict
531  );
532 
533  out.append(val); // Append content
534  }
535  else
536  {
537  // Something like '$()', '$[]', etc - pass through
538  out += s[index]; // Append char
539  }
540  }
541  else if (s[index] == '}')
542  {
543  // Closing an expression or variable
544 
545  if (isExpr)
546  {
547  // Closes with '}}'
548  ++index; // Index past closing '}'
549 
550  if (s[index] == '}')
551  {
552  ++index; // Index past closing '}'
553  }
554  else if (dictptr != nullptr)
555  {
556  // Missing '}'? - Warn/error/ignore
557  FatalIOErrorInFunction(*dictptr)
558  << "Expansion ${{ is missing a closing '}}'\n"
559  << exit(FatalIOError);
560  }
561  else
562  {
564  << "Expansion ${{ is missing a closing '}}'\n"
565  << exit(FatalError);
566  }
567 
569 
570  // Even with allow empty, expressions may need content
571 
572  string val(stringOps::evaluate(out));
574 
575  return val;
576  }
577  else if (isVar)
578  {
579  // Variable - closes with '}'
580 
581  ++index; // Index past closing '}'
582 
583  return
585  (
586  out,
587  dictptr,
588  allowEnv,
589  allowEmpty,
590  allowSubDict
591  );
592  }
593  else
594  {
595  // Stray '}'? - Leave on output
596 
597  out += s[index]; // append char
598  }
599  }
600  else
601  {
602  out += s[index]; // append char
603  }
604  }
605 
606  return out;
607 }
608 
609 
610 static void expandString
611 (
612  std::string& s,
613  const dictionary* dictptr,
614  const bool allowEnv,
615  const bool allowEmpty,
616  const bool allowSubDict,
617  const char sigil
618 )
619 {
620  std::string::size_type varBeg = 0;
621 
622  // Expand $VAR, ${VAR} or ${{EXPR}}
623  // Repeat until nothing more is found
624  while
625  (
626  (varBeg = s.find(sigil, varBeg)) != std::string::npos
627  && varBeg < s.size()-1
628  )
629  {
630  if (varBeg == 0 || s[varBeg-1] != '\\')
631  {
632  if (s[varBeg+1] == '{')
633  {
634  // Recursive variable expansion mode: '${' or '${{'
635  const auto replaceBeg = varBeg;
636 
637  varBeg += 2;
638  string varValue
639  (
641  (
642  s,
643  varBeg,
644  dictptr,
645  allowEnv,
646  allowEmpty,
647  allowSubDict
648  )
649  );
650 
651  s.replace(replaceBeg, varBeg - replaceBeg, varValue);
652  varBeg = replaceBeg+varValue.size();
653  }
654  else if (validVariableChar(s[varBeg+1]))
655  {
656  // A regular $var expansion without surrounding {}.
657  const auto varLen(findVariableLen(s, varBeg, sigil));
658  const word varName(s.substr(varBeg+1, varLen), false);
659 
660  string varValue
661  (
663  (
664  varName,
665  dictptr,
666  allowEnv,
667  allowEmpty,
668  allowSubDict
669  )
670  );
671 
672  s.replace(varBeg, varName.size()+1, varValue);
673  varBeg += varValue.size();
674  }
675  else
676  {
677  ++varBeg;
678  }
679  }
680  else
681  {
682  ++varBeg;
683  }
684  }
685 
686  expandLeading(s);
687 }
689 } // End namespace Foam
690 
691 
692 // * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
693 
695 (
696  const std::string& s,
697  const char c
698 )
699 {
700  return std::count(s.cbegin(), s.cend(), c);
701 }
702 
703 
704 std::string::size_type Foam::stringOps::count(const char* s, const char c)
705 {
706  size_t len = (s ? strlen(s) : 0);
707  return len ? std::count(s, (s + len), c) : 0;
708 }
709 
710 
712 (
713  const std::string& s,
714  const HashTable<string>& mapping,
715  const char sigil
716 )
717 {
718  string out(s);
719  inplaceExpand(out, mapping);
720  return out;
721 }
722 
723 
725 (
726  std::string& s,
727  const HashTable<string>& mapping,
728  const char sigil
729 )
730 {
731  std::string::size_type varBeg = 0;
732 
733  // Expand $VAR or ${VAR}
734  // Repeat until nothing more is found
735  while
736  (
737  (varBeg = s.find(sigil, varBeg)) != std::string::npos
738  && varBeg < s.size()-1
739  )
740  {
741  if (varBeg == 0 || s[varBeg-1] != '\\')
742  {
743  // Find end of first occurrence
744  std::string::size_type varEnd = varBeg;
745  std::string::size_type delim = 0;
746 
747  // The type/position of the ":-" or ":+" alternative values
748  int altType = 0;
749  auto altPos = std::string::npos;
750 
751  if (s[varBeg+1] == '{')
752  {
753  varEnd = s.find('}', varBeg);
754  delim = 1;
755 
756  // Check for ${parameter:-word} or ${parameter:+word}
757  if (varEnd != std::string::npos)
758  {
759  altPos = varBeg;
760  altType = findParameterAlternative(s, altPos, varEnd);
761  }
762  }
763  else
764  {
765  varEnd += findVariableLen(s, varBeg, sigil);
766  }
767 
768  if (varEnd == std::string::npos)
769  {
770  // Likely parsed '${...' without closing '}' - abort
771  break;
772  }
773  else if (varEnd == varBeg)
774  {
775  // Something like '$()', '$[]', etc - pass through
776  ++varBeg;
777  }
778  else
779  {
780  const word varName
781  (
782  s.substr
783  (
784  varBeg + 1 + delim,
785  (
786  (altPos == std::string::npos ? varEnd : altPos)
787  - varBeg - 2*delim
788  )
789  ),
790  false
791  );
792 
793  std::string altValue;
794  if (altPos != std::string::npos)
795  {
796  // Had ":-" or ":+" alternative value
797  altValue = s.substr
798  (
799  altPos + 2,
800  varEnd - altPos - 2*delim
801  );
802  }
803 
804 
805  const auto fnd = mapping.cfind(varName);
806 
807  if (fnd.found() ? (altType == '+') : (altType == '-'))
808  {
809  // Found and ":+" alternative
810  // Not-found and ":-" alternative
811 
812  s.replace(varBeg, varEnd - varBeg + 1, altValue);
813  varBeg += altValue.size();
814  }
815  else if (fnd.found())
816  {
817  // Found: use value
818  s.replace(varBeg, varEnd - varBeg + 1, *fnd);
819  varBeg += (*fnd).size();
820  }
821  else
822  {
823  // Not-found: empty value
824  s.erase(varBeg, varEnd - varBeg + 1);
825  }
826  }
827  }
828  else
829  {
830  ++varBeg;
831  }
832  }
833 }
834 
835 
837 (
838  const std::string& s,
839  const dictionary& dict,
840  const char sigil
841 )
842 {
843  string out(s);
844  inplaceExpand(out, dict, sigil);
845  return out;
846 }
847 
848 
850 (
851  std::string& s,
852  const dictionary& dict,
853  const bool allowEnv,
854  const bool allowEmpty,
855  const bool allowSubDict,
856  const char sigil
857 )
858 {
859  expandString(s, &dict, allowEnv, allowEmpty, allowSubDict, sigil);
860 }
861 
862 
864 (
865  std::string& s,
866  const dictionary& dict,
867  const char sigil
868 )
869 {
870  // Allow everything, including subDict expansions
871  // env=true, empty=true, subDict=true
872  expandString(s, &dict, true, true, true, sigil);
873 }
874 
875 
877 (
878  const std::string& s,
879  const bool allowEmpty
880 )
881 {
882  string out(s);
883  inplaceExpand(out, allowEmpty);
884  return out;
885 }
886 
887 
889 (
890  std::string& s,
891  const bool allowEmpty
892 )
893 {
894  // Expand without a dictionary context
895  // allowEnv=true, allowSubDict=N/A
896  expandString(s, nullptr, true, allowEmpty, false, '$');
897 }
898 
899 
900 bool Foam::stringOps::inplaceReplaceVar(std::string& s, const word& varName)
901 {
902  if (s.empty() || varName.empty())
903  {
904  return false;
905  }
906 
907  const string content(Foam::getEnv(varName));
908  if (content.empty())
909  {
910  return false;
911  }
912 
913  const auto i = s.find(content);
914  if (i == std::string::npos)
915  {
916  return false;
917  }
918 
919  s.replace(i, content.size(), string("${" + varName + "}"));
920  return true;
921 }
922 
923 
924 Foam::string Foam::stringOps::trimLeft(const std::string& s)
925 {
926  if (!s.empty())
927  {
929  const auto end = s.length();
930 
931  while (pos < end && std::isspace(s[pos]))
932  {
933  ++pos;
934  }
935 
936  if (pos)
937  {
938  return s.substr(pos);
939  }
940  }
941 
942  return s;
943 }
944 
945 
946 void Foam::stringOps::inplaceTrimLeft(std::string& s)
947 {
948  if (!s.empty())
949  {
951  const auto end = s.length();
952 
953  while (pos < end && std::isspace(s[pos]))
954  {
955  ++pos;
956  }
957 
958  if (pos)
959  {
960  s.erase(0, pos);
961  }
962  }
963 }
964 
965 
966 Foam::string Foam::stringOps::trimRight(const std::string& s)
967 {
968  if (!s.empty())
969  {
970  auto end = s.length();
971  while (end && std::isspace(s[end-1]))
972  {
973  --end;
974  }
975 
976  if (end < s.length())
977  {
978  return s.substr(0, end);
979  }
980  }
981 
982  return s;
983 }
984 
985 
986 void Foam::stringOps::inplaceTrimRight(std::string& s)
987 {
988  if (!s.empty())
989  {
990  auto end = s.length();
991  while (end && std::isspace(s[end-1]))
992  {
993  --end;
994  }
995 
996  s.erase(end);
997  }
998 }
999 
1000 
1001 std::pair<std::size_t, std::size_t>
1003 (
1004  const std::string& s,
1005  std::size_t pos,
1006  std::size_t len
1007 )
1008 {
1009  size_t end = s.length();
1010  if (pos >= end)
1011  {
1012  pos = end;
1013  }
1014  else if (len != std::string::npos)
1015  {
1016  len += pos;
1017 
1018  if (len < end)
1019  {
1020  end = len;
1021  }
1022  }
1023 
1024  // Right = last
1025  while (pos < end && std::isspace(s[end-1]))
1026  {
1027  --end;
1028  }
1029 
1030  // Left = first
1031  while (pos < end && std::isspace(s[pos]))
1032  {
1033  ++pos;
1034  }
1035 
1036  return std::pair<std::size_t, std::size_t>(pos, end);
1037 }
1038 
1039 
1040 Foam::string Foam::stringOps::trim(const std::string& s)
1041 {
1043  std::string::size_type end = s.length();
1044 
1045  // Right
1046  while (pos < end && std::isspace(s[end-1]))
1047  {
1048  --end;
1049  }
1050 
1051  // Left
1052  while (pos < end && std::isspace(s[pos]))
1053  {
1054  ++pos;
1055  }
1056 
1057  return s.substr(pos, end-pos);
1058 }
1059 
1060 
1062 {
1064  inplaceTrimLeft(s);
1065 }
1066 
1068 void Foam::stringOps::inplaceRemoveSpace(std::string& s)
1069 {
1070  s.erase(std::remove_if(s.begin(), s.end(), ::isspace), s.end());
1071 }
1072 
1073 
1076  string out(s);
1077  inplaceRemoveComments(out);
1078  return out;
1079 }
1080 
1081 
1082 void Foam::stringOps::inplaceRemoveComments(std::string& s)
1083 {
1084  const auto len = s.length();
1085 
1086  if (len < 2)
1087  {
1088  return;
1089  }
1090 
1092 
1093  for (std::string::size_type i = 0; i < len; ++i)
1094  {
1095  char c = s[i];
1096 
1097  if (n != i)
1098  {
1099  s[n] = c;
1100  }
1101  ++n;
1102 
1103  // The start of a C/C++ comment?
1104  if (c == '/')
1105  {
1106  ++i;
1107 
1108  if (i == len)
1109  {
1110  // No further characters
1111  break;
1112  }
1113 
1114  c = s[i];
1115 
1116  if (c == '/')
1117  {
1118  // C++ comment - remove to end-of-line
1119 
1120  --n;
1121  s[n] = '\n';
1122 
1123  // Backtrack to eliminate leading spaces,
1124  // up to the previous newline
1125 
1126  while (n && std::isspace(s[n-1]))
1127  {
1128  --n;
1129 
1130  if (s[n] == '\n')
1131  {
1132  break;
1133  }
1134 
1135  s[n] = '\n';
1136  }
1137 
1138  i = s.find('\n', ++i);
1139 
1140  if (i == std::string::npos)
1141  {
1142  // Truncated - done
1143  break;
1144  }
1145 
1146  ++n; // Include newline in output
1147  }
1148  else if (c == '*')
1149  {
1150  // C comment - search for '*/'
1151  --n;
1152  i = s.find("*/", ++i, 2);
1153 
1154  if (i == std::string::npos)
1155  {
1156  // Truncated - done
1157  break;
1158  }
1159 
1160  ++i; // Index past first of "*/", loop increment does the rest
1161  }
1162  else
1163  {
1164  // Not a C/C++ comment
1165  if (n != i)
1166  {
1167  s[n] = c;
1168  }
1169  ++n;
1170  }
1171  }
1172  }
1173 
1174  s.erase(n);
1175 }
1176 
1177 
1178 Foam::string Foam::stringOps::lower(const std::string& s)
1179 {
1180  string out;
1181  out.resize(s.length());
1182 
1183  std::transform(s.begin(), s.end(), out.begin(), ::tolower);
1184  return out;
1185 }
1186 
1188 void Foam::stringOps::inplaceLower(std::string& s)
1189 {
1190  std::transform(s.begin(), s.end(), s.begin(), ::tolower);
1191 }
1192 
1193 
1194 Foam::string Foam::stringOps::upper(const std::string& s)
1195 {
1196  string out;
1197  out.resize(s.length());
1198 
1199  std::transform(s.begin(), s.end(), out.begin(), ::toupper);
1200  return out;
1201 }
1202 
1203 
1205 {
1206  std::transform(s.begin(), s.end(), s.begin(), ::toupper);
1207 }
1208 
1209 
1211 (
1212  OSstream& os,
1213  const std::string& str,
1214  const std::string::size_type width,
1216  const bool escape
1217 )
1218 {
1219  // Disabled below some minimal lower limit
1220  if (width <= 8)
1221  {
1222  char c = 0;
1223 
1224  const auto len = str.size();
1225 
1226  for (std::string::size_type pos = 0; pos < len; ++pos)
1227  {
1228  c = str[pos];
1229 
1230  if (escape && c == '\\')
1231  {
1232  os << '\\';
1233  }
1234  os << c;
1235  }
1236 
1237  // Trailing newline for non-empty string and if still pending
1238  if (len && c != '\n')
1239  {
1240  os << '\n';
1241  }
1242 
1243  return;
1244  }
1245 
1246 
1247  // Normal case
1248  std::size_t pos = 0;
1249  const auto len = str.size();
1250 
1251  // Output leading newlines without any indention
1252  while (pos < len && str[pos] == '\n')
1253  {
1254  os << '\n';
1255  ++pos;
1256  }
1257 
1258  while (pos < len)
1259  {
1260  // Potential end point, break point and next point
1261  std::string::size_type endp = pos + width;
1262  std::string::size_type breakp = str.find('\n', pos);
1263  std::string::size_type nextp = endp;
1264 
1265  if (std::string::npos != breakp && breakp < endp)
1266  {
1267  // Embedded line break
1268  endp = breakp;
1269  nextp = breakp + 1; // Skip this newline in the next chunk
1270 
1271  // Trim trailing space
1272  while
1273  (
1274  (endp > pos)
1275  && (str[endp-1] == ' ' || str[endp-1] == '\t')
1276  )
1277  {
1278  --endp;
1279  }
1280  }
1281  else if (endp >= len)
1282  {
1283  // Can output the rest without any wrapping, no line-breaks
1284  nextp = endp = len;
1285  }
1286  else
1287  {
1288  // Find a good point to break the string
1289  // try to find space/tab, or use punctuation as a fallback
1290 
1291  breakp = nextp = endp;
1292  std::string::size_type punc = std::string::npos;
1293 
1294  // Backtrack to find whitespace
1295  bool foundBreak = false;
1296  while (breakp > pos)
1297  {
1298  --breakp;
1299 
1300  const char c = str[breakp];
1301 
1302  if (c == ' ' || c == '\t')
1303  {
1304  foundBreak = true;
1305  endp = breakp;
1306  // Found a space, but continue loop anyhow
1307  // (trims trailing space)
1308  }
1309  else if (foundBreak)
1310  {
1311  // Non-whitespace encountered while consuming
1312  // trailing space. We are done
1313  break;
1314  }
1315  else
1316  {
1317  // Potentially viable as last non-whitespace?
1318  nextp = breakp;
1319 
1320  // Remember if we see any punctuation characters
1321  // - useful later as fallback
1322  if (punc == std::string::npos)
1323  {
1324  switch (c)
1325  {
1326  // Break before the punctuation
1327  case '(' : case '<' :
1328  {
1329  punc = breakp;
1330  break;
1331  }
1332  // Break after the punctuation
1333  case ')' : case '>' :
1334  case ',' : case '.' :
1335  case ':' : case ';' :
1336  case '/' : case '|' :
1337  {
1338  punc = (breakp + 1);
1339  break;
1340  }
1341  }
1342  }
1343  }
1344  }
1345 
1346  if (!foundBreak)
1347  {
1348  // No whitespace breaks, but perhaps a punctuation break.
1349  // Otherwise can't do much else
1350 
1351  if (punc != std::string::npos)
1352  {
1353  nextp = endp = punc;
1354  }
1355  else
1356  {
1357  nextp = endp;
1358  }
1359  }
1360  }
1361 
1362 
1363  // Output
1364  // ~~~~~~
1365  // Indent subsequent lines.
1366  // - assuming the one was done prior to calling this routine.
1367  // - no extra indent if it will only have a newline
1368 
1369  if (pos && (pos < endp))
1370  {
1371  // Put indent
1372  for (std::string::size_type i = 0; i < indent; ++i)
1373  {
1374  os << ' ';
1375  }
1376  }
1377 
1378  while (pos < endp)
1379  {
1380  const char c = str[pos];
1381 
1382  if (escape && c == '\\')
1383  {
1384  os << '\\';
1385  }
1386  os << c;
1387 
1388  ++pos;
1389  }
1390  os << nl;
1391 
1392  pos = nextp;
1393  }
1394 }
1395 
1396 
1397 // ************************************************************************* //
void inplaceUpper(std::string &s)
Inplace transform string with std::toupper on each character.
Definition: stringOps.C:1197
static std::string entryToString(const entry *eptr, const bool allowSubDict)
Definition: stringOps.C:218
Generic output stream using a standard (STL) stream.
Definition: OSstream.H:50
dictionary dict
A class for handling file names.
Definition: fileName.H:71
Ostream & indent(Ostream &os)
Indent stream.
Definition: Ostream.H:449
string trimLeft(const std::string &s)
Return string trimmed of leading whitespace.
Definition: stringOps.C:917
bool inplaceReplaceVar(std::string &s, const word &varName)
Replace environment variable contents with its name.
Definition: stringOps.C:893
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
static char findParameterAlternative(const std::string &s, std::string::size_type &pos, std::string::size_type endPos=std::string::npos)
static unsigned short modeToLocation(const std::string &mode, std::size_t pos=0)
Definition: stringOps.C:44
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:120
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:578
Input/output from string buffers.
std::pair< size_t, size_t > findTrim(const std::string &s, size_t pos=0, size_t len=std::string::npos)
Find (first, last) non-space locations in string or sub-string.
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:49
void inplaceRemoveSpace(std::string &s)
Eliminate whitespace inplace.
Definition: stringOps.C:1061
void inplaceTrim(std::string &s)
Trim leading and trailing whitespace inplace.
Definition: stringOps.C:1054
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:487
static unsigned int defaultPrecision() noexcept
Return the default precision.
Definition: IOstream.H:416
string upper(const std::string &s)
Return string copy transformed with std::toupper on each character.
Definition: stringOps.C:1187
string getEnv(const std::string &envName)
Get environment value for given envName.
Definition: POSIX.C:292
IOstream & fixed(IOstream &io)
Definition: IOstream.H:560
fileName home()
Return home directory path name for the current user.
Definition: POSIX.C:393
static void expandLeading(std::string &s)
Definition: stringOps.C:179
const entry * findScoped(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Search for a scoped entry (const access) with the given keyword.
Definition: dictionaryI.H:110
static bool warnAboutAge(const int version) noexcept
Test if an age warning should be emitted.
Definition: error.C:51
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
dimensionedScalar pos(const dimensionedScalar &ds)
unsigned int count(const UList< bool > &bools, const bool val=true)
Count number of &#39;true&#39; entries.
Definition: BitOps.H:73
void inplaceTrimRight(std::string &s)
Trim trailing whitespace inplace.
Definition: stringOps.C:979
bool assign(const token &tok)
Assign from word or string token.
Definition: fileNameIO.C:35
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:52
static bool master(const label communicator=-1)
Like Pstream::master but with a Pstream::parRun guard in case Pstream has not yet been initialised...
Definition: error.C:37
const_iterator cfind(const Key &key) const
Find and return an const_iterator set at the hashed entry.
Definition: HashTableI.H:134
const dimensionedScalar b
Wien displacement law constant: default SI units: [m.K].
Definition: createFields.H:27
A class for handling words, derived from Foam::string.
Definition: word.H:63
Functions to search &#39;etc&#39; directories for configuration files etc.
string evaluate(label fieldWidth, const std::string &s, size_t pos=0, size_t len=std::string::npos)
String evaluation with specified (positive, non-zero) field width.
string trimRight(const std::string &s)
Return string trimmed of trailing whitespace.
Definition: stringOps.C:959
void inplaceTrimLeft(std::string &s)
Trim leading whitespace inplace.
Definition: stringOps.C:939
string trim(const std::string &s)
Return string trimmed of leading and trailing whitespace.
Definition: stringOps.C:1033
A HashTable similar to std::unordered_map.
Definition: HashTable.H:102
graph_traits< Graph >::vertices_size_type size_type
Definition: SloanRenumber.C:68
void inplaceRemoveComments(std::string &s)
Remove C/C++ comments inplace.
Definition: stringOps.C:1075
void inplaceExpand(std::string &s, const HashTable< string > &mapping, const char sigil='$')
Inplace expand occurrences of variables according to the mapping. Does not use environment values...
Definition: stringOps.C:718
constexpr auto end(C &c) -> decltype(c.end())
Return iterator to the end of the container c.
Definition: stdFoam.H:193
OBJstream os(runTime.globalPath()/outputName)
static Foam::string recursiveExpand(const std::string &s, std::string::size_type &index, const dictionary *dictptr, const bool allowEnv, const bool allowEmpty, const bool allowSubDict)
Definition: stringOps.C:453
fileName findEtcEntry(const fileName &name, unsigned short location=0777, const fileName::Type typeRequired=fileName::Type::UNDEFINED)
Search for a single FILE or DIRECTORY within the etc directories.
Definition: etcFiles.C:408
static Foam::string getVariable(const word &name, const dictionary *dictptr, const bool allowEnv, const bool allowEmpty, const bool allowSubDict)
Definition: stringOps.C:372
const std::string version
OpenFOAM version (name or stringified number) as a std::string.
void writeWrapped(OSstream &os, const std::string &str, const std::string::size_type width, const std::string::size_type indent=0, const bool escape=false)
Output string with text wrapping.
Definition: stringOps.C:1204
string lower(const std::string &s)
Return string copy transformed with std::tolower on each character.
Definition: stringOps.C:1171
fileName findEtcFile(const fileName &name, const bool mandatory=false, unsigned short location=0777)
Search for a single FILE within the etc directories.
Definition: etcFiles.C:439
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:607
void inplaceLower(std::string &s)
Inplace transform string with std::tolower on each character.
Definition: stringOps.C:1181
static void expandLeadingTilde(std::string &s)
Definition: stringOps.C:126
const dimensionedScalar c
Speed of light in a vacuum.
bool isspace(char c) noexcept
Test for whitespace (C-locale)
Definition: char.H:71
std::string::size_type count(const std::string &s, const char c)
Count the number of occurrences of the specified character.
Definition: stringOps.C:688
fileName cwd()
The physical or logical current working directory path name.
Definition: POSIX.C:543
static void expandString(std::string &s, const dictionary *dictptr, const bool allowEnv, const bool allowEmpty, const bool allowSubDict, const char sigil)
Definition: stringOps.C:604
string removeComments(const std::string &s)
Return string with C/C++ comments removed.
Definition: stringOps.C:1067
label n
gmvFile<< "tracers "<< particles.size()<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().x()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().y()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.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))
static void expandLeadingTag(std::string &s, const char b, const char e)
Definition: stringOps.C:68
dimensionSet transform(const dimensionSet &ds)
Return the argument; transformations do not change the dimensions.
Definition: dimensionSet.C:529
mode_t mode(const fileName &name, const bool followLink=true)
Return the file mode, normally following symbolic links.
Definition: POSIX.C:726
A class for handling character strings derived from std::string.
Definition: string.H:72
Namespace for OpenFOAM.
A keyword and a list of tokens is an &#39;entry&#39;.
Definition: entry.H:63
string expand(const std::string &s, const HashTable< string > &mapping, const char sigil='$')
Expand occurrences of variables according to the mapping and return the expanded string.
Definition: stringOps.C:705
IOerror FatalIOError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL IO ERROR&#39; header text and ...