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-2023 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  // Note, for OpenFOAM-v2212 and earlier: used 'fixed' notation
235  // to force floating point numbers to be printed with at least
236  // some decimal digits. However, in the meantime we are more
237  // flexible with handling float/int input so remove this constraint.
238 
239  if (eptr->isDict())
240  {
241  if (allowSubDict)
242  {
243  OStringStream buf;
244  buf.precision(16); // Some reasonably high precision
245 
246  eptr->dict().write(buf, false);
247  str = buf.str();
248  }
249  else
250  {
251  // Ignore silently...
252  }
253  }
254  else
255  {
256  // Serialized with spaces (primitiveEntry)
257  ITstream& its = eptr->stream();
258  return its.toString();
259  }
260  }
261 
262  return str;
263 }
264 
265 } // End namespace Foam
266 
267 
268 // Details for handling dictionary expansion
269 
270 namespace
271 {
272 
273 // Acceptable values for $variable names.
274 //
275 // Similar to word::valid(), except we don't have the benefit of a parser
276 // to filter out other unacceptable entries for us.
277 //
278 // Does not currently accept '/' in a variable name.
279 // We would like "$file/$name" to expand as two variables.
280 static inline bool validVariableChar(char c)
281 {
282  return
283  (
284  std::isalnum(c)
285  || c == '.'
286  || c == ':'
287  || c == '_'
288  );
289 }
290 
291 
292 // Find the type/position of the ":-" or ":+" alternative values
293 // Returns 0, '-', '+' corresponding to not-found or ':-' or ':+'
294 static inline int findParameterAlternative
295 (
296  const std::string& s,
299 )
300 {
301  while (pos != std::string::npos)
302  {
303  pos = s.find(':', pos);
304  if (pos != std::string::npos)
305  {
306  if (pos < endPos)
307  {
308  // in-range: check for '+' or '-' following the ':'
309  const int altType = s[pos+1];
310  if (altType == '+' || altType == '-')
311  {
312  return altType;
313  }
314 
315  ++pos; // unknown/unsupported - continue at next position
316  }
317  else
318  {
319  // out-of-range: abort
320  pos = std::string::npos;
321  }
322  }
323  }
324 
325  return 0;
326 }
327 
328 
329 // For input string of "$variable with other" return the length of
330 // the variable.
331 //
332 // Intentionally will not capture ':+', ':-' alterations. Use ${ .. } for that
333 static inline std::string::size_type findVariableLen
334 (
335  const std::string& s,
337  const char sigil = '$'
338 )
339 {
340  std::string::size_type len = 0;
341 
342  if (pos < s.length())
343  {
344  if (s[pos] == sigil)
345  {
346  // Skip leading '$' in the count!
347  ++pos;
348  }
349 
350  for
351  (
352  auto iter = s.cbegin() + pos;
353  iter != s.cend() && validVariableChar(*iter);
354  ++iter
355  )
356  {
357  ++len;
358  }
359  }
360 
361  return len;
362 }
363 
364 } // End namespace anonymous
365 
366 
367 namespace Foam
368 {
369 
370 // Get dictionary or (optionally) environment variable
371 //
372 // Handles default and alternative values as per the POSIX shell.
373 // \code
374 // ${parameter:-defValue}
375 // ${parameter:+altValue}
376 // \endcode
378 (
379  const word& name,
380  const dictionary* dictptr,
381  const bool allowEnv,
382  const bool allowEmpty,
383  const bool allowSubDict
384 )
385 {
386  // The type/position of the ":-" or ":+" alternative values
387  std::string::size_type altPos = 0;
388 
389  // Check for parameter:-word or parameter:+word
390  const int altType =
391  findParameterAlternative(name, altPos, name.size()-1);
392 
393  const word lookupName =
394  (altType ? word(name.substr(0,altPos), false) : name);
395 
396  const entry* eptr =
397  (
398  (dictptr != nullptr)
399  ? dictptr->findScoped(lookupName, keyType::LITERAL_RECURSIVE)
400  : nullptr
401  );
402 
403  string value;
404  if (eptr)
405  {
406  value = entryToString(eptr, allowSubDict);
407  }
408  else if (allowEnv || dictptr == nullptr)
409  {
410  value = Foam::getEnv(lookupName);
411  }
412 
413  if (value.empty() ? (altType == '-') : (altType == '+'))
414  {
415  // Not found or empty: use ":-" alternative value
416  // Found and not empty: use ":+" alternative value
417  value = name.substr(altPos + 2);
418  }
419 
420  if (!allowEmpty && value.empty())
421  {
422  if (dictptr != nullptr)
423  {
424  auto& err =
425  FatalIOErrorInFunction(*dictptr)
426  << "Cannot find dictionary entry ";
427 
428  if (allowEnv)
429  {
430  err << "or environment ";
431  }
432 
433  err << "variable '" << lookupName << "'" << nl
434  << exit(FatalIOError);
435  }
436  else
437  {
439  << "Unknown variable '" << lookupName << "'" << nl
440  << exit(FatalError);
441  }
442  }
443 
444  return value;
445 }
446 
447 
448 // Recursively expands (dictionary or environment) variable
449 // starting at index in string. Updates index.
450 //
451 // String: "abc ${var} def",
452 // Receive: "var} def"
453 //
454 // String: "abc ${{expr}} def"
455 // Receive: "{expr}} def"
456 //
457 // On return, the index will be adjust to be AFTER the closing '}'
459 (
460  const std::string& s,
461  std::string::size_type& index,
462  const dictionary* dictptr,
463  const bool allowEnv,
464  const bool allowEmpty,
465  const bool allowSubDict
466 )
467 {
469 
470  // Track ${{ expr }} expressions
471  const bool isExpr = (index < s.size() && s[index] == '{');
472 
473  if (isExpr)
474  {
475  ++index;
476  }
477 
478  // Initially called for a ${variable}, not ${{expr}}
479  bool isVar = !isExpr;
480 
481  string out;
482 
483  for (/*nil*/; index < s.size(); ++index)
484  {
486  if (s[index] == '$')
487  {
488  if (s[index+1] == '{')
489  {
490  // Recurse to parse variable name
491  index += 2;
492 
493  string val =
495  (
496  s,
497  index,
498  dictptr,
499  allowEnv,
500  allowEmpty,
501  allowSubDict
502  );
503 
504  out.append(val); // Append content
505 
507 
508  // Already skipped past '}' terminator?
509  if (s[index-1] == '}')
510  {
511  --index;
512  }
513  }
514  else if (validVariableChar(s[index+1]))
515  {
516  // A regular $var expansion without a surrounding {}.
517 
518  const auto varLen = findVariableLen(s, index);
519  const word varName(s.substr(index+1, varLen), false);
520  index += varLen;
521 
522  string val =
524  (
525  varName,
526  dictptr,
527  allowEnv,
528  allowEmpty,
529  allowSubDict
530  );
531 
532  out.append(val); // Append content
533  }
534  else
535  {
536  // Something like '$()', '$[]', etc - pass through
537  out += s[index]; // Append char
538  }
539  }
540  else if (s[index] == '}')
541  {
542  // Closing an expression or variable
543 
544  if (isExpr)
545  {
546  // Closes with '}}'
547  ++index; // Index past closing '}'
548 
549  if (s[index] == '}')
550  {
551  ++index; // Index past closing '}'
552  }
553  else if (dictptr != nullptr)
554  {
555  // Missing '}'? - Warn/error/ignore
556  FatalIOErrorInFunction(*dictptr)
557  << "Expansion ${{ is missing a closing '}}'\n"
558  << exit(FatalIOError);
559  }
560  else
561  {
563  << "Expansion ${{ is missing a closing '}}'\n"
564  << exit(FatalError);
565  }
566 
568 
569  // Even with allow empty, expressions may need content
570 
571  string val(stringOps::evaluate(out));
573 
574  return val;
575  }
576  else if (isVar)
577  {
578  // Variable - closes with '}'
579 
580  ++index; // Index past closing '}'
581 
582  return
584  (
585  out,
586  dictptr,
587  allowEnv,
588  allowEmpty,
589  allowSubDict
590  );
591  }
592  else
593  {
594  // Stray '}'? - Leave on output
595 
596  out += s[index]; // append char
597  }
598  }
599  else
600  {
601  out += s[index]; // append char
602  }
603  }
604 
605  return out;
606 }
607 
608 
609 static void expandString
610 (
611  std::string& s,
612  const dictionary* dictptr,
613  const bool allowEnv,
614  const bool allowEmpty,
615  const bool allowSubDict,
616  const char sigil
617 )
618 {
619  std::string::size_type varBeg = 0;
620 
621  // Expand $VAR, ${VAR} or ${{EXPR}}
622  // Repeat until nothing more is found
623  while
624  (
625  (varBeg = s.find(sigil, varBeg)) != std::string::npos
626  && varBeg < s.size()-1
627  )
628  {
629  if (varBeg == 0 || s[varBeg-1] != '\\')
630  {
631  if (s[varBeg+1] == '{')
632  {
633  // Recursive variable expansion mode: '${' or '${{'
634  const auto replaceBeg = varBeg;
635 
636  varBeg += 2;
637  string varValue
638  (
640  (
641  s,
642  varBeg,
643  dictptr,
644  allowEnv,
645  allowEmpty,
646  allowSubDict
647  )
648  );
649 
650  s.replace(replaceBeg, varBeg - replaceBeg, varValue);
651  varBeg = replaceBeg+varValue.size();
652  }
653  else if (validVariableChar(s[varBeg+1]))
654  {
655  // A regular $var expansion without surrounding {}.
656  const auto varLen(findVariableLen(s, varBeg, sigil));
657  const word varName(s.substr(varBeg+1, varLen), false);
658 
659  string varValue
660  (
662  (
663  varName,
664  dictptr,
665  allowEnv,
666  allowEmpty,
667  allowSubDict
668  )
669  );
670 
671  s.replace(varBeg, varName.size()+1, varValue);
672  varBeg += varValue.size();
673  }
674  else
675  {
676  ++varBeg;
677  }
678  }
679  else
680  {
681  ++varBeg;
682  }
683  }
684 
685  expandLeading(s);
686 }
687 
688 } // End namespace Foam
689 
690 
691 // * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
692 
693 std::string::size_type Foam::stringOps::count(const char* s, const char c)
694 {
695  // or: size_t len = Foam::string::length(s);
696  size_t len = (s ? strlen(s) : 0);
697  return len ? std::count(s, (s + len), c) : 0;
698 }
699 
700 
702 (
703  const std::string& s,
704  const char c
705 )
706 {
707  return std::count(s.cbegin(), s.cend(), c);
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.good() ? (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.good())
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 // ************************************************************************* //
std::string::size_type count(const char *s, const char c)
Count the number of occurrences of the specified character.
Definition: stringOps.C:686
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:72
Ostream & indent(Ostream &os)
Indent stream.
Definition: Ostream.H:493
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:129
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:598
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:50
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:531
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:339
fileName home()
Return home directory path name for the current user.
Definition: POSIX.C:440
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:114
static bool warnAboutAge(const int version) noexcept
Test if an age warning should be emitted.
Definition: error.C:67
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 expressions::valueTypeCode::INVALID.
Definition: exprTraits.C:127
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:53
const_iterator cfind(const Key &key) const
Find and return an const_iterator set at the hashed entry.
Definition: HashTableI.H:113
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:108
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:201
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:452
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:371
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:627
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:69
fileName cwd()
The physical or logical current working directory path name.
Definition: POSIX.C:590
static void expandString(std::string &s, const dictionary *dictptr, const bool allowEnv, const bool allowEmpty, const bool allowSubDict, const char sigil)
Definition: stringOps.C:603
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:521
mode_t mode(const fileName &name, const bool followLink=true)
Return the file mode, normally following symbolic links.
Definition: POSIX.C:773
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 ...