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-2018 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 <cctype>
30 
31 // * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
32 
33 char Foam::ISstream::nextValid()
34 {
35  char c = 0;
36 
37  while (true)
38  {
39  // Get next non-whitespace character
40  while (get(c) && isspace(c))
41  {}
42 
43  // Return if stream is bad - ie, previous get() failed
44  if (bad() || isspace(c))
45  {
46  break;
47  }
48 
49  // Is this the start of a C/C++ comment?
50  if (c == '/')
51  {
52  if (!get(c))
53  {
54  // cannot get another character - return this one
55  return '/';
56  }
57 
58  if (c == '/')
59  {
60  // C++ style single-line comment - skip through past end-of-line
61  while (get(c) && c != '\n')
62  {}
63  }
64  else if (c == '*')
65  {
66  // within a C-style comment
67  while (true)
68  {
69  // search for end of C-style comment - '*/'
70  if (get(c) && c == '*')
71  {
72  if (get(c))
73  {
74  if (c == '/')
75  {
76  // matched '*/'
77  break;
78  }
79  else if (c == '*')
80  {
81  // check again
82  putback(c);
83  }
84  }
85  }
86 
87  if (!good())
88  {
89  return 0;
90  }
91  }
92  }
93  else
94  {
95  // The '/' did not start a C/C++ comment - return it
96  putback(c);
97  return '/';
98  }
99  }
100  else
101  {
102  // a valid character - return it
103  return c;
104  }
105  }
106 
107  return 0;
108 }
109 
110 
111 void Foam::ISstream::readWordToken(token& t)
112 {
113  word* wPtr = new word;
114 
115  if (read(*wPtr).bad())
116  {
117  delete wPtr;
118  t.setBad();
119  }
120  else if (token::compound::isCompound(*wPtr))
121  {
122  t = token::compound::New(*wPtr, *this).ptr();
123  delete wPtr;
124  }
125  else
126  {
127  t = wPtr;
128  }
129 }
130 
131 
133 {
134  static const int maxLen = 128;
135  static char buf[maxLen];
136 
137  // Return the put back token if it exists
138  if (Istream::getBack(t))
139  {
140  return *this;
141  }
142 
143  // Assume that the streams supplied are in working order.
144  // Lines are counted by '\n'
145 
146  // Get next 'valid character': i.e. proceed through any whitespace
147  // and/or comments until a semantically valid character is found
148 
149  char c = nextValid();
150 
151  // Set the line number of this token to the current stream line number
152  t.lineNumber() = lineNumber();
153 
154  // return on error
155  if (!c)
156  {
157  t.setBad();
158  return *this;
159  }
160 
161  // Analyse input starting with this character.
162  switch (c)
163  {
164  // Check for punctuation first
165 
166  case token::END_STATEMENT :
167  case token::BEGIN_LIST :
168  case token::END_LIST :
169  case token::BEGIN_SQR :
170  case token::END_SQR :
171  case token::BEGIN_BLOCK :
172  case token::END_BLOCK :
173  case token::COLON :
174  case token::COMMA :
175  case token::ASSIGN :
176  case token::ADD :
177  // NB: token::SUBTRACT handled later as the possible start of a Number
178  case token::MULTIPLY :
179  case token::DIVIDE :
180  {
182  return *this;
183  }
184 
185 
186  // String: enclosed by double quotes.
187  case token::BEGIN_STRING :
188  {
189  putback(c);
190  string* sPtr = new string;
191 
192  if (read(*sPtr).bad())
193  {
194  delete sPtr;
195  t.setBad();
196  }
197  else
198  {
199  t = sPtr;
200  }
201 
202  return *this;
203  }
204  // Possible verbatim string or dictionary functionEntry
205  case token::HASH :
206  {
207  char nextC;
208  if (read(nextC).bad())
209  {
210  // Return hash as word
211  t = token(word(c));
212  return *this;
213  }
214  else if (nextC == token::BEGIN_BLOCK)
215  {
216  // Verbatim string
217  string* sPtr = new string;
218 
219  if (readVerbatim(*sPtr).bad())
220  {
221  delete sPtr;
222  t.setBad();
223  }
224  else
225  {
226  t = sPtr;
228  }
229 
230  return *this;
231  }
232  else
233  {
234  // Word beginning with #
235  putback(nextC);
236  putback(c);
237 
238  readWordToken(t);
239 
240  return *this;
241  }
242  }
243 
244  case '$':
245  {
246  // Look ahead
247  char nextC;
248  if (read(nextC).bad())
249  {
250  // Return $ as word
251  t = token(word(c));
252  return *this;
253  }
254  else if (nextC == token::BEGIN_BLOCK)
255  {
256  putback(nextC);
257  putback(c);
258 
259  string* sPtr = new string;
260 
261  if (readVariable(*sPtr).bad())
262  {
263  delete sPtr;
264  t.setBad();
265  }
266  else
267  {
268  t = sPtr;
269  t.type() = token::VARIABLE;
270  }
271  return *this;
272  }
273  else
274  {
275  putback(nextC);
276  putback(c);
277  readWordToken(t);
278  return *this;
279  }
280  }
281 
282  // Number: integer or floating point
283  //
284  // ideally match the equivalent of this regular expression
285  //
286  // /[-+]?([0-9]+\.?[0-9]*|\.[0-9]+)([Ee][-+]?[0-9]+)?/
287  //
288  case '-' :
289  case '.' :
290  case '0' : case '1' : case '2' : case '3' : case '4' :
291  case '5' : case '6' : case '7' : case '8' : case '9' :
292  {
293  bool asLabel = (c != '.');
294 
295  int nChar = 0;
296  buf[nChar++] = c;
297 
298  // get everything that could resemble a number and let
299  // readScalar determine the validity
300  while
301  (
302  is_.get(c)
303  && (
304  isdigit(c)
305  || c == '+'
306  || c == '-'
307  || c == '.'
308  || c == 'E'
309  || c == 'e'
310  )
311  )
312  {
313  if (asLabel)
314  {
315  asLabel = isdigit(c);
316  }
317 
318  buf[nChar++] = c;
319  if (nChar == maxLen)
320  {
321  // runaway argument - avoid buffer overflow
322  buf[maxLen-1] = '\0';
323 
325  << "number '" << buf << "...'\n"
326  << " is too long (max. " << maxLen << " characters)"
327  << exit(FatalIOError);
328 
329  t.setBad();
330  return *this;
331  }
332  }
333  buf[nChar] = '\0';
334 
335  setState(is_.rdstate());
336  if (is_.bad())
337  {
338  t.setBad();
339  }
340  else
341  {
342  is_.putback(c);
343 
344  if (nChar == 1 && buf[0] == '-')
345  {
346  // a single '-' is punctuation
348  }
349  else
350  {
351  if (asLabel)
352  {
353  label labelVal = 0;
354  if (Foam::read(buf, labelVal))
355  {
356  t = labelVal;
357  }
358  else
359  {
360  // Maybe too big? Try as scalar
361  scalar scalarVal;
362  if (readScalar(buf, scalarVal))
363  {
364  t = scalarVal;
365  }
366  else
367  {
368  t.setBad();
369  }
370  }
371  }
372  else
373  {
374  scalar scalarVal;
375  if (readScalar(buf, scalarVal))
376  {
377  t = scalarVal;
378  }
379  else
380  {
381  t.setBad();
382  }
383  }
384  }
385  }
386 
387  return *this;
388  }
389 
390 
391  // Should be a word (which can also be a single character)
392  default:
393  {
394  putback(c);
395  readWordToken(t);
396 
397  return *this;
398  }
399  }
400 }
401 
402 
404 {
405  c = nextValid();
406  return *this;
407 }
408 
409 
411 {
412  static const int maxLen = 1024;
413  static const int errLen = 80; // truncate error message for readability
414  static char buf[maxLen];
415 
416  int nChar = 0;
417  int listDepth = 0;
418  char c;
419 
420  while (get(c) && word::valid(c))
421  {
422  if (c == token::BEGIN_LIST)
423  {
424  listDepth++;
425  }
426  else if (c == token::END_LIST)
427  {
428  if (listDepth)
429  {
430  listDepth--;
431  }
432  else
433  {
434  break;
435  }
436  }
437 
438  buf[nChar++] = c;
439  if (nChar == maxLen)
440  {
441  buf[errLen] = '\0';
442 
444  << "word '" << buf << "...'\n"
445  << " is too long (max. " << maxLen << " characters)"
446  << exit(FatalIOError);
447 
448  return *this;
449  }
450  }
451 
452  // we could probably skip this check
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  // done reading
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  // done reading
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  || word::valid(c)
614  )
615  )
616  {
617  buf[nChar++] = c;
618  if (nChar == maxLen)
619  {
620  buf[errLen] = '\0';
621 
623  << "word '" << 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 
651  while (get(c) && word::valid(c))
652  {
653  buf[nChar++] = c;
654  if (nChar == maxLen)
655  {
656  buf[errLen] = '\0';
657 
659  << "word '" << buf << "...'\n"
660  << " is too long (max. " << maxLen << " characters)"
661  << exit(FatalIOError);
662 
663  return *this;
664  }
665  }
666  }
667 
668  // we could probably skip this check
669  if (bad())
670  {
671  buf[errLen] = buf[nChar] = '\0';
672 
674  << "problem while reading string '" << buf << "...' after "
675  << nChar << " characters\n"
676  << exit(FatalIOError);
677 
678  return *this;
679  }
680 
681  if (nChar == 0)
682  {
684  << "invalid first character found : " << c
685  << exit(FatalIOError);
686  }
687 
688  // done reading
689  buf[nChar] = '\0';
690  str = buf;
691 
692  // Note: check if we exited due to '}' or just !word::valid.
693  if (c != token::END_BLOCK)
694  {
695  putback(c);
696  }
697 
698  return *this;
699 }
700 
701 
702 Foam::Istream& Foam::ISstream::readVerbatim(string& str)
703 {
704  static const int maxLen = 8000;
705  static const int errLen = 80; // truncate error message for readability
706  static char buf[maxLen];
707 
708  char c;
709 
710  int nChar = 0;
711 
712  while (get(c))
713  {
714  if (c == token::HASH)
715  {
716  char nextC;
717  get(nextC);
718  if (nextC == token::END_BLOCK)
719  {
720  buf[nChar] = '\0';
721  str = buf;
722  return *this;
723  }
724  else
725  {
726  putback(nextC);
727  }
728  }
729 
730  buf[nChar++] = c;
731  if (nChar == maxLen)
732  {
733  buf[errLen] = '\0';
734 
736  << "string \"" << buf << "...\"\n"
737  << " is too long (max. " << maxLen << " characters)"
738  << exit(FatalIOError);
739 
740  return *this;
741  }
742  }
743 
744 
745  // don't worry about a dangling backslash if string terminated prematurely
746  buf[errLen] = buf[nChar] = '\0';
747 
749  << "problem while reading string \"" << buf << "...\""
750  << exit(FatalIOError);
751 
752  return *this;
753 }
754 
755 
757 {
758  is_ >> val;
759  setState(is_.rdstate());
760  return *this;
761 }
762 
763 
765 {
766  is_ >> val;
767  setState(is_.rdstate());
768  return *this;
769 }
770 
771 
773 {
774  is_ >> val;
775  setState(is_.rdstate());
776  return *this;
777 }
778 
779 
781 {
782  is_ >> val;
783  setState(is_.rdstate());
784  return *this;
785 }
786 
787 
788 // read binary block
789 Foam::Istream& Foam::ISstream::read(char* buf, std::streamsize count)
790 {
791  if (format() != BINARY)
792  {
794  << "stream format not binary"
795  << exit(FatalIOError);
796  }
797 
798  readBegin("binaryBlock");
799  is_.read(buf, count);
800  readEnd("binaryBlock");
801 
802  setState(is_.rdstate());
803 
804  return *this;
805 }
806 
807 
809 {
810  stdStream().rdbuf()->pubseekpos(0);
811 
812  return *this;
813 }
814 
815 
816 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
817 
818 
819 std::ios_base::fmtflags Foam::ISstream::flags() const
820 {
821  return is_.flags();
822 }
823 
824 
825 std::ios_base::fmtflags Foam::ISstream::flags(const ios_base::fmtflags f)
826 {
827  return is_.flags(f);
828 }
829 
830 
831 // ************************************************************************* //
Istream & readBegin(const char *funcName)
Definition: Istream.C:86
static bool valid(char)
Is this character valid for a word.
Definition: wordI.H:115
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:69
label lineNumber() const
Return current stream line number.
Definition: IOstream.H:438
virtual Istream & read(token &)
Return next token from stream.
Definition: ISstream.C:132
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:819
virtual Istream & rewind()
Rewind and return the stream so that it may be read again.
Definition: ISstream.C:808
Istream & readEnd(const char *funcName)
Definition: Istream.C:103
bool read(const char *, int32_t &)
Definition: int32IO.C:85
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:429
punctuationToken
Standard punctuation tokens.
Definition: token.H:94
double doubleScalar
Double precision floating point scalar type.
Definition: doubleScalar.H:52
label lineNumber() const
Definition: tokenI.H:418
bool readScalar(const char *buf, doubleScalar &s)
Read whole of buf as a scalar. Return true if successful.
Definition: doubleScalar.H:68
streamFormat format() const
Return current stream format.
Definition: IOstream.H:377
virtual istream & stdStream()
Access to underlying std::istream.
Definition: ISstream.H:178
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 &)
Raw, low-level putback character function.
Definition: ISstreamI.H:87
bool isspace(char c)
Definition: char.H:53
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:331
tokenType type() const
Definition: tokenI.H:187
bool getBack(token &)
Get the put back token if there is one and return true.
Definition: Istream.C:52
const dimensionedScalar c
Speed of light in a vacuum.
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:74
static autoPtr< compound > New(const word &type, Istream &)
Select null constructed.
Definition: token.C:60
IOerror FatalIOError