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 }
688 } // End namespace Foam
689 
690 
691 // * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
692 
694 (
695  const std::string& s,
696  const char c
697 )
698 {
699  return std::count(s.cbegin(), s.cend(), c);
700 }
701 
702 
703 std::string::size_type Foam::stringOps::count(const char* s, const char c)
704 {
705  size_t len = (s ? strlen(s) : 0);
706  return len ? std::count(s, (s + len), c) : 0;
707 }
708 
709 
711 (
712  const std::string& s,
713  const HashTable<string>& mapping,
714  const char sigil
715 )
716 {
717  string out(s);
718  inplaceExpand(out, mapping);
719  return out;
720 }
721 
722 
724 (
725  std::string& s,
726  const HashTable<string>& mapping,
727  const char sigil
728 )
729 {
730  std::string::size_type varBeg = 0;
731 
732  // Expand $VAR or ${VAR}
733  // Repeat until nothing more is found
734  while
735  (
736  (varBeg = s.find(sigil, varBeg)) != std::string::npos
737  && varBeg < s.size()-1
738  )
739  {
740  if (varBeg == 0 || s[varBeg-1] != '\\')
741  {
742  // Find end of first occurrence
743  std::string::size_type varEnd = varBeg;
744  std::string::size_type delim = 0;
745 
746  // The type/position of the ":-" or ":+" alternative values
747  int altType = 0;
748  auto altPos = std::string::npos;
749 
750  if (s[varBeg+1] == '{')
751  {
752  varEnd = s.find('}', varBeg);
753  delim = 1;
754 
755  // Check for ${parameter:-word} or ${parameter:+word}
756  if (varEnd != std::string::npos)
757  {
758  altPos = varBeg;
759  altType = findParameterAlternative(s, altPos, varEnd);
760  }
761  }
762  else
763  {
764  varEnd += findVariableLen(s, varBeg, sigil);
765  }
766 
767  if (varEnd == std::string::npos)
768  {
769  // Likely parsed '${...' without closing '}' - abort
770  break;
771  }
772  else if (varEnd == varBeg)
773  {
774  // Something like '$()', '$[]', etc - pass through
775  ++varBeg;
776  }
777  else
778  {
779  const word varName
780  (
781  s.substr
782  (
783  varBeg + 1 + delim,
784  (
785  (altPos == std::string::npos ? varEnd : altPos)
786  - varBeg - 2*delim
787  )
788  ),
789  false
790  );
791 
792  std::string altValue;
793  if (altPos != std::string::npos)
794  {
795  // Had ":-" or ":+" alternative value
796  altValue = s.substr
797  (
798  altPos + 2,
799  varEnd - altPos - 2*delim
800  );
801  }
802 
803 
804  const auto fnd = mapping.cfind(varName);
805 
806  if (fnd.good() ? (altType == '+') : (altType == '-'))
807  {
808  // Found and ":+" alternative
809  // Not-found and ":-" alternative
810 
811  s.replace(varBeg, varEnd - varBeg + 1, altValue);
812  varBeg += altValue.size();
813  }
814  else if (fnd.good())
815  {
816  // Found: use value
817  s.replace(varBeg, varEnd - varBeg + 1, *fnd);
818  varBeg += (*fnd).size();
819  }
820  else
821  {
822  // Not-found: empty value
823  s.erase(varBeg, varEnd - varBeg + 1);
824  }
825  }
826  }
827  else
828  {
829  ++varBeg;
830  }
831  }
832 }
833 
834 
836 (
837  const std::string& s,
838  const dictionary& dict,
839  const char sigil
840 )
841 {
842  string out(s);
843  inplaceExpand(out, dict, sigil);
844  return out;
845 }
846 
847 
849 (
850  std::string& s,
851  const dictionary& dict,
852  const bool allowEnv,
853  const bool allowEmpty,
854  const bool allowSubDict,
855  const char sigil
856 )
857 {
858  expandString(s, &dict, allowEnv, allowEmpty, allowSubDict, sigil);
859 }
860 
861 
863 (
864  std::string& s,
865  const dictionary& dict,
866  const char sigil
867 )
868 {
869  // Allow everything, including subDict expansions
870  // env=true, empty=true, subDict=true
871  expandString(s, &dict, true, true, true, sigil);
872 }
873 
874 
876 (
877  const std::string& s,
878  const bool allowEmpty
879 )
880 {
881  string out(s);
882  inplaceExpand(out, allowEmpty);
883  return out;
884 }
885 
886 
888 (
889  std::string& s,
890  const bool allowEmpty
891 )
892 {
893  // Expand without a dictionary context
894  // allowEnv=true, allowSubDict=N/A
895  expandString(s, nullptr, true, allowEmpty, false, '$');
896 }
897 
898 
899 bool Foam::stringOps::inplaceReplaceVar(std::string& s, const word& varName)
900 {
901  if (s.empty() || varName.empty())
902  {
903  return false;
904  }
905 
906  const string content(Foam::getEnv(varName));
907  if (content.empty())
908  {
909  return false;
910  }
911 
912  const auto i = s.find(content);
913  if (i == std::string::npos)
914  {
915  return false;
916  }
917 
918  s.replace(i, content.size(), string("${" + varName + "}"));
919  return true;
920 }
921 
922 
923 Foam::string Foam::stringOps::trimLeft(const std::string& s)
924 {
925  if (!s.empty())
926  {
928  const auto end = s.length();
929 
930  while (pos < end && std::isspace(s[pos]))
931  {
932  ++pos;
933  }
934 
935  if (pos)
936  {
937  return s.substr(pos);
938  }
939  }
940 
941  return s;
942 }
943 
944 
945 void Foam::stringOps::inplaceTrimLeft(std::string& s)
946 {
947  if (!s.empty())
948  {
950  const auto end = s.length();
951 
952  while (pos < end && std::isspace(s[pos]))
953  {
954  ++pos;
955  }
956 
957  if (pos)
958  {
959  s.erase(0, pos);
960  }
961  }
962 }
963 
964 
965 Foam::string Foam::stringOps::trimRight(const std::string& s)
966 {
967  if (!s.empty())
968  {
969  auto end = s.length();
970  while (end && std::isspace(s[end-1]))
971  {
972  --end;
973  }
974 
975  if (end < s.length())
976  {
977  return s.substr(0, end);
978  }
979  }
980 
981  return s;
982 }
983 
984 
985 void Foam::stringOps::inplaceTrimRight(std::string& s)
986 {
987  if (!s.empty())
988  {
989  auto end = s.length();
990  while (end && std::isspace(s[end-1]))
991  {
992  --end;
993  }
994 
995  s.erase(end);
996  }
997 }
998 
999 
1000 std::pair<std::size_t, std::size_t>
1002 (
1003  const std::string& s,
1004  std::size_t pos,
1005  std::size_t len
1006 )
1007 {
1008  size_t end = s.length();
1009  if (pos >= end)
1010  {
1011  pos = end;
1012  }
1013  else if (len != std::string::npos)
1014  {
1015  len += pos;
1016 
1017  if (len < end)
1018  {
1019  end = len;
1020  }
1021  }
1022 
1023  // Right = last
1024  while (pos < end && std::isspace(s[end-1]))
1025  {
1026  --end;
1027  }
1028 
1029  // Left = first
1030  while (pos < end && std::isspace(s[pos]))
1031  {
1032  ++pos;
1033  }
1034 
1035  return std::pair<std::size_t, std::size_t>(pos, end);
1036 }
1037 
1038 
1039 Foam::string Foam::stringOps::trim(const std::string& s)
1040 {
1042  std::string::size_type end = s.length();
1043 
1044  // Right
1045  while (pos < end && std::isspace(s[end-1]))
1046  {
1047  --end;
1048  }
1049 
1050  // Left
1051  while (pos < end && std::isspace(s[pos]))
1052  {
1053  ++pos;
1054  }
1055 
1056  return s.substr(pos, end-pos);
1057 }
1058 
1059 
1061 {
1063  inplaceTrimLeft(s);
1064 }
1065 
1067 void Foam::stringOps::inplaceRemoveSpace(std::string& s)
1068 {
1069  s.erase(std::remove_if(s.begin(), s.end(), ::isspace), s.end());
1070 }
1071 
1072 
1075  string out(s);
1076  inplaceRemoveComments(out);
1077  return out;
1078 }
1079 
1080 
1081 void Foam::stringOps::inplaceRemoveComments(std::string& s)
1082 {
1083  const auto len = s.length();
1084 
1085  if (len < 2)
1086  {
1087  return;
1088  }
1089 
1091 
1092  for (std::string::size_type i = 0; i < len; ++i)
1093  {
1094  char c = s[i];
1095 
1096  if (n != i)
1097  {
1098  s[n] = c;
1099  }
1100  ++n;
1101 
1102  // The start of a C/C++ comment?
1103  if (c == '/')
1104  {
1105  ++i;
1106 
1107  if (i == len)
1108  {
1109  // No further characters
1110  break;
1111  }
1112 
1113  c = s[i];
1114 
1115  if (c == '/')
1116  {
1117  // C++ comment - remove to end-of-line
1118 
1119  --n;
1120  s[n] = '\n';
1121 
1122  // Backtrack to eliminate leading spaces,
1123  // up to the previous newline
1124 
1125  while (n && std::isspace(s[n-1]))
1126  {
1127  --n;
1128 
1129  if (s[n] == '\n')
1130  {
1131  break;
1132  }
1133 
1134  s[n] = '\n';
1135  }
1136 
1137  i = s.find('\n', ++i);
1138 
1139  if (i == std::string::npos)
1140  {
1141  // Truncated - done
1142  break;
1143  }
1144 
1145  ++n; // Include newline in output
1146  }
1147  else if (c == '*')
1148  {
1149  // C comment - search for '*/'
1150  --n;
1151  i = s.find("*/", ++i, 2);
1152 
1153  if (i == std::string::npos)
1154  {
1155  // Truncated - done
1156  break;
1157  }
1158 
1159  ++i; // Index past first of "*/", loop increment does the rest
1160  }
1161  else
1162  {
1163  // Not a C/C++ comment
1164  if (n != i)
1165  {
1166  s[n] = c;
1167  }
1168  ++n;
1169  }
1170  }
1171  }
1172 
1173  s.erase(n);
1174 }
1175 
1176 
1177 Foam::string Foam::stringOps::lower(const std::string& s)
1178 {
1179  string out;
1180  out.resize(s.length());
1181 
1182  std::transform(s.begin(), s.end(), out.begin(), ::tolower);
1183  return out;
1184 }
1185 
1187 void Foam::stringOps::inplaceLower(std::string& s)
1188 {
1189  std::transform(s.begin(), s.end(), s.begin(), ::tolower);
1190 }
1191 
1192 
1193 Foam::string Foam::stringOps::upper(const std::string& s)
1194 {
1195  string out;
1196  out.resize(s.length());
1197 
1198  std::transform(s.begin(), s.end(), out.begin(), ::toupper);
1199  return out;
1200 }
1201 
1202 
1204 {
1205  std::transform(s.begin(), s.end(), s.begin(), ::toupper);
1206 }
1207 
1208 
1210 (
1211  OSstream& os,
1212  const std::string& str,
1213  const std::string::size_type width,
1215  const bool escape
1216 )
1217 {
1218  // Disabled below some minimal lower limit
1219  if (width <= 8)
1220  {
1221  char c = 0;
1222 
1223  const auto len = str.size();
1224 
1225  for (std::string::size_type pos = 0; pos < len; ++pos)
1226  {
1227  c = str[pos];
1228 
1229  if (escape && c == '\\')
1230  {
1231  os << '\\';
1232  }
1233  os << c;
1234  }
1235 
1236  // Trailing newline for non-empty string and if still pending
1237  if (len && c != '\n')
1238  {
1239  os << '\n';
1240  }
1241 
1242  return;
1243  }
1244 
1245 
1246  // Normal case
1247  std::size_t pos = 0;
1248  const auto len = str.size();
1249 
1250  // Output leading newlines without any indention
1251  while (pos < len && str[pos] == '\n')
1252  {
1253  os << '\n';
1254  ++pos;
1255  }
1256 
1257  while (pos < len)
1258  {
1259  // Potential end point, break point and next point
1260  std::string::size_type endp = pos + width;
1261  std::string::size_type breakp = str.find('\n', pos);
1262  std::string::size_type nextp = endp;
1263 
1264  if (std::string::npos != breakp && breakp < endp)
1265  {
1266  // Embedded line break
1267  endp = breakp;
1268  nextp = breakp + 1; // Skip this newline in the next chunk
1269 
1270  // Trim trailing space
1271  while
1272  (
1273  (endp > pos)
1274  && (str[endp-1] == ' ' || str[endp-1] == '\t')
1275  )
1276  {
1277  --endp;
1278  }
1279  }
1280  else if (endp >= len)
1281  {
1282  // Can output the rest without any wrapping, no line-breaks
1283  nextp = endp = len;
1284  }
1285  else
1286  {
1287  // Find a good point to break the string
1288  // try to find space/tab, or use punctuation as a fallback
1289 
1290  breakp = nextp = endp;
1291  std::string::size_type punc = std::string::npos;
1292 
1293  // Backtrack to find whitespace
1294  bool foundBreak = false;
1295  while (breakp > pos)
1296  {
1297  --breakp;
1298 
1299  const char c = str[breakp];
1300 
1301  if (c == ' ' || c == '\t')
1302  {
1303  foundBreak = true;
1304  endp = breakp;
1305  // Found a space, but continue loop anyhow
1306  // (trims trailing space)
1307  }
1308  else if (foundBreak)
1309  {
1310  // Non-whitespace encountered while consuming
1311  // trailing space. We are done
1312  break;
1313  }
1314  else
1315  {
1316  // Potentially viable as last non-whitespace?
1317  nextp = breakp;
1318 
1319  // Remember if we see any punctuation characters
1320  // - useful later as fallback
1321  if (punc == std::string::npos)
1322  {
1323  switch (c)
1324  {
1325  // Break before the punctuation
1326  case '(' : case '<' :
1327  {
1328  punc = breakp;
1329  break;
1330  }
1331  // Break after the punctuation
1332  case ')' : case '>' :
1333  case ',' : case '.' :
1334  case ':' : case ';' :
1335  case '/' : case '|' :
1336  {
1337  punc = (breakp + 1);
1338  break;
1339  }
1340  }
1341  }
1342  }
1343  }
1344 
1345  if (!foundBreak)
1346  {
1347  // No whitespace breaks, but perhaps a punctuation break.
1348  // Otherwise can't do much else
1349 
1350  if (punc != std::string::npos)
1351  {
1352  nextp = endp = punc;
1353  }
1354  else
1355  {
1356  nextp = endp;
1357  }
1358  }
1359  }
1360 
1361 
1362  // Output
1363  // ~~~~~~
1364  // Indent subsequent lines.
1365  // - assuming the one was done prior to calling this routine.
1366  // - no extra indent if it will only have a newline
1367 
1368  if (pos && (pos < endp))
1369  {
1370  // Put indent
1371  for (std::string::size_type i = 0; i < indent; ++i)
1372  {
1373  os << ' ';
1374  }
1375  }
1376 
1377  while (pos < endp)
1378  {
1379  const char c = str[pos];
1380 
1381  if (escape && c == '\\')
1382  {
1383  os << '\\';
1384  }
1385  os << c;
1386 
1387  ++pos;
1388  }
1389  os << nl;
1390 
1391  pos = nextp;
1392  }
1393 }
1394 
1395 
1396 // ************************************************************************* //
void inplaceUpper(std::string &s)
Inplace transform string with std::toupper on each character.
Definition: stringOps.C:1196
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:916
bool inplaceReplaceVar(std::string &s, const word &varName)
Replace environment variable contents with its name.
Definition: stringOps.C:892
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:1060
void inplaceTrim(std::string &s)
Trim leading and trailing whitespace inplace.
Definition: stringOps.C:1053
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:487
string upper(const std::string &s)
Return string copy transformed with std::toupper on each character.
Definition: stringOps.C:1186
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: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:978
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: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:958
void inplaceTrimLeft(std::string &s)
Trim leading whitespace inplace.
Definition: stringOps.C:938
string trim(const std::string &s)
Return string trimmed of leading and trailing whitespace.
Definition: stringOps.C:1032
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:1074
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:717
constexpr auto end(C &c) -> decltype(c.end())
Return iterator to the end of the container c.
Definition: stdFoam.H:194
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:1203
string lower(const std::string &s)
Return string copy transformed with std::tolower on each character.
Definition: stringOps.C:1170
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:1180
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
std::string::size_type count(const std::string &s, const char c)
Count the number of occurrences of the specified character.
Definition: stringOps.C:687
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:1066
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:704
IOerror FatalIOError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL IO ERROR&#39; header text and ...