0031340: LDOM fails to read XML file starting with BOM
[occt.git] / src / LDOM / LDOM_XmlReader.cxx
1 // Created on: 2001-07-20
2 // Created by: Alexander GRIGORIEV
3 // Copyright (c) 2001-2014 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 //AGV 060302: Input from std::istream
17 //            AGV 130302: bug corr: was error if strlen(root_elem_name) < 7
18
19 #include <LDOM_XmlReader.hxx>
20 #include <Standard_Stream.hxx>
21 #include <LDOM_MemManager.hxx>
22 #include <LDOM_BasicAttribute.hxx>
23 #include <LDOM_CharReference.hxx>
24 #include <LDOM_OSStream.hxx>
25
26 #include <string.h>
27 #include <errno.h>
28 #ifdef _MSC_VER
29 #include <io.h>
30 #else
31 #include <unistd.h>
32 #endif
33
34 //#include <ctype.h>
35
36 const int XML_MIN_BUFFER = 10;
37
38 typedef enum {
39   STATE_WAITING = 0,
40   STATE_HEADER,
41   STATE_DOCTYPE,
42   STATE_DOCTYPE_MARKUP,
43   STATE_ELEMENT,
44   STATE_ELEMENT_END,
45   STATE_ATTRIBUTE_NAME,
46   STATE_ATTRIBUTE_EQUAL,
47   STATE_ATTRIBUTE_VALUE,
48   STATE_COMMENT,
49   STATE_CDATA,
50   STATE_TEXT
51 } ParserState;
52
53 #define TEXT_COMPARE(aPtr,aPattern) \
54   (memcmp ((aPtr), (aPattern), sizeof(aPattern) - 1) == 0)
55
56 static Standard_Boolean isName          (const char             * aString,
57                                          const char             * aStringEnd,
58                                          const char             *& aNameEnd);
59
60 //=======================================================================
61 //function : LDOM_XmlReader()
62 //purpose  : Constructor (file descriptor)
63 //=======================================================================
64
65 LDOM_XmlReader::LDOM_XmlReader (
66                                 const Handle(LDOM_MemManager)&  theDocument,
67                                 TCollection_AsciiString&        theErrorString,
68                                 const Standard_Boolean theTagPerStep)
69 : myEOF      (Standard_False),
70   myError    (theErrorString),
71   myDocument (theDocument),
72   myElement  (NULL),
73   myLastChild(NULL), 
74   myPtr      (&myBuffer[0]),
75   myEndPtr   (&myBuffer[0]),
76   myTagPerStep (theTagPerStep),
77   myBOM      (LDOM_OSStream::BOM_UNDEFINED)
78 {
79 }
80
81 //=======================================================================
82 //function : ReadRecord
83 //purpose  : Read a record from XML file
84 //=======================================================================
85
86 LDOM_XmlReader::RecordType LDOM_XmlReader::ReadRecord (Standard_IStream& theIStream,
87                                         LDOM_OSStream& theData)
88 {
89   theData.Clear();
90   myError.Clear();
91   ParserState aState = STATE_WAITING;
92   const char * aStartData = NULL, * aNameEnd = NULL, * aPtr;
93   LDOMBasicString anAttrName, anAttrValue;
94   char anAttDelimiter = '\0';
95   Standard_Boolean aHasRead = Standard_False;
96   Standard_Boolean isFileStart = !myEOF && theIStream.tellg() == std::iostream::pos_type(0);
97
98   for(;;) {
99     //  Check if the current file buffer is exhausted
100     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
101     //  There should always be some bytes available in the buffer for analysis
102     Standard_Integer aBytesRest = (Standard_Integer)(myEndPtr - myPtr);
103     if (aBytesRest < XML_MIN_BUFFER)
104     {
105       if (myEOF == Standard_True)
106       {
107         if (aBytesRest <= 0)
108           break;                        // END of processing
109       }
110       else if (myTagPerStep && aHasRead)
111       {
112         // in myTagPerStep mode, we should parse the buffer to the end before
113         // getting more characters from the stream.
114       }
115       else
116       {
117         // If we are reading some data, save the beginning and preserve the state
118         if (aStartData /* && aState != STATE_WAITING */) {
119           if (myPtr > aStartData)
120             theData.rdbuf()->sputn(aStartData, myPtr - aStartData);
121           aStartData = &myBuffer[0];
122         }
123         // Copy the rest of file data to the beginning of buffer
124         if (aBytesRest > 0)
125         {
126           // do not use memcpy here because aBytesRest may be greater than myPtr-myBuffer, so, overlap
127           memmove (&myBuffer[0], myPtr, aBytesRest);
128         }
129
130         // Read the full buffer and reset start and end buffer pointers
131         myPtr    = &myBuffer[0];
132         Standard_Size aNBytes;
133
134         if (myTagPerStep)
135         {
136           theIStream.getline (&myBuffer[aBytesRest], XML_BUFFER_SIZE - aBytesRest, '>');
137           aHasRead = Standard_True;
138         }
139         else
140         {
141           theIStream.read (&myBuffer[aBytesRest], XML_BUFFER_SIZE - aBytesRest);
142         }
143         aNBytes = (Standard_Size)theIStream.gcount();
144         
145         if (aNBytes == 0)
146         {
147           myEOF = Standard_True;                  // END-OF-FILE
148         }
149         else if (myTagPerStep)
150         {
151           // replace \0 (being inserted by getline method) with > 
152           myBuffer[aBytesRest + aNBytes - 1] = '>';
153         }
154         myEndPtr = &myBuffer[aBytesRest + aNBytes];
155         myBuffer[aBytesRest + aNBytes] = '\0';
156       }
157     }
158     if (isFileStart)
159     {
160       isFileStart = Standard_False;
161       // check for BOM block
162       Standard_Utf8UChar aFirstChar = Standard_Utf8UChar(myPtr[0]);
163       switch(aFirstChar) {
164       case 0xEF:
165         if (Standard_Utf8UChar(myPtr[1]) == 0xBB && Standard_Utf8UChar(myPtr[2]) == 0xBF)
166         {
167           myBOM = LDOM_OSStream::BOM_UTF8;
168           myPtr += 3;
169         }
170         break;
171       case 0xFE:
172         if (Standard_Utf8UChar(myPtr[1]) == 0xFF)
173         {
174           myBOM = LDOM_OSStream::BOM_UTF16BE;
175           myPtr += 2;
176         }
177         break;
178       case 0xFF:
179         if (Standard_Utf8UChar(myPtr[1]) == 0xFE)
180         {
181           if (myPtr[2] == 0 && myPtr[3] == 0)
182           {
183             myBOM = LDOM_OSStream::BOM_UTF32LE;
184             myPtr += 4;
185           }
186           else
187           {
188             myBOM = LDOM_OSStream::BOM_UTF16LE;
189             myPtr += 2;
190           }
191         }
192         break;
193       case 0x00:
194         if (myPtr[1] == 0 && Standard_Utf8UChar(myPtr[2]) == 0xFE && Standard_Utf8UChar(myPtr[3]) == 0xFF)
195         {
196           myBOM = LDOM_OSStream::BOM_UTF32BE;
197           myPtr += 4;
198         }
199         break;
200       case 0x2B:
201         if (myPtr[1] == 47 && myPtr[2] == 118 &&
202             (myPtr[3] == 43 || myPtr[3] == 47 || myPtr[3] == 56 || myPtr[3] == 57))
203         {
204           myBOM = LDOM_OSStream::BOM_UTF7;
205           if (myPtr[3] == 56 && myPtr[3] == 45)
206             myPtr += 5;
207           else
208             myPtr += 4;
209         }
210         break;
211       case 0xF7:
212         if (myPtr[1] == 100 && myPtr[2] == 76)
213         {
214           myBOM = LDOM_OSStream::BOM_UTF1;
215           myPtr += 3;
216         }
217         break;
218       case 0xDD:
219         if (myPtr[1] == 115 && myPtr[2] == 102 && myPtr[3] == 115)
220         {
221           myBOM = LDOM_OSStream::BOM_UTFEBCDIC;
222           myPtr += 4;
223         }
224         break;
225       case 0x0E:
226         if (Standard_Utf8UChar(myPtr[1]) == 0xFE && Standard_Utf8UChar(myPtr[2]) == 0xFF)
227         {
228           myBOM = LDOM_OSStream::BOM_SCSU;
229           myPtr += 3;
230         }
231         break;
232       case 0xFB:
233         if (Standard_Utf8UChar(myPtr[1]) == 0xEE && myPtr[2] == 40)
234         {
235           myBOM = LDOM_OSStream::BOM_BOCU1;
236           myPtr += 3;
237         }
238         break;
239       case 0x84:
240         if (myPtr[1] == 49 && Standard_Utf8UChar(myPtr[2]) == 0x95 && myPtr[3] == 51)
241         {
242           myBOM = LDOM_OSStream::BOM_GB18030;
243           myPtr += 4;
244         }
245         break;
246       }
247       if (myBOM != LDOM_OSStream::BOM_UNDEFINED)
248         continue;
249     }
250
251     //  Check the character data
252     switch (aState) {
253
254       // Checking the characters in STATE_WAITING (blank, TEXT or markup)
255       // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
256     case STATE_WAITING:
257       switch (myPtr[0]) {
258       case ' ':
259       case '\t':
260       case '\n':
261       case '\r':
262         ++ myPtr;
263         continue;
264       case '<':
265         // XML markup found, then make detect the record type
266         switch (myPtr[1]) {
267         case '?':
268           aState = STATE_HEADER;
269           myPtr += 2;
270           aStartData = myPtr;
271           continue;
272         case '/':
273           aState = STATE_ELEMENT_END;
274           myPtr += 2;
275           aStartData = myPtr;
276           continue;
277         case '!':
278           if (myPtr[2] == '-' && myPtr[3] == '-') {
279             aState = STATE_COMMENT;
280             myPtr += 4;
281           } else if (TEXT_COMPARE (&myPtr[2], "DOCTYPE")) {
282             char ch = myPtr[9];
283             if (ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r')
284               break;
285             aState = STATE_DOCTYPE;
286             myPtr += 10;
287           } else if (TEXT_COMPARE (&myPtr[2], "[CDATA[")) {
288             aState = STATE_CDATA;
289             myPtr += 9;
290           } else break;                   // ERROR
291           aStartData = myPtr;
292           continue;
293         default:
294           if (::isName (&myPtr[1], myEndPtr, aNameEnd)) {
295             aStartData = myPtr + 1;
296             myPtr = aNameEnd;
297             if (myPtr < myEndPtr) {
298               myElement = & LDOM_BasicElement::Create (aStartData,
299                                                        (Standard_Integer)(myPtr - aStartData),
300                                                        myDocument);
301               myLastChild = NULL;
302               aState = STATE_ATTRIBUTE_NAME;
303               aStartData = NULL;
304             }else
305               aState = STATE_ELEMENT;
306             continue;
307           }       // otherwise ERROR
308         }     // end of switch
309         myError = "Unknown XML object: ";
310         myError += TCollection_AsciiString (myPtr, XML_MIN_BUFFER);
311         return XML_UNKNOWN;
312       case '\0':
313         if (myEOF == Standard_True) continue;
314         Standard_FALLTHROUGH
315       default:
316         //      Limitation: we do not treat '&' as special character
317         aPtr = (const char *) memchr (myPtr, '<', myEndPtr - myPtr);
318         if (aPtr) {
319           // The end of text field reached
320           theData.rdbuf()->sputn(myPtr, aPtr - myPtr);
321           myPtr = aPtr;
322           return XML_TEXT;
323         }
324         aState = STATE_TEXT;
325         aStartData = myPtr;
326         myPtr = myEndPtr;
327         aHasRead = Standard_False;
328       }   // end of checking in STATE_WAITING
329       continue;
330
331       // Checking the characters in STATE_HEADER, seek for "?>" sequence
332       // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
333     case STATE_HEADER:
334       aPtr = (const char *) memchr (aStartData, '?', (myEndPtr-1) - aStartData);
335       if (aPtr) {
336         // The end of XML declaration found
337         if (aPtr[1] != '>') {           // ERROR
338           myError = "Character \'>\' is expected in the end of XML declaration";
339           return XML_UNKNOWN;
340         }
341         // The XML declaration is retrieved
342         theData.rdbuf()->sputn(aStartData, aPtr - aStartData);
343         myPtr = aPtr + 2;
344         return XML_HEADER;
345       }
346       myPtr = myEndPtr - 1;
347       aHasRead = Standard_False;
348       continue;
349
350       // Checking the characters in STATE_DOCTYPE, seek for "]>" sequence
351       // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
352     case STATE_DOCTYPE:
353       for (aPtr = aStartData; aPtr < myEndPtr-1; aPtr++) {
354         const int aChar = aPtr[0];
355         if (aChar == '[') {
356           aState = STATE_DOCTYPE_MARKUP;
357           aStartData = &aPtr[1];
358           goto state_doctype_markup;
359         }
360         if (aChar == '>') {
361           // The DOCTYPE declaration is retrieved
362           theData.rdbuf()->sputn(aStartData, aPtr - aStartData - 1);
363           myPtr = aPtr + 1;
364           return XML_DOCTYPE;
365         }
366       }
367       myPtr = myEndPtr - 1;
368       aHasRead = Standard_False;
369       continue;
370
371     state_doctype_markup:
372     case STATE_DOCTYPE_MARKUP:
373       aPtr = (const char *) memchr (aStartData, ']', (myEndPtr-1) - aStartData);
374       if (aPtr) {
375         // The end of DOCTYPE declaration found
376         if (aPtr[1] != '>') {           // ERROR
377           myError =
378             "Character \'>\' is expected in the end of DOCTYPE declaration";
379           return XML_UNKNOWN;
380         }
381         // The DOCTYPE declaration is retrieved
382         theData.rdbuf()->sputn(aStartData, aPtr - aStartData);
383         myPtr = aPtr + 2;
384         return XML_DOCTYPE;
385       }
386       myPtr = myEndPtr - 1;
387       aHasRead = Standard_False;
388       continue;
389
390         // Checking the characters in STATE_COMMENT, seek for "-->" sequence
391         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
392     case STATE_COMMENT:
393       aPtr = aStartData;
394       for(;;) {
395         aPtr = (const char *) memchr (aPtr, '-', (myEndPtr - 2) - aPtr);
396         if (aPtr == NULL) break;
397         if (aPtr[1] != '-') ++ aPtr;
398         else {
399           if (aPtr[2] != '>') {       // ERROR
400             myError = "Character \'>\' is expected in the end of comment";
401             return XML_UNKNOWN;
402           }
403           theData.rdbuf()->sputn(aStartData, aPtr - aStartData);
404           myPtr = aPtr + 3;
405           return XML_COMMENT;
406         }
407       }
408       myPtr = myEndPtr - 2;
409       aHasRead = Standard_False;
410       continue;
411
412         // Checking the characters in STATE_TEXT, seek for "<"
413         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
414     case STATE_TEXT:
415       aPtr = (const char *) memchr (aStartData, '<', myEndPtr - aStartData);
416       if (aPtr) {
417         // The end of text field reached
418         theData.rdbuf()->sputn(aStartData, aPtr - aStartData);
419         myPtr = aPtr;
420         return XML_TEXT;
421       }
422       myPtr = myEndPtr;
423       aHasRead = Standard_False;
424       continue;
425
426         // Checking the characters in STATE_CDATA, seek for "]]"
427         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
428     case STATE_CDATA:
429       aPtr = aStartData;
430       for(;;) {
431         aPtr = (const char *) memchr (aPtr, ']', (myEndPtr - 1) - aStartData);
432         if (aPtr == NULL) break;
433         if (aPtr[1] != ']') {           // ERROR
434           myError = "Characters \']]\' are expected in the end of CDATA";
435           return XML_UNKNOWN;
436         }
437         theData.rdbuf()->sputn(aStartData, aPtr - aStartData);
438         myPtr = aPtr + 2;
439         return XML_CDATA;
440       }
441       myPtr = myEndPtr - 1;
442       aHasRead = Standard_False;
443       continue;
444
445         // Checking the characters in STATE_ELEMENT, seek the end of TagName
446         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
447     case STATE_ELEMENT:
448       if (::isName (myPtr, myEndPtr, aNameEnd) == Standard_False)
449         if (theData.Length() == 0 || aNameEnd != myPtr) {
450           myError = "Invalid tag name";
451           return XML_UNKNOWN;
452         }
453       {
454         theData.rdbuf()->sputn(aStartData, aNameEnd - aStartData);
455         char* aDataString = (char *)theData.str();
456         myElement = & LDOM_BasicElement::Create (aDataString, theData.Length(),
457                                                  myDocument);
458         theData.Clear();
459         myLastChild = NULL;
460         delete [] aDataString;
461         aState = STATE_ATTRIBUTE_NAME;
462         aStartData = NULL;
463         myPtr = aNameEnd;
464         continue;
465       }
466         // Parsing a single attribute (STATE_ATTRIBUTE)
467         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
468     case STATE_ATTRIBUTE_NAME:          // attribute name
469       switch (myPtr[0]) {
470       case ' ' :
471       case '\t':
472       case '\n':
473       case '\r':
474         if (aStartData) goto attr_name;
475         ++ myPtr;
476         continue;
477       case '/' :
478         if (aStartData)
479           myError = "Inexpected end of attribute";
480         else if (myPtr[1] != '>')
481           myError = "Improper element tag termination";
482         else {
483           myPtr += 2;
484 #ifdef OCCT_DEBUG
485           theData.Clear();
486           theData << myElement->GetTagName();
487 #endif
488           return XML_FULL_ELEMENT;
489         }
490         return XML_UNKNOWN;
491       case '>' :
492         if (aStartData) {
493           myError = "Inexpected end of attribute";
494           return XML_UNKNOWN;
495         }
496         ++ myPtr;
497 #ifdef OCCT_DEBUG
498         theData.Clear();
499         theData << myElement->GetTagName();
500 #endif
501         return XML_START_ELEMENT;
502       default  :
503         if (::isName (myPtr, myEndPtr, aNameEnd) == Standard_False)
504           if (theData.Length() == 0 || aNameEnd != myPtr) {
505             myError = "Invalid attribute name";
506             return XML_UNKNOWN;
507           }
508         if (aNameEnd >= myEndPtr)
509           aStartData = myPtr;
510         else {
511           if (theData.Length() == 0)
512             anAttrName = LDOMBasicString(myPtr, (Standard_Integer)(aNameEnd - myPtr), myDocument);
513           else {
514             theData.rdbuf()->sputn(myPtr, aNameEnd - myPtr);
515 attr_name:
516             char* aDataString = (char *)theData.str();
517             theData.Clear();
518             anAttrName = LDOMBasicString (aDataString, myDocument);
519             delete [] aDataString;
520           }
521           aStartData = NULL;
522           aState = STATE_ATTRIBUTE_EQUAL;
523         }
524         myPtr = aNameEnd;
525         continue;
526       }
527     case STATE_ATTRIBUTE_EQUAL:          // attribute 'equal' sign
528       switch (myPtr[0]) {
529       case '=' :
530         aState = STATE_ATTRIBUTE_VALUE;
531         Standard_FALLTHROUGH
532       case ' ' :
533       case '\t':
534       case '\n':
535       case '\r':
536         ++ myPtr;
537         continue;
538       default:
539         myError = "Equal sign expected in attribute definition";
540         return XML_UNKNOWN;
541       }
542
543     case STATE_ATTRIBUTE_VALUE:          // attribute value
544       switch (myPtr[0]) {
545       case ' ' :
546       case '\t':
547       case '\n':
548       case '\r':
549         if (aStartData == NULL) {
550           ++ myPtr;
551           continue;
552       default:
553           if (anAttDelimiter == '\0') {
554             myError = "Expected an attribute value";
555             return XML_UNKNOWN;
556       case '\"':
557       case '\'':
558             if (aStartData == NULL) {
559               aStartData     = &myPtr[1];
560               anAttDelimiter = myPtr[0];
561             }
562           }
563         }
564         //      Limitation: we do not take into account that '<' and '&'
565         //      are not allowed in attribute values
566         aPtr = (const char *) memchr (aStartData, anAttDelimiter,
567                                       myEndPtr - aStartData);
568         if (aPtr) {
569           (char&) aPtr[0] = '\0';
570           anAttDelimiter  = '\0';
571           char          * aDataString   = (char *) aStartData;
572           const char    * ePtr          = aPtr;
573
574           //    Append the end of the string to previously taken data
575           if (theData.Length() > 0) {
576             theData.rdbuf()->sputn(aStartData, aPtr-aStartData);
577             aDataString = (char *)theData.str();
578             ePtr = strchr (aDataString, '\0');
579           }
580
581           Standard_Integer aDataLen;
582           aDataString = LDOM_CharReference::Decode (aDataString, aDataLen);
583           if (IsDigit(aDataString[0])) {
584             if (getInteger (anAttrValue, aDataString, ePtr))
585               anAttrValue = LDOMBasicString (aDataString,aDataLen,myDocument);
586           } else
587             anAttrValue = LDOMBasicString (aDataString, aDataLen, myDocument);
588
589           if (theData.Length() > 0) {
590             theData.Clear();
591             delete [] aDataString;
592           }
593           //    Create an attribute
594           myLastChild = myElement -> AddAttribute (anAttrName, anAttrValue,
595                                                    myDocument, myLastChild);
596           myPtr = aPtr + 1;
597           aStartData = NULL;
598           aState = STATE_ATTRIBUTE_NAME;
599         }
600         else {
601           myPtr = myEndPtr;
602           aHasRead = Standard_False;
603         }
604         continue;
605       }
606         // Checking the characters in STATE_ELEMENT_END, seek for ">"
607         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
608     case STATE_ELEMENT_END:
609       aPtr = (const char *) memchr (aStartData, '>', myEndPtr - aStartData);
610       if (aPtr) {
611         // The end of the end-element markup
612         theData.rdbuf()->sputn(aStartData, aPtr - aStartData);
613         myPtr = aPtr + 1;
614         return XML_END_ELEMENT;
615       }
616       myPtr = myEndPtr;
617       aHasRead = Standard_False;
618       continue;
619     }
620   }
621   if (aState != STATE_WAITING) {
622     myError = "Unexpected end of file";
623     return XML_UNKNOWN;
624   }
625   return XML_EOF;
626 }
627
628 //=======================================================================
629 //function : isName
630 //type     : static
631 //purpose  : Check if aString is a valid XML Name
632 //=======================================================================
633
634 static Standard_Boolean isName (const char  * aString,
635                                 const char  * aStringEnd,
636                                 const char  *& aNameEnd)
637 {
638   Standard_Boolean aResult;
639   char aCh = aString[0];
640   if (IsAlphabetic(aCh) || aCh == '_' || aCh == ':') {
641     const char * aPtr = &aString[1];
642     while (aPtr < aStringEnd) {
643       aCh = * aPtr;
644       switch (aCh) {
645       case ' ' :
646       case '\n':
647       case '\r':
648       case '\t':
649       case '=' :
650       case '\0':
651       case '/' :
652       case '>' :
653         aNameEnd = aPtr;
654         return Standard_True;
655       default:
656         if (IsAlphanumeric(aCh) == 0) {
657           aNameEnd = aPtr;
658           return Standard_False;
659         }
660         Standard_FALLTHROUGH
661       case '.' :
662       case '-' :
663       case '_' :
664       case ':' :
665         ++ aPtr;
666       }
667     }
668     aNameEnd = aPtr;
669     aResult = Standard_True;
670   } else {
671     aNameEnd = aString;
672     aResult = Standard_False;
673   }
674   return aResult;
675 }
676
677 //=======================================================================
678 //function : CreateElement
679 //purpose  : 
680 //=======================================================================
681 void LDOM_XmlReader::CreateElement( const char *theName, const Standard_Integer theLen )
682 {
683   myElement = &LDOM_BasicElement::Create (theName, theLen, myDocument);
684 }
685
686 //=======================================================================
687 //function : getInteger
688 //purpose  : Try to initialize theValue as Integer; return False on success
689 //=======================================================================
690
691 Standard_Boolean LDOM_XmlReader::getInteger (LDOMBasicString&    theValue,
692                                              const char          * theStart,
693                                              const char          * theEnd)
694 {
695   char * ptr;
696   errno = 0;
697   if (theEnd - theStart == 1 || theStart[0] != '0')
698   {
699       long aResult = strtol (theStart, &ptr, 10);
700       if (ptr == theEnd && errno == 0) 
701       {
702         theValue = Standard_Integer(aResult);
703         return Standard_False;
704       }
705   }
706   return Standard_True;
707 }