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-2020 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  static const int maxLen = 128;
136  static char buf[maxLen];
137 
138  // Return the put back token if it exists
139  if (Istream::getBack(t))
140  {
141  return *this;
142  }
143 
144  // Assume that the streams supplied are in working order.
145  // Lines are counted by '\n'
146 
147  // Get next 'valid character': i.e. proceed through any whitespace
148  // and/or comments until a semantically valid character is found
149 
150  char c = nextValid();
151 
152  // Set the line number of this token to the current stream line number
153  t.lineNumber() = lineNumber();
154 
155  // Return on error
156  if (!c)
157  {
158  t.setBad();
159  return *this;
160  }
161 
162  // Analyse input starting with this character.
163  switch (c)
164  {
165  // Check for punctuation first
166 
167  case token::END_STATEMENT :
168  case token::BEGIN_LIST :
169  case token::END_LIST :
170  case token::BEGIN_SQR :
171  case token::END_SQR :
172  case token::BEGIN_BLOCK :
173  case token::END_BLOCK :
174  case token::COLON :
175  case token::COMMA :
176  case token::ASSIGN :
177  case token::ADD :
178  // NB: token::SUBTRACT handled later as the possible start of a Number
179  case token::MULTIPLY :
180  case token::DIVIDE :
181  {
183  return *this;
184  }
185 
186 
187  // String: enclosed by double quotes.
188  case token::BEGIN_STRING :
189  {
190  putback(c);
191  string* sPtr = new string;
192 
193  if (read(*sPtr).bad())
194  {
195  delete sPtr;
196  t.setBad();
197  }
198  else
199  {
200  t = sPtr;
201  }
202 
203  return *this;
204  }
205  // Possible verbatim string or dictionary functionEntry
206  case token::HASH :
207  {
208  char nextC;
209  if (read(nextC).bad())
210  {
211  // Return hash as word
212  t = token(word(c));
213  return *this;
214  }
215  else if (nextC == token::BEGIN_BLOCK)
216  {
217  // Verbatim string
218  verbatimString* vsPtr = new verbatimString;
219 
220  if (readVerbatim(*vsPtr).bad())
221  {
222  delete vsPtr;
223  t.setBad();
224  }
225  else
226  {
227  t = vsPtr;
228  }
229  return *this;
230  }
231  else
232  {
233  // Function name beginning with a #
234  putback(nextC);
235  putback(c);
236 
237  functionName* fnPtr = new functionName;
238 
239  if (read(*fnPtr).bad())
240  {
241  delete fnPtr;
242  t.setBad();
243  }
244  else
245  {
246  t = fnPtr;
247  }
248  return *this;
249  }
250  }
251 
252  case '$' :
253  {
254  // Look ahead
255  char nextC;
256  if (read(nextC).bad())
257  {
258  // Return $ as a word
259  t = token(word(c));
260  return *this;
261  }
262  else
263  {
264  putback(nextC);
265  putback(c);
266 
267  variable* vPtr = new variable;
268 
269  if (readVariable(*vPtr).bad())
270  {
271  delete vPtr;
272  t.setBad();
273  }
274  else
275  {
276  t = vPtr;
277  }
278 
279  return *this;
280  }
281  }
282 
283  // Number: integer or floating point
284  //
285  // ideally match the equivalent of this regular expression
286  //
287  // /[-+]?([0-9]+\.?[0-9]*|\.[0-9]+)([Ee][-+]?[0-9]+)?/
288  //
289  case '-' :
290  case '.' :
291  case '0' : case '1' : case '2' : case '3' : case '4' :
292  case '5' : case '6' : case '7' : case '8' : case '9' :
293  {
294  bool asLabel = (c != '.');
295 
296  int nChar = 0;
297  buf[nChar++] = c;
298 
299  // Get everything that could resemble a number and let
300  // readScalar determine the validity
301  while
302  (
303  is_.get(c)
304  && (
305  isdigit(c)
306  || c == '+'
307  || c == '-'
308  || c == '.'
309  || c == 'E'
310  || c == 'e'
311  )
312  )
313  {
314  if (asLabel)
315  {
316  asLabel = isdigit(c);
317  }
318 
319  buf[nChar++] = c;
320  if (nChar == maxLen)
321  {
322  // Runaway argument - avoid buffer overflow
323  buf[maxLen-1] = '\0';
324 
326  << "number '" << buf << "...'\n"
327  << " is too long (max. " << maxLen << " characters)"
328  << exit(FatalIOError);
329 
330  t.setBad();
331  return *this;
332  }
333  }
334  buf[nChar] = '\0';
335 
336  setState(is_.rdstate());
337  if (is_.bad())
338  {
339  t.setBad();
340  }
341  else
342  {
343  is_.putback(c);
344 
345  if (nChar == 1 && buf[0] == '-')
346  {
347  // A single '-' is punctuation
349  }
350  else
351  {
352  if (asLabel)
353  {
354  label labelVal = 0;
355  if (Foam::read(buf, labelVal))
356  {
357  t = labelVal;
358  }
359  else
360  {
361  // Maybe too big? Try as scalar
362  scalar scalarVal;
363  if (readScalar(buf, scalarVal))
364  {
365  t = scalarVal;
366  }
367  else
368  {
369  t.setBad();
370  }
371  }
372  }
373  else
374  {
375  scalar scalarVal;
376  if (readScalar(buf, scalarVal))
377  {
378  t = scalarVal;
379  }
380  else
381  {
382  t.setBad();
383  }
384  }
385  }
386  }
387 
388  return *this;
389  }
390 
391 
392  // Should be a word (which can also be a single character)
393  default:
394  {
395  putback(c);
396  readWordToken(t);
397 
398  return *this;
399  }
400  }
401 }
402 
403 
405 {
406  c = nextValid();
407  return *this;
408 }
409 
410 
412 {
413  static const int maxLen = 1024;
414  static const int errLen = 80; // Truncate error message for readability
415  static char buf[maxLen];
416 
417  int nChar = 0;
418  int listDepth = 0;
419  char c;
420 
421  while (get(c) && word::valid(c))
422  {
423  if (c == token::BEGIN_LIST)
424  {
425  listDepth++;
426  }
427  else if (c == token::END_LIST)
428  {
429  if (listDepth)
430  {
431  listDepth--;
432  }
433  else
434  {
435  break;
436  }
437  }
438 
439  buf[nChar++] = c;
440  if (nChar == maxLen)
441  {
442  buf[errLen] = '\0';
443 
445  << "word '" << buf << "...'\n"
446  << " is too long (max. " << maxLen << " characters)"
447  << exit(FatalIOError);
448 
449  return *this;
450  }
451  }
452 
453  if (bad())
454  {
455  buf[errLen] = buf[nChar] = '\0';
456 
458  << "problem while reading word '" << buf << "...' after "
459  << nChar << " characters\n"
460  << exit(FatalIOError);
461 
462  return *this;
463  }
464 
465  if (nChar == 0)
466  {
468  << "invalid first character found : " << c
469  << exit(FatalIOError);
470  }
471 
472  // Append end string character
473  buf[nChar] = '\0';
474  str = buf;
475  putback(c);
476 
477  return *this;
478 }
479 
480 
482 {
483  static const int maxLen = 1024;
484  static const int errLen = 80; // Truncate error message for readability
485  static char buf[maxLen];
486 
487  char c;
488 
489  if (!get(c))
490  {
492  << "cannot read start of string"
493  << exit(FatalIOError);
494 
495  return *this;
496  }
497 
498  // Note, we could also handle single-quoted strings here (if desired)
499  if (c != token::BEGIN_STRING)
500  {
502  << "Incorrect start of string character found : " << c
503  << exit(FatalIOError);
504 
505  return *this;
506  }
507 
508  int nChar = 0;
509  bool escaped = false;
510 
511  while (get(c))
512  {
513  if (c == token::END_STRING)
514  {
515  if (escaped)
516  {
517  escaped = false;
518  nChar--; // Overwrite backslash
519  }
520  else
521  {
522  // Append end string character
523  buf[nChar] = '\0';
524  str = buf;
525  return *this;
526  }
527  }
528  else if (c == token::NL)
529  {
530  if (escaped)
531  {
532  escaped = false;
533  nChar--; // Overwrite backslash
534  }
535  else
536  {
537  buf[errLen] = buf[nChar] = '\0';
538 
540  << "found '\\n' while reading string \""
541  << buf << "...\""
542  << exit(FatalIOError);
543 
544  return *this;
545  }
546  }
547  else if (c == '\\')
548  {
549  escaped = !escaped; // Toggle state (retains backslashes)
550  }
551  else
552  {
553  escaped = false;
554  }
555 
556  buf[nChar++] = c;
557  if (nChar == maxLen)
558  {
559  buf[errLen] = '\0';
560 
562  << "string \"" << buf << "...\"\n"
563  << " is too long (max. " << maxLen << " characters)"
564  << exit(FatalIOError);
565 
566  return *this;
567  }
568  }
569 
570 
571  // Don't worry about a dangling backslash if string terminated prematurely
572  buf[errLen] = buf[nChar] = '\0';
573 
575  << "problem while reading string \"" << buf << "...\""
576  << exit(FatalIOError);
577 
578  return *this;
579 }
580 
581 
582 Foam::Istream& Foam::ISstream::readVariable(string& str)
583 {
584  static const int maxLen = 1024;
585  static const int errLen = 80; // Truncate error message for readability
586  static char buf[maxLen];
587 
588  int nChar = 0;
589  int blockCount = 0;
590  char c;
591 
592  if (!get(c) || c != '$')
593  {
595  << "invalid first character found : " << c
596  << exit(FatalIOError);
597  }
598 
599  buf[nChar++] = c;
600 
601  // Read next character to see if '{'
602  if (get(c) && c == token::BEGIN_BLOCK)
603  {
604  // Read, counting brackets
605  buf[nChar++] = c;
606 
607  while
608  (
609  get(c)
610  && (
611  c == token::BEGIN_BLOCK
612  || c == token::END_BLOCK
613  || variable::valid(c)
614  )
615  )
616  {
617  buf[nChar++] = c;
618  if (nChar == maxLen)
619  {
620  buf[errLen] = '\0';
621 
623  << "variable '" << buf << "...'\n"
624  << " is too long (max. " << maxLen << " characters)"
625  << exit(FatalIOError);
626 
627  return *this;
628  }
629 
630  if (c == token::BEGIN_BLOCK)
631  {
632  blockCount++;
633  }
634  else if (c == token::END_BLOCK)
635  {
636  if (blockCount)
637  {
638  blockCount--;
639  }
640  else
641  {
642  break;
643  }
644  }
645  }
646  }
647  else
648  {
649  buf[nChar++] = c;
650  int listDepth = 0;
651 
652  while (get(c) && variable::valid(c))
653  {
654  if (c == token::BEGIN_LIST)
655  {
656  listDepth++;
657  }
658  else if (c == token::END_LIST)
659  {
660  if (listDepth)
661  {
662  listDepth--;
663  }
664  else
665  {
666  break;
667  }
668  }
669 
670  buf[nChar++] = c;
671  if (nChar == maxLen)
672  {
673  buf[errLen] = '\0';
674 
676  << "variable '" << buf << "...'\n"
677  << " is too long (max. " << maxLen << " characters)"
678  << exit(FatalIOError);
679 
680  return *this;
681  }
682  }
683  }
684 
685  if (bad())
686  {
687  buf[errLen] = buf[nChar] = '\0';
688 
690  << "problem while reading string '" << buf << "...' after "
691  << nChar << " characters\n"
692  << exit(FatalIOError);
693 
694  return *this;
695  }
696 
697  if (nChar == 0)
698  {
700  << "invalid first character found : " << c
701  << exit(FatalIOError);
702  }
703 
704  // Append end string character
705  buf[nChar] = '\0';
706  str = buf;
707 
708  // Note: check if we exited due to '}' or just !variable::valid.
709  if (c != token::END_BLOCK)
710  {
711  putback(c);
712  }
713 
714  return *this;
715 }
716 
717 
718 Foam::Istream& Foam::ISstream::readVerbatim(verbatimString& str)
719 {
720  static const int maxLen = 8000;
721  static const int errLen = 80; // Truncate error message for readability
722  static char buf[maxLen];
723 
724  char c;
725 
726  int nChar = 0;
727 
728  while (get(c))
729  {
730  if (c == token::HASH)
731  {
732  char nextC;
733  get(nextC);
734  if (nextC == token::END_BLOCK)
735  {
736  buf[nChar] = '\0';
737  str = buf;
738  return *this;
739  }
740  else
741  {
742  putback(nextC);
743  }
744  }
745 
746  buf[nChar++] = c;
747  if (nChar == maxLen)
748  {
749  buf[errLen] = '\0';
750 
752  << "string \"" << buf << "...\"\n"
753  << " is too long (max. " << maxLen << " characters)"
754  << exit(FatalIOError);
755 
756  return *this;
757  }
758  }
759 
760 
761  // Don't worry about a dangling backslash if string terminated prematurely
762  buf[errLen] = buf[nChar] = '\0';
763 
765  << "problem while reading string \"" << buf << "...\""
766  << exit(FatalIOError);
767 
768  return *this;
769 }
770 
771 
773 {
774  getline(is_, s);
775  setState(is_.rdstate());
776  lineNumber_++;
777 
778  while (s.back() == '\\')
779  {
780  string contLine;
781  getline(is_, contLine);
782  setState(is_.rdstate());
783  lineNumber_++;
784  s.pop_back();
785  s += contLine;
786  }
787 
788  return *this;
789 }
790 
791 
792 Foam::Istream& Foam::ISstream::readDelimited
793 (
794  string& str,
795  const char begin,
796  const char end
797 )
798 {
799  str.clear();
800 
801  int listDepth = 0;
802  char c;
803 
804  while (get(c))
805  {
806  str += c;
807 
808  if (c == begin)
809  {
810  listDepth++;
811  }
812  else if (c == end)
813  {
814  listDepth--;
815 
816  if (listDepth <= 0)
817  {
818  break;
819  }
820  }
821  }
822 
823  if (bad() || listDepth != 0)
824  {
826  << " problem while reading delimited string \n"
827  << str.c_str() << endl
828  << exit(FatalIOError);
829  }
830 
831  return *this;
832 }
833 
834 
836 {
837  return readDelimited(str, token::BEGIN_LIST, token::END_LIST);
838 }
839 
840 
842 {
843  return readDelimited(str, token::BEGIN_BLOCK, token::END_BLOCK);
844 }
845 
846 
848 {
849  is_ >> val;
850  setState(is_.rdstate());
851  return *this;
852 }
853 
854 
856 {
857  is_ >> val;
858  setState(is_.rdstate());
859  return *this;
860 }
861 
862 
864 {
865  is_ >> val;
866  setState(is_.rdstate());
867  return *this;
868 }
869 
870 
872 {
873  is_ >> val;
874  setState(is_.rdstate());
875  return *this;
876 }
877 
878 
879 Foam::Istream& Foam::ISstream::read(char* buf, std::streamsize count)
880 {
881  if (format() != BINARY)
882  {
884  << "stream format not binary"
885  << exit(FatalIOError);
886  }
887 
888  readBegin("binaryBlock");
889  is_.read(buf, count);
890  readEnd("binaryBlock");
891 
892  setState(is_.rdstate());
893 
894  return *this;
895 }
896 
897 
899 {
900  stdStream().rdbuf()->pubseekpos(0);
901 
902  return *this;
903 }
904 
905 
906 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
907 
908 
909 std::ios_base::fmtflags Foam::ISstream::flags() const
910 {
911  return is_.flags();
912 }
913 
914 
915 std::ios_base::fmtflags Foam::ISstream::flags(const ios_base::fmtflags f)
916 {
917  return is_.flags(f);
918 }
919 
920 
921 // ************************************************************************* //
A class for handling verbatimStrings, derived from string.
Istream & readBegin(const char *funcName)
Definition: Istream.C:86
label lineNumber_
Definition: IOstream.H:231
static bool valid(char)
Is this character valid for a variable.
Definition: variableI.H:106
static bool valid(char)
Is this character valid for a word.
Definition: wordI.H:115
Istream & readList(string &)
Read a &#39;(...)&#39; delimited set of characters into a string.
Definition: ISstream.C:835
System integer.
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
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
bool bad() const
Return true if stream is corrupted.
Definition: IOstream.H:351
An Istream is an abstract base class for all input systems (streams, files, token lists etc)...
Definition: Istream.H:57
A token holds items read from Istream.
Definition: token.H:72
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
label lineNumber() const
Return current stream line number.
Definition: IOstream.H:438
virtual Istream & read(token &)
Return next token from stream.
Definition: ISstream.C:133
const dimensionedScalar & c
Speed of light in a vacuum.
bool good() const
Return true if next operation might succeed.
Definition: IOstream.H:333
virtual ios_base::fmtflags flags() const
Return flags of output stream.
Definition: ISstream.C:909
virtual Istream & rewind()
Rewind and return the stream so that it may be read again.
Definition: ISstream.C:898
Istream & readEnd(const char *funcName)
Definition: Istream.C:103
bool read(const char *, int32_t &)
Definition: int32IO.C:85
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.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
A functionName is a word starting with &#39;#&#39;.
Definition: functionName.H:57
A class for handling words, derived from string.
Definition: word.H:59
float floatScalar
Float precision floating point scalar type.
Definition: floatScalar.H:52
void setBad()
Set bad.
Definition: tokenI.H:550
punctuationToken
Standard punctuation tokens.
Definition: token.H:98
double doubleScalar
Double precision floating point scalar type.
Definition: doubleScalar.H:52
label lineNumber() const
Definition: tokenI.H:539
bool readScalar(const char *buf, doubleScalar &s)
Read whole of buf as a scalar. Return true if successful.
Definition: doubleScalar.H:75
streamFormat format() const
Return current stream format.
Definition: IOstream.H:377
Istream & readBlock(string &)
Read a &#39;{...}&#39; delimited set of characters into a string.
Definition: ISstream.C:841
virtual istream & stdStream()
Access to underlying std::istream.
Definition: ISstream.H:185
static bool isCompound(const word &name)
Return true if name is a compound type.
Definition: token.C:83
labelList f(nPoints)
long double longDoubleScalar
Lang double precision floating point scalar type.
ISstream & putback(const char &)
Low-level putback character function.
Definition: ISstreamI.H:77
Generic input stream.
Definition: ISstream.H:51
bool isspace(char c)
Definition: char.H:53
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:331
bool getBack(token &)
Get the put back token if there is one and return true.
Definition: Istream.C:52
void setState(ios_base::iostate state)
Set stream state.
Definition: IOstream.H:251
A class for handling character strings derived from std::string.
Definition: string.H:76
static autoPtr< compound > New(const word &type, Istream &)
Select null constructed.
Definition: token.C:60
A variable is a word with support for additional characters, in particular &#39;$&#39; and &#39;/&#39;...
Definition: variable.H:58
ISstream & getLine(string &)
Read line into a string.
Definition: ISstream.C:772
IOerror FatalIOError