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-2023 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 
184  // String: enclosed by double quotes.
185  case token::BEGIN_STRING :
186  {
187  putback(c);
188  string* sPtr = new string;
189 
190  if (read(*sPtr).bad())
191  {
192  delete sPtr;
193  t.setBad();
194  }
195  else
196  {
197  t = sPtr;
198  }
199 
200  return *this;
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
336  {
337  if (asLabel)
338  {
339  label labelVal = 0;
340  if (Foam::read(buf_.cdata(), labelVal))
341  {
342  t = labelVal;
343  }
344  else
345  {
346  // Maybe too big? Try as scalar
347  scalar scalarVal;
348  if (readScalar(buf_.cdata(), scalarVal))
349  {
350  t = scalarVal;
351  }
352  else
353  {
354  t.setBad();
355  }
356  }
357  }
358  else
359  {
360  scalar scalarVal;
361  if (readScalar(buf_.cdata(), scalarVal))
362  {
363  t = scalarVal;
364  }
365  else
366  {
367  t.setBad();
368  }
369  }
370  }
371  }
372 
373  return *this;
374  }
375 
376 
377  // Should be a word (which can also be a single character)
378  default:
379  {
380  putback(c);
381  readWordToken(t);
382 
383  return *this;
384  }
385  }
386 }
387 
388 
390 {
391  c = nextValid();
392  return *this;
393 }
394 
395 
397 {
398  buf_.clear();
399 
400  int listDepth = 0;
401  char c;
402 
403  while (get(c) && word::valid(c))
404  {
405  if (c == token::BEGIN_LIST)
406  {
407  listDepth++;
408  }
409  else if (c == token::END_LIST)
410  {
411  if (listDepth)
412  {
413  listDepth--;
414  }
415  else
416  {
417  break;
418  }
419  }
420 
421  buf_.append(c);
422  }
423 
424  if (bad())
425  {
426  // Truncate the string to the maximum error length
427  buf_.data()[bufErrorLength] = buf_.last() = '\0';
428 
430  << "problem while reading word '" << buf_.cdata() << "...' after "
431  << buf_.size() << " characters\n"
432  << exit(FatalIOError);
433 
434  return *this;
435  }
436 
437  if (buf_.empty())
438  {
440  << "invalid first character found : " << c
441  << exit(FatalIOError);
442  }
443 
444  // Append end string character
445  buf_.append('\0');
446  str = buf_.cdata();
447  putback(c);
448 
449  return *this;
450 }
451 
452 
454 {
455  buf_.clear();
456 
457  char c;
458 
459  if (!get(c))
460  {
462  << "cannot read start of string"
463  << exit(FatalIOError);
464 
465  return *this;
466  }
467 
468  // Note, we could also handle single-quoted strings here (if desired)
469  if (c != token::BEGIN_STRING)
470  {
472  << "Incorrect start of string character found : " << c
473  << exit(FatalIOError);
474 
475  return *this;
476  }
477 
478  bool escaped = false;
479 
480  while (get(c))
481  {
482  if (c == token::END_STRING)
483  {
484  if (escaped)
485  {
486  escaped = false;
487  buf_.remove(); // Overwrite backslash
488  }
489  else
490  {
491  // Append end string character
492  buf_.append('\0');
493  str = buf_.cdata();
494  return *this;
495  }
496  }
497  else if (c == token::NL)
498  {
499  if (escaped)
500  {
501  escaped = false;
502  buf_.remove(); // Overwrite backslash
503  }
504  else
505  {
506  // Truncate the string to the maximum error length
507  buf_.data()[bufErrorLength] = buf_.last() = '\0';
508 
510  << "found '\\n' while reading string \""
511  << buf_.cdata() << "...\""
512  << exit(FatalIOError);
513 
514  return *this;
515  }
516  }
517  else if (c == '\\')
518  {
519  escaped = !escaped; // Toggle state (retains backslashes)
520  }
521  else
522  {
523  escaped = false;
524  }
525 
526  buf_.append(c);
527  }
528 
529  // Truncate the string to the maximum error length
530  buf_.data()[bufErrorLength] = buf_.last() = '\0';
531 
533  << "problem while reading string \"" << buf_.cdata() << "...\""
534  << exit(FatalIOError);
535 
536  return *this;
537 }
538 
539 
540 Foam::Istream& Foam::ISstream::readVariable(string& str)
541 {
542  buf_.clear();
543 
544  char c;
545 
546  if (!get(c) || c != '$')
547  {
549  << "invalid first character found : " << c
550  << exit(FatalIOError);
551  }
552 
553  buf_.append(c);
554 
555  // Read next character to see if '{'
556  if (get(c) && c == token::BEGIN_BLOCK)
557  {
558  // Read, counting brackets
559  buf_.append(c);
560 
561  while
562  (
563  get(c)
564  && (
566  || c == token::END_BLOCK
567  || variable::valid(c)
568  )
569  )
570  {
571  buf_.append(c);
572 
573  int blockCount = 1;
574 
575  if (c == token::BEGIN_BLOCK)
576  {
577  blockCount++;
578  }
579  else if (c == token::END_BLOCK)
580  {
581  if (blockCount)
582  {
583  blockCount--;
584  }
585  else
586  {
587  break;
588  }
589  }
590  }
591  }
592  else
593  {
594  buf_.append(c);
595 
596  int listDepth = 0;
597 
598  while (get(c) && variable::valid(c))
599  {
600  if (c == token::BEGIN_LIST)
601  {
602  listDepth++;
603  }
604  else if (c == token::END_LIST)
605  {
606  if (listDepth)
607  {
608  listDepth--;
609  }
610  else
611  {
612  break;
613  }
614  }
615 
616  buf_.append(c);
617  }
618  }
619 
620  if (bad())
621  {
622  // Truncate the string to the maximum error length
623  buf_.data()[bufErrorLength] = buf_.last() = '\0';
624 
626  << "problem while reading string '" << buf_.cdata() << "...' after "
627  << buf_.size() << " characters\n"
628  << exit(FatalIOError);
629 
630  return *this;
631  }
632 
633  if (buf_.empty())
634  {
636  << "invalid first character found : " << c
637  << exit(FatalIOError);
638  }
639 
640  // Append end string character
641  buf_.append('\0');
642  str = buf_.cdata();
643 
644  // Note: check if we exited due to '}' or just !variable::valid.
645  if (c != token::END_BLOCK)
646  {
647  putback(c);
648  }
649 
650  return *this;
651 }
652 
653 
654 Foam::Istream& Foam::ISstream::readVerbatim(verbatimString& str)
655 {
656  buf_.clear();
657 
658  char c;
659 
660  while (get(c))
661  {
662  if (c == token::HASH)
663  {
664  char nextC;
665  get(nextC);
666  if (nextC == token::END_BLOCK)
667  {
668  buf_.append('\0');
669  str = buf_.cdata();
670  return *this;
671  }
672  else
673  {
674  putback(nextC);
675  }
676  }
677 
678  buf_.append(c);
679  }
680 
681  // Truncate the string to the maximum error length
682  buf_.data()[bufErrorLength] = buf_.last() = '\0';
683 
685  << "problem while reading string \"" << buf_.cdata() << "...\""
686  << exit(FatalIOError);
687 
688  return *this;
689 }
690 
691 
692 Foam::ISstream& Foam::ISstream::getLine(string& s, const bool continuation)
693 {
694  getline(is_, s);
695  setState(is_.rdstate());
696  lineNumber_++;
697 
698  if (continuation && s.size())
699  {
700  while (s.back() == '\\')
701  {
702  string contLine;
703  getline(is_, contLine);
704  setState(is_.rdstate());
705  lineNumber_++;
706  s.pop_back();
707  s += contLine;
708  }
709  }
710 
711  return *this;
712 }
713 
714 
715 Foam::Istream& Foam::ISstream::readDelimited
716 (
717  string& str,
718  const char begin,
719  const char end
720 )
721 {
722  str.clear();
723 
724  int listDepth = 0;
725  char c;
726 
727  while (get(c))
728  {
729  str += c;
730 
731  if (c == begin)
732  {
733  listDepth++;
734  }
735  else if (c == end)
736  {
737  listDepth--;
738 
739  if (listDepth <= 0)
740  {
741  break;
742  }
743  }
744  }
745 
746  if (bad() || listDepth != 0)
747  {
749  << " problem while reading delimited string \n"
750  << str.c_str() << endl
751  << exit(FatalIOError);
752  }
753 
754  return *this;
755 }
756 
757 
759 {
760  return readDelimited(str, token::BEGIN_LIST, token::END_LIST);
761 }
762 
763 
765 {
766  return readDelimited(str, token::BEGIN_BLOCK, token::END_BLOCK);
767 }
768 
769 
771 {
772  is_ >> val;
773  setState(is_.rdstate());
774  return *this;
775 }
776 
777 
779 {
780  is_ >> val;
781  setState(is_.rdstate());
782  return *this;
783 }
784 
785 
787 {
788  is_ >> val;
789  setState(is_.rdstate());
790  return *this;
791 }
792 
793 
795 {
796  is_ >> val;
797  setState(is_.rdstate());
798  return *this;
799 }
800 
801 
802 Foam::Istream& Foam::ISstream::read(char* buf, std::streamsize count)
803 {
804  if (format() != BINARY)
805  {
807  << "stream format not binary"
808  << exit(FatalIOError);
809  }
810 
811  readBegin("binaryBlock");
812  is_.read(buf, count);
813  readEnd("binaryBlock");
814 
815  setState(is_.rdstate());
816 
817  return *this;
818 }
819 
820 
822 {
823  stdStream().rdbuf()->pubseekpos(0);
824 
825  return *this;
826 }
827 
828 
829 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
830 
831 
832 std::ios_base::fmtflags Foam::ISstream::flags() const
833 {
834  return is_.flags();
835 }
836 
837 
838 std::ios_base::fmtflags Foam::ISstream::flags(const ios_base::fmtflags f)
839 {
840  return is_.flags(f);
841 }
842 
843 
844 // ************************************************************************* //
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:821
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:758
ISstream & getLine(string &, const bool continuation=true)
Read line into a string.
Definition: ISstream.C:692
Istream & readBlock(string &)
Read a '{...}' delimited set of characters into a string.
Definition: ISstream.C:764
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:832
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:59
static bool isCompound(const word &name)
Return true if name is a compound type.
Definition: token.C:82
A token holds items read from Istream.
Definition: token.H:73
punctuationToken
Standard punctuation tokens.
Definition: token.H:99
@ BEGIN_STRING
Definition: token.H:116
@ DIVIDE
Definition: token.H:123
@ BEGIN_BLOCK
Definition: token.H:110
@ BEGIN_SQR
Definition: token.H:108
@ END_BLOCK
Definition: token.H:111
@ END_STRING
Definition: token.H:117
@ ASSIGN
Definition: token.H:119
@ END_STATEMENT
Definition: token.H:105
@ BEGIN_LIST
Definition: token.H:106
@ SUBTRACT
Definition: token.H:121
@ END_LIST
Definition: token.H:107
@ END_SQR
Definition: token.H:109
@ MULTIPLY
Definition: token.H:122
void setBad()
Set bad.
Definition: tokenI.H:550
label lineNumber() const
Definition: tokenI.H:539
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:318
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:251
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
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)