ISstream.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2011-2024 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "ISstream.H"
27 #include "int.H"
28 #include "token.H"
29 #include "DynamicList.H"
30 #include <cctype>
31 
32 // * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
33 
34 char Foam::ISstream::nextValid()
35 {
36  char c = 0;
37 
38  while (true)
39  {
40  // Get next non-whitespace character
41  while (get(c) && isspace(c))
42  {}
43 
44  // Return if stream is bad - ie, previous get() failed
45  if (bad() || isspace(c))
46  {
47  break;
48  }
49 
50  // Is this the start of a C/C++ comment?
51  if (c == '/')
52  {
53  if (!get(c))
54  {
55  // Cannot get another character - return this one
56  return '/';
57  }
58 
59  if (c == '/')
60  {
61  // C++ style single-line comment - skip through past end-of-line
62  while (get(c) && c != '\n')
63  {}
64  }
65  else if (c == '*')
66  {
67  // Within a C-style comment
68  while (true)
69  {
70  // Search for end of C-style comment - '*/'
71  if (get(c) && c == '*')
72  {
73  if (get(c))
74  {
75  if (c == '/')
76  {
77  // Matched '*/'
78  break;
79  }
80  else if (c == '*')
81  {
82  // Check again
83  putback(c);
84  }
85  }
86  }
87 
88  if (!good())
89  {
90  return 0;
91  }
92  }
93  }
94  else
95  {
96  // The '/' did not start a C/C++ comment - return it
97  putback(c);
98  return '/';
99  }
100  }
101  else
102  {
103  // A valid character - return it
104  return c;
105  }
106  }
107 
108  return 0;
109 }
110 
111 
112 void Foam::ISstream::readWordToken(token& t)
113 {
114  word* wPtr = new word;
115 
116  if (read(*wPtr).bad())
117  {
118  delete wPtr;
119  t.setBad();
120  }
121  else if (token::compound::isCompound(*wPtr))
122  {
123  t = token::compound::New(*wPtr, *this).ptr();
124  delete wPtr;
125  }
126  else
127  {
128  t = wPtr;
129  }
130 }
131 
132 
134 {
135  // Return the put back token if it exists
136  if (Istream::getBack(t))
137  {
138  return *this;
139  }
140 
141  // Assume that the streams supplied are in working order.
142  // Lines are counted by '\n'
143 
144  // Get next 'valid character': i.e. proceed through any whitespace
145  // and/or comments until a semantically valid character is found
146 
147  char c = nextValid();
148 
149  // Set the line number of this token to the current stream line number
150  t.lineNumber() = lineNumber();
151 
152  // Return on error
153  if (!c)
154  {
155  t.setBad();
156  return *this;
157  }
158 
159  // Analyse input starting with this character.
160  switch (c)
161  {
162  // Check for punctuation first
163 
164  case token::END_STATEMENT :
165  case token::BEGIN_LIST :
166  case token::END_LIST :
167  case token::BEGIN_SQR :
168  case token::END_SQR :
169  case token::BEGIN_BLOCK :
170  case token::END_BLOCK :
171  case token::COLON :
172  case token::COMMA :
173  case token::ASSIGN :
174  case token::ADD :
175  // NB: token::SUBTRACT handled later as the possible start of a Number
176  case token::MULTIPLY :
177  case token::DIVIDE :
178  {
180  return *this;
181  }
182 
183  // String: enclosed by double quotes.
184  case token::BEGIN_STRING :
185  {
186  putback(c);
187  string* sPtr = new string;
188 
189  if (read(*sPtr).bad())
190  {
191  delete sPtr;
192  t.setBad();
193  }
194  else
195  {
196  t = sPtr;
197  }
198 
199  return *this;
200  }
201 
202  // Possible verbatim string or dictionary functionEntry
203  case token::HASH :
204  {
205  char nextC;
206  if (read(nextC).bad())
207  {
208  // Return hash as word
209  t = token(word(c));
210  return *this;
211  }
212  else if (nextC == token::BEGIN_BLOCK)
213  {
214  // Verbatim string
215  verbatimString* vsPtr = new verbatimString;
216 
217  if (readVerbatim(*vsPtr).bad())
218  {
219  delete vsPtr;
220  t.setBad();
221  }
222  else
223  {
224  t = vsPtr;
225  }
226  return *this;
227  }
228  else
229  {
230  // Function name beginning with a #
231  putback(nextC);
232  putback(c);
233 
234  functionName* fnPtr = new functionName;
235 
236  if (read(*fnPtr).bad())
237  {
238  delete fnPtr;
239  t.setBad();
240  }
241  else
242  {
243  t = fnPtr;
244  }
245  return *this;
246  }
247  }
248 
249  case '$' :
250  {
251  // Look ahead
252  char nextC;
253  if (read(nextC).bad())
254  {
255  // Return $ as a word
256  t = token(word(c));
257  return *this;
258  }
259  else
260  {
261  putback(nextC);
262  putback(c);
263 
264  variable* vPtr = new variable;
265 
266  if (readVariable(*vPtr).bad())
267  {
268  delete vPtr;
269  t.setBad();
270  }
271  else
272  {
273  t = vPtr;
274  }
275 
276  return *this;
277  }
278  }
279 
280  // Number: integer or floating point
281  //
282  // ideally match the equivalent of this regular expression
283  //
284  // /[-+]?([0-9]+\.?[0-9]*|\.[0-9]+)([Ee][-+]?[0-9]+)?/
285  //
286  case '-' :
287  case '.' :
288  case '0' : case '1' : case '2' : case '3' : case '4' :
289  case '5' : case '6' : case '7' : case '8' : case '9' :
290  {
291  bool asLabel = (c != '.');
292 
293  buf_.clear();
294  buf_.append(c);
295 
296  // Get everything that could resemble a number and let
297  // readScalar determine the validity
298  while
299  (
300  is_.get(c)
301  && (
302  isdigit(c)
303  || c == '+'
304  || c == '-'
305  || c == '.'
306  || c == 'E'
307  || c == 'e'
308  )
309  )
310  {
311  if (asLabel)
312  {
313  asLabel = isdigit(c);
314  }
315 
316  buf_.append(c);
317  }
318 
319  buf_.append('\0');
320 
321  setState(is_.rdstate());
322  if (is_.bad())
323  {
324  t.setBad();
325  }
326  else
327  {
328  is_.putback(c);
329 
330  if (buf_.size() == 2 && buf_[0] == '-')
331  {
332  // A single '-' is punctuation
334  }
335  else if (asLabel)
336  {
337  label labelVal = 0;
338  uLabel uLabelVal = 0;
339  #if WM_LABEL_SIZE == 32
340  int64_t int64Val = 0;
341  uint64_t uint64Val = 0;
342  #endif
343  scalar scalarVal;
344  if (Foam::read(buf_.cdata(), labelVal))
345  {
346  t = labelVal;
347  }
348  else if (Foam::read(buf_.cdata(), uLabelVal))
349  {
350  t = uLabelVal;
351  }
352  #if WM_LABEL_SIZE == 32
353  else if (Foam::read(buf_.cdata(), int64Val))
354  {
355  t = int64Val;
356  }
357  else if (Foam::read(buf_.cdata(), uint64Val))
358  {
359  t = uint64Val;
360  }
361  #endif
362  else if (readScalar(buf_.cdata(), scalarVal))
363  {
364  t = scalarVal;
365  }
366  else
367  {
368  t.setBad();
369  }
370  }
371  else
372  {
373  scalar scalarVal;
374  if (readScalar(buf_.cdata(), scalarVal))
375  {
376  t = scalarVal;
377  }
378  else
379  {
380  t.setBad();
381  }
382  }
383  }
384 
385  return *this;
386  }
387 
388  // Should be a word (which can also be a single character)
389  default:
390  {
391  putback(c);
392  readWordToken(t);
393 
394  return *this;
395  }
396  }
397 }
398 
399 
401 {
402  c = nextValid();
403  return *this;
404 }
405 
406 
408 {
409  buf_.clear();
410 
411  int listDepth = 0;
412  char c;
413 
414  while (get(c) && word::valid(c))
415  {
416  if (c == token::BEGIN_LIST)
417  {
418  listDepth++;
419  }
420  else if (c == token::END_LIST)
421  {
422  if (listDepth)
423  {
424  listDepth--;
425  }
426  else
427  {
428  break;
429  }
430  }
431 
432  buf_.append(c);
433  }
434 
435  if (bad())
436  {
437  // Truncate the string to the maximum error length
438  buf_.data()[bufErrorLength] = buf_.last() = '\0';
439 
441  << "problem while reading word '" << buf_.cdata() << "...' after "
442  << buf_.size() << " characters\n"
443  << exit(FatalIOError);
444 
445  return *this;
446  }
447 
448  if (buf_.empty())
449  {
451  << "invalid first character found : " << c
452  << exit(FatalIOError);
453  }
454 
455  // Append end string character
456  buf_.append('\0');
457  str = buf_.cdata();
458  putback(c);
459 
460  return *this;
461 }
462 
463 
465 {
466  buf_.clear();
467 
468  char c;
469 
470  if (!get(c))
471  {
473  << "cannot read start of string"
474  << exit(FatalIOError);
475 
476  return *this;
477  }
478 
479  // Note, we could also handle single-quoted strings here (if desired)
480  if (c != token::BEGIN_STRING)
481  {
483  << "Incorrect start of string character found : " << c
484  << exit(FatalIOError);
485 
486  return *this;
487  }
488 
489  bool escaped = false;
490 
491  while (get(c))
492  {
493  if (c == token::END_STRING)
494  {
495  if (escaped)
496  {
497  escaped = false;
498  buf_.remove(); // Overwrite backslash
499  }
500  else
501  {
502  // Append end string character
503  buf_.append('\0');
504  str = buf_.cdata();
505  return *this;
506  }
507  }
508  else if (c == token::NL)
509  {
510  if (escaped)
511  {
512  escaped = false;
513  buf_.remove(); // Overwrite backslash
514  }
515  else
516  {
517  // Truncate the string to the maximum error length
518  buf_.data()[bufErrorLength] = buf_.last() = '\0';
519 
521  << "found '\\n' while reading string \""
522  << buf_.cdata() << "...\""
523  << exit(FatalIOError);
524 
525  return *this;
526  }
527  }
528  else if (c == '\\')
529  {
530  escaped = !escaped; // Toggle state (retains backslashes)
531  }
532  else
533  {
534  escaped = false;
535  }
536 
537  buf_.append(c);
538  }
539 
540  // Truncate the string to the maximum error length
541  buf_.data()[bufErrorLength] = buf_.last() = '\0';
542 
544  << "problem while reading string \"" << buf_.cdata() << "...\""
545  << exit(FatalIOError);
546 
547  return *this;
548 }
549 
550 
551 Foam::Istream& Foam::ISstream::readVariable(string& str)
552 {
553  buf_.clear();
554 
555  char c;
556 
557  if (!get(c) || c != '$')
558  {
560  << "invalid first character found : " << c
561  << exit(FatalIOError);
562  }
563 
564  buf_.append(c);
565 
566  // Read next character to see if '{'
567  if (get(c) && c == token::BEGIN_BLOCK)
568  {
569  // Read, counting brackets
570  buf_.append(c);
571 
572  while
573  (
574  get(c)
575  && (
577  || c == token::END_BLOCK
578  || variable::valid(c)
579  )
580  )
581  {
582  buf_.append(c);
583 
584  int blockCount = 1;
585 
586  if (c == token::BEGIN_BLOCK)
587  {
588  blockCount++;
589  }
590  else if (c == token::END_BLOCK)
591  {
592  if (blockCount)
593  {
594  blockCount--;
595  }
596  else
597  {
598  break;
599  }
600  }
601  }
602  }
603  else
604  {
605  buf_.append(c);
606 
607  int listDepth = 0;
608 
609  while (get(c) && variable::valid(c))
610  {
611  if (c == token::BEGIN_LIST)
612  {
613  listDepth++;
614  }
615  else if (c == token::END_LIST)
616  {
617  if (listDepth)
618  {
619  listDepth--;
620  }
621  else
622  {
623  break;
624  }
625  }
626 
627  buf_.append(c);
628  }
629  }
630 
631  if (bad())
632  {
633  // Truncate the string to the maximum error length
634  buf_.data()[bufErrorLength] = buf_.last() = '\0';
635 
637  << "problem while reading string '" << buf_.cdata() << "...' after "
638  << buf_.size() << " characters\n"
639  << exit(FatalIOError);
640 
641  return *this;
642  }
643 
644  if (buf_.empty())
645  {
647  << "invalid first character found : " << c
648  << exit(FatalIOError);
649  }
650 
651  // Append end string character
652  buf_.append('\0');
653  str = buf_.cdata();
654 
655  // Note: check if we exited due to '}' or just !variable::valid.
656  if (c != token::END_BLOCK)
657  {
658  putback(c);
659  }
660 
661  return *this;
662 }
663 
664 
665 Foam::Istream& Foam::ISstream::readVerbatim(verbatimString& str)
666 {
667  buf_.clear();
668 
669  char c;
670 
671  while (get(c))
672  {
673  if (c == token::HASH)
674  {
675  char nextC;
676  get(nextC);
677  if (nextC == token::END_BLOCK)
678  {
679  buf_.append('\0');
680  str = buf_.cdata();
681  return *this;
682  }
683  else
684  {
685  putback(nextC);
686  }
687  }
688 
689  buf_.append(c);
690  }
691 
692  // Truncate the string to the maximum error length
693  buf_.data()[bufErrorLength] = buf_.last() = '\0';
694 
696  << "problem while reading string \"" << buf_.cdata() << "...\""
697  << exit(FatalIOError);
698 
699  return *this;
700 }
701 
702 
703 Foam::ISstream& Foam::ISstream::getLine(string& s, const bool continuation)
704 {
705  getline(is_, s);
706  setState(is_.rdstate());
707  lineNumber_++;
708 
709  if (continuation && s.size())
710  {
711  while (s.back() == '\\')
712  {
713  string contLine;
714  getline(is_, contLine);
715  setState(is_.rdstate());
716  lineNumber_++;
717  s.pop_back();
718  s += contLine;
719  }
720  }
721 
722  return *this;
723 }
724 
725 
726 Foam::Istream& Foam::ISstream::readDelimited
727 (
728  string& str,
729  const char begin,
730  const char end
731 )
732 {
733  str.clear();
734 
735  int listDepth = 0;
736  char c;
737 
738  while (get(c))
739  {
740  str += c;
741 
742  if (c == begin)
743  {
744  listDepth++;
745  }
746  else if (c == end)
747  {
748  listDepth--;
749 
750  if (listDepth <= 0)
751  {
752  break;
753  }
754  }
755  }
756 
757  if (bad() || listDepth != 0)
758  {
760  << " problem while reading delimited string \n"
761  << str.c_str() << endl
762  << exit(FatalIOError);
763  }
764 
765  return *this;
766 }
767 
768 
770 {
771  return readDelimited(str, token::BEGIN_LIST, token::END_LIST);
772 }
773 
774 
776 {
777  return readDelimited(str, token::BEGIN_BLOCK, token::END_BLOCK);
778 }
779 
780 
782 {
783  is_ >> val;
784  setState(is_.rdstate());
785  return *this;
786 }
787 
788 
790 {
791  is_ >> val;
792  setState(is_.rdstate());
793  return *this;
794 }
795 
796 
798 {
799  is_ >> val;
800  setState(is_.rdstate());
801  return *this;
802 }
803 
804 
806 {
807  is_ >> val;
808  setState(is_.rdstate());
809  return *this;
810 }
811 
812 
814 {
815  is_ >> val;
816  setState(is_.rdstate());
817  return *this;
818 }
819 
820 
822 {
823  is_ >> val;
824  setState(is_.rdstate());
825  return *this;
826 }
827 
828 
830 {
831  is_ >> val;
832  setState(is_.rdstate());
833  return *this;
834 }
835 
836 
837 Foam::Istream& Foam::ISstream::read(char* buf, std::streamsize count)
838 {
839  if (format() != BINARY)
840  {
842  << "stream format not binary"
843  << exit(FatalIOError);
844  }
845 
846  readBegin("binaryBlock");
847  is_.read(buf, count);
848  readEnd("binaryBlock");
849 
850  setState(is_.rdstate());
851 
852  return *this;
853 }
854 
855 
857 {
858  stdStream().rdbuf()->pubseekpos(0);
859 
860  return *this;
861 }
862 
863 
864 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
865 
866 
867 std::ios_base::fmtflags Foam::ISstream::flags() const
868 {
869  return is_.flags();
870 }
871 
872 
873 std::ios_base::fmtflags Foam::ISstream::flags(const ios_base::fmtflags f)
874 {
875  return is_.flags(f);
876 }
877 
878 
879 // ************************************************************************* //
bool bad() const
Return true if stream is corrupted.
Definition: IOstream.H:348
bool good() const
Return true if next operation might succeed.
Definition: IOstream.H:330
Generic input stream.
Definition: ISstream.H:55
virtual Istream & rewind()
Rewind and return the stream so that it may be read again.
Definition: ISstream.C:856
ISstream & get(char &)
Low-level get character function.
Definition: ISstreamI.H:58
virtual Istream & read(token &)
Return next token from stream.
Definition: ISstream.C:133
Istream & readList(string &)
Read a '(...)' delimited set of characters into a string.
Definition: ISstream.C:769
ISstream & getLine(string &, const bool continuation=true)
Read line into a string.
Definition: ISstream.C:703
Istream & readBlock(string &)
Read a '{...}' delimited set of characters into a string.
Definition: ISstream.C:775
ISstream & putback(const char &)
Low-level putback character function.
Definition: ISstreamI.H:78
virtual ios_base::fmtflags flags() const
Return flags of output stream.
Definition: ISstream.C:867
An Istream is an abstract base class for all input systems (streams, files, token lists etc)....
Definition: Istream.H:60
bool getBack(token &)
Get the put back token if there is one and return true.
Definition: Istream.C:52
A functionName is a word starting with '#'.
Definition: functionName.H:60
A class for handling character strings derived from std::string.
Definition: string.H:79
bool remove(const char)
Remove all occurrences of character returning true if string changed.
Definition: string.C:132
static autoPtr< compound > New(const word &type, Istream &)
Select null constructed.
Definition: token.C:78
static bool isCompound(const word &name)
Return true if name is a compound type.
Definition: token.C:118
A token holds items read from Istream.
Definition: token.H:73
punctuationToken
Standard punctuation tokens.
Definition: token.H:102
@ BEGIN_STRING
Definition: token.H:119
@ DIVIDE
Definition: token.H:126
@ BEGIN_BLOCK
Definition: token.H:113
@ BEGIN_SQR
Definition: token.H:111
@ END_BLOCK
Definition: token.H:114
@ END_STRING
Definition: token.H:120
@ ASSIGN
Definition: token.H:122
@ END_STATEMENT
Definition: token.H:108
@ BEGIN_LIST
Definition: token.H:109
@ SUBTRACT
Definition: token.H:124
@ END_LIST
Definition: token.H:110
@ END_SQR
Definition: token.H:112
@ MULTIPLY
Definition: token.H:125
void setBad()
Set bad.
Definition: tokenI.H:814
label lineNumber() const
Definition: tokenI.H:803
A variable is a word with support for additional characters, in particular '$' and '/'.
Definition: variable.H:61
static bool valid(char)
Is this character valid for a variable.
Definition: variableI.H:106
A class for handling verbatimStrings, derived from string.
A class for handling words, derived from string.
Definition: word.H:62
static bool valid(char)
Is this character valid for a word.
Definition: wordI.H:115
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:346
gmvFile<< "tracers "<< particles.size()<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().x()<< " ";}gmvFile<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().y()<< " ";}gmvFile<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.name(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
System integer.
const dimensionedScalar c
Speed of light in a vacuum.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
bool read(const char *, int32_t &)
Definition: int32IO.C:85
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:257
double doubleScalar
Double precision floating point scalar type.
Definition: doubleScalar.H:52
bool readScalar(const char *buf, doubleScalar &s)
Read whole of buf as a scalar. Return true if successful.
Definition: doubleScalar.H:75
float floatScalar
Float precision floating point scalar type.
Definition: floatScalar.H:52
IOerror FatalIOError
uintWM_LABEL_SIZE_t uLabel
A uLabel is an uint32_t or uint64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: uLabel.H:59
bool isspace(char c)
Definition: char.H:53
label count(const ListType &l, typename ListType::const_reference x)
Count the number of occurrences of a value in a list.
long double longDoubleScalar
Lang double precision floating point scalar type.
word format(conversionProperties.lookup("format"))
labelList f(nPoints)