0024275: Cppcheck warnings on uninitialized class members
[occt.git] / src / LDOM / LDOM_XmlReader.cxx
1 // Created on: 2001-07-20
2 // Created by: Alexander GRIGORIEV
3 // Copyright (c) 2001-2012 OPEN CASCADE SAS
4 //
5 // The content of this file is subject to the Open CASCADE Technology Public
6 // License Version 6.5 (the "License"). You may not use the content of this file
7 // except in compliance with the License. Please obtain a copy of the License
8 // at http://www.opencascade.org and read it completely before using this file.
9 //
10 // The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
11 // main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
12 //
13 // The Original Code and all software distributed under the License is
14 // distributed on an "AS IS" basis, without warranty of any kind, and the
15 // Initial Developer hereby disclaims all such warranties, including without
16 // limitation, any warranties of merchantability, fitness for a particular
17 // purpose or non-infringement. Please see the License for the specific terms
18 // and conditions governing the rights and limitations under the License.
19
20 //AGV 060302: Input from istream
21 //            AGV 130302: bug corr: was error if strlen(root_elem_name) < 7
22
23 #include <LDOM_XmlReader.hxx>
24 #include <Standard_Stream.hxx>
25 #include <LDOM_MemManager.hxx>
26 #include <LDOM_BasicAttribute.hxx>
27 #include <LDOM_CharReference.hxx>
28 #include <LDOM_OSStream.hxx>
29
30 #include <string.h>
31 #include <errno.h>
32 #ifdef WNT
33 #include <io.h>
34 #else
35 #include <unistd.h>
36 #endif
37
38 //#include <ctype.h>
39
40 const int XML_MIN_BUFFER = 10;
41 const int FILE_NONVALUE  = -1;
42
43 typedef enum {
44   STATE_WAITING = 0,
45   STATE_HEADER,
46   STATE_DOCTYPE,
47   STATE_DOCTYPE_MARKUP,
48   STATE_ELEMENT,
49   STATE_ELEMENT_END,
50   STATE_ATTRIBUTE_NAME,
51   STATE_ATTRIBUTE_EQUAL,
52   STATE_ATTRIBUTE_VALUE,
53   STATE_COMMENT,
54   STATE_CDATA,
55   STATE_TEXT
56 } ParserState;
57
58 #define TEXT_COMPARE(aPtr,aPattern) \
59   (memcmp ((aPtr), (aPattern), sizeof(aPattern) - 1) == 0)
60
61 static Standard_Boolean isName          (const char             * aString,
62                                          const char             * aStringEnd,
63                                          const char             *& aNameEnd);
64
65 //=======================================================================
66 //function : LDOM_XmlReader()
67 //purpose  : Constructor (file descriptor)
68 //=======================================================================
69
70 LDOM_XmlReader::LDOM_XmlReader (const int                       aFileDes,
71                                 const Handle(LDOM_MemManager)&  aDocument,
72                                 TCollection_AsciiString&        anErrorString)
73      : myEOF            (Standard_False),
74        myFileDes        (aFileDes),
75 #ifdef WNT
76        myIStream        (cin),  // one quirk of MSVC6.0: can't initialise by 0
77 #else
78        myIStream        (* (istream *) UndefinedHandleAddress),
79 #endif
80        myError          (anErrorString),
81        myDocument       (aDocument),
82            myElement        (NULL),
83        myPtr            (&myBuffer[0]),
84        myEndPtr         (&myBuffer[0])
85 {}
86
87 //=======================================================================
88 //function : LDOM_XmlReader()
89 //purpose  : Constructor (istream)
90 //=======================================================================
91
92 LDOM_XmlReader::LDOM_XmlReader (istream&                        anInput,
93                                 const Handle(LDOM_MemManager)&  aDocument,
94                                 TCollection_AsciiString&        anErrorString)
95      : myEOF            (Standard_False),
96        myFileDes        (FILE_NONVALUE),
97        myIStream        (anInput),
98        myError          (anErrorString),
99        myDocument       (aDocument),
100            myElement        (NULL),
101        myPtr            (&myBuffer[0]),
102        myEndPtr         (&myBuffer[0])
103 {}
104
105 //=======================================================================
106 //function : ReadRecord
107 //purpose  : Read a record from XML file
108 //=======================================================================
109
110 LDOM_XmlReader::RecordType LDOM_XmlReader::ReadRecord
111                                         (LDOM_OSStream& theData)
112 {
113   theData.Clear();
114   myError.Clear();
115   ParserState aState = STATE_WAITING;
116   const char * aStartData = NULL, * aNameEnd = NULL, * aPtr;
117   LDOMBasicString anAttrName, anAttrValue;
118   char anAttDelimiter = '\0';
119
120   for(;;) {
121     //  Check if the current file buffer is exhausted
122     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
123     //  There should always be some bytes available in the buffer for analysis
124     Standard_Integer aBytesRest = (Standard_Integer)(myEndPtr - myPtr);
125     if (aBytesRest < XML_MIN_BUFFER) {
126       if (myEOF == Standard_True) {
127         if (aBytesRest <= 0)
128           break;                        // END of processing
129       } else {
130       // If we are reading some data, save the beginning and preserve the state
131         if (aStartData /* && aState != STATE_WAITING */) {
132           if (myPtr > aStartData)
133             theData.rdbuf()->sputn(aStartData, myPtr - aStartData);
134           aStartData = &myBuffer[0];
135         }
136       // Copy the rest of file data to the beginning of buffer
137         if (aBytesRest > 0)
138           memcpy (&myBuffer[0], myPtr, aBytesRest);
139
140       // Read the full buffer and reset start and end buffer pointers
141         myPtr    = &myBuffer[0];
142         Standard_Size aNBytes;
143         if (myFileDes != FILE_NONVALUE)
144           aNBytes = read (myFileDes, &myBuffer[aBytesRest],
145                           XML_BUFFER_SIZE - aBytesRest);
146         else {
147           myIStream.read (&myBuffer[aBytesRest],
148                           XML_BUFFER_SIZE - aBytesRest);
149           aNBytes = (Standard_Size)myIStream.gcount();
150         }
151         if (aNBytes == 0)
152           myEOF = Standard_True;                  // END-OF-FILE
153         myEndPtr = &myBuffer[aBytesRest + aNBytes];
154         myBuffer[aBytesRest + aNBytes] = '\0';
155       }
156     }
157
158     //  Check the character data
159     switch (aState) {
160
161       // Checking the characters in STATE_WAITING (blank, TEXT or markup)
162       // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
163     case STATE_WAITING:
164       switch (myPtr[0]) {
165       case ' ':
166       case '\t':
167       case '\n':
168       case '\r':
169         ++ myPtr;
170         continue;
171       case '<':
172         // XML markup found, then make detect the record type
173         switch (myPtr[1]) {
174         case '?':
175           aState = STATE_HEADER;
176           myPtr += 2;
177           aStartData = myPtr;
178           continue;
179         case '/':
180           aState = STATE_ELEMENT_END;
181           myPtr += 2;
182           aStartData = myPtr;
183           continue;
184         case '!':
185           if (myPtr[2] == '-' && myPtr[3] == '-') {
186             aState = STATE_COMMENT;
187             myPtr += 4;
188           } else if (TEXT_COMPARE (&myPtr[2], "DOCTYPE")) {
189             char ch = myPtr[9];
190             if (ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r')
191               break;
192             aState = STATE_DOCTYPE;
193             myPtr += 10;
194           } else if (TEXT_COMPARE (&myPtr[2], "[CDATA[")) {
195             aState = STATE_CDATA;
196             myPtr += 9;
197           } else break;                   // ERROR
198           aStartData = myPtr;
199           continue;
200         default:
201           if (::isName (&myPtr[1], myEndPtr, aNameEnd)) {
202             aStartData = myPtr + 1;
203             myPtr = aNameEnd;
204             if (myPtr < myEndPtr) {
205               myElement = & LDOM_BasicElement::Create (aStartData,
206                                                        (Standard_Integer)(myPtr - aStartData),
207                                                        myDocument);
208               myLastChild = NULL;
209               aState = STATE_ATTRIBUTE_NAME;
210               aStartData = NULL;
211             }else
212               aState = STATE_ELEMENT;
213             continue;
214           }       // otherwise ERROR
215         }     // end of switch
216         myError = "Unknown XML object: ";
217         myError += TCollection_AsciiString ((const Standard_CString)myPtr,
218                                             XML_MIN_BUFFER);
219         return XML_UNKNOWN;
220       case '\0':
221         if (myEOF == Standard_True) continue;
222       default:
223         //      Limitation: we do not treat '&' as special character
224         aPtr = (const char *) memchr (myPtr, '<', myEndPtr - myPtr);
225         if (aPtr) {
226           // The end of text field reached
227           theData.rdbuf()->sputn(myPtr, aPtr - myPtr);
228           myPtr = aPtr;
229           return XML_TEXT;
230         }
231         aState = STATE_TEXT;
232         aStartData = myPtr;
233         myPtr = myEndPtr;
234       }   // end of checking in STATE_WAITING
235       continue;
236
237       // Checking the characters in STATE_HEADER, seek for "?>" sequence
238       // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
239     case STATE_HEADER:
240       aPtr = (const char *) memchr (aStartData, '?', (myEndPtr-1) - aStartData);
241       if (aPtr) {
242         // The end of XML declaration found
243         if (aPtr[1] != '>') {           // ERROR
244           myError = "Character \'>\' is expected in the end of XML declaration";
245           return XML_UNKNOWN;
246         }
247         // The XML declaration is retrieved
248         theData.rdbuf()->sputn(aStartData, aPtr - aStartData);
249         myPtr = aPtr + 2;
250         return XML_HEADER;
251       }
252       myPtr = myEndPtr - 1;
253       continue;
254
255       // Checking the characters in STATE_DOCTYPE, seek for "]>" sequence
256       // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
257     case STATE_DOCTYPE:
258       for (aPtr = aStartData; aPtr < myEndPtr-1; aPtr++) {
259         const int aChar = aPtr[0];
260         if (aChar == '[') {
261           aState = STATE_DOCTYPE_MARKUP;
262           aStartData = &aPtr[1];
263           goto state_doctype_markup;
264         }
265         if (aChar == '>') {
266           // The DOCTYPE declaration is retrieved
267           theData.rdbuf()->sputn(aStartData, aPtr - aStartData - 1);
268           myPtr = aPtr + 1;
269           return XML_DOCTYPE;
270         }
271       }
272       myPtr = myEndPtr - 1;
273       continue;
274
275     state_doctype_markup:
276     case STATE_DOCTYPE_MARKUP:
277       aPtr = (const char *) memchr (aStartData, ']', (myEndPtr-1) - aStartData);
278       if (aPtr) {
279         // The end of DOCTYPE declaration found
280         if (aPtr[1] != '>') {           // ERROR
281           myError =
282             "Character \'>\' is expected in the end of DOCTYPE declaration";
283           return XML_UNKNOWN;
284         }
285         // The DOCTYPE declaration is retrieved
286         theData.rdbuf()->sputn(aStartData, aPtr - aStartData);
287         myPtr = aPtr + 2;
288         return XML_DOCTYPE;
289       }
290       myPtr = myEndPtr - 1;
291       continue;
292
293         // Checking the characters in STATE_COMMENT, seek for "-->" sequence
294         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
295     case STATE_COMMENT:
296       aPtr = aStartData;
297       for(;;) {
298         aPtr = (const char *) memchr (aPtr, '-', (myEndPtr - 2) - aPtr);
299         if (aPtr == NULL) break;
300         if (aPtr[1] != '-') ++ aPtr;
301         else {
302           if (aPtr[2] != '>') {       // ERROR
303             myError = "Character \'>\' is expected in the end of comment";
304             return XML_UNKNOWN;
305           }
306           theData.rdbuf()->sputn(aStartData, aPtr - aStartData);
307           myPtr = aPtr + 3;
308           return XML_COMMENT;
309         }
310       }
311       myPtr = myEndPtr - 2;
312       continue;
313
314         // Checking the characters in STATE_TEXT, seek for "<"
315         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
316     case STATE_TEXT:
317       aPtr = (const char *) memchr (aStartData, '<', myEndPtr - aStartData);
318       if (aPtr) {
319         // The end of text field reached
320         theData.rdbuf()->sputn(aStartData, aPtr - aStartData);
321         myPtr = aPtr;
322         return XML_TEXT;
323       }
324       myPtr = myEndPtr;
325       continue;
326
327         // Checking the characters in STATE_CDATA, seek for "]]"
328         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
329     case STATE_CDATA:
330       aPtr = aStartData;
331       for(;;) {
332         aPtr = (const char *) memchr (aPtr, ']', (myEndPtr - 1) - aStartData);
333         if (aPtr == NULL) break;
334         if (aPtr[1] != ']') {           // ERROR
335           myError = "Characters \']]\' are expected in the end of CDATA";
336           return XML_UNKNOWN;
337         }
338         theData.rdbuf()->sputn(aStartData, aPtr - aStartData);
339         myPtr = aPtr + 2;
340         return XML_CDATA;
341       }
342       myPtr = myEndPtr - 1;
343       continue;
344
345         // Checking the characters in STATE_ELEMENT, seek the end of TagName
346         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
347     case STATE_ELEMENT:
348       if (::isName (myPtr, myEndPtr, aNameEnd) == Standard_False)
349         if (theData.Length() == 0 || aNameEnd != myPtr) {
350           myError = "Invalid tag name";
351           return XML_UNKNOWN;
352         }
353       {
354         theData.rdbuf()->sputn(aStartData, aNameEnd - aStartData);
355         char* aDataString = (char *)theData.str();
356         myElement = & LDOM_BasicElement::Create (aDataString, theData.Length(),
357                                                  myDocument);
358         theData.Clear();
359         myLastChild = NULL;
360         delete [] aDataString;
361         aState = STATE_ATTRIBUTE_NAME;
362         aStartData = NULL;
363         myPtr = aNameEnd;
364         continue;
365       }
366         // Parsing a single attribute (STATE_ATTRIBUTE)
367         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
368     case STATE_ATTRIBUTE_NAME:          // attribute name
369       switch (myPtr[0]) {
370       case ' ' :
371       case '\t':
372       case '\n':
373       case '\r':
374         if (aStartData) goto attr_name;
375         ++ myPtr;
376         continue;
377       case '/' :
378         if (aStartData)
379           myError = "Inexpected end of attribute";
380         else if (myPtr[1] != '>')
381           myError = "Improper element tag termination";
382         else {
383           myPtr += 2;
384 #ifdef DEB
385           theData.Clear();
386           theData << myElement->GetTagName();
387 #endif
388           return XML_FULL_ELEMENT;
389         }
390         return XML_UNKNOWN;
391       case '>' :
392         if (aStartData) {
393           myError = "Inexpected end of attribute";
394           return XML_UNKNOWN;
395         }
396         ++ myPtr;
397 #ifdef DEB
398         theData.Clear();
399         theData << myElement->GetTagName();
400 #endif
401         return XML_START_ELEMENT;
402       default  :
403         if (::isName (myPtr, myEndPtr, aNameEnd) == Standard_False)
404           if (theData.Length() == 0 || aNameEnd != myPtr) {
405             myError = "Invalid attribute name";
406             return XML_UNKNOWN;
407           }
408         if (aNameEnd >= myEndPtr)
409           aStartData = myPtr;
410         else {
411           if (theData.Length() == 0)
412             anAttrName = LDOMBasicString(myPtr, (Standard_Integer)(aNameEnd - myPtr), myDocument);
413           else {
414             theData.rdbuf()->sputn(myPtr, aNameEnd - myPtr);
415 attr_name:
416             char* aDataString = (char *)theData.str();
417             theData.Clear();
418             anAttrName = LDOMBasicString (aDataString, myDocument);
419             delete [] aDataString;
420           }
421           aStartData = NULL;
422           aState = STATE_ATTRIBUTE_EQUAL;
423         }
424         myPtr = aNameEnd;
425         continue;
426       }
427     case STATE_ATTRIBUTE_EQUAL:          // attribute 'equal' sign
428       switch (myPtr[0]) {
429       case '=' :
430         aState = STATE_ATTRIBUTE_VALUE;
431       case ' ' :
432       case '\t':
433       case '\n':
434       case '\r':
435         ++ myPtr;
436         continue;
437       default:
438         myError = "Equal sign expected in attribute definition";
439         return XML_UNKNOWN;
440       }
441
442     case STATE_ATTRIBUTE_VALUE:          // attribute value
443       switch (myPtr[0]) {
444       case ' ' :
445       case '\t':
446       case '\n':
447       case '\r':
448         if (aStartData == NULL) {
449           ++ myPtr;
450           continue;
451       default:
452           if (anAttDelimiter == '\0') {
453             myError = "Expected an attribute value";
454             return XML_UNKNOWN;
455       case '\"':
456       case '\'':
457             if (aStartData == NULL) {
458               aStartData     = &myPtr[1];
459               anAttDelimiter = myPtr[0];
460             }
461           }
462         }
463         //      Limitation: we do not take into account that '<' and '&'
464         //      are not allowed in attribute values
465         aPtr = (const char *) memchr (aStartData, anAttDelimiter,
466                                       myEndPtr - aStartData);
467         if (aPtr) {
468           (char&) aPtr[0] = '\0';
469           anAttDelimiter  = '\0';
470           char          * aDataString   = (char *) aStartData;
471           const char    * ePtr          = aPtr;
472
473           //    Append the end of the string to previously taken data
474           if (theData.Length() > 0) {
475             theData.rdbuf()->sputn(aStartData, aPtr-aStartData);
476             aDataString = (char *)theData.str();
477             ePtr = strchr (aDataString, '\0');
478           }
479
480           Standard_Integer aDataLen;
481           aDataString = LDOM_CharReference::Decode (aDataString, aDataLen);
482           if (IsDigit(aDataString[0])) {
483             if (getInteger (anAttrValue, aDataString, ePtr))
484               anAttrValue = LDOMBasicString (aDataString,aDataLen,myDocument);
485           } else
486             anAttrValue = LDOMBasicString (aDataString, aDataLen, myDocument);
487
488           if (theData.Length() > 0) {
489             theData.Clear();
490             delete [] aDataString;
491           }
492           //    Create an attribute
493           myLastChild = myElement -> AddAttribute (anAttrName, anAttrValue,
494                                                    myDocument, myLastChild);
495           myPtr = aPtr + 1;
496           aStartData = NULL;
497           aState = STATE_ATTRIBUTE_NAME;
498         } else
499           myPtr = myEndPtr;
500         continue;
501       }
502         // Checking the characters in STATE_ELEMENT_END, seek for ">"
503         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
504     case STATE_ELEMENT_END:
505       aPtr = (const char *) memchr (aStartData, '>', myEndPtr - aStartData);
506       if (aPtr) {
507         // The end of the end-element markup
508         theData.rdbuf()->sputn(aStartData, aPtr - aStartData);
509         myPtr = aPtr + 1;
510         return XML_END_ELEMENT;
511       }
512       myPtr = myEndPtr;
513       continue;
514     }
515   }
516   if (aState != STATE_WAITING) {
517     myError = "Unexpected end of file";
518     return XML_UNKNOWN;
519   }
520   return XML_EOF;
521 }
522
523 //=======================================================================
524 //function : isName
525 //type     : static
526 //purpose  : Check if aString is a valid XML Name
527 //=======================================================================
528
529 static Standard_Boolean isName (const char  * aString,
530                                 const char  * aStringEnd,
531                                 const char  *& aNameEnd)
532 {
533   Standard_Boolean aResult;
534   char aCh = aString[0];
535   if (IsAlphabetic(aCh) || aCh == '_' || aCh == ':') {
536     const char * aPtr = &aString[1];
537     while (aPtr < aStringEnd) {
538       aCh = * aPtr;
539       switch (aCh) {
540       case ' ' :
541       case '\n':
542       case '\r':
543       case '\t':
544       case '=' :
545       case '\0':
546       case '/' :
547       case '>' :
548         aNameEnd = aPtr;
549         return Standard_True;
550       default:
551         if (IsAlphanumeric(aCh) == 0) {
552           aNameEnd = aPtr;
553           return Standard_False;
554         }
555       case '.' :
556       case '-' :
557       case '_' :
558       case ':' :
559         ++ aPtr;
560       }
561     }
562     aNameEnd = aPtr;
563     aResult = Standard_True;
564   } else {
565     aNameEnd = aString;
566     aResult = Standard_False;
567   }
568   return aResult;
569 }
570
571 //=======================================================================
572 //function : getInteger
573 //purpose  : Try to initialize theValue as Integer; return False on success
574 //=======================================================================
575
576 Standard_Boolean LDOM_XmlReader::getInteger (LDOMBasicString&    theValue,
577                                              const char          * theStart,
578                                              const char          * theEnd)
579 {
580   char * ptr;
581   errno = 0;
582   if (theEnd - theStart == 1 || theStart[0] != '0')
583   {
584       long aResult = strtol (theStart, &ptr, 10);
585       if (ptr == theEnd && errno == 0) 
586       {
587         theValue = Standard_Integer(aResult);
588         return Standard_False;
589       }
590   }
591   return Standard_True;
592 }