]> git.cworth.org Git - vogl/blob - src/voglgen/tinyxml/tinyxmlparser.cpp
Initial vogl checkin
[vogl] / src / voglgen / tinyxml / tinyxmlparser.cpp
1 /*\r
2 www.sourceforge.net/projects/tinyxml\r
3 Original code by Lee Thomason (www.grinninglizard.com)\r
4 \r
5 This software is provided 'as-is', without any express or implied\r
6 warranty. In no event will the authors be held liable for any\r
7 damages arising from the use of this software.\r
8 \r
9 Permission is granted to anyone to use this software for any\r
10 purpose, including commercial applications, and to alter it and\r
11 redistribute it freely, subject to the following restrictions:\r
12 \r
13 1. The origin of this software must not be misrepresented; you must\r
14 not claim that you wrote the original software. If you use this\r
15 software in a product, an acknowledgment in the product documentation\r
16 would be appreciated but is not required.\r
17 \r
18 2. Altered source versions must be plainly marked as such, and\r
19 must not be misrepresented as being the original software.\r
20 \r
21 3. This notice may not be removed or altered from any source\r
22 distribution.\r
23 */\r
24 \r
25 #include <ctype.h>\r
26 #include <stddef.h>\r
27 \r
28 #include "tinyxml.h"\r
29 \r
30 //#define DEBUG_PARSER\r
31 #if defined(DEBUG_PARSER)\r
32 #if defined(DEBUG) && defined(_MSC_VER)\r
33 #include <windows.h>\r
34 #define TIXML_LOG OutputDebugString\r
35 #else\r
36 #define TIXML_LOG printf\r
37 #endif\r
38 #endif\r
39 \r
40 // Note tha "PutString" hardcodes the same list. This\r
41 // is less flexible than it appears. Changing the entries\r
42 // or order will break putstring.\r
43 TiXmlBase::Entity TiXmlBase::entity[TiXmlBase::NUM_ENTITY] =\r
44     {\r
45         { "&amp;", 5, '&' },\r
46         { "&lt;", 4, '<' },\r
47         { "&gt;", 4, '>' },\r
48         { "&quot;", 6, '\"' },\r
49         { "&apos;", 6, '\'' }\r
50     };\r
51 \r
52 // Bunch of unicode info at:\r
53 //              http://www.unicode.org/faq/utf_bom.html\r
54 // Including the basic of this table, which determines the #bytes in the\r
55 // sequence from the lead byte. 1 placed for invalid sequences --\r
56 // although the result will be junk, pass it through as much as possible.\r
57 // Beware of the non-characters in UTF-8:\r
58 //                              ef bb bf (Microsoft "lead bytes")\r
59 //                              ef bf be\r
60 //                              ef bf bf\r
61 \r
62 const unsigned char TIXML_UTF_LEAD_0 = 0xefU;\r
63 const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;\r
64 const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;\r
65 \r
66 const int TiXmlBase::utf8ByteTable[256] =\r
67     {\r
68         //      0       1       2       3       4       5       6       7       8       9       a       b       c       d       e       f\r
69         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00\r
70         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10\r
71         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20\r
72         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30\r
73         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40\r
74         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50\r
75         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60\r
76         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range\r
77         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid\r
78         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90\r
79         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0\r
80         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0\r
81         1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte\r
82         2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0\r
83         3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte\r
84         4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1  // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid\r
85     };\r
86 \r
87 void TiXmlBase::ConvertUTF32ToUTF8(unsigned long input, char *output, int *length)\r
88 {\r
89     const unsigned long BYTE_MASK = 0xBF;\r
90     const unsigned long BYTE_MARK = 0x80;\r
91     const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };\r
92 \r
93     if (input < 0x80)\r
94         *length = 1;\r
95     else if (input < 0x800)\r
96         *length = 2;\r
97     else if (input < 0x10000)\r
98         *length = 3;\r
99     else if (input < 0x200000)\r
100         *length = 4;\r
101     else\r
102     {\r
103         *length = 0; // This code won't covert this correctly anyway.\r
104         return;\r
105     }\r
106 \r
107     output += *length;\r
108 \r
109     // Scary scary fall throughs.\r
110     switch (*length)\r
111     {\r
112         case 4:\r
113             --output;\r
114             *output = (char)((input | BYTE_MARK) & BYTE_MASK);\r
115             input >>= 6;\r
116         case 3:\r
117             --output;\r
118             *output = (char)((input | BYTE_MARK) & BYTE_MASK);\r
119             input >>= 6;\r
120         case 2:\r
121             --output;\r
122             *output = (char)((input | BYTE_MARK) & BYTE_MASK);\r
123             input >>= 6;\r
124         case 1:\r
125             --output;\r
126             *output = (char)(input | FIRST_BYTE_MARK[*length]);\r
127     }\r
128 }\r
129 \r
130 /*static*/ int TiXmlBase::IsAlpha(unsigned char anyByte, TiXmlEncoding /*encoding*/)\r
131 {\r
132     // This will only work for low-ascii, everything else is assumed to be a valid\r
133     // letter. I'm not sure this is the best approach, but it is quite tricky trying\r
134     // to figure out alhabetical vs. not across encoding. So take a very\r
135     // conservative approach.\r
136 \r
137     //  if ( encoding == TIXML_ENCODING_UTF8 )\r
138     //  {\r
139     if (anyByte < 127)\r
140         return isalpha(anyByte);\r
141     else\r
142         return 1; // What else to do? The unicode set is huge...get the english ones right.\r
143                   //    }\r
144                   //    else\r
145                   //    {\r
146                   //            return isalpha( anyByte );\r
147                   //    }\r
148 }\r
149 \r
150 /*static*/ int TiXmlBase::IsAlphaNum(unsigned char anyByte, TiXmlEncoding /*encoding*/)\r
151 {\r
152     // This will only work for low-ascii, everything else is assumed to be a valid\r
153     // letter. I'm not sure this is the best approach, but it is quite tricky trying\r
154     // to figure out alhabetical vs. not across encoding. So take a very\r
155     // conservative approach.\r
156 \r
157     //  if ( encoding == TIXML_ENCODING_UTF8 )\r
158     //  {\r
159     if (anyByte < 127)\r
160         return isalnum(anyByte);\r
161     else\r
162         return 1; // What else to do? The unicode set is huge...get the english ones right.\r
163                   //    }\r
164                   //    else\r
165                   //    {\r
166                   //            return isalnum( anyByte );\r
167                   //    }\r
168 }\r
169 \r
170 class TiXmlParsingData\r
171 {\r
172     friend class TiXmlDocument;\r
173 \r
174 public:\r
175     void Stamp(const char *now, TiXmlEncoding encoding);\r
176 \r
177     const TiXmlCursor &Cursor() const\r
178     {\r
179         return cursor;\r
180     }\r
181 \r
182 private:\r
183     // Only used by the document!\r
184     TiXmlParsingData(const char *start, int _tabsize, int row, int col)\r
185     {\r
186         assert(start);\r
187         stamp = start;\r
188         tabsize = _tabsize;\r
189         cursor.row = row;\r
190         cursor.col = col;\r
191     }\r
192 \r
193     TiXmlCursor cursor;\r
194     const char *stamp;\r
195     int tabsize;\r
196 };\r
197 \r
198 void TiXmlParsingData::Stamp(const char *now, TiXmlEncoding encoding)\r
199 {\r
200     assert(now);\r
201 \r
202     // Do nothing if the tabsize is 0.\r
203     if (tabsize < 1)\r
204     {\r
205         return;\r
206     }\r
207 \r
208     // Get the current row, column.\r
209     int row = cursor.row;\r
210     int col = cursor.col;\r
211     const char *p = stamp;\r
212     assert(p);\r
213 \r
214     while (p < now)\r
215     {\r
216         // Treat p as unsigned, so we have a happy compiler.\r
217         const unsigned char *pU = (const unsigned char *)p;\r
218 \r
219         // Code contributed by Fletcher Dunn: (modified by lee)\r
220         switch (*pU)\r
221         {\r
222             case 0:\r
223                 // We *should* never get here, but in case we do, don't\r
224                 // advance past the terminating null character, ever\r
225                 return;\r
226 \r
227             case '\r':\r
228                 // bump down to the next line\r
229                 ++row;\r
230                 col = 0;\r
231                 // Eat the character\r
232                 ++p;\r
233 \r
234                 // Check for \r\n sequence, and treat this as a single character\r
235                 if (*p == '\n')\r
236                 {\r
237                     ++p;\r
238                 }\r
239                 break;\r
240 \r
241             case '\n':\r
242                 // bump down to the next line\r
243                 ++row;\r
244                 col = 0;\r
245 \r
246                 // Eat the character\r
247                 ++p;\r
248 \r
249                 // Check for \n\r sequence, and treat this as a single\r
250                 // character.  (Yes, this bizarre thing does occur still\r
251                 // on some arcane platforms...)\r
252                 if (*p == '\r')\r
253                 {\r
254                     ++p;\r
255                 }\r
256                 break;\r
257 \r
258             case '\t':\r
259                 // Eat the character\r
260                 ++p;\r
261 \r
262                 // Skip to next tab stop\r
263                 col = (col / tabsize + 1) * tabsize;\r
264                 break;\r
265 \r
266             case TIXML_UTF_LEAD_0:\r
267                 if (encoding == TIXML_ENCODING_UTF8)\r
268                 {\r
269                     if (*(p + 1) && *(p + 2))\r
270                     {\r
271                         // In these cases, don't advance the column. These are\r
272                         // 0-width spaces.\r
273                         if (*(pU + 1) == TIXML_UTF_LEAD_1 && *(pU + 2) == TIXML_UTF_LEAD_2)\r
274                             p += 3;\r
275                         else if (*(pU + 1) == 0xbfU && *(pU + 2) == 0xbeU)\r
276                             p += 3;\r
277                         else if (*(pU + 1) == 0xbfU && *(pU + 2) == 0xbfU)\r
278                             p += 3;\r
279                         else\r
280                         {\r
281                             p += 3; // A normal character.\r
282                             ++col;\r
283                         }\r
284                     }\r
285                 }\r
286                 else\r
287                 {\r
288                     ++p;\r
289                     ++col;\r
290                 }\r
291                 break;\r
292 \r
293             default:\r
294                 if (encoding == TIXML_ENCODING_UTF8)\r
295                 {\r
296                     // Eat the 1 to 4 byte utf8 character.\r
297                     int step = TiXmlBase::utf8ByteTable[*((const unsigned char *)p)];\r
298                     if (step == 0)\r
299                         step = 1; // Error case from bad encoding, but handle gracefully.\r
300                     p += step;\r
301 \r
302                     // Just advance one column, of course.\r
303                     ++col;\r
304                 }\r
305                 else\r
306                 {\r
307                     ++p;\r
308                     ++col;\r
309                 }\r
310                 break;\r
311         }\r
312     }\r
313     cursor.row = row;\r
314     cursor.col = col;\r
315     assert(cursor.row >= -1);\r
316     assert(cursor.col >= -1);\r
317     stamp = p;\r
318     assert(stamp);\r
319 }\r
320 \r
321 const char *TiXmlBase::SkipWhiteSpace(const char *p, TiXmlEncoding encoding)\r
322 {\r
323     if (!p || !*p)\r
324     {\r
325         return 0;\r
326     }\r
327     if (encoding == TIXML_ENCODING_UTF8)\r
328     {\r
329         while (*p)\r
330         {\r
331             const unsigned char *pU = (const unsigned char *)p;\r
332 \r
333             // Skip the stupid Microsoft UTF-8 Byte order marks\r
334             if (*(pU + 0) == TIXML_UTF_LEAD_0 && *(pU + 1) == TIXML_UTF_LEAD_1 && *(pU + 2) == TIXML_UTF_LEAD_2)\r
335             {\r
336                 p += 3;\r
337                 continue;\r
338             }\r
339             else if (*(pU + 0) == TIXML_UTF_LEAD_0 && *(pU + 1) == 0xbfU && *(pU + 2) == 0xbeU)\r
340             {\r
341                 p += 3;\r
342                 continue;\r
343             }\r
344             else if (*(pU + 0) == TIXML_UTF_LEAD_0 && *(pU + 1) == 0xbfU && *(pU + 2) == 0xbfU)\r
345             {\r
346                 p += 3;\r
347                 continue;\r
348             }\r
349 \r
350             if (IsWhiteSpace(*p)) // Still using old rules for white space.\r
351                 ++p;\r
352             else\r
353                 break;\r
354         }\r
355     }\r
356     else\r
357     {\r
358         while (*p && IsWhiteSpace(*p))\r
359             ++p;\r
360     }\r
361 \r
362     return p;\r
363 }\r
364 \r
365 #ifdef TIXML_USE_STL\r
366 /*static*/ bool TiXmlBase::StreamWhiteSpace(std::istream *in, TIXML_STRING *tag)\r
367 {\r
368     for (;;)\r
369     {\r
370         if (!in->good())\r
371             return false;\r
372 \r
373         int c = in->peek();\r
374         // At this scope, we can't get to a document. So fail silently.\r
375         if (!IsWhiteSpace(c) || c <= 0)\r
376             return true;\r
377 \r
378         *tag += (char)in->get();\r
379     }\r
380 }\r
381 \r
382 /*static*/ bool TiXmlBase::StreamTo(std::istream *in, int character, TIXML_STRING *tag)\r
383 {\r
384     //assert( character > 0 && character < 128 );       // else it won't work in utf-8\r
385     while (in->good())\r
386     {\r
387         int c = in->peek();\r
388         if (c == character)\r
389             return true;\r
390         if (c <= 0) // Silent failure: can't get document at this scope\r
391             return false;\r
392 \r
393         in->get();\r
394         *tag += (char)c;\r
395     }\r
396     return false;\r
397 }\r
398 #endif\r
399 \r
400 // One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The\r
401 // "assign" optimization removes over 10% of the execution time.\r
402 //\r
403 const char *TiXmlBase::ReadName(const char *p, TIXML_STRING *name, TiXmlEncoding encoding)\r
404 {\r
405     // Oddly, not supported on some comilers,\r
406     //name->clear();\r
407     // So use this:\r
408     *name = "";\r
409     assert(p);\r
410 \r
411     // Names start with letters or underscores.\r
412     // Of course, in unicode, tinyxml has no idea what a letter *is*. The\r
413     // algorithm is generous.\r
414     //\r
415     // After that, they can be letters, underscores, numbers,\r
416     // hyphens, or colons. (Colons are valid ony for namespaces,\r
417     // but tinyxml can't tell namespaces from names.)\r
418     if (p && *p && (IsAlpha((unsigned char)*p, encoding) || *p == '_'))\r
419     {\r
420         const char *start = p;\r
421         while (p && *p && (IsAlphaNum((unsigned char)*p, encoding) || *p == '_' || *p == '-' || *p == '.' || *p == ':'))\r
422         {\r
423             //(*name) += *p; // expensive\r
424             ++p;\r
425         }\r
426         if (p - start > 0)\r
427         {\r
428             name->assign(start, p - start);\r
429         }\r
430         return p;\r
431     }\r
432     return 0;\r
433 }\r
434 \r
435 const char *TiXmlBase::GetEntity(const char *p, char *value, int *length, TiXmlEncoding encoding)\r
436 {\r
437     // Presume an entity, and pull it out.\r
438     TIXML_STRING ent;\r
439     int i;\r
440     *length = 0;\r
441 \r
442     if (*(p + 1) && *(p + 1) == '#' && *(p + 2))\r
443     {\r
444         unsigned long ucs = 0;\r
445         ptrdiff_t delta = 0;\r
446         unsigned mult = 1;\r
447 \r
448         if (*(p + 2) == 'x')\r
449         {\r
450             // Hexadecimal.\r
451             if (!*(p + 3))\r
452                 return 0;\r
453 \r
454             const char *q = p + 3;\r
455             q = strchr(q, ';');\r
456 \r
457             if (!q || !*q)\r
458                 return 0;\r
459 \r
460             delta = q - p;\r
461             --q;\r
462 \r
463             while (*q != 'x')\r
464             {\r
465                 if (*q >= '0' && *q <= '9')\r
466                     ucs += mult * (*q - '0');\r
467                 else if (*q >= 'a' && *q <= 'f')\r
468                     ucs += mult * (*q - 'a' + 10);\r
469                 else if (*q >= 'A' && *q <= 'F')\r
470                     ucs += mult * (*q - 'A' + 10);\r
471                 else\r
472                     return 0;\r
473                 mult *= 16;\r
474                 --q;\r
475             }\r
476         }\r
477         else\r
478         {\r
479             // Decimal.\r
480             if (!*(p + 2))\r
481                 return 0;\r
482 \r
483             const char *q = p + 2;\r
484             q = strchr(q, ';');\r
485 \r
486             if (!q || !*q)\r
487                 return 0;\r
488 \r
489             delta = q - p;\r
490             --q;\r
491 \r
492             while (*q != '#')\r
493             {\r
494                 if (*q >= '0' && *q <= '9')\r
495                     ucs += mult * (*q - '0');\r
496                 else\r
497                     return 0;\r
498                 mult *= 10;\r
499                 --q;\r
500             }\r
501         }\r
502         if (encoding == TIXML_ENCODING_UTF8)\r
503         {\r
504             // convert the UCS to UTF-8\r
505             ConvertUTF32ToUTF8(ucs, value, length);\r
506         }\r
507         else\r
508         {\r
509             *value = (char)ucs;\r
510             *length = 1;\r
511         }\r
512         return p + delta + 1;\r
513     }\r
514 \r
515     // Now try to match it.\r
516     for (i = 0; i < NUM_ENTITY; ++i)\r
517     {\r
518         if (strncmp(entity[i].str, p, entity[i].strLength) == 0)\r
519         {\r
520             assert(strlen(entity[i].str) == entity[i].strLength);\r
521             *value = entity[i].chr;\r
522             *length = 1;\r
523             return (p + entity[i].strLength);\r
524         }\r
525     }\r
526 \r
527     // So it wasn't an entity, its unrecognized, or something like that.\r
528     *value = *p; // Don't put back the last one, since we return it!\r
529     //*length = 1;      // Leave unrecognized entities - this doesn't really work.\r
530     // Just writes strange XML.\r
531     return p + 1;\r
532 }\r
533 \r
534 bool TiXmlBase::StringEqual(const char *p,\r
535                             const char *tag,\r
536                             bool ignoreCase,\r
537                             TiXmlEncoding encoding)\r
538 {\r
539     assert(p);\r
540     assert(tag);\r
541     if (!p || !*p)\r
542     {\r
543         assert(0);\r
544         return false;\r
545     }\r
546 \r
547     const char *q = p;\r
548 \r
549     if (ignoreCase)\r
550     {\r
551         while (*q && *tag && ToLower(*q, encoding) == ToLower(*tag, encoding))\r
552         {\r
553             ++q;\r
554             ++tag;\r
555         }\r
556 \r
557         if (*tag == 0)\r
558             return true;\r
559     }\r
560     else\r
561     {\r
562         while (*q && *tag && *q == *tag)\r
563         {\r
564             ++q;\r
565             ++tag;\r
566         }\r
567 \r
568         if (*tag == 0) // Have we found the end of the tag, and everything equal?\r
569             return true;\r
570     }\r
571     return false;\r
572 }\r
573 \r
574 const char *TiXmlBase::ReadText(const char *p,\r
575                                 TIXML_STRING *text,\r
576                                 bool trimWhiteSpace,\r
577                                 const char *endTag,\r
578                                 bool caseInsensitive,\r
579                                 TiXmlEncoding encoding)\r
580 {\r
581     *text = "";\r
582     if (!trimWhiteSpace         // certain tags always keep whitespace\r
583         || !condenseWhiteSpace) // if true, whitespace is always kept\r
584     {\r
585         // Keep all the white space.\r
586         while (p && *p && !StringEqual(p, endTag, caseInsensitive, encoding))\r
587         {\r
588             int len;\r
589             char cArr[4] = { 0, 0, 0, 0 };\r
590             p = GetChar(p, cArr, &len, encoding);\r
591             text->append(cArr, len);\r
592         }\r
593     }\r
594     else\r
595     {\r
596         bool whitespace = false;\r
597 \r
598         // Remove leading white space:\r
599         p = SkipWhiteSpace(p, encoding);\r
600         while (p && *p && !StringEqual(p, endTag, caseInsensitive, encoding))\r
601         {\r
602             if (*p == '\r' || *p == '\n')\r
603             {\r
604                 whitespace = true;\r
605                 ++p;\r
606             }\r
607             else if (IsWhiteSpace(*p))\r
608             {\r
609                 whitespace = true;\r
610                 ++p;\r
611             }\r
612             else\r
613             {\r
614                 // If we've found whitespace, add it before the\r
615                 // new character. Any whitespace just becomes a space.\r
616                 if (whitespace)\r
617                 {\r
618                     (*text) += ' ';\r
619                     whitespace = false;\r
620                 }\r
621                 int len;\r
622                 char cArr[4] = { 0, 0, 0, 0 };\r
623                 p = GetChar(p, cArr, &len, encoding);\r
624                 if (len == 1)\r
625                     (*text) += cArr[0]; // more efficient\r
626                 else\r
627                     text->append(cArr, len);\r
628             }\r
629         }\r
630     }\r
631     if (p && *p)\r
632         p += strlen(endTag);\r
633     return (p && *p) ? p : 0;\r
634 }\r
635 \r
636 #ifdef TIXML_USE_STL\r
637 \r
638 void TiXmlDocument::StreamIn(std::istream *in, TIXML_STRING *tag)\r
639 {\r
640     // The basic issue with a document is that we don't know what we're\r
641     // streaming. Read something presumed to be a tag (and hope), then\r
642     // identify it, and call the appropriate stream method on the tag.\r
643     //\r
644     // This "pre-streaming" will never read the closing ">" so the\r
645     // sub-tag can orient itself.\r
646 \r
647     if (!StreamTo(in, '<', tag))\r
648     {\r
649         SetError(TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN);\r
650         return;\r
651     }\r
652 \r
653     while (in->good())\r
654     {\r
655         int tagIndex = (int)tag->length();\r
656         while (in->good() && in->peek() != '>')\r
657         {\r
658             int c = in->get();\r
659             if (c <= 0)\r
660             {\r
661                 SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);\r
662                 break;\r
663             }\r
664             (*tag) += (char)c;\r
665         }\r
666 \r
667         if (in->good())\r
668         {\r
669             // We now have something we presume to be a node of\r
670             // some sort. Identify it, and call the node to\r
671             // continue streaming.\r
672             TiXmlNode *node = Identify(tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING);\r
673 \r
674             if (node)\r
675             {\r
676                 node->StreamIn(in, tag);\r
677                 bool isElement = node->ToElement() != 0;\r
678                 delete node;\r
679                 node = 0;\r
680 \r
681                 // If this is the root element, we're done. Parsing will be\r
682                 // done by the >> operator.\r
683                 if (isElement)\r
684                 {\r
685                     return;\r
686                 }\r
687             }\r
688             else\r
689             {\r
690                 SetError(TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN);\r
691                 return;\r
692             }\r
693         }\r
694     }\r
695     // We should have returned sooner.\r
696     SetError(TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN);\r
697 }\r
698 \r
699 #endif\r
700 \r
701 const char *TiXmlDocument::Parse(const char *p, TiXmlParsingData *prevData, TiXmlEncoding encoding)\r
702 {\r
703     ClearError();\r
704 \r
705     // Parse away, at the document level. Since a document\r
706     // contains nothing but other tags, most of what happens\r
707     // here is skipping white space.\r
708     if (!p || !*p)\r
709     {\r
710         SetError(TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN);\r
711         return 0;\r
712     }\r
713 \r
714     // Note that, for a document, this needs to come\r
715     // before the while space skip, so that parsing\r
716     // starts from the pointer we are given.\r
717     location.Clear();\r
718     if (prevData)\r
719     {\r
720         location.row = prevData->cursor.row;\r
721         location.col = prevData->cursor.col;\r
722     }\r
723     else\r
724     {\r
725         location.row = 0;\r
726         location.col = 0;\r
727     }\r
728     TiXmlParsingData data(p, TabSize(), location.row, location.col);\r
729     location = data.Cursor();\r
730 \r
731     if (encoding == TIXML_ENCODING_UNKNOWN)\r
732     {\r
733         // Check for the Microsoft UTF-8 lead bytes.\r
734         const unsigned char *pU = (const unsigned char *)p;\r
735         if (*(pU + 0) && *(pU + 0) == TIXML_UTF_LEAD_0 && *(pU + 1) && *(pU + 1) == TIXML_UTF_LEAD_1 && *(pU + 2) && *(pU + 2) == TIXML_UTF_LEAD_2)\r
736         {\r
737             encoding = TIXML_ENCODING_UTF8;\r
738             useMicrosoftBOM = true;\r
739         }\r
740     }\r
741 \r
742     p = SkipWhiteSpace(p, encoding);\r
743     if (!p)\r
744     {\r
745         SetError(TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN);\r
746         return 0;\r
747     }\r
748 \r
749     while (p && *p)\r
750     {\r
751         TiXmlNode *node = Identify(p, encoding);\r
752         if (node)\r
753         {\r
754             p = node->Parse(p, &data, encoding);\r
755             LinkEndChild(node);\r
756         }\r
757         else\r
758         {\r
759             break;\r
760         }\r
761 \r
762         // Did we get encoding info?\r
763         if (encoding == TIXML_ENCODING_UNKNOWN && node->ToDeclaration())\r
764         {\r
765             TiXmlDeclaration *dec = node->ToDeclaration();\r
766             const char *enc = dec->Encoding();\r
767             assert(enc);\r
768 \r
769             if (*enc == 0)\r
770                 encoding = TIXML_ENCODING_UTF8;\r
771             else if (StringEqual(enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN))\r
772                 encoding = TIXML_ENCODING_UTF8;\r
773             else if (StringEqual(enc, "UTF8", true, TIXML_ENCODING_UNKNOWN))\r
774                 encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice\r
775             else\r
776                 encoding = TIXML_ENCODING_LEGACY;\r
777         }\r
778 \r
779         p = SkipWhiteSpace(p, encoding);\r
780     }\r
781 \r
782     // Was this empty?\r
783     if (!firstChild)\r
784     {\r
785         SetError(TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding);\r
786         return 0;\r
787     }\r
788 \r
789     // All is well.\r
790     return p;\r
791 }\r
792 \r
793 void TiXmlDocument::SetError(int err, const char *pError, TiXmlParsingData *data, TiXmlEncoding encoding)\r
794 {\r
795     // The first error in a chain is more accurate - don't set again!\r
796     if (error)\r
797         return;\r
798 \r
799     assert(err > 0 && err < TIXML_ERROR_STRING_COUNT);\r
800     error = true;\r
801     errorId = err;\r
802     errorDesc = errorString[errorId];\r
803 \r
804     errorLocation.Clear();\r
805     if (pError && data)\r
806     {\r
807         data->Stamp(pError, encoding);\r
808         errorLocation = data->Cursor();\r
809     }\r
810 }\r
811 \r
812 TiXmlNode *TiXmlNode::Identify(const char *p, TiXmlEncoding encoding)\r
813 {\r
814     TiXmlNode *returnNode = 0;\r
815 \r
816     p = SkipWhiteSpace(p, encoding);\r
817     if (!p || !*p || *p != '<')\r
818     {\r
819         return 0;\r
820     }\r
821 \r
822     p = SkipWhiteSpace(p, encoding);\r
823 \r
824     if (!p || !*p)\r
825     {\r
826         return 0;\r
827     }\r
828 \r
829     // What is this thing?\r
830     // - Elements start with a letter or underscore, but xml is reserved.\r
831     // - Comments: <!--\r
832     // - Decleration: <?xml\r
833     // - Everthing else is unknown to tinyxml.\r
834     //\r
835 \r
836     const char *xmlHeader = { "<?xml" };\r
837     const char *commentHeader = { "<!--" };\r
838     const char *dtdHeader = { "<!" };\r
839     const char *cdataHeader = { "<![CDATA[" };\r
840 \r
841     if (StringEqual(p, xmlHeader, true, encoding))\r
842     {\r
843 #ifdef DEBUG_PARSER\r
844         TIXML_LOG("XML parsing Declaration\n");\r
845 #endif\r
846         returnNode = new TiXmlDeclaration();\r
847     }\r
848     else if (StringEqual(p, commentHeader, false, encoding))\r
849     {\r
850 #ifdef DEBUG_PARSER\r
851         TIXML_LOG("XML parsing Comment\n");\r
852 #endif\r
853         returnNode = new TiXmlComment();\r
854     }\r
855     else if (StringEqual(p, cdataHeader, false, encoding))\r
856     {\r
857 #ifdef DEBUG_PARSER\r
858         TIXML_LOG("XML parsing CDATA\n");\r
859 #endif\r
860         TiXmlText *text = new TiXmlText("");\r
861         text->SetCDATA(true);\r
862         returnNode = text;\r
863     }\r
864     else if (StringEqual(p, dtdHeader, false, encoding))\r
865     {\r
866 #ifdef DEBUG_PARSER\r
867         TIXML_LOG("XML parsing Unknown(1)\n");\r
868 #endif\r
869         returnNode = new TiXmlUnknown();\r
870     }\r
871     else if (IsAlpha(*(p + 1), encoding) || *(p + 1) == '_')\r
872     {\r
873 #ifdef DEBUG_PARSER\r
874         TIXML_LOG("XML parsing Element\n");\r
875 #endif\r
876         returnNode = new TiXmlElement("");\r
877     }\r
878     else\r
879     {\r
880 #ifdef DEBUG_PARSER\r
881         TIXML_LOG("XML parsing Unknown(2)\n");\r
882 #endif\r
883         returnNode = new TiXmlUnknown();\r
884     }\r
885 \r
886     if (returnNode)\r
887     {\r
888         // Set the parent, so it can report errors\r
889         returnNode->parent = this;\r
890     }\r
891     return returnNode;\r
892 }\r
893 \r
894 #ifdef TIXML_USE_STL\r
895 \r
896 void TiXmlElement::StreamIn(std::istream *in, TIXML_STRING *tag)\r
897 {\r
898     // We're called with some amount of pre-parsing. That is, some of "this"\r
899     // element is in "tag". Go ahead and stream to the closing ">"\r
900     while (in->good())\r
901     {\r
902         int c = in->get();\r
903         if (c <= 0)\r
904         {\r
905             TiXmlDocument *document = GetDocument();\r
906             if (document)\r
907                 document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);\r
908             return;\r
909         }\r
910         (*tag) += (char)c;\r
911 \r
912         if (c == '>')\r
913             break;\r
914     }\r
915 \r
916     if (tag->length() < 3)\r
917         return;\r
918 \r
919     // Okay...if we are a "/>" tag, then we're done. We've read a complete tag.\r
920     // If not, identify and stream.\r
921 \r
922     if (tag->at(tag->length() - 1) == '>' && tag->at(tag->length() - 2) == '/')\r
923     {\r
924         // All good!\r
925         return;\r
926     }\r
927     else if (tag->at(tag->length() - 1) == '>')\r
928     {\r
929         // There is more. Could be:\r
930         //              text\r
931         //              cdata text (which looks like another node)\r
932         //              closing tag\r
933         //              another node.\r
934         for (;;)\r
935         {\r
936             StreamWhiteSpace(in, tag);\r
937 \r
938             // Do we have text?\r
939             if (in->good() && in->peek() != '<')\r
940             {\r
941                 // Yep, text.\r
942                 TiXmlText text("");\r
943                 text.StreamIn(in, tag);\r
944 \r
945                 // What follows text is a closing tag or another node.\r
946                 // Go around again and figure it out.\r
947                 continue;\r
948             }\r
949 \r
950             // We now have either a closing tag...or another node.\r
951             // We should be at a "<", regardless.\r
952             if (!in->good())\r
953                 return;\r
954             assert(in->peek() == '<');\r
955             int tagIndex = (int)tag->length();\r
956 \r
957             bool closingTag = false;\r
958             bool firstCharFound = false;\r
959 \r
960             for (;;)\r
961             {\r
962                 if (!in->good())\r
963                     return;\r
964 \r
965                 int c = in->peek();\r
966                 if (c <= 0)\r
967                 {\r
968                     TiXmlDocument *document = GetDocument();\r
969                     if (document)\r
970                         document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);\r
971                     return;\r
972                 }\r
973 \r
974                 if (c == '>')\r
975                     break;\r
976 \r
977                 *tag += (char)c;\r
978                 in->get();\r
979 \r
980                 // Early out if we find the CDATA id.\r
981                 if (c == '[' && tag->size() >= 9)\r
982                 {\r
983                     size_t len = tag->size();\r
984                     const char *start = tag->c_str() + len - 9;\r
985                     if (strcmp(start, "<![CDATA[") == 0)\r
986                     {\r
987                         assert(!closingTag);\r
988                         break;\r
989                     }\r
990                 }\r
991 \r
992                 if (!firstCharFound && c != '<' && !IsWhiteSpace(c))\r
993                 {\r
994                     firstCharFound = true;\r
995                     if (c == '/')\r
996                         closingTag = true;\r
997                 }\r
998             }\r
999             // If it was a closing tag, then read in the closing '>' to clean up the input stream.\r
1000             // If it was not, the streaming will be done by the tag.\r
1001             if (closingTag)\r
1002             {\r
1003                 if (!in->good())\r
1004                     return;\r
1005 \r
1006                 int c = in->get();\r
1007                 if (c <= 0)\r
1008                 {\r
1009                     TiXmlDocument *document = GetDocument();\r
1010                     if (document)\r
1011                         document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);\r
1012                     return;\r
1013                 }\r
1014                 assert(c == '>');\r
1015                 *tag += (char)c;\r
1016 \r
1017                 // We are done, once we've found our closing tag.\r
1018                 return;\r
1019             }\r
1020             else\r
1021             {\r
1022                 // If not a closing tag, id it, and stream.\r
1023                 const char *tagloc = tag->c_str() + tagIndex;\r
1024                 TiXmlNode *node = Identify(tagloc, TIXML_DEFAULT_ENCODING);\r
1025                 if (!node)\r
1026                     return;\r
1027                 node->StreamIn(in, tag);\r
1028                 delete node;\r
1029                 node = 0;\r
1030 \r
1031                 // No return: go around from the beginning: text, closing tag, or node.\r
1032             }\r
1033         }\r
1034     }\r
1035 }\r
1036 #endif\r
1037 \r
1038 const char *TiXmlElement::Parse(const char *p, TiXmlParsingData *data, TiXmlEncoding encoding)\r
1039 {\r
1040     p = SkipWhiteSpace(p, encoding);\r
1041     TiXmlDocument *document = GetDocument();\r
1042 \r
1043     if (!p || !*p)\r
1044     {\r
1045         if (document)\r
1046             document->SetError(TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding);\r
1047         return 0;\r
1048     }\r
1049 \r
1050     if (data)\r
1051     {\r
1052         data->Stamp(p, encoding);\r
1053         location = data->Cursor();\r
1054     }\r
1055 \r
1056     if (*p != '<')\r
1057     {\r
1058         if (document)\r
1059             document->SetError(TIXML_ERROR_PARSING_ELEMENT, p, data, encoding);\r
1060         return 0;\r
1061     }\r
1062 \r
1063     p = SkipWhiteSpace(p + 1, encoding);\r
1064 \r
1065     // Read the name.\r
1066     const char *pErr = p;\r
1067 \r
1068     p = ReadName(p, &value, encoding);\r
1069     if (!p || !*p)\r
1070     {\r
1071         if (document)\r
1072             document->SetError(TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding);\r
1073         return 0;\r
1074     }\r
1075 \r
1076     TIXML_STRING endTag("</");\r
1077     endTag += value;\r
1078 \r
1079     // Check for and read attributes. Also look for an empty\r
1080     // tag or an end tag.\r
1081     while (p && *p)\r
1082     {\r
1083         pErr = p;\r
1084         p = SkipWhiteSpace(p, encoding);\r
1085         if (!p || !*p)\r
1086         {\r
1087             if (document)\r
1088                 document->SetError(TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding);\r
1089             return 0;\r
1090         }\r
1091         if (*p == '/')\r
1092         {\r
1093             ++p;\r
1094             // Empty tag.\r
1095             if (*p != '>')\r
1096             {\r
1097                 if (document)\r
1098                     document->SetError(TIXML_ERROR_PARSING_EMPTY, p, data, encoding);\r
1099                 return 0;\r
1100             }\r
1101             return (p + 1);\r
1102         }\r
1103         else if (*p == '>')\r
1104         {\r
1105             // Done with attributes (if there were any.)\r
1106             // Read the value -- which can include other\r
1107             // elements -- read the end tag, and return.\r
1108             ++p;\r
1109             p = ReadValue(p, data, encoding); // Note this is an Element method, and will set the error if one happens.\r
1110             if (!p || !*p)\r
1111             {\r
1112                 // We were looking for the end tag, but found nothing.\r
1113                 // Fix for [ 1663758 ] Failure to report error on bad XML\r
1114                 if (document)\r
1115                     document->SetError(TIXML_ERROR_READING_END_TAG, p, data, encoding);\r
1116                 return 0;\r
1117             }\r
1118 \r
1119             // We should find the end tag now\r
1120             // note that:\r
1121             // </foo > and\r
1122             // </foo>\r
1123             // are both valid end tags.\r
1124             if (StringEqual(p, endTag.c_str(), false, encoding))\r
1125             {\r
1126                 p += endTag.length();\r
1127                 p = SkipWhiteSpace(p, encoding);\r
1128                 if (p && *p && *p == '>')\r
1129                 {\r
1130                     ++p;\r
1131                     return p;\r
1132                 }\r
1133                 if (document)\r
1134                     document->SetError(TIXML_ERROR_READING_END_TAG, p, data, encoding);\r
1135                 return 0;\r
1136             }\r
1137             else\r
1138             {\r
1139                 if (document)\r
1140                     document->SetError(TIXML_ERROR_READING_END_TAG, p, data, encoding);\r
1141                 return 0;\r
1142             }\r
1143         }\r
1144         else\r
1145         {\r
1146             // Try to read an attribute:\r
1147             TiXmlAttribute *attrib = new TiXmlAttribute();\r
1148             if (!attrib)\r
1149             {\r
1150                 return 0;\r
1151             }\r
1152 \r
1153             attrib->SetDocument(document);\r
1154             pErr = p;\r
1155             p = attrib->Parse(p, data, encoding);\r
1156 \r
1157             if (!p || !*p)\r
1158             {\r
1159                 if (document)\r
1160                     document->SetError(TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding);\r
1161                 delete attrib;\r
1162                 return 0;\r
1163             }\r
1164 \r
1165 // Handle the strange case of double attributes:\r
1166 #ifdef TIXML_USE_STL\r
1167             TiXmlAttribute *node = attributeSet.Find(attrib->NameTStr());\r
1168 #else\r
1169             TiXmlAttribute *node = attributeSet.Find(attrib->Name());\r
1170 #endif\r
1171             if (node)\r
1172             {\r
1173                 if (document)\r
1174                     document->SetError(TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding);\r
1175                 delete attrib;\r
1176                 return 0;\r
1177             }\r
1178 \r
1179             attributeSet.Add(attrib);\r
1180         }\r
1181     }\r
1182     return p;\r
1183 }\r
1184 \r
1185 const char *TiXmlElement::ReadValue(const char *p, TiXmlParsingData *data, TiXmlEncoding encoding)\r
1186 {\r
1187     TiXmlDocument *document = GetDocument();\r
1188 \r
1189     // Read in text and elements in any order.\r
1190     const char *pWithWhiteSpace = p;\r
1191     p = SkipWhiteSpace(p, encoding);\r
1192 \r
1193     while (p && *p)\r
1194     {\r
1195         if (*p != '<')\r
1196         {\r
1197             // Take what we have, make a text element.\r
1198             TiXmlText *textNode = new TiXmlText("");\r
1199 \r
1200             if (!textNode)\r
1201             {\r
1202                 return 0;\r
1203             }\r
1204 \r
1205             if (TiXmlBase::IsWhiteSpaceCondensed())\r
1206             {\r
1207                 p = textNode->Parse(p, data, encoding);\r
1208             }\r
1209             else\r
1210             {\r
1211                 // Special case: we want to keep the white space\r
1212                 // so that leading spaces aren't removed.\r
1213                 p = textNode->Parse(pWithWhiteSpace, data, encoding);\r
1214             }\r
1215 \r
1216             if (!textNode->Blank())\r
1217                 LinkEndChild(textNode);\r
1218             else\r
1219                 delete textNode;\r
1220         }\r
1221         else\r
1222         {\r
1223             // We hit a '<'\r
1224             // Have we hit a new element or an end tag? This could also be\r
1225             // a TiXmlText in the "CDATA" style.\r
1226             if (StringEqual(p, "</", false, encoding))\r
1227             {\r
1228                 return p;\r
1229             }\r
1230             else\r
1231             {\r
1232                 TiXmlNode *node = Identify(p, encoding);\r
1233                 if (node)\r
1234                 {\r
1235                     p = node->Parse(p, data, encoding);\r
1236                     LinkEndChild(node);\r
1237                 }\r
1238                 else\r
1239                 {\r
1240                     return 0;\r
1241                 }\r
1242             }\r
1243         }\r
1244         pWithWhiteSpace = p;\r
1245         p = SkipWhiteSpace(p, encoding);\r
1246     }\r
1247 \r
1248     if (!p)\r
1249     {\r
1250         if (document)\r
1251             document->SetError(TIXML_ERROR_READING_ELEMENT_VALUE, 0, 0, encoding);\r
1252     }\r
1253     return p;\r
1254 }\r
1255 \r
1256 #ifdef TIXML_USE_STL\r
1257 void TiXmlUnknown::StreamIn(std::istream *in, TIXML_STRING *tag)\r
1258 {\r
1259     while (in->good())\r
1260     {\r
1261         int c = in->get();\r
1262         if (c <= 0)\r
1263         {\r
1264             TiXmlDocument *document = GetDocument();\r
1265             if (document)\r
1266                 document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);\r
1267             return;\r
1268         }\r
1269         (*tag) += (char)c;\r
1270 \r
1271         if (c == '>')\r
1272         {\r
1273             // All is well.\r
1274             return;\r
1275         }\r
1276     }\r
1277 }\r
1278 #endif\r
1279 \r
1280 const char *TiXmlUnknown::Parse(const char *p, TiXmlParsingData *data, TiXmlEncoding encoding)\r
1281 {\r
1282     TiXmlDocument *document = GetDocument();\r
1283     p = SkipWhiteSpace(p, encoding);\r
1284 \r
1285     if (data)\r
1286     {\r
1287         data->Stamp(p, encoding);\r
1288         location = data->Cursor();\r
1289     }\r
1290     if (!p || !*p || *p != '<')\r
1291     {\r
1292         if (document)\r
1293             document->SetError(TIXML_ERROR_PARSING_UNKNOWN, p, data, encoding);\r
1294         return 0;\r
1295     }\r
1296     ++p;\r
1297     value = "";\r
1298 \r
1299     while (p && *p && *p != '>')\r
1300     {\r
1301         value += *p;\r
1302         ++p;\r
1303     }\r
1304 \r
1305     if (!p)\r
1306     {\r
1307         if (document)\r
1308             document->SetError(TIXML_ERROR_PARSING_UNKNOWN, 0, 0, encoding);\r
1309     }\r
1310     if (p && *p == '>')\r
1311         return p + 1;\r
1312     return p;\r
1313 }\r
1314 \r
1315 #ifdef TIXML_USE_STL\r
1316 void TiXmlComment::StreamIn(std::istream *in, TIXML_STRING *tag)\r
1317 {\r
1318     while (in->good())\r
1319     {\r
1320         int c = in->get();\r
1321         if (c <= 0)\r
1322         {\r
1323             TiXmlDocument *document = GetDocument();\r
1324             if (document)\r
1325                 document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);\r
1326             return;\r
1327         }\r
1328 \r
1329         (*tag) += (char)c;\r
1330 \r
1331         if (c == '>' && tag->at(tag->length() - 2) == '-' && tag->at(tag->length() - 3) == '-')\r
1332         {\r
1333             // All is well.\r
1334             return;\r
1335         }\r
1336     }\r
1337 }\r
1338 #endif\r
1339 \r
1340 const char *TiXmlComment::Parse(const char *p, TiXmlParsingData *data, TiXmlEncoding encoding)\r
1341 {\r
1342     TiXmlDocument *document = GetDocument();\r
1343     value = "";\r
1344 \r
1345     p = SkipWhiteSpace(p, encoding);\r
1346 \r
1347     if (data)\r
1348     {\r
1349         data->Stamp(p, encoding);\r
1350         location = data->Cursor();\r
1351     }\r
1352     const char *startTag = "<!--";\r
1353     const char *endTag = "-->";\r
1354 \r
1355     if (!StringEqual(p, startTag, false, encoding))\r
1356     {\r
1357         if (document)\r
1358             document->SetError(TIXML_ERROR_PARSING_COMMENT, p, data, encoding);\r
1359         return 0;\r
1360     }\r
1361     p += strlen(startTag);\r
1362 \r
1363     // [ 1475201 ] TinyXML parses entities in comments\r
1364     // Oops - ReadText doesn't work, because we don't want to parse the entities.\r
1365     // p = ReadText( p, &value, false, endTag, false, encoding );\r
1366     //\r
1367     // from the XML spec:\r
1368     /*\r
1369          [Definition: Comments may appear anywhere in a document outside other markup; in addition,\r
1370                       they may appear within the document type declaration at places allowed by the grammar.\r
1371                                   They are not part of the document's character data; an XML processor MAY, but need not,\r
1372                                   make it possible for an application to retrieve the text of comments. For compatibility,\r
1373                                   the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity\r
1374                                   references MUST NOT be recognized within comments.\r
1375 \r
1376                                   An example of a comment:\r
1377 \r
1378                                   <!-- declarations for <head> & <body> -->\r
1379         */\r
1380 \r
1381     value = "";\r
1382     // Keep all the white space.\r
1383     while (p && *p && !StringEqual(p, endTag, false, encoding))\r
1384     {\r
1385         value.append(p, 1);\r
1386         ++p;\r
1387     }\r
1388     if (p && *p)\r
1389         p += strlen(endTag);\r
1390 \r
1391     return p;\r
1392 }\r
1393 \r
1394 const char *TiXmlAttribute::Parse(const char *p, TiXmlParsingData *data, TiXmlEncoding encoding)\r
1395 {\r
1396     p = SkipWhiteSpace(p, encoding);\r
1397     if (!p || !*p)\r
1398         return 0;\r
1399 \r
1400     if (data)\r
1401     {\r
1402         data->Stamp(p, encoding);\r
1403         location = data->Cursor();\r
1404     }\r
1405     // Read the name, the '=' and the value.\r
1406     const char *pErr = p;\r
1407     p = ReadName(p, &name, encoding);\r
1408     if (!p || !*p)\r
1409     {\r
1410         if (document)\r
1411             document->SetError(TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding);\r
1412         return 0;\r
1413     }\r
1414     p = SkipWhiteSpace(p, encoding);\r
1415     if (!p || !*p || *p != '=')\r
1416     {\r
1417         if (document)\r
1418             document->SetError(TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding);\r
1419         return 0;\r
1420     }\r
1421 \r
1422     ++p; // skip '='\r
1423     p = SkipWhiteSpace(p, encoding);\r
1424     if (!p || !*p)\r
1425     {\r
1426         if (document)\r
1427             document->SetError(TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding);\r
1428         return 0;\r
1429     }\r
1430 \r
1431     const char *end;\r
1432     const char SINGLE_QUOTE = '\'';\r
1433     const char DOUBLE_QUOTE = '\"';\r
1434 \r
1435     if (*p == SINGLE_QUOTE)\r
1436     {\r
1437         ++p;\r
1438         end = "\'"; // single quote in string\r
1439         p = ReadText(p, &value, false, end, false, encoding);\r
1440     }\r
1441     else if (*p == DOUBLE_QUOTE)\r
1442     {\r
1443         ++p;\r
1444         end = "\""; // double quote in string\r
1445         p = ReadText(p, &value, false, end, false, encoding);\r
1446     }\r
1447     else\r
1448     {\r
1449         // All attribute values should be in single or double quotes.\r
1450         // But this is such a common error that the parser will try\r
1451         // its best, even without them.\r
1452         value = "";\r
1453         while (p && *p                    // existence\r
1454                && !IsWhiteSpace(*p)       // whitespace\r
1455                && *p != '/' && *p != '>') // tag end\r
1456         {\r
1457             if (*p == SINGLE_QUOTE || *p == DOUBLE_QUOTE)\r
1458             {\r
1459                 // [ 1451649 ] Attribute values with trailing quotes not handled correctly\r
1460                 // We did not have an opening quote but seem to have a\r
1461                 // closing one. Give up and throw an error.\r
1462                 if (document)\r
1463                     document->SetError(TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding);\r
1464                 return 0;\r
1465             }\r
1466             value += *p;\r
1467             ++p;\r
1468         }\r
1469     }\r
1470     return p;\r
1471 }\r
1472 \r
1473 #ifdef TIXML_USE_STL\r
1474 void TiXmlText::StreamIn(std::istream *in, TIXML_STRING *tag)\r
1475 {\r
1476     while (in->good())\r
1477     {\r
1478         int c = in->peek();\r
1479         if (!cdata && (c == '<'))\r
1480         {\r
1481             return;\r
1482         }\r
1483         if (c <= 0)\r
1484         {\r
1485             TiXmlDocument *document = GetDocument();\r
1486             if (document)\r
1487                 document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);\r
1488             return;\r
1489         }\r
1490 \r
1491         (*tag) += (char)c;\r
1492         in->get(); // "commits" the peek made above\r
1493 \r
1494         if (cdata && c == '>' && tag->size() >= 3)\r
1495         {\r
1496             size_t len = tag->size();\r
1497             if ((*tag)[len - 2] == ']' && (*tag)[len - 3] == ']')\r
1498             {\r
1499                 // terminator of cdata.\r
1500                 return;\r
1501             }\r
1502         }\r
1503     }\r
1504 }\r
1505 #endif\r
1506 \r
1507 const char *TiXmlText::Parse(const char *p, TiXmlParsingData *data, TiXmlEncoding encoding)\r
1508 {\r
1509     value = "";\r
1510     TiXmlDocument *document = GetDocument();\r
1511 \r
1512     if (data)\r
1513     {\r
1514         data->Stamp(p, encoding);\r
1515         location = data->Cursor();\r
1516     }\r
1517 \r
1518     const char *const startTag = "<![CDATA[";\r
1519     const char *const endTag = "]]>";\r
1520 \r
1521     if (cdata || StringEqual(p, startTag, false, encoding))\r
1522     {\r
1523         cdata = true;\r
1524 \r
1525         if (!StringEqual(p, startTag, false, encoding))\r
1526         {\r
1527             if (document)\r
1528                 document->SetError(TIXML_ERROR_PARSING_CDATA, p, data, encoding);\r
1529             return 0;\r
1530         }\r
1531         p += strlen(startTag);\r
1532 \r
1533         // Keep all the white space, ignore the encoding, etc.\r
1534         while (p && *p && !StringEqual(p, endTag, false, encoding))\r
1535         {\r
1536             value += *p;\r
1537             ++p;\r
1538         }\r
1539 \r
1540         TIXML_STRING dummy;\r
1541         p = ReadText(p, &dummy, false, endTag, false, encoding);\r
1542         return p;\r
1543     }\r
1544     else\r
1545     {\r
1546         bool ignoreWhite = true;\r
1547 \r
1548         const char *end = "<";\r
1549         p = ReadText(p, &value, ignoreWhite, end, false, encoding);\r
1550         if (p && *p)\r
1551             return p - 1; // don't truncate the '<'\r
1552         return 0;\r
1553     }\r
1554 }\r
1555 \r
1556 #ifdef TIXML_USE_STL\r
1557 void TiXmlDeclaration::StreamIn(std::istream *in, TIXML_STRING *tag)\r
1558 {\r
1559     while (in->good())\r
1560     {\r
1561         int c = in->get();\r
1562         if (c <= 0)\r
1563         {\r
1564             TiXmlDocument *document = GetDocument();\r
1565             if (document)\r
1566                 document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);\r
1567             return;\r
1568         }\r
1569         (*tag) += (char)c;\r
1570 \r
1571         if (c == '>')\r
1572         {\r
1573             // All is well.\r
1574             return;\r
1575         }\r
1576     }\r
1577 }\r
1578 #endif\r
1579 \r
1580 const char *TiXmlDeclaration::Parse(const char *p, TiXmlParsingData *data, TiXmlEncoding _encoding)\r
1581 {\r
1582     p = SkipWhiteSpace(p, _encoding);\r
1583     // Find the beginning, find the end, and look for\r
1584     // the stuff in-between.\r
1585     TiXmlDocument *document = GetDocument();\r
1586     if (!p || !*p || !StringEqual(p, "<?xml", true, _encoding))\r
1587     {\r
1588         if (document)\r
1589             document->SetError(TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding);\r
1590         return 0;\r
1591     }\r
1592     if (data)\r
1593     {\r
1594         data->Stamp(p, _encoding);\r
1595         location = data->Cursor();\r
1596     }\r
1597     p += 5;\r
1598 \r
1599     version = "";\r
1600     encoding = "";\r
1601     standalone = "";\r
1602 \r
1603     while (p && *p)\r
1604     {\r
1605         if (*p == '>')\r
1606         {\r
1607             ++p;\r
1608             return p;\r
1609         }\r
1610 \r
1611         p = SkipWhiteSpace(p, _encoding);\r
1612         if (StringEqual(p, "version", true, _encoding))\r
1613         {\r
1614             TiXmlAttribute attrib;\r
1615             p = attrib.Parse(p, data, _encoding);\r
1616             version = attrib.Value();\r
1617         }\r
1618         else if (StringEqual(p, "encoding", true, _encoding))\r
1619         {\r
1620             TiXmlAttribute attrib;\r
1621             p = attrib.Parse(p, data, _encoding);\r
1622             encoding = attrib.Value();\r
1623         }\r
1624         else if (StringEqual(p, "standalone", true, _encoding))\r
1625         {\r
1626             TiXmlAttribute attrib;\r
1627             p = attrib.Parse(p, data, _encoding);\r
1628             standalone = attrib.Value();\r
1629         }\r
1630         else\r
1631         {\r
1632             // Read over whatever it is.\r
1633             while (p && *p && *p != '>' && !IsWhiteSpace(*p))\r
1634                 ++p;\r
1635         }\r
1636     }\r
1637     return 0;\r
1638 }\r
1639 \r
1640 bool TiXmlText::Blank() const\r
1641 {\r
1642     for (unsigned i = 0; i < value.length(); i++)\r
1643         if (!IsWhiteSpace(value[i]))\r
1644             return false;\r
1645     return true;\r
1646 }\r