Coverage Report

Created: 2025-12-05 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tinyxml2/tinyxml2.cpp
Line
Count
Source
1
/*
2
Original code by Lee Thomason (www.grinninglizard.com)
3
4
This software is provided 'as-is', without any express or implied
5
warranty. In no event will the authors be held liable for any
6
damages arising from the use of this software.
7
8
Permission is granted to anyone to use this software for any
9
purpose, including commercial applications, and to alter it and
10
redistribute it freely, subject to the following restrictions:
11
12
1. The origin of this software must not be misrepresented; you must
13
not claim that you wrote the original software. If you use this
14
software in a product, an acknowledgment in the product documentation
15
would be appreciated but is not required.
16
17
2. Altered source versions must be plainly marked as such, and
18
must not be misrepresented as being the original software.
19
20
3. This notice may not be removed or altered from any source
21
distribution.
22
*/
23
24
#include "tinyxml2.h"
25
26
#include <new>    // yes, this one new style header, is in the Android SDK.
27
#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) || defined(__CC_ARM)
28
#   include <stddef.h>
29
#   include <stdarg.h>
30
#else
31
#   include <cstddef>
32
#   include <cstdarg>
33
#endif
34
35
// Handle fallthrough attribute for different compilers
36
#ifndef __has_attribute
37
#   define __has_attribute(x) 0
38
#endif
39
#ifndef __has_cpp_attribute
40
#  define __has_cpp_attribute(x) 0
41
#endif
42
43
#if defined(_MSC_VER)
44
#   define TIXML_FALLTHROUGH (void(0))
45
#elif (__cplusplus >= 201703L && __has_cpp_attribute(fallthrough))
46
1.31k
#   define TIXML_FALLTHROUGH [[fallthrough]]
47
#elif __has_cpp_attribute(clang::fallthrough)
48
#   define TIXML_FALLTHROUGH [[clang::fallthrough]]
49
#elif __has_attribute(fallthrough)
50
#   define TIXML_FALLTHROUGH __attribute__((fallthrough))
51
#else
52
#   define TIXML_FALLTHROUGH (void(0))
53
#endif
54
55
56
#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
57
  // Microsoft Visual Studio, version 2005 and higher. Not WinCE.
58
  /*int _snprintf_s(
59
     char *buffer,
60
     size_t sizeOfBuffer,
61
     size_t count,
62
     const char *format [,
63
      argument] ...
64
  );*/
65
  static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... )
66
  {
67
    va_list va;
68
    va_start( va, format );
69
    const int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
70
    va_end( va );
71
    return result;
72
  }
73
74
  static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va )
75
  {
76
    const int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
77
    return result;
78
  }
79
80
  #define TIXML_VSCPRINTF _vscprintf
81
  #define TIXML_SSCANF  sscanf_s
82
#elif defined _MSC_VER
83
  // Microsoft Visual Studio 2003 and earlier or WinCE
84
  #define TIXML_SNPRINTF  _snprintf
85
  #define TIXML_VSNPRINTF _vsnprintf
86
  #define TIXML_SSCANF  sscanf
87
  #if (_MSC_VER < 1400 ) && (!defined WINCE)
88
    // Microsoft Visual Studio 2003 and not WinCE.
89
    #define TIXML_VSCPRINTF   _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have.
90
  #else
91
    // Microsoft Visual Studio 2003 and earlier or WinCE.
92
    static inline int TIXML_VSCPRINTF( const char* format, va_list va )
93
    {
94
      int len = 512;
95
      for (;;) {
96
        len = len*2;
97
        char* str = new char[len]();
98
        const int required = _vsnprintf(str, len, format, va);
99
        delete[] str;
100
        if ( required != -1 ) {
101
          TIXMLASSERT( required >= 0 );
102
          len = required;
103
          break;
104
        }
105
      }
106
      TIXMLASSERT( len >= 0 );
107
      return len;
108
    }
109
  #endif
110
#else
111
  // GCC version 3 and higher
112
  //#warning( "Using sn* functions." )
113
3.15k
  #define TIXML_SNPRINTF  snprintf
114
1.14k
  #define TIXML_VSNPRINTF vsnprintf
115
  static inline int TIXML_VSCPRINTF( const char* format, va_list va )
116
0
  {
117
0
    int len = vsnprintf( 0, 0, format, va );
118
0
    TIXMLASSERT( len >= 0 );
119
0
    return len;
120
0
  }
121
0
  #define TIXML_SSCANF   sscanf
122
#endif
123
124
#if defined(_WIN64)
125
  #define TIXML_FSEEK _fseeki64
126
  #define TIXML_FTELL _ftelli64
127
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__CYGWIN__)
128
  #define TIXML_FSEEK fseeko
129
  #define TIXML_FTELL ftello
130
#elif defined(__ANDROID__) && __ANDROID_API__ > 24
131
  #define TIXML_FSEEK fseeko64
132
  #define TIXML_FTELL ftello64
133
#else
134
6.33k
  #define TIXML_FSEEK fseek
135
2.11k
  #define TIXML_FTELL ftell
136
#endif
137
138
139
static const char LINE_FEED       = static_cast<char>(0x0a);      // all line endings are normalized to LF
140
static const char LF = LINE_FEED;
141
static const char CARRIAGE_RETURN   = static_cast<char>(0x0d);      // CR gets filtered out
142
static const char CR = CARRIAGE_RETURN;
143
static const char SINGLE_QUOTE      = '\'';
144
static const char DOUBLE_QUOTE      = '\"';
145
146
// Bunch of unicode info at:
147
//    http://www.unicode.org/faq/utf_bom.html
148
//  ef bb bf (Microsoft "lead bytes") - designates UTF-8
149
150
static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
151
static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
152
static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
153
154
namespace tinyxml2
155
{
156
157
struct Entity {
158
    const char* pattern;
159
    int length;
160
    char value;
161
};
162
163
static const int NUM_ENTITIES = 5;
164
static const Entity entities[NUM_ENTITIES] = {
165
    { "quot", 4,  DOUBLE_QUOTE },
166
    { "amp", 3,   '&'  },
167
    { "apos", 4,  SINGLE_QUOTE },
168
    { "lt", 2,    '<'  },
169
    { "gt", 2,    '>'  }
170
};
171
172
173
StrPair::~StrPair()
174
13.4M
{
175
13.4M
    Reset();
176
13.4M
}
177
178
179
void StrPair::TransferTo( StrPair* other )
180
2.28k
{
181
2.28k
    if ( this == other ) {
182
0
        return;
183
0
    }
184
    // This in effect implements the assignment operator by "moving"
185
    // ownership (as in auto_ptr).
186
187
2.28k
    TIXMLASSERT( other != 0 );
188
2.28k
    TIXMLASSERT( other->_flags == 0 );
189
2.28k
    TIXMLASSERT( other->_start == 0 );
190
2.28k
    TIXMLASSERT( other->_end == 0 );
191
192
2.28k
    other->Reset();
193
194
2.28k
    other->_flags = _flags;
195
2.28k
    other->_start = _start;
196
2.28k
    other->_end = _end;
197
198
2.28k
    _flags = 0;
199
2.28k
    _start = 0;
200
2.28k
    _end = 0;
201
2.28k
}
202
203
204
void StrPair::Reset()
205
20.3M
{
206
20.3M
    if ( _flags & NEEDS_DELETE ) {
207
2.01k
        delete [] _start;
208
2.01k
    }
209
20.3M
    _flags = 0;
210
20.3M
    _start = 0;
211
20.3M
    _end = 0;
212
20.3M
}
213
214
215
void StrPair::SetStr( const char* str, int flags )
216
2.01k
{
217
2.01k
    TIXMLASSERT( str );
218
2.01k
    Reset();
219
2.01k
    size_t len = strlen( str );
220
2.01k
    TIXMLASSERT( _start == 0 );
221
2.01k
    _start = new char[ len+1 ];
222
2.01k
    memcpy( _start, str, len+1 );
223
2.01k
    _end = _start + len;
224
2.01k
    _flags = flags | NEEDS_DELETE;
225
2.01k
}
226
227
228
char* StrPair::ParseText( char* p, const char* endTag, int strFlags, int* curLineNumPtr )
229
5.28M
{
230
5.28M
    TIXMLASSERT( p );
231
5.28M
    TIXMLASSERT( endTag && *endTag );
232
5.28M
  TIXMLASSERT(curLineNumPtr);
233
234
5.28M
    char* start = p;
235
5.28M
    const char  endChar = *endTag;
236
5.28M
    size_t length = strlen( endTag );
237
238
    // Inner loop of text parsing.
239
6.91M
    while ( *p ) {
240
6.91M
        if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
241
5.28M
            Set( start, p, strFlags );
242
5.28M
            return p + length;
243
5.28M
        } else if (*p == '\n') {
244
2.77k
            ++(*curLineNumPtr);
245
2.77k
        }
246
1.62M
        ++p;
247
1.62M
        TIXMLASSERT( p );
248
1.62M
    }
249
612
    return 0;
250
5.28M
}
251
252
253
char* StrPair::ParseName( char* p )
254
1.63M
{
255
1.63M
    if ( !p || !(*p) ) {
256
152
        return 0;
257
152
    }
258
1.63M
    if ( !XMLUtil::IsNameStartChar( static_cast<unsigned char>(*p) ) ) {
259
35
        return 0;
260
35
    }
261
262
1.63M
    char* const start = p;
263
1.63M
    ++p;
264
2.77M
    while ( *p && XMLUtil::IsNameChar( static_cast<unsigned char>(*p) ) ) {
265
1.14M
        ++p;
266
1.14M
    }
267
268
1.63M
    Set( start, p, 0 );
269
1.63M
    return p;
270
1.63M
}
271
272
273
void StrPair::CollapseWhitespace()
274
0
{
275
    // Adjusting _start would cause undefined behavior on delete[]
276
0
    TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 );
277
    // Trim leading space.
278
0
    _start = XMLUtil::SkipWhiteSpace( _start, 0 );
279
280
0
    if ( *_start ) {
281
0
        const char* p = _start; // the read pointer
282
0
        char* q = _start; // the write pointer
283
284
0
        while( *p ) {
285
0
            if ( XMLUtil::IsWhiteSpace( *p )) {
286
0
                p = XMLUtil::SkipWhiteSpace( p, 0 );
287
0
                if ( *p == 0 ) {
288
0
                    break;    // don't write to q; this trims the trailing space.
289
0
                }
290
0
                *q = ' ';
291
0
                ++q;
292
0
            }
293
0
            *q = *p;
294
0
            ++q;
295
0
            ++p;
296
0
        }
297
0
        *q = 0;
298
0
    }
299
0
}
300
301
302
const char* StrPair::GetStr()
303
1.32M
{
304
1.32M
    TIXMLASSERT( _start );
305
1.32M
    TIXMLASSERT( _end );
306
1.32M
    if ( _flags & NEEDS_FLUSH ) {
307
198k
        *_end = 0;
308
198k
        _flags ^= NEEDS_FLUSH;
309
310
198k
        if ( _flags ) {
311
447
            const char* p = _start; // the read pointer
312
447
            char* q = _start; // the write pointer
313
314
111k
            while( p < _end ) {
315
110k
                if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
316
                    // CR-LF pair becomes LF
317
                    // CR alone becomes LF
318
                    // LF-CR becomes LF
319
738
                    if ( *(p+1) == LF ) {
320
194
                        p += 2;
321
194
                    }
322
544
                    else {
323
544
                        ++p;
324
544
                    }
325
738
                    *q = LF;
326
738
                    ++q;
327
738
                }
328
109k
                else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
329
416
                    if ( *(p+1) == CR ) {
330
194
                        p += 2;
331
194
                    }
332
222
                    else {
333
222
                        ++p;
334
222
                    }
335
416
                    *q = LF;
336
416
                    ++q;
337
416
                }
338
109k
                else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
339
                    // Entities handled by tinyXML2:
340
                    // - special entities in the entity table [in/out]
341
                    // - numeric character reference [in]
342
                    //   &#20013; or &#x4e2d;
343
344
7.56k
                    if ( *(p+1) == '#' ) {
345
5.70k
                        const int buflen = 10;
346
5.70k
                        char buf[buflen] = { 0 };
347
5.70k
                        int len = 0;
348
5.70k
                        const char* adjusted = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
349
5.70k
                        if ( adjusted == 0 ) {
350
4.49k
                            *q = *p;
351
4.49k
                            ++p;
352
4.49k
                            ++q;
353
4.49k
                        }
354
1.20k
                        else {
355
1.20k
                            TIXMLASSERT( 0 <= len && len <= buflen );
356
1.20k
                            TIXMLASSERT( q + len <= adjusted );
357
1.20k
                            p = adjusted;
358
1.20k
                            memcpy( q, buf, len );
359
1.20k
                            q += len;
360
1.20k
                        }
361
5.70k
                    }
362
1.86k
                    else {
363
1.86k
                        bool entityFound = false;
364
10.0k
                        for( int i = 0; i < NUM_ENTITIES; ++i ) {
365
8.72k
                            const Entity& entity = entities[i];
366
8.72k
                            if ( strncmp( p + 1, entity.pattern, entity.length ) == 0
367
786
                                    && *( p + entity.length + 1 ) == ';' ) {
368
                                // Found an entity - convert.
369
584
                                *q = entity.value;
370
584
                                ++q;
371
584
                                p += entity.length + 2;
372
584
                                entityFound = true;
373
584
                                break;
374
584
                            }
375
8.72k
                        }
376
1.86k
                        if ( !entityFound ) {
377
                            // fixme: treat as error?
378
1.27k
                            ++p;
379
1.27k
                            ++q;
380
1.27k
                        }
381
1.86k
                    }
382
7.56k
                }
383
101k
                else {
384
101k
                    *q = *p;
385
101k
                    ++p;
386
101k
                    ++q;
387
101k
                }
388
110k
            }
389
447
            *q = 0;
390
447
        }
391
        // The loop below has plenty going on, and this
392
        // is a less useful mode. Break it out.
393
198k
        if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) {
394
0
            CollapseWhitespace();
395
0
        }
396
198k
        _flags = (_flags & NEEDS_DELETE);
397
198k
    }
398
1.32M
    TIXMLASSERT( _start );
399
1.32M
    return _start;
400
1.32M
}
401
402
403
404
405
// --------- XMLUtil ----------- //
406
407
const char* XMLUtil::writeBoolTrue  = "true";
408
const char* XMLUtil::writeBoolFalse = "false";
409
410
void XMLUtil::SetBoolSerialization(const char* writeTrue, const char* writeFalse)
411
0
{
412
0
  static const char* defTrue  = "true";
413
0
  static const char* defFalse = "false";
414
415
0
  writeBoolTrue = (writeTrue) ? writeTrue : defTrue;
416
0
  writeBoolFalse = (writeFalse) ? writeFalse : defFalse;
417
0
}
418
419
420
const char* XMLUtil::ReadBOM( const char* p, bool* bom )
421
2.11k
{
422
2.11k
    TIXMLASSERT( p );
423
2.11k
    TIXMLASSERT( bom );
424
2.11k
    *bom = false;
425
2.11k
    const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
426
    // Check for BOM:
427
2.11k
    if (    *(pu+0) == TIXML_UTF_LEAD_0
428
33
            && *(pu+1) == TIXML_UTF_LEAD_1
429
19
            && *(pu+2) == TIXML_UTF_LEAD_2 ) {
430
3
        *bom = true;
431
3
        p += 3;
432
3
    }
433
2.11k
    TIXMLASSERT( p );
434
2.11k
    return p;
435
2.11k
}
436
437
438
void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
439
1.19k
{
440
1.19k
    const unsigned long BYTE_MASK = 0xBF;
441
1.19k
    const unsigned long BYTE_MARK = 0x80;
442
1.19k
    const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
443
444
1.19k
    if (input < 0x80) {
445
546
        *length = 1;
446
546
    }
447
647
    else if ( input < 0x800 ) {
448
205
        *length = 2;
449
205
    }
450
442
    else if ( input < 0x10000 ) {
451
220
        *length = 3;
452
220
    }
453
222
    else if ( input < 0x200000 ) {
454
222
        *length = 4;
455
222
    }
456
0
    else {
457
0
        *length = 0;    // This code won't convert this correctly anyway.
458
0
        return;
459
0
    }
460
461
1.19k
    output += *length;
462
463
    // Scary scary fall throughs are annotated with carefully designed comments
464
    // to suppress compiler warnings such as -Wimplicit-fallthrough in gcc
465
1.19k
    switch (*length) {
466
222
        case 4:
467
222
            --output;
468
222
            *output = static_cast<char>((input | BYTE_MARK) & BYTE_MASK);
469
222
            input >>= 6;
470
222
            TIXML_FALLTHROUGH;
471
442
        case 3:
472
442
            --output;
473
442
            *output = static_cast<char>((input | BYTE_MARK) & BYTE_MASK);
474
442
            input >>= 6;
475
442
            TIXML_FALLTHROUGH;
476
647
        case 2:
477
647
            --output;
478
647
            *output = static_cast<char>((input | BYTE_MARK) & BYTE_MASK);
479
647
            input >>= 6;
480
647
            TIXML_FALLTHROUGH;
481
1.19k
        case 1:
482
1.19k
            --output;
483
1.19k
            *output = static_cast<char>(input | FIRST_BYTE_MARK[*length]);
484
1.19k
            break;
485
0
        default:
486
0
            TIXMLASSERT( false );
487
1.19k
    }
488
1.19k
}
489
490
491
const char* XMLUtil::GetCharacterRef(const char* p, char* value, int* length)
492
5.70k
{
493
    // Assume an entity, and pull it out.
494
5.70k
    *length = 0;
495
496
5.70k
    static const uint32_t MAX_CODE_POINT = 0x10FFFF;
497
498
5.70k
    if (*(p + 1) == '#' && *(p + 2)) {
499
5.68k
        uint32_t ucs = 0;
500
5.68k
        ptrdiff_t delta = 0;
501
5.68k
        uint32_t mult = 1;
502
5.68k
        static const char SEMICOLON = ';';
503
504
5.68k
        bool hex = false;
505
5.68k
        uint32_t radix = 10;
506
5.68k
        const char* q = 0;
507
5.68k
        char terminator = '#';
508
509
5.68k
        if (*(p + 2) == 'x') {
510
            // Hexadecimal.
511
3.59k
            hex = true;
512
3.59k
            radix = 16;
513
3.59k
            terminator = 'x';
514
515
3.59k
            q = p + 3;
516
3.59k
        }
517
2.09k
        else {
518
            // Decimal.
519
2.09k
            q = p + 2;
520
2.09k
        }
521
5.68k
        if (!(*q)) {
522
8
            return 0;
523
8
        }
524
525
5.68k
        q = strchr(q, SEMICOLON);
526
5.68k
        if (!q) {
527
455
            return 0;
528
455
        }
529
5.22k
        TIXMLASSERT(*q == SEMICOLON);
530
531
5.22k
        delta = q - p;
532
5.22k
        --q;
533
534
13.9k
        while (*q != terminator) {
535
12.5k
            uint32_t digit = 0;
536
537
12.5k
            if (*q >= '0' && *q <= '9') {
538
5.42k
                digit = *q - '0';
539
5.42k
            }
540
7.08k
            else if (hex && (*q >= 'a' && *q <= 'f')) {
541
1.76k
                digit = *q - 'a' + 10;
542
1.76k
            }
543
5.32k
            else if (hex && (*q >= 'A' && *q <= 'F')) {
544
1.56k
                digit = *q - 'A' + 10;
545
1.56k
            }
546
3.75k
            else {
547
3.75k
                return 0;
548
3.75k
            }
549
8.74k
            TIXMLASSERT(digit < radix);
550
551
8.74k
            const unsigned int digitScaled = mult * digit;
552
8.74k
            ucs += digitScaled;
553
8.74k
            mult *= radix;       
554
            
555
            // Security check: could a value exist that is out of range?
556
            // Easily; limit to the MAX_CODE_POINT, which also allows for a
557
            // bunch of leading zeroes.
558
8.74k
            if (mult > MAX_CODE_POINT) {
559
3.49k
                mult = MAX_CODE_POINT;
560
3.49k
            }
561
8.74k
            --q;
562
8.74k
        }
563
        // Out of range:
564
1.46k
        if (ucs > MAX_CODE_POINT) {
565
276
            return 0;
566
276
        }
567
        // convert the UCS to UTF-8
568
1.19k
        ConvertUTF32ToUTF8(ucs, value, length);
569
1.19k
    if (length == 0) {
570
            // If length is 0, there was an error. (Security? Bad input?)
571
            // Fail safely.
572
0
      return 0;
573
0
    }
574
1.19k
        return p + delta + 1;
575
1.19k
    }
576
15
    return p + 1;
577
5.70k
}
578
579
void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
580
0
{
581
0
    TIXML_SNPRINTF( buffer, bufferSize, "%d", v );
582
0
}
583
584
585
void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize )
586
0
{
587
0
    TIXML_SNPRINTF( buffer, bufferSize, "%u", v );
588
0
}
589
590
591
void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )
592
0
{
593
0
    TIXML_SNPRINTF( buffer, bufferSize, "%s", v ? writeBoolTrue : writeBoolFalse);
594
0
}
595
596
/*
597
  ToStr() of a number is a very tricky topic.
598
  https://github.com/leethomason/tinyxml2/issues/106
599
*/
600
void XMLUtil::ToStr( float v, char* buffer, int bufferSize )
601
0
{
602
0
    TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v );
603
0
}
604
605
606
void XMLUtil::ToStr( double v, char* buffer, int bufferSize )
607
0
{
608
0
    TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v );
609
0
}
610
611
612
void XMLUtil::ToStr( int64_t v, char* buffer, int bufferSize )
613
0
{
614
  // horrible syntax trick to make the compiler happy about %lld
615
0
  TIXML_SNPRINTF(buffer, bufferSize, "%lld", static_cast<long long>(v));
616
0
}
617
618
void XMLUtil::ToStr( uint64_t v, char* buffer, int bufferSize )
619
0
{
620
    // horrible syntax trick to make the compiler happy about %llu
621
0
    TIXML_SNPRINTF(buffer, bufferSize, "%llu", static_cast<unsigned long long>(v));
622
0
}
623
624
bool XMLUtil::ToInt(const char* str, int* value)
625
0
{
626
0
    if (IsPrefixHex(str)) {
627
0
        unsigned v;
628
0
        if (TIXML_SSCANF(str, "%x", &v) == 1) {
629
0
            *value = static_cast<int>(v);
630
0
            return true;
631
0
        }
632
0
    }
633
0
    else {
634
0
        if (TIXML_SSCANF(str, "%d", value) == 1) {
635
0
            return true;
636
0
        }
637
0
    }
638
0
    return false;
639
0
}
640
641
bool XMLUtil::ToUnsigned(const char* str, unsigned* value)
642
0
{
643
0
    if (TIXML_SSCANF(str, IsPrefixHex(str) ? "%x" : "%u", value) == 1) {
644
0
        return true;
645
0
    }
646
0
    return false;
647
0
}
648
649
bool XMLUtil::ToBool( const char* str, bool* value )
650
0
{
651
0
    int ival = 0;
652
0
    if ( ToInt( str, &ival )) {
653
0
        *value = (ival==0) ? false : true;
654
0
        return true;
655
0
    }
656
0
    static const char* TRUE_VALS[] = { "true", "True", "TRUE", 0 };
657
0
    static const char* FALSE_VALS[] = { "false", "False", "FALSE", 0 };
658
659
0
    for (int i = 0; TRUE_VALS[i]; ++i) {
660
0
        if (StringEqual(str, TRUE_VALS[i])) {
661
0
            *value = true;
662
0
            return true;
663
0
        }
664
0
    }
665
0
    for (int i = 0; FALSE_VALS[i]; ++i) {
666
0
        if (StringEqual(str, FALSE_VALS[i])) {
667
0
            *value = false;
668
0
            return true;
669
0
        }
670
0
    }
671
0
    return false;
672
0
}
673
674
675
bool XMLUtil::ToFloat( const char* str, float* value )
676
0
{
677
0
    if ( TIXML_SSCANF( str, "%f", value ) == 1 ) {
678
0
        return true;
679
0
    }
680
0
    return false;
681
0
}
682
683
684
bool XMLUtil::ToDouble( const char* str, double* value )
685
0
{
686
0
    if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) {
687
0
        return true;
688
0
    }
689
0
    return false;
690
0
}
691
692
693
bool XMLUtil::ToInt64(const char* str, int64_t* value)
694
0
{
695
0
    if (IsPrefixHex(str)) {
696
0
        unsigned long long v = 0; // horrible syntax trick to make the compiler happy about %llx
697
0
        if (TIXML_SSCANF(str, "%llx", &v) == 1) {
698
0
            *value = static_cast<int64_t>(v);
699
0
            return true;
700
0
        }
701
0
    }
702
0
    else {
703
0
        long long v = 0;  // horrible syntax trick to make the compiler happy about %lld
704
0
        if (TIXML_SSCANF(str, "%lld", &v) == 1) {
705
0
            *value = static_cast<int64_t>(v);
706
0
            return true;
707
0
        }
708
0
    }
709
0
  return false;
710
0
}
711
712
713
0
bool XMLUtil::ToUnsigned64(const char* str, uint64_t* value) {
714
0
    unsigned long long v = 0; // horrible syntax trick to make the compiler happy about %llu
715
0
    if(TIXML_SSCANF(str, IsPrefixHex(str) ? "%llx" : "%llu", &v) == 1) {
716
0
        *value = static_cast<uint64_t>(v);
717
0
        return true;
718
0
    }
719
0
    return false;
720
0
}
721
722
723
char* XMLDocument::Identify( char* p, XMLNode** node, bool first )
724
6.53M
{
725
6.53M
    TIXMLASSERT( node );
726
6.53M
    TIXMLASSERT( p );
727
6.53M
    char* const start = p;
728
6.53M
    int const startLine = _parseCurLineNum;
729
6.53M
    p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
730
6.53M
    if( !*p ) {
731
24
        *node = 0;
732
24
        TIXMLASSERT( p );
733
24
        return p;
734
24
    }
735
736
    // These strings define the matching patterns:
737
6.53M
    static const char* xmlHeader    = { "<?" };
738
6.53M
    static const char* commentHeader  = { "<!--" };
739
6.53M
    static const char* cdataHeader    = { "<![CDATA[" };
740
6.53M
    static const char* dtdHeader    = { "<!" };
741
6.53M
    static const char* elementHeader  = { "<" };  // and a header for everything else; check last.
742
743
6.53M
    static const int xmlHeaderLen   = 2;
744
6.53M
    static const int commentHeaderLen = 4;
745
6.53M
    static const int cdataHeaderLen   = 9;
746
6.53M
    static const int dtdHeaderLen   = 2;
747
6.53M
    static const int elementHeaderLen = 1;
748
749
6.53M
    TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) );   // use same memory pool
750
6.53M
    TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
751
6.53M
    XMLNode* returnNode = 0;
752
6.53M
    if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
753
669
        returnNode = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
754
669
        returnNode->_parseLineNum = _parseCurLineNum;
755
669
        p += xmlHeaderLen;
756
669
    }
757
6.53M
    else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
758
372
        returnNode = CreateUnlinkedNode<XMLComment>( _commentPool );
759
372
        returnNode->_parseLineNum = _parseCurLineNum;
760
372
        p += commentHeaderLen;
761
372
    }
762
6.53M
    else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
763
338
        XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
764
338
        returnNode = text;
765
338
        returnNode->_parseLineNum = _parseCurLineNum;
766
338
        p += cdataHeaderLen;
767
338
        text->SetCData( true );
768
338
    }
769
6.53M
    else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
770
3.61M
        returnNode = CreateUnlinkedNode<XMLUnknown>( _commentPool );
771
3.61M
        returnNode->_parseLineNum = _parseCurLineNum;
772
3.61M
        p += dtdHeaderLen;
773
3.61M
    }
774
2.91M
    else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
775
776
        // Preserve whitespace pedantically before closing tag, when it's immediately after opening tag
777
1.44M
        if (WhitespaceMode() == PEDANTIC_WHITESPACE && first && p != start && *(p + elementHeaderLen) == '/') {
778
0
            returnNode = CreateUnlinkedNode<XMLText>(_textPool);
779
0
            returnNode->_parseLineNum = startLine;
780
0
            p = start;  // Back it up, all the text counts.
781
0
            _parseCurLineNum = startLine;
782
0
        }
783
1.44M
        else {
784
1.44M
            returnNode = CreateUnlinkedNode<XMLElement>(_elementPool);
785
1.44M
            returnNode->_parseLineNum = _parseCurLineNum;
786
1.44M
            p += elementHeaderLen;
787
1.44M
        }
788
1.44M
    }
789
1.47M
    else {
790
1.47M
        returnNode = CreateUnlinkedNode<XMLText>( _textPool );
791
1.47M
        returnNode->_parseLineNum = _parseCurLineNum; // Report line of first non-whitespace character
792
1.47M
        p = start;  // Back it up, all the text counts.
793
1.47M
        _parseCurLineNum = startLine;
794
1.47M
    }
795
796
6.53M
    TIXMLASSERT( returnNode );
797
6.53M
    TIXMLASSERT( p );
798
6.53M
    *node = returnNode;
799
6.53M
    return p;
800
6.53M
}
801
802
803
bool XMLDocument::Accept( XMLVisitor* visitor ) const
804
0
{
805
0
    TIXMLASSERT( visitor );
806
0
    if ( visitor->VisitEnter( *this ) ) {
807
0
        for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
808
0
            if ( !node->Accept( visitor ) ) {
809
0
                break;
810
0
            }
811
0
        }
812
0
    }
813
0
    return visitor->VisitExit( *this );
814
0
}
815
816
817
// --------- XMLNode ----------- //
818
819
XMLNode::XMLNode( XMLDocument* doc ) :
820
6.53M
    _document( doc ),
821
6.53M
    _parent( 0 ),
822
6.53M
    _value(),
823
6.53M
    _parseLineNum( 0 ),
824
6.53M
    _firstChild( 0 ), _lastChild( 0 ),
825
6.53M
    _prev( 0 ), _next( 0 ),
826
6.53M
  _userData( 0 ),
827
6.53M
    _memPool( 0 )
828
6.53M
{
829
6.53M
}
830
831
832
XMLNode::~XMLNode()
833
6.53M
{
834
6.53M
    DeleteChildren();
835
6.53M
    if ( _parent ) {
836
0
        _parent->Unlink( this );
837
0
    }
838
6.53M
}
839
840
// ChildElementCount was originally suggested by msteiger on the sourceforge page for TinyXML and modified by KB1SPH for TinyXML-2.
841
842
0
int XMLNode::ChildElementCount(const char *value) const {
843
0
  int count = 0;
844
845
0
  const XMLElement *e = FirstChildElement(value);
846
847
0
  while (e) {
848
0
    e = e->NextSiblingElement(value);
849
0
    count++;
850
0
  }
851
852
0
  return count;
853
0
}
854
855
0
int XMLNode::ChildElementCount() const {
856
0
  int count = 0;
857
858
0
  const XMLElement *e = FirstChildElement();
859
860
0
  while (e) {
861
0
    e = e->NextSiblingElement();
862
0
    count++;
863
0
  }
864
865
0
  return count;
866
0
}
867
868
const char* XMLNode::Value() const
869
3.42k
{
870
    // Edge case: XMLDocuments don't have a Value. Return null.
871
3.42k
    if ( this->ToDocument() )
872
0
        return 0;
873
3.42k
    return _value.GetStr();
874
3.42k
}
875
876
void XMLNode::SetValue( const char* str, bool staticMem )
877
0
{
878
0
    if ( staticMem ) {
879
0
        _value.SetInternedStr( str );
880
0
    }
881
0
    else {
882
0
        _value.SetStr( str );
883
0
    }
884
0
}
885
886
XMLNode* XMLNode::DeepClone(XMLDocument* target) const
887
0
{
888
0
  XMLNode* clone = this->ShallowClone(target);
889
0
  if (!clone) return 0;
890
891
0
  for (const XMLNode* child = this->FirstChild(); child; child = child->NextSibling()) {
892
0
    XMLNode* childClone = child->DeepClone(target);
893
0
    TIXMLASSERT(childClone);
894
0
    clone->InsertEndChild(childClone);
895
0
  }
896
0
  return clone;
897
0
}
898
899
void XMLNode::DeleteChildren()
900
6.54M
{
901
13.0M
    while( _firstChild ) {
902
6.47M
        TIXMLASSERT( _lastChild );
903
6.47M
        DeleteChild( _firstChild );
904
6.47M
    }
905
6.54M
    _firstChild = _lastChild = 0;
906
6.54M
}
907
908
909
void XMLNode::Unlink( XMLNode* child )
910
6.47M
{
911
6.47M
    TIXMLASSERT( child );
912
6.47M
    TIXMLASSERT( child->_document == _document );
913
6.47M
    TIXMLASSERT( child->_parent == this );
914
6.47M
    if ( child == _firstChild ) {
915
6.47M
        _firstChild = _firstChild->_next;
916
6.47M
    }
917
6.47M
    if ( child == _lastChild ) {
918
10.2k
        _lastChild = _lastChild->_prev;
919
10.2k
    }
920
921
6.47M
    if ( child->_prev ) {
922
0
        child->_prev->_next = child->_next;
923
0
    }
924
6.47M
    if ( child->_next ) {
925
6.46M
        child->_next->_prev = child->_prev;
926
6.46M
    }
927
6.47M
  child->_next = 0;
928
6.47M
  child->_prev = 0;
929
6.47M
  child->_parent = 0;
930
6.47M
}
931
932
933
void XMLNode::DeleteChild( XMLNode* node )
934
6.47M
{
935
6.47M
    TIXMLASSERT( node );
936
6.47M
    TIXMLASSERT( node->_document == _document );
937
6.47M
    TIXMLASSERT( node->_parent == this );
938
6.47M
    Unlink( node );
939
6.47M
  TIXMLASSERT(node->_prev == 0);
940
6.47M
  TIXMLASSERT(node->_next == 0);
941
6.47M
  TIXMLASSERT(node->_parent == 0);
942
6.47M
    DeleteNode( node );
943
6.47M
}
944
945
946
XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
947
6.47M
{
948
6.47M
    TIXMLASSERT( addThis );
949
6.47M
    if ( addThis->_document != _document ) {
950
0
        TIXMLASSERT( false );
951
0
        return 0;
952
0
    }
953
6.47M
    InsertChildPreamble( addThis );
954
955
6.47M
    if ( _lastChild ) {
956
6.46M
        TIXMLASSERT( _firstChild );
957
6.46M
        TIXMLASSERT( _lastChild->_next == 0 );
958
6.46M
        _lastChild->_next = addThis;
959
6.46M
        addThis->_prev = _lastChild;
960
6.46M
        _lastChild = addThis;
961
962
6.46M
        addThis->_next = 0;
963
6.46M
    }
964
10.2k
    else {
965
10.2k
        TIXMLASSERT( _firstChild == 0 );
966
10.2k
        _firstChild = _lastChild = addThis;
967
968
10.2k
        addThis->_prev = 0;
969
10.2k
        addThis->_next = 0;
970
10.2k
    }
971
6.47M
    addThis->_parent = this;
972
6.47M
    return addThis;
973
6.47M
}
974
975
976
XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
977
0
{
978
0
    TIXMLASSERT( addThis );
979
0
    if ( addThis->_document != _document ) {
980
0
        TIXMLASSERT( false );
981
0
        return 0;
982
0
    }
983
0
    InsertChildPreamble( addThis );
984
985
0
    if ( _firstChild ) {
986
0
        TIXMLASSERT( _lastChild );
987
0
        TIXMLASSERT( _firstChild->_prev == 0 );
988
989
0
        _firstChild->_prev = addThis;
990
0
        addThis->_next = _firstChild;
991
0
        _firstChild = addThis;
992
993
0
        addThis->_prev = 0;
994
0
    }
995
0
    else {
996
0
        TIXMLASSERT( _lastChild == 0 );
997
0
        _firstChild = _lastChild = addThis;
998
999
0
        addThis->_prev = 0;
1000
0
        addThis->_next = 0;
1001
0
    }
1002
0
    addThis->_parent = this;
1003
0
    return addThis;
1004
0
}
1005
1006
1007
XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
1008
0
{
1009
0
    TIXMLASSERT( addThis );
1010
0
    if ( addThis->_document != _document ) {
1011
0
        TIXMLASSERT( false );
1012
0
        return 0;
1013
0
    }
1014
1015
0
    TIXMLASSERT( afterThis );
1016
1017
0
    if ( afterThis->_parent != this ) {
1018
0
        TIXMLASSERT( false );
1019
0
        return 0;
1020
0
    }
1021
0
    if ( afterThis == addThis ) {
1022
        // Current state: BeforeThis -> AddThis -> OneAfterAddThis
1023
        // Now AddThis must disappear from it's location and then
1024
        // reappear between BeforeThis and OneAfterAddThis.
1025
        // So just leave it where it is.
1026
0
        return addThis;
1027
0
    }
1028
1029
0
    if ( afterThis->_next == 0 ) {
1030
        // The last node or the only node.
1031
0
        return InsertEndChild( addThis );
1032
0
    }
1033
0
    InsertChildPreamble( addThis );
1034
0
    addThis->_prev = afterThis;
1035
0
    addThis->_next = afterThis->_next;
1036
0
    afterThis->_next->_prev = addThis;
1037
0
    afterThis->_next = addThis;
1038
0
    addThis->_parent = this;
1039
0
    return addThis;
1040
0
}
1041
1042
1043
1044
1045
const XMLElement* XMLNode::FirstChildElement( const char* name ) const
1046
0
{
1047
0
    for( const XMLNode* node = _firstChild; node; node = node->_next ) {
1048
0
        const XMLElement* element = node->ToElementWithName( name );
1049
0
        if ( element ) {
1050
0
            return element;
1051
0
        }
1052
0
    }
1053
0
    return 0;
1054
0
}
1055
1056
1057
const XMLElement* XMLNode::LastChildElement( const char* name ) const
1058
0
{
1059
0
    for( const XMLNode* node = _lastChild; node; node = node->_prev ) {
1060
0
        const XMLElement* element = node->ToElementWithName( name );
1061
0
        if ( element ) {
1062
0
            return element;
1063
0
        }
1064
0
    }
1065
0
    return 0;
1066
0
}
1067
1068
1069
const XMLElement* XMLNode::NextSiblingElement( const char* name ) const
1070
0
{
1071
0
    for( const XMLNode* node = _next; node; node = node->_next ) {
1072
0
        const XMLElement* element = node->ToElementWithName( name );
1073
0
        if ( element ) {
1074
0
            return element;
1075
0
        }
1076
0
    }
1077
0
    return 0;
1078
0
}
1079
1080
1081
const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const
1082
0
{
1083
0
    for( const XMLNode* node = _prev; node; node = node->_prev ) {
1084
0
        const XMLElement* element = node->ToElementWithName( name );
1085
0
        if ( element ) {
1086
0
            return element;
1087
0
        }
1088
0
    }
1089
0
    return 0;
1090
0
}
1091
1092
1093
char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )
1094
57.5k
{
1095
    // This is a recursive method, but thinking about it "at the current level"
1096
    // it is a pretty simple flat list:
1097
    //    <foo/>
1098
    //    <!-- comment -->
1099
    //
1100
    // With a special case:
1101
    //    <foo>
1102
    //    </foo>
1103
    //    <!-- comment -->
1104
    //
1105
    // Where the closing element (/foo) *must* be the next thing after the opening
1106
    // element, and the names must match. BUT the tricky bit is that the closing
1107
    // element will be read by the child.
1108
    //
1109
    // 'endTag' is the end tag for this node, it is returned by a call to a child.
1110
    // 'parentEnd' is the end tag for the parent, which is filled in and returned.
1111
1112
57.5k
  XMLDocument::DepthTracker tracker(_document);
1113
57.5k
  if (_document->Error())
1114
2
    return 0;
1115
1116
57.5k
  bool first = true;
1117
6.53M
  while( p && *p ) {
1118
6.53M
        XMLNode* node = 0;
1119
1120
6.53M
        p = _document->Identify( p, &node, first );
1121
6.53M
        TIXMLASSERT( p );
1122
6.53M
        if ( node == 0 ) {
1123
24
            break;
1124
24
        }
1125
6.53M
        first = false;
1126
1127
6.53M
       const int initialLineNum = node->_parseLineNum;
1128
1129
6.53M
        StrPair endTag;
1130
6.53M
        p = node->ParseDeep( p, &endTag, curLineNumPtr );
1131
6.53M
        if ( !p ) {
1132
54.8k
            _document->DeleteNode( node );
1133
54.8k
            if ( !_document->Error() ) {
1134
256
                _document->SetError( XML_ERROR_PARSING, initialLineNum, 0);
1135
256
            }
1136
54.8k
            break;
1137
54.8k
        }
1138
1139
6.48M
        const XMLDeclaration* const decl = node->ToDeclaration();
1140
6.48M
        if ( decl ) {
1141
            // Declarations are only allowed at document level
1142
            //
1143
            // Multiple declarations are allowed but all declarations
1144
            // must occur before anything else. 
1145
            //
1146
            // Optimized due to a security test case. If the first node is 
1147
            // a declaration, and the last node is a declaration, then only 
1148
            // declarations have so far been added.
1149
597
            bool wellLocated = false;
1150
1151
597
            if (ToDocument()) {
1152
580
                if (FirstChild()) {
1153
556
                    wellLocated =
1154
556
                        FirstChild() &&
1155
556
                        FirstChild()->ToDeclaration() &&
1156
487
                        LastChild() &&
1157
487
                        LastChild()->ToDeclaration();
1158
556
                }
1159
24
                else {
1160
24
                    wellLocated = true;
1161
24
                }
1162
580
            }
1163
597
            if ( !wellLocated ) {
1164
87
                _document->SetError( XML_ERROR_PARSING_DECLARATION, initialLineNum, "XMLDeclaration value=%s", decl->Value());
1165
87
                _document->DeleteNode( node );
1166
87
                break;
1167
87
            }
1168
597
        }
1169
1170
6.48M
        XMLElement* ele = node->ToElement();
1171
6.48M
        if ( ele ) {
1172
            // We read the end tag. Return it to the parent.
1173
1.38M
            if ( ele->ClosingType() == XMLElement::CLOSING ) {
1174
2.29k
                if ( parentEndTag ) {
1175
2.28k
                    ele->_value.TransferTo( parentEndTag );
1176
2.28k
                }
1177
2.29k
                node->_memPool->SetTracked();   // created and then immediately deleted.
1178
2.29k
                DeleteNode( node );
1179
2.29k
                return p;
1180
2.29k
            }
1181
1182
            // Handle an end tag returned to this level.
1183
            // And handle a bunch of annoying errors.
1184
1.38M
            bool mismatch = false;
1185
1.38M
            if ( endTag.Empty() ) {
1186
1.38M
                if ( ele->ClosingType() == XMLElement::OPEN ) {
1187
24
                    mismatch = true;
1188
24
                }
1189
1.38M
            }
1190
2.28k
            else {
1191
2.28k
                if ( ele->ClosingType() != XMLElement::OPEN ) {
1192
0
                    mismatch = true;
1193
0
                }
1194
2.28k
                else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) {
1195
156
                    mismatch = true;
1196
156
                }
1197
2.28k
            }
1198
1.38M
            if ( mismatch ) {
1199
180
                _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, initialLineNum, "XMLElement name=%s", ele->Name());
1200
180
                _document->DeleteNode( node );
1201
180
                break;
1202
180
            }
1203
1.38M
        }
1204
6.47M
        InsertEndChild( node );
1205
6.47M
    }
1206
55.2k
    return 0;
1207
57.5k
}
1208
1209
/*static*/ void XMLNode::DeleteNode( XMLNode* node )
1210
6.53M
{
1211
6.53M
    if ( node == 0 ) {
1212
0
        return;
1213
0
    }
1214
6.53M
  TIXMLASSERT(node->_document);
1215
6.53M
  if (!node->ToDocument()) {
1216
6.53M
    node->_document->MarkInUse(node);
1217
6.53M
  }
1218
1219
6.53M
    MemPool* pool = node->_memPool;
1220
6.53M
    node->~XMLNode();
1221
6.53M
    pool->Free( node );
1222
6.53M
}
1223
1224
void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const
1225
6.47M
{
1226
6.47M
    TIXMLASSERT( insertThis );
1227
6.47M
    TIXMLASSERT( insertThis->_document == _document );
1228
1229
6.47M
  if (insertThis->_parent) {
1230
0
        insertThis->_parent->Unlink( insertThis );
1231
0
  }
1232
6.47M
  else {
1233
6.47M
    insertThis->_document->MarkInUse(insertThis);
1234
6.47M
        insertThis->_memPool->SetTracked();
1235
6.47M
  }
1236
6.47M
}
1237
1238
const XMLElement* XMLNode::ToElementWithName( const char* name ) const
1239
0
{
1240
0
    const XMLElement* element = this->ToElement();
1241
0
    if ( element == 0 ) {
1242
0
        return 0;
1243
0
    }
1244
0
    if ( name == 0 ) {
1245
0
        return element;
1246
0
    }
1247
0
    if ( XMLUtil::StringEqual( element->Name(), name ) ) {
1248
0
       return element;
1249
0
    }
1250
0
    return 0;
1251
0
}
1252
1253
// --------- XMLText ---------- //
1254
char* XMLText::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
1255
1.47M
{
1256
1.47M
    if ( this->CData() ) {
1257
338
        p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
1258
338
        if ( !p ) {
1259
79
            _document->SetError( XML_ERROR_PARSING_CDATA, _parseLineNum, 0 );
1260
79
        }
1261
338
        return p;
1262
338
    }
1263
1.47M
    else {
1264
1.47M
        int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
1265
1.47M
        if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
1266
0
            flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING;
1267
0
        }
1268
1269
1.47M
        p = _value.ParseText( p, "<", flags, curLineNumPtr );
1270
1.47M
        if ( p && *p ) {
1271
1.47M
            return p-1;
1272
1.47M
        }
1273
173
        if ( !p ) {
1274
164
            _document->SetError( XML_ERROR_PARSING_TEXT, _parseLineNum, 0 );
1275
164
        }
1276
173
    }
1277
173
    return 0;
1278
1.47M
}
1279
1280
1281
XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
1282
0
{
1283
0
    if ( !doc ) {
1284
0
        doc = _document;
1285
0
    }
1286
0
    XMLText* text = doc->NewText( Value() );  // fixme: this will always allocate memory. Intern?
1287
0
    text->SetCData( this->CData() );
1288
0
    return text;
1289
0
}
1290
1291
1292
bool XMLText::ShallowEqual( const XMLNode* compare ) const
1293
0
{
1294
0
    TIXMLASSERT( compare );
1295
0
    const XMLText* text = compare->ToText();
1296
0
    return ( text && XMLUtil::StringEqual( text->Value(), Value() ) );
1297
0
}
1298
1299
1300
bool XMLText::Accept( XMLVisitor* visitor ) const
1301
0
{
1302
0
    TIXMLASSERT( visitor );
1303
0
    return visitor->Visit( *this );
1304
0
}
1305
1306
1307
// --------- XMLComment ---------- //
1308
1309
372
XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
1310
372
{
1311
372
}
1312
1313
1314
XMLComment::~XMLComment()
1315
{
1316
}
1317
1318
1319
char* XMLComment::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
1320
372
{
1321
    // Comment parses as text.
1322
372
    p = _value.ParseText( p, "-->", StrPair::COMMENT, curLineNumPtr );
1323
372
    if ( p == 0 ) {
1324
85
        _document->SetError( XML_ERROR_PARSING_COMMENT, _parseLineNum, 0 );
1325
85
    }
1326
372
    return p;
1327
372
}
1328
1329
1330
XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
1331
0
{
1332
0
    if ( !doc ) {
1333
0
        doc = _document;
1334
0
    }
1335
0
    XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
1336
0
    return comment;
1337
0
}
1338
1339
1340
bool XMLComment::ShallowEqual( const XMLNode* compare ) const
1341
0
{
1342
0
    TIXMLASSERT( compare );
1343
0
    const XMLComment* comment = compare->ToComment();
1344
0
    return ( comment && XMLUtil::StringEqual( comment->Value(), Value() ));
1345
0
}
1346
1347
1348
bool XMLComment::Accept( XMLVisitor* visitor ) const
1349
0
{
1350
0
    TIXMLASSERT( visitor );
1351
0
    return visitor->Visit( *this );
1352
0
}
1353
1354
1355
// --------- XMLDeclaration ---------- //
1356
1357
669
XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
1358
669
{
1359
669
}
1360
1361
1362
XMLDeclaration::~XMLDeclaration()
1363
{
1364
    //printf( "~XMLDeclaration\n" );
1365
}
1366
1367
1368
char* XMLDeclaration::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
1369
669
{
1370
    // Declaration parses as text.
1371
669
    p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
1372
669
    if ( p == 0 ) {
1373
72
        _document->SetError( XML_ERROR_PARSING_DECLARATION, _parseLineNum, 0 );
1374
72
    }
1375
669
    return p;
1376
669
}
1377
1378
1379
XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
1380
0
{
1381
0
    if ( !doc ) {
1382
0
        doc = _document;
1383
0
    }
1384
0
    XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
1385
0
    return dec;
1386
0
}
1387
1388
1389
bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
1390
0
{
1391
0
    TIXMLASSERT( compare );
1392
0
    const XMLDeclaration* declaration = compare->ToDeclaration();
1393
0
    return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() ));
1394
0
}
1395
1396
1397
1398
bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
1399
0
{
1400
0
    TIXMLASSERT( visitor );
1401
0
    return visitor->Visit( *this );
1402
0
}
1403
1404
// --------- XMLUnknown ---------- //
1405
1406
3.61M
XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
1407
3.61M
{
1408
3.61M
}
1409
1410
1411
XMLUnknown::~XMLUnknown()
1412
{
1413
}
1414
1415
1416
char* XMLUnknown::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
1417
3.61M
{
1418
    // Unknown parses as text.
1419
3.61M
    p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
1420
3.61M
    if ( !p ) {
1421
162
        _document->SetError( XML_ERROR_PARSING_UNKNOWN, _parseLineNum, 0 );
1422
162
    }
1423
3.61M
    return p;
1424
3.61M
}
1425
1426
1427
XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
1428
0
{
1429
0
    if ( !doc ) {
1430
0
        doc = _document;
1431
0
    }
1432
0
    XMLUnknown* text = doc->NewUnknown( Value() );  // fixme: this will always allocate memory. Intern?
1433
0
    return text;
1434
0
}
1435
1436
1437
bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
1438
0
{
1439
0
    TIXMLASSERT( compare );
1440
0
    const XMLUnknown* unknown = compare->ToUnknown();
1441
0
    return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() ));
1442
0
}
1443
1444
1445
bool XMLUnknown::Accept( XMLVisitor* visitor ) const
1446
0
{
1447
0
    TIXMLASSERT( visitor );
1448
0
    return visitor->Visit( *this );
1449
0
}
1450
1451
// --------- XMLAttribute ---------- //
1452
1453
const char* XMLAttribute::Name() const
1454
1.31M
{
1455
1.31M
    return _name.GetStr();
1456
1.31M
}
1457
1458
const char* XMLAttribute::Value() const
1459
360
{
1460
360
    return _value.GetStr();
1461
360
}
1462
1463
char* XMLAttribute::ParseDeep( char* p, bool processEntities, int* curLineNumPtr )
1464
192k
{
1465
    // Parse using the name rules: bug fix, was using ParseText before
1466
192k
    p = _name.ParseName( p );
1467
192k
    if ( !p || !*p ) {
1468
100
        return 0;
1469
100
    }
1470
1471
    // Skip white space before =
1472
192k
    p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
1473
192k
    if ( *p != '=' ) {
1474
36
        return 0;
1475
36
    }
1476
1477
192k
    ++p;  // move up to opening quote
1478
192k
    p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
1479
192k
    if ( *p != '\"' && *p != '\'' ) {
1480
39
        return 0;
1481
39
    }
1482
1483
192k
    const char endTag[2] = { *p, 0 };
1484
192k
    ++p;  // move past opening quote
1485
1486
192k
    p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr );
1487
192k
    return p;
1488
192k
}
1489
1490
1491
void XMLAttribute::SetName( const char* n )
1492
0
{
1493
0
    _name.SetStr( n );
1494
0
}
1495
1496
1497
XMLError XMLAttribute::QueryIntValue( int* value ) const
1498
0
{
1499
0
    if ( XMLUtil::ToInt( Value(), value )) {
1500
0
        return XML_SUCCESS;
1501
0
    }
1502
0
    return XML_WRONG_ATTRIBUTE_TYPE;
1503
0
}
1504
1505
1506
XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
1507
0
{
1508
0
    if ( XMLUtil::ToUnsigned( Value(), value )) {
1509
0
        return XML_SUCCESS;
1510
0
    }
1511
0
    return XML_WRONG_ATTRIBUTE_TYPE;
1512
0
}
1513
1514
1515
XMLError XMLAttribute::QueryInt64Value(int64_t* value) const
1516
0
{
1517
0
  if (XMLUtil::ToInt64(Value(), value)) {
1518
0
    return XML_SUCCESS;
1519
0
  }
1520
0
  return XML_WRONG_ATTRIBUTE_TYPE;
1521
0
}
1522
1523
1524
XMLError XMLAttribute::QueryUnsigned64Value(uint64_t* value) const
1525
0
{
1526
0
    if(XMLUtil::ToUnsigned64(Value(), value)) {
1527
0
        return XML_SUCCESS;
1528
0
    }
1529
0
    return XML_WRONG_ATTRIBUTE_TYPE;
1530
0
}
1531
1532
1533
XMLError XMLAttribute::QueryBoolValue( bool* value ) const
1534
0
{
1535
0
    if ( XMLUtil::ToBool( Value(), value )) {
1536
0
        return XML_SUCCESS;
1537
0
    }
1538
0
    return XML_WRONG_ATTRIBUTE_TYPE;
1539
0
}
1540
1541
1542
XMLError XMLAttribute::QueryFloatValue( float* value ) const
1543
0
{
1544
0
    if ( XMLUtil::ToFloat( Value(), value )) {
1545
0
        return XML_SUCCESS;
1546
0
    }
1547
0
    return XML_WRONG_ATTRIBUTE_TYPE;
1548
0
}
1549
1550
1551
XMLError XMLAttribute::QueryDoubleValue( double* value ) const
1552
0
{
1553
0
    if ( XMLUtil::ToDouble( Value(), value )) {
1554
0
        return XML_SUCCESS;
1555
0
    }
1556
0
    return XML_WRONG_ATTRIBUTE_TYPE;
1557
0
}
1558
1559
1560
void XMLAttribute::SetAttribute( const char* v )
1561
0
{
1562
0
    _value.SetStr( v );
1563
0
}
1564
1565
1566
void XMLAttribute::SetAttribute( int v )
1567
0
{
1568
0
    char buf[BUF_SIZE];
1569
0
    XMLUtil::ToStr( v, buf, BUF_SIZE );
1570
0
    _value.SetStr( buf );
1571
0
}
1572
1573
1574
void XMLAttribute::SetAttribute( unsigned v )
1575
0
{
1576
0
    char buf[BUF_SIZE];
1577
0
    XMLUtil::ToStr( v, buf, BUF_SIZE );
1578
0
    _value.SetStr( buf );
1579
0
}
1580
1581
1582
void XMLAttribute::SetAttribute(int64_t v)
1583
0
{
1584
0
  char buf[BUF_SIZE];
1585
0
  XMLUtil::ToStr(v, buf, BUF_SIZE);
1586
0
  _value.SetStr(buf);
1587
0
}
1588
1589
void XMLAttribute::SetAttribute(uint64_t v)
1590
0
{
1591
0
    char buf[BUF_SIZE];
1592
0
    XMLUtil::ToStr(v, buf, BUF_SIZE);
1593
0
    _value.SetStr(buf);
1594
0
}
1595
1596
1597
void XMLAttribute::SetAttribute( bool v )
1598
0
{
1599
0
    char buf[BUF_SIZE];
1600
0
    XMLUtil::ToStr( v, buf, BUF_SIZE );
1601
0
    _value.SetStr( buf );
1602
0
}
1603
1604
void XMLAttribute::SetAttribute( double v )
1605
0
{
1606
0
    char buf[BUF_SIZE];
1607
0
    XMLUtil::ToStr( v, buf, BUF_SIZE );
1608
0
    _value.SetStr( buf );
1609
0
}
1610
1611
void XMLAttribute::SetAttribute( float v )
1612
0
{
1613
0
    char buf[BUF_SIZE];
1614
0
    XMLUtil::ToStr( v, buf, BUF_SIZE );
1615
0
    _value.SetStr( buf );
1616
0
}
1617
1618
1619
// --------- XMLElement ---------- //
1620
1.44M
XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
1621
1.44M
    _closingType( OPEN ),
1622
1.44M
    _rootAttribute( 0 )
1623
1.44M
{
1624
1.44M
}
1625
1626
1627
XMLElement::~XMLElement()
1628
1.44M
{
1629
1.63M
    while( _rootAttribute ) {
1630
191k
        XMLAttribute* next = _rootAttribute->_next;
1631
191k
        DeleteAttribute( _rootAttribute );
1632
191k
        _rootAttribute = next;
1633
191k
    }
1634
1.44M
}
1635
1636
1637
const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
1638
192k
{
1639
1.31M
    for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) {
1640
1.12M
        if ( XMLUtil::StringEqual( a->Name(), name ) ) {
1641
360
            return a;
1642
360
        }
1643
1.12M
    }
1644
191k
    return 0;
1645
192k
}
1646
1647
1648
const char* XMLElement::Attribute( const char* name, const char* value ) const
1649
192k
{
1650
192k
    const XMLAttribute* a = FindAttribute( name );
1651
192k
    if ( !a ) {
1652
191k
        return 0;
1653
191k
    }
1654
360
    if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
1655
360
        return a->Value();
1656
360
    }
1657
0
    return 0;
1658
360
}
1659
1660
int XMLElement::IntAttribute(const char* name, int defaultValue) const
1661
0
{
1662
0
  int i = defaultValue;
1663
0
  QueryIntAttribute(name, &i);
1664
0
  return i;
1665
0
}
1666
1667
unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const
1668
0
{
1669
0
  unsigned i = defaultValue;
1670
0
  QueryUnsignedAttribute(name, &i);
1671
0
  return i;
1672
0
}
1673
1674
int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const
1675
0
{
1676
0
  int64_t i = defaultValue;
1677
0
  QueryInt64Attribute(name, &i);
1678
0
  return i;
1679
0
}
1680
1681
uint64_t XMLElement::Unsigned64Attribute(const char* name, uint64_t defaultValue) const
1682
0
{
1683
0
  uint64_t i = defaultValue;
1684
0
  QueryUnsigned64Attribute(name, &i);
1685
0
  return i;
1686
0
}
1687
1688
bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const
1689
0
{
1690
0
  bool b = defaultValue;
1691
0
  QueryBoolAttribute(name, &b);
1692
0
  return b;
1693
0
}
1694
1695
double XMLElement::DoubleAttribute(const char* name, double defaultValue) const
1696
0
{
1697
0
  double d = defaultValue;
1698
0
  QueryDoubleAttribute(name, &d);
1699
0
  return d;
1700
0
}
1701
1702
float XMLElement::FloatAttribute(const char* name, float defaultValue) const
1703
0
{
1704
0
  float f = defaultValue;
1705
0
  QueryFloatAttribute(name, &f);
1706
0
  return f;
1707
0
}
1708
1709
const char* XMLElement::GetText() const
1710
0
{
1711
    /* skip comment node */
1712
0
    const XMLNode* node = FirstChild();
1713
0
    while (node) {
1714
0
        if (node->ToComment()) {
1715
0
            node = node->NextSibling();
1716
0
            continue;
1717
0
        }
1718
0
        break;
1719
0
    }
1720
1721
0
    if ( node && node->ToText() ) {
1722
0
        return node->Value();
1723
0
    }
1724
0
    return 0;
1725
0
}
1726
1727
1728
void  XMLElement::SetText( const char* inText )
1729
0
{
1730
0
  if ( FirstChild() && FirstChild()->ToText() )
1731
0
    FirstChild()->SetValue( inText );
1732
0
  else {
1733
0
    XMLText*  theText = GetDocument()->NewText( inText );
1734
0
    InsertFirstChild( theText );
1735
0
  }
1736
0
}
1737
1738
1739
void XMLElement::SetText( int v )
1740
0
{
1741
0
    char buf[BUF_SIZE];
1742
0
    XMLUtil::ToStr( v, buf, BUF_SIZE );
1743
0
    SetText( buf );
1744
0
}
1745
1746
1747
void XMLElement::SetText( unsigned v )
1748
0
{
1749
0
    char buf[BUF_SIZE];
1750
0
    XMLUtil::ToStr( v, buf, BUF_SIZE );
1751
0
    SetText( buf );
1752
0
}
1753
1754
1755
void XMLElement::SetText(int64_t v)
1756
0
{
1757
0
  char buf[BUF_SIZE];
1758
0
  XMLUtil::ToStr(v, buf, BUF_SIZE);
1759
0
  SetText(buf);
1760
0
}
1761
1762
0
void XMLElement::SetText(uint64_t v) {
1763
0
    char buf[BUF_SIZE];
1764
0
    XMLUtil::ToStr(v, buf, BUF_SIZE);
1765
0
    SetText(buf);
1766
0
}
1767
1768
1769
void XMLElement::SetText( bool v )
1770
0
{
1771
0
    char buf[BUF_SIZE];
1772
0
    XMLUtil::ToStr( v, buf, BUF_SIZE );
1773
0
    SetText( buf );
1774
0
}
1775
1776
1777
void XMLElement::SetText( float v )
1778
0
{
1779
0
    char buf[BUF_SIZE];
1780
0
    XMLUtil::ToStr( v, buf, BUF_SIZE );
1781
0
    SetText( buf );
1782
0
}
1783
1784
1785
void XMLElement::SetText( double v )
1786
0
{
1787
0
    char buf[BUF_SIZE];
1788
0
    XMLUtil::ToStr( v, buf, BUF_SIZE );
1789
0
    SetText( buf );
1790
0
}
1791
1792
1793
XMLError XMLElement::QueryIntText( int* ival ) const
1794
0
{
1795
0
    if ( FirstChild() && FirstChild()->ToText() ) {
1796
0
        const char* t = FirstChild()->Value();
1797
0
        if ( XMLUtil::ToInt( t, ival ) ) {
1798
0
            return XML_SUCCESS;
1799
0
        }
1800
0
        return XML_CAN_NOT_CONVERT_TEXT;
1801
0
    }
1802
0
    return XML_NO_TEXT_NODE;
1803
0
}
1804
1805
1806
XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
1807
0
{
1808
0
    if ( FirstChild() && FirstChild()->ToText() ) {
1809
0
        const char* t = FirstChild()->Value();
1810
0
        if ( XMLUtil::ToUnsigned( t, uval ) ) {
1811
0
            return XML_SUCCESS;
1812
0
        }
1813
0
        return XML_CAN_NOT_CONVERT_TEXT;
1814
0
    }
1815
0
    return XML_NO_TEXT_NODE;
1816
0
}
1817
1818
1819
XMLError XMLElement::QueryInt64Text(int64_t* ival) const
1820
0
{
1821
0
  if (FirstChild() && FirstChild()->ToText()) {
1822
0
    const char* t = FirstChild()->Value();
1823
0
    if (XMLUtil::ToInt64(t, ival)) {
1824
0
      return XML_SUCCESS;
1825
0
    }
1826
0
    return XML_CAN_NOT_CONVERT_TEXT;
1827
0
  }
1828
0
  return XML_NO_TEXT_NODE;
1829
0
}
1830
1831
1832
XMLError XMLElement::QueryUnsigned64Text(uint64_t* uval) const
1833
0
{
1834
0
    if(FirstChild() && FirstChild()->ToText()) {
1835
0
        const char* t = FirstChild()->Value();
1836
0
        if(XMLUtil::ToUnsigned64(t, uval)) {
1837
0
            return XML_SUCCESS;
1838
0
        }
1839
0
        return XML_CAN_NOT_CONVERT_TEXT;
1840
0
    }
1841
0
    return XML_NO_TEXT_NODE;
1842
0
}
1843
1844
1845
XMLError XMLElement::QueryBoolText( bool* bval ) const
1846
0
{
1847
0
    if ( FirstChild() && FirstChild()->ToText() ) {
1848
0
        const char* t = FirstChild()->Value();
1849
0
        if ( XMLUtil::ToBool( t, bval ) ) {
1850
0
            return XML_SUCCESS;
1851
0
        }
1852
0
        return XML_CAN_NOT_CONVERT_TEXT;
1853
0
    }
1854
0
    return XML_NO_TEXT_NODE;
1855
0
}
1856
1857
1858
XMLError XMLElement::QueryDoubleText( double* dval ) const
1859
0
{
1860
0
    if ( FirstChild() && FirstChild()->ToText() ) {
1861
0
        const char* t = FirstChild()->Value();
1862
0
        if ( XMLUtil::ToDouble( t, dval ) ) {
1863
0
            return XML_SUCCESS;
1864
0
        }
1865
0
        return XML_CAN_NOT_CONVERT_TEXT;
1866
0
    }
1867
0
    return XML_NO_TEXT_NODE;
1868
0
}
1869
1870
1871
XMLError XMLElement::QueryFloatText( float* fval ) const
1872
0
{
1873
0
    if ( FirstChild() && FirstChild()->ToText() ) {
1874
0
        const char* t = FirstChild()->Value();
1875
0
        if ( XMLUtil::ToFloat( t, fval ) ) {
1876
0
            return XML_SUCCESS;
1877
0
        }
1878
0
        return XML_CAN_NOT_CONVERT_TEXT;
1879
0
    }
1880
0
    return XML_NO_TEXT_NODE;
1881
0
}
1882
1883
int XMLElement::IntText(int defaultValue) const
1884
0
{
1885
0
  int i = defaultValue;
1886
0
  QueryIntText(&i);
1887
0
  return i;
1888
0
}
1889
1890
unsigned XMLElement::UnsignedText(unsigned defaultValue) const
1891
0
{
1892
0
  unsigned i = defaultValue;
1893
0
  QueryUnsignedText(&i);
1894
0
  return i;
1895
0
}
1896
1897
int64_t XMLElement::Int64Text(int64_t defaultValue) const
1898
0
{
1899
0
  int64_t i = defaultValue;
1900
0
  QueryInt64Text(&i);
1901
0
  return i;
1902
0
}
1903
1904
uint64_t XMLElement::Unsigned64Text(uint64_t defaultValue) const
1905
0
{
1906
0
  uint64_t i = defaultValue;
1907
0
  QueryUnsigned64Text(&i);
1908
0
  return i;
1909
0
}
1910
1911
bool XMLElement::BoolText(bool defaultValue) const
1912
0
{
1913
0
  bool b = defaultValue;
1914
0
  QueryBoolText(&b);
1915
0
  return b;
1916
0
}
1917
1918
double XMLElement::DoubleText(double defaultValue) const
1919
0
{
1920
0
  double d = defaultValue;
1921
0
  QueryDoubleText(&d);
1922
0
  return d;
1923
0
}
1924
1925
float XMLElement::FloatText(float defaultValue) const
1926
0
{
1927
0
  float f = defaultValue;
1928
0
  QueryFloatText(&f);
1929
0
  return f;
1930
0
}
1931
1932
1933
XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1934
0
{
1935
0
    XMLAttribute* last = 0;
1936
0
    XMLAttribute* attrib = 0;
1937
0
    for( attrib = _rootAttribute;
1938
0
            attrib;
1939
0
            last = attrib, attrib = attrib->_next ) {
1940
0
        if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
1941
0
            break;
1942
0
        }
1943
0
    }
1944
0
    if ( !attrib ) {
1945
0
        attrib = CreateAttribute();
1946
0
        TIXMLASSERT( attrib );
1947
0
        if ( last ) {
1948
0
            TIXMLASSERT( last->_next == 0 );
1949
0
            last->_next = attrib;
1950
0
        }
1951
0
        else {
1952
0
            TIXMLASSERT( _rootAttribute == 0 );
1953
0
            _rootAttribute = attrib;
1954
0
        }
1955
0
        attrib->SetName( name );
1956
0
    }
1957
0
    return attrib;
1958
0
}
1959
1960
1961
void XMLElement::DeleteAttribute( const char* name )
1962
0
{
1963
0
    XMLAttribute* prev = 0;
1964
0
    for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
1965
0
        if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1966
0
            if ( prev ) {
1967
0
                prev->_next = a->_next;
1968
0
            }
1969
0
            else {
1970
0
                _rootAttribute = a->_next;
1971
0
            }
1972
0
            DeleteAttribute( a );
1973
0
            break;
1974
0
        }
1975
0
        prev = a;
1976
0
    }
1977
0
}
1978
1979
1980
char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr )
1981
1.44M
{
1982
1.44M
    XMLAttribute* prevAttribute = 0;
1983
1984
    // Read the attributes.
1985
1.63M
    while( p ) {
1986
1.63M
        p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
1987
1.63M
        if ( !(*p) ) {
1988
287
            _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, "XMLElement name=%s", Name() );
1989
287
            return 0;
1990
287
        }
1991
1992
        // attribute.
1993
1.63M
        if (XMLUtil::IsNameStartChar( static_cast<unsigned char>(*p) ) ) {
1994
192k
            XMLAttribute* attrib = CreateAttribute();
1995
192k
            TIXMLASSERT( attrib );
1996
192k
            attrib->_parseLineNum = _document->_parseCurLineNum;
1997
1998
192k
            const int attrLineNum = attrib->_parseLineNum;
1999
2000
192k
            p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr );
2001
192k
            if ( !p || Attribute( attrib->Name() ) ) {
2002
585
                DeleteAttribute( attrib );
2003
585
                _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, attrLineNum, "XMLElement name=%s", Name() );
2004
585
                return 0;
2005
585
            }
2006
            // There is a minor bug here: if the attribute in the source xml
2007
            // document is duplicated, it will not be detected and the
2008
            // attribute will be doubly added. However, tracking the 'prevAttribute'
2009
            // avoids re-scanning the attribute list. Preferring performance for
2010
            // now, may reconsider in the future.
2011
191k
            if ( prevAttribute ) {
2012
171k
                TIXMLASSERT( prevAttribute->_next == 0 );
2013
171k
                prevAttribute->_next = attrib;
2014
171k
            }
2015
20.9k
            else {
2016
20.9k
                TIXMLASSERT( _rootAttribute == 0 );
2017
20.9k
                _rootAttribute = attrib;
2018
20.9k
            }
2019
191k
            prevAttribute = attrib;
2020
191k
        }
2021
        // end of the tag
2022
1.44M
        else if ( *p == '>' ) {
2023
57.7k
            ++p;
2024
57.7k
            break;
2025
57.7k
        }
2026
        // end of the tag
2027
1.38M
        else if ( *p == '/' && *(p+1) == '>' ) {
2028
1.38M
            _closingType = CLOSED;
2029
1.38M
            return p+2; // done; sealed element.
2030
1.38M
        }
2031
35
        else {
2032
35
            _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, 0 );
2033
35
            return 0;
2034
35
        }
2035
1.63M
    }
2036
57.7k
    return p;
2037
1.44M
}
2038
2039
void XMLElement::DeleteAttribute( XMLAttribute* attribute )
2040
192k
{
2041
192k
    if ( attribute == 0 ) {
2042
0
        return;
2043
0
    }
2044
192k
    MemPool* pool = attribute->_memPool;
2045
192k
    attribute->~XMLAttribute();
2046
192k
    pool->Free( attribute );
2047
192k
}
2048
2049
XMLAttribute* XMLElement::CreateAttribute()
2050
192k
{
2051
192k
    TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
2052
192k
    XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
2053
192k
    TIXMLASSERT( attrib );
2054
192k
    attrib->_memPool = &_document->_attributePool;
2055
192k
    attrib->_memPool->SetTracked();
2056
192k
    return attrib;
2057
192k
}
2058
2059
2060
XMLElement* XMLElement::InsertNewChildElement(const char* name)
2061
0
{
2062
0
    XMLElement* node = _document->NewElement(name);
2063
0
    return InsertEndChild(node) ? node : 0;
2064
0
}
2065
2066
XMLComment* XMLElement::InsertNewComment(const char* comment)
2067
0
{
2068
0
    XMLComment* node = _document->NewComment(comment);
2069
0
    return InsertEndChild(node) ? node : 0;
2070
0
}
2071
2072
XMLText* XMLElement::InsertNewText(const char* text)
2073
0
{
2074
0
    XMLText* node = _document->NewText(text);
2075
0
    return InsertEndChild(node) ? node : 0;
2076
0
}
2077
2078
XMLDeclaration* XMLElement::InsertNewDeclaration(const char* text)
2079
0
{
2080
0
    XMLDeclaration* node = _document->NewDeclaration(text);
2081
0
    return InsertEndChild(node) ? node : 0;
2082
0
}
2083
2084
XMLUnknown* XMLElement::InsertNewUnknown(const char* text)
2085
0
{
2086
0
    XMLUnknown* node = _document->NewUnknown(text);
2087
0
    return InsertEndChild(node) ? node : 0;
2088
0
}
2089
2090
2091
2092
//
2093
//  <ele></ele>
2094
//  <ele>foo<b>bar</b></ele>
2095
//
2096
char* XMLElement::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )
2097
1.44M
{
2098
    // Read the element name.
2099
1.44M
    p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
2100
2101
    // The closing element is the </element> form. It is
2102
    // parsed just like a regular element then deleted from
2103
    // the DOM.
2104
1.44M
    if ( *p == '/' ) {
2105
2.49k
        _closingType = CLOSING;
2106
2.49k
        ++p;
2107
2.49k
    }
2108
2109
1.44M
    p = _value.ParseName( p );
2110
1.44M
    if ( _value.Empty() ) {
2111
187
        return 0;
2112
187
    }
2113
2114
1.44M
    p = ParseAttributes( p, curLineNumPtr );
2115
1.44M
    if ( !p || !*p || _closingType != OPEN ) {
2116
1.38M
        return p;
2117
1.38M
    }
2118
2119
55.4k
    p = XMLNode::ParseDeep( p, parentEndTag, curLineNumPtr );
2120
55.4k
    return p;
2121
1.44M
}
2122
2123
2124
2125
XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
2126
0
{
2127
0
    if ( !doc ) {
2128
0
        doc = _document;
2129
0
    }
2130
0
    XMLElement* element = doc->NewElement( Value() );         // fixme: this will always allocate memory. Intern?
2131
0
    for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
2132
0
        element->SetAttribute( a->Name(), a->Value() );         // fixme: this will always allocate memory. Intern?
2133
0
    }
2134
0
    return element;
2135
0
}
2136
2137
2138
bool XMLElement::ShallowEqual( const XMLNode* compare ) const
2139
0
{
2140
0
    TIXMLASSERT( compare );
2141
0
    const XMLElement* other = compare->ToElement();
2142
0
    if ( other && XMLUtil::StringEqual( other->Name(), Name() )) {
2143
2144
0
        const XMLAttribute* a=FirstAttribute();
2145
0
        const XMLAttribute* b=other->FirstAttribute();
2146
2147
0
        while ( a && b ) {
2148
0
            if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
2149
0
                return false;
2150
0
            }
2151
0
            a = a->Next();
2152
0
            b = b->Next();
2153
0
        }
2154
0
        if ( a || b ) {
2155
            // different count
2156
0
            return false;
2157
0
        }
2158
0
        return true;
2159
0
    }
2160
0
    return false;
2161
0
}
2162
2163
2164
bool XMLElement::Accept( XMLVisitor* visitor ) const
2165
0
{
2166
0
    TIXMLASSERT( visitor );
2167
0
    if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
2168
0
        for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
2169
0
            if ( !node->Accept( visitor ) ) {
2170
0
                break;
2171
0
            }
2172
0
        }
2173
0
    }
2174
0
    return visitor->VisitExit( *this );
2175
0
}
2176
2177
2178
// --------- XMLDocument ----------- //
2179
2180
// Warning: List must match 'enum XMLError'
2181
const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = {
2182
    "XML_SUCCESS",
2183
    "XML_NO_ATTRIBUTE",
2184
    "XML_WRONG_ATTRIBUTE_TYPE",
2185
    "XML_ERROR_FILE_NOT_FOUND",
2186
    "XML_ERROR_FILE_COULD_NOT_BE_OPENED",
2187
    "XML_ERROR_FILE_READ_ERROR",
2188
    "XML_ERROR_PARSING_ELEMENT",
2189
    "XML_ERROR_PARSING_ATTRIBUTE",
2190
    "XML_ERROR_PARSING_TEXT",
2191
    "XML_ERROR_PARSING_CDATA",
2192
    "XML_ERROR_PARSING_COMMENT",
2193
    "XML_ERROR_PARSING_DECLARATION",
2194
    "XML_ERROR_PARSING_UNKNOWN",
2195
    "XML_ERROR_EMPTY_DOCUMENT",
2196
    "XML_ERROR_MISMATCHED_ELEMENT",
2197
    "XML_ERROR_PARSING",
2198
    "XML_CAN_NOT_CONVERT_TEXT",
2199
    "XML_NO_TEXT_NODE",
2200
  "XML_ELEMENT_DEPTH_EXCEEDED"
2201
};
2202
2203
2204
XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) :
2205
2.11k
    XMLNode( 0 ),
2206
2.11k
    _writeBOM( false ),
2207
2.11k
    _processEntities( processEntities ),
2208
2.11k
    _errorID(XML_SUCCESS),
2209
2.11k
    _whitespaceMode( whitespaceMode ),
2210
2.11k
    _errorStr(),
2211
2.11k
    _errorLineNum( 0 ),
2212
2.11k
    _charBuffer( 0 ),
2213
2.11k
    _parseCurLineNum( 0 ),
2214
2.11k
  _parsingDepth(0),
2215
2.11k
    _unlinked(),
2216
2.11k
    _elementPool(),
2217
2.11k
    _attributePool(),
2218
2.11k
    _textPool(),
2219
2.11k
    _commentPool()
2220
2.11k
{
2221
    // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+)
2222
2.11k
    _document = this;
2223
2.11k
}
2224
2225
2226
XMLDocument::~XMLDocument()
2227
2.11k
{
2228
2.11k
    Clear();
2229
2.11k
}
2230
2231
2232
void XMLDocument::MarkInUse(const XMLNode* const node)
2233
13.0M
{
2234
13.0M
  TIXMLASSERT(node);
2235
13.0M
  TIXMLASSERT(node->_parent == 0);
2236
2237
345M
  for (size_t i = 0; i < _unlinked.Size(); ++i) {
2238
338M
    if (node == _unlinked[i]) {
2239
6.53M
      _unlinked.SwapRemove(i);
2240
6.53M
      break;
2241
6.53M
    }
2242
338M
  }
2243
13.0M
}
2244
2245
void XMLDocument::Clear()
2246
6.33k
{
2247
6.33k
    DeleteChildren();
2248
6.33k
  while( _unlinked.Size()) {
2249
0
    DeleteNode(_unlinked[0]); // Will remove from _unlinked as part of delete.
2250
0
  }
2251
2252
#ifdef TINYXML2_DEBUG
2253
    const bool hadError = Error();
2254
#endif
2255
6.33k
    ClearError();
2256
2257
6.33k
    delete [] _charBuffer;
2258
6.33k
    _charBuffer = 0;
2259
6.33k
  _parsingDepth = 0;
2260
2261
#if 0
2262
    _textPool.Trace( "text" );
2263
    _elementPool.Trace( "element" );
2264
    _commentPool.Trace( "comment" );
2265
    _attributePool.Trace( "attribute" );
2266
#endif
2267
2268
#ifdef TINYXML2_DEBUG
2269
    if ( !hadError ) {
2270
        TIXMLASSERT( _elementPool.CurrentAllocs()   == _elementPool.Untracked() );
2271
        TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
2272
        TIXMLASSERT( _textPool.CurrentAllocs()      == _textPool.Untracked() );
2273
        TIXMLASSERT( _commentPool.CurrentAllocs()   == _commentPool.Untracked() );
2274
    }
2275
#endif
2276
6.33k
}
2277
2278
2279
void XMLDocument::DeepCopy(XMLDocument* target) const
2280
0
{
2281
0
  TIXMLASSERT(target);
2282
0
    if (target == this) {
2283
0
        return; // technically success - a no-op.
2284
0
    }
2285
2286
0
  target->Clear();
2287
0
  for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) {
2288
0
    target->InsertEndChild(node->DeepClone(target));
2289
0
  }
2290
0
}
2291
2292
XMLElement* XMLDocument::NewElement( const char* name )
2293
0
{
2294
0
    XMLElement* ele = CreateUnlinkedNode<XMLElement>( _elementPool );
2295
0
    ele->SetName( name );
2296
0
    return ele;
2297
0
}
2298
2299
2300
XMLComment* XMLDocument::NewComment( const char* str )
2301
0
{
2302
0
    XMLComment* comment = CreateUnlinkedNode<XMLComment>( _commentPool );
2303
0
    comment->SetValue( str );
2304
0
    return comment;
2305
0
}
2306
2307
2308
XMLText* XMLDocument::NewText( const char* str )
2309
0
{
2310
0
    XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
2311
0
    text->SetValue( str );
2312
0
    return text;
2313
0
}
2314
2315
2316
XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
2317
0
{
2318
0
    XMLDeclaration* dec = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
2319
0
    dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
2320
0
    return dec;
2321
0
}
2322
2323
2324
XMLUnknown* XMLDocument::NewUnknown( const char* str )
2325
0
{
2326
0
    XMLUnknown* unk = CreateUnlinkedNode<XMLUnknown>( _commentPool );
2327
0
    unk->SetValue( str );
2328
0
    return unk;
2329
0
}
2330
2331
static FILE* callfopen( const char* filepath, const char* mode )
2332
2.11k
{
2333
2.11k
    TIXMLASSERT( filepath );
2334
2.11k
    TIXMLASSERT( mode );
2335
#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
2336
    FILE* fp = 0;
2337
    const errno_t err = fopen_s( &fp, filepath, mode );
2338
    if ( err ) {
2339
        return 0;
2340
    }
2341
#else
2342
2.11k
    FILE* fp = fopen( filepath, mode );
2343
2.11k
#endif
2344
2.11k
    return fp;
2345
2.11k
}
2346
2347
55.1k
void XMLDocument::DeleteNode( XMLNode* node ) {
2348
55.1k
    TIXMLASSERT( node );
2349
55.1k
    TIXMLASSERT(node->_document == this );
2350
55.1k
    if (node->_parent) {
2351
0
        node->_parent->DeleteChild( node );
2352
0
    }
2353
55.1k
    else {
2354
        // Isn't in the tree.
2355
        // Use the parent delete.
2356
        // Also, we need to mark it tracked: we 'know'
2357
        // it was never used.
2358
55.1k
        node->_memPool->SetTracked();
2359
        // Call the static XMLNode version:
2360
55.1k
        XMLNode::DeleteNode(node);
2361
55.1k
    }
2362
55.1k
}
2363
2364
2365
XMLError XMLDocument::LoadFile( const char* filename )
2366
2.11k
{
2367
2.11k
    if ( !filename ) {
2368
0
        TIXMLASSERT( false );
2369
0
        SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=<null>" );
2370
0
        return _errorID;
2371
0
    }
2372
2373
2.11k
    Clear();
2374
2.11k
    FILE* fp = callfopen( filename, "rb" );
2375
2.11k
    if ( !fp ) {
2376
0
        SetError( XML_ERROR_FILE_NOT_FOUND, 0, "filename=%s", filename );
2377
0
        return _errorID;
2378
0
    }
2379
2.11k
    LoadFile( fp );
2380
2.11k
    fclose( fp );
2381
2.11k
    return _errorID;
2382
2.11k
}
2383
2384
XMLError XMLDocument::LoadFile( FILE* fp )
2385
2.11k
{
2386
2.11k
    Clear();
2387
2388
2.11k
    TIXML_FSEEK( fp, 0, SEEK_SET );
2389
2.11k
    if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) {
2390
0
        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2391
0
        return _errorID;
2392
0
    }
2393
2394
2.11k
    TIXML_FSEEK( fp, 0, SEEK_END );
2395
2396
2.11k
    unsigned long long filelength;
2397
2.11k
    {
2398
2.11k
        const long long fileLengthSigned = TIXML_FTELL( fp );
2399
2.11k
        TIXML_FSEEK( fp, 0, SEEK_SET );
2400
2.11k
        if ( fileLengthSigned == -1L ) {
2401
0
            SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2402
0
            return _errorID;
2403
0
        }
2404
2.11k
        TIXMLASSERT( fileLengthSigned >= 0 );
2405
2.11k
        filelength = static_cast<unsigned long long>(fileLengthSigned);
2406
2.11k
    }
2407
2408
0
    const size_t maxSizeT = static_cast<size_t>(-1);
2409
    // We'll do the comparison as an unsigned long long, because that's guaranteed to be at
2410
    // least 8 bytes, even on a 32-bit platform.
2411
2.11k
    if ( filelength >= static_cast<unsigned long long>(maxSizeT) ) {
2412
        // Cannot handle files which won't fit in buffer together with null terminator
2413
0
        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2414
0
        return _errorID;
2415
0
    }
2416
2417
2.11k
    if ( filelength == 0 ) {
2418
0
        SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2419
0
        return _errorID;
2420
0
    }
2421
2422
2.11k
    const size_t size = static_cast<size_t>(filelength);
2423
2.11k
    TIXMLASSERT( _charBuffer == 0 );
2424
2.11k
    _charBuffer = new char[size+1];
2425
2.11k
    const size_t read = fread( _charBuffer, 1, size, fp );
2426
2.11k
    if ( read != size ) {
2427
0
        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2428
0
        return _errorID;
2429
0
    }
2430
2431
2.11k
    _charBuffer[size] = 0;
2432
2433
2.11k
    Parse();
2434
2.11k
    return _errorID;
2435
2.11k
}
2436
2437
2438
XMLError XMLDocument::SaveFile( const char* filename, bool compact )
2439
0
{
2440
0
    if ( !filename ) {
2441
0
        TIXMLASSERT( false );
2442
0
        SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=<null>" );
2443
0
        return _errorID;
2444
0
    }
2445
2446
0
    FILE* fp = callfopen( filename, "w" );
2447
0
    if ( !fp ) {
2448
0
        SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=%s", filename );
2449
0
        return _errorID;
2450
0
    }
2451
0
    SaveFile(fp, compact);
2452
0
    fclose( fp );
2453
0
    return _errorID;
2454
0
}
2455
2456
2457
XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
2458
0
{
2459
    // Clear any error from the last save, otherwise it will get reported
2460
    // for *this* call.
2461
0
    ClearError();
2462
0
    XMLPrinter stream( fp, compact );
2463
0
    Print( &stream );
2464
0
    return _errorID;
2465
0
}
2466
2467
2468
XMLError XMLDocument::Parse( const char* xml, size_t nBytes )
2469
0
{
2470
0
    Clear();
2471
2472
0
    if ( nBytes == 0 || !xml || !*xml ) {
2473
0
        SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2474
0
        return _errorID;
2475
0
    }
2476
0
    if ( nBytes == static_cast<size_t>(-1) ) {
2477
0
        nBytes = strlen( xml );
2478
0
    }
2479
0
    TIXMLASSERT( _charBuffer == 0 );
2480
0
    _charBuffer = new char[ nBytes+1 ];
2481
0
    memcpy( _charBuffer, xml, nBytes );
2482
0
    _charBuffer[nBytes] = 0;
2483
2484
0
    Parse();
2485
0
    if ( Error() ) {
2486
        // clean up now essentially dangling memory.
2487
        // and the parse fail can put objects in the
2488
        // pools that are dead and inaccessible.
2489
0
        DeleteChildren();
2490
0
        _elementPool.Clear();
2491
0
        _attributePool.Clear();
2492
0
        _textPool.Clear();
2493
0
        _commentPool.Clear();
2494
0
    }
2495
0
    return _errorID;
2496
0
}
2497
2498
2499
void XMLDocument::Print( XMLPrinter* streamer ) const
2500
0
{
2501
0
    if ( streamer ) {
2502
0
        Accept( streamer );
2503
0
    }
2504
0
    else {
2505
0
        XMLPrinter stdoutStreamer( stdout );
2506
0
        Accept( &stdoutStreamer );
2507
0
    }
2508
0
}
2509
2510
2511
6.33k
void XMLDocument::ClearError() {
2512
6.33k
    _errorID = XML_SUCCESS;
2513
6.33k
    _errorLineNum = 0;
2514
6.33k
    _errorStr.Reset();
2515
6.33k
}
2516
2517
2518
void XMLDocument::SetError( XMLError error, int lineNum, const char* format, ... )
2519
2.01k
{
2520
2.01k
    TIXMLASSERT(error >= 0 && error < XML_ERROR_COUNT);
2521
2.01k
    _errorID = error;
2522
2.01k
    _errorLineNum = lineNum;
2523
2.01k
  _errorStr.Reset();
2524
2525
2.01k
    const size_t BUFFER_SIZE = 1000;
2526
2.01k
    char* buffer = new char[BUFFER_SIZE];
2527
2528
2.01k
    TIXMLASSERT(sizeof(error) <= sizeof(int));
2529
2.01k
    TIXML_SNPRINTF(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d",
2530
2.01k
        ErrorIDToName(error), static_cast<int>(error), static_cast<unsigned int>(error), lineNum);
2531
2532
2.01k
  if (format) {
2533
1.14k
    size_t len = strlen(buffer);
2534
1.14k
    TIXML_SNPRINTF(buffer + len, BUFFER_SIZE - len, ": ");
2535
1.14k
    len = strlen(buffer);
2536
2537
1.14k
    va_list va;
2538
1.14k
    va_start(va, format);
2539
1.14k
    TIXML_VSNPRINTF(buffer + len, BUFFER_SIZE - len, format, va);
2540
1.14k
    va_end(va);
2541
1.14k
  }
2542
2.01k
  _errorStr.SetStr(buffer);
2543
2.01k
  delete[] buffer;
2544
2.01k
}
2545
2546
2547
/*static*/ const char* XMLDocument::ErrorIDToName(XMLError errorID)
2548
2.01k
{
2549
2.01k
  TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT );
2550
2.01k
    const char* errorName = _errorNames[errorID];
2551
2.01k
    TIXMLASSERT( errorName && errorName[0] );
2552
2.01k
    return errorName;
2553
2.01k
}
2554
2555
const char* XMLDocument::ErrorStr() const
2556
0
{
2557
0
  return _errorStr.Empty() ? "" : _errorStr.GetStr();
2558
0
}
2559
2560
2561
void XMLDocument::PrintError() const
2562
0
{
2563
0
    printf("%s\n", ErrorStr());
2564
0
}
2565
2566
const char* XMLDocument::ErrorName() const
2567
0
{
2568
0
    return ErrorIDToName(_errorID);
2569
0
}
2570
2571
void XMLDocument::Parse()
2572
2.11k
{
2573
2.11k
    TIXMLASSERT( NoChildren() ); // Clear() must have been called previously
2574
2.11k
    TIXMLASSERT( _charBuffer );
2575
2.11k
    _parseCurLineNum = 1;
2576
2.11k
    _parseLineNum = 1;
2577
2.11k
    char* p = _charBuffer;
2578
2.11k
    p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
2579
2.11k
    p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) );
2580
2.11k
    if ( !*p ) {
2581
24
        SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2582
24
        return;
2583
24
    }
2584
2.08k
    ParseDeep(p, 0, &_parseCurLineNum );
2585
2.08k
}
2586
2587
void XMLDocument::PushDepth()
2588
57.5k
{
2589
57.5k
  _parsingDepth++;
2590
57.5k
  if (_parsingDepth == TINYXML2_MAX_ELEMENT_DEPTH) {
2591
2
    SetError(XML_ELEMENT_DEPTH_EXCEEDED, _parseCurLineNum, "Element nesting is too deep." );
2592
2
  }
2593
57.5k
}
2594
2595
void XMLDocument::PopDepth()
2596
57.5k
{
2597
57.5k
  TIXMLASSERT(_parsingDepth > 0);
2598
57.5k
  --_parsingDepth;
2599
57.5k
}
2600
2601
XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth, EscapeAposCharsInAttributes aposInAttributes ) :
2602
0
    _elementJustOpened( false ),
2603
0
    _stack(),
2604
0
    _firstElement( true ),
2605
0
    _fp( file ),
2606
0
    _depth( depth ),
2607
0
    _textDepth( -1 ),
2608
0
    _processEntities( true ),
2609
0
    _compactMode( compact ),
2610
0
    _buffer()
2611
0
{
2612
0
    for( int i=0; i<ENTITY_RANGE; ++i ) {
2613
0
        _entityFlag[i] = false;
2614
0
        _restrictedEntityFlag[i] = false;
2615
0
    }
2616
0
    for( int i=0; i<NUM_ENTITIES; ++i ) {
2617
0
        const char entityValue = entities[i].value;
2618
0
        if ((aposInAttributes == ESCAPE_APOS_CHARS_IN_ATTRIBUTES) || (entityValue != SINGLE_QUOTE)) {
2619
0
            const unsigned char flagIndex = static_cast<unsigned char>(entityValue);
2620
0
            TIXMLASSERT( flagIndex < ENTITY_RANGE );
2621
0
            _entityFlag[flagIndex] = true;
2622
0
        }
2623
0
    }
2624
0
    _restrictedEntityFlag[static_cast<unsigned char>('&')] = true;
2625
0
    _restrictedEntityFlag[static_cast<unsigned char>('<')] = true;
2626
0
    _restrictedEntityFlag[static_cast<unsigned char>('>')] = true;  // not required, but consistency is nice
2627
0
    _buffer.Push( 0 );
2628
0
}
2629
2630
2631
void XMLPrinter::Print( const char* format, ... )
2632
0
{
2633
0
    va_list     va;
2634
0
    va_start( va, format );
2635
2636
0
    if ( _fp ) {
2637
0
        vfprintf( _fp, format, va );
2638
0
    }
2639
0
    else {
2640
0
        const int len = TIXML_VSCPRINTF( format, va );
2641
        // Close out and re-start the va-args
2642
0
        va_end( va );
2643
0
        TIXMLASSERT( len >= 0 );
2644
0
        va_start( va, format );
2645
0
        TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 );
2646
0
        char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator.
2647
0
    TIXML_VSNPRINTF( p, len+1, format, va );
2648
0
    }
2649
0
    va_end( va );
2650
0
}
2651
2652
2653
void XMLPrinter::Write( const char* data, size_t size )
2654
0
{
2655
0
    if ( _fp ) {
2656
0
        fwrite ( data , sizeof(char), size, _fp);
2657
0
    }
2658
0
    else {
2659
0
        char* p = _buffer.PushArr( size ) - 1;   // back up over the null terminator.
2660
0
        memcpy( p, data, size );
2661
0
        p[size] = 0;
2662
0
    }
2663
0
}
2664
2665
2666
void XMLPrinter::Putc( char ch )
2667
0
{
2668
0
    if ( _fp ) {
2669
0
        fputc ( ch, _fp);
2670
0
    }
2671
0
    else {
2672
0
        char* p = _buffer.PushArr( sizeof(char) ) - 1;   // back up over the null terminator.
2673
0
        p[0] = ch;
2674
0
        p[1] = 0;
2675
0
    }
2676
0
}
2677
2678
2679
void XMLPrinter::PrintSpace( int depth )
2680
0
{
2681
0
    for( int i=0; i<depth; ++i ) {
2682
0
        Write( "    " );
2683
0
    }
2684
0
}
2685
2686
2687
void XMLPrinter::PrintString( const char* p, bool restricted )
2688
0
{
2689
    // Look for runs of bytes between entities to print.
2690
0
    const char* q = p;
2691
2692
0
    if ( _processEntities ) {
2693
0
        const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
2694
0
        while ( *q ) {
2695
0
            TIXMLASSERT( p <= q );
2696
            // Remember, char is sometimes signed. (How many times has that bitten me?)
2697
0
            if ( *q > 0 && *q < ENTITY_RANGE ) {
2698
                // Check for entities. If one is found, flush
2699
                // the stream up until the entity, write the
2700
                // entity, and keep looking.
2701
0
                if ( flag[static_cast<unsigned char>(*q)] ) {
2702
0
                    while ( p < q ) {
2703
0
                        const size_t delta = q - p;
2704
0
                        const int toPrint = ( INT_MAX < delta ) ? INT_MAX : static_cast<int>(delta);
2705
0
                        Write( p, toPrint );
2706
0
                        p += toPrint;
2707
0
                    }
2708
0
                    bool entityPatternPrinted = false;
2709
0
                    for( int i=0; i<NUM_ENTITIES; ++i ) {
2710
0
                        if ( entities[i].value == *q ) {
2711
0
                            Putc( '&' );
2712
0
                            Write( entities[i].pattern, entities[i].length );
2713
0
                            Putc( ';' );
2714
0
                            entityPatternPrinted = true;
2715
0
                            break;
2716
0
                        }
2717
0
                    }
2718
0
                    if ( !entityPatternPrinted ) {
2719
                        // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release
2720
0
                        TIXMLASSERT( false );
2721
0
                    }
2722
0
                    ++p;
2723
0
                }
2724
0
            }
2725
0
            ++q;
2726
0
            TIXMLASSERT( p <= q );
2727
0
        }
2728
        // Flush the remaining string. This will be the entire
2729
        // string if an entity wasn't found.
2730
0
        if ( p < q ) {
2731
0
            const size_t delta = q - p;
2732
0
            const int toPrint = ( INT_MAX < delta ) ? INT_MAX : static_cast<int>(delta);
2733
0
            Write( p, toPrint );
2734
0
        }
2735
0
    }
2736
0
    else {
2737
0
        Write( p );
2738
0
    }
2739
0
}
2740
2741
2742
void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
2743
0
{
2744
0
    if ( writeBOM ) {
2745
0
        static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
2746
0
        Write( reinterpret_cast< const char* >( bom ) );
2747
0
    }
2748
0
    if ( writeDec ) {
2749
0
        PushDeclaration( "xml version=\"1.0\"" );
2750
0
    }
2751
0
}
2752
2753
void XMLPrinter::PrepareForNewNode( bool compactMode )
2754
0
{
2755
0
    SealElementIfJustOpened();
2756
2757
0
    if ( compactMode ) {
2758
0
        return;
2759
0
    }
2760
2761
0
    if ( _firstElement ) {
2762
0
        PrintSpace (_depth);
2763
0
    } else if ( _textDepth < 0) {
2764
0
        Putc( '\n' );
2765
0
        PrintSpace( _depth );
2766
0
    }
2767
2768
0
    _firstElement = false;
2769
0
}
2770
2771
void XMLPrinter::OpenElement( const char* name, bool compactMode )
2772
0
{
2773
0
    PrepareForNewNode( compactMode );
2774
0
    _stack.Push( name );
2775
2776
0
    Write ( "<" );
2777
0
    Write ( name );
2778
2779
0
    _elementJustOpened = true;
2780
0
    ++_depth;
2781
0
}
2782
2783
2784
void XMLPrinter::PushAttribute( const char* name, const char* value )
2785
0
{
2786
0
    TIXMLASSERT( _elementJustOpened );
2787
0
    Putc ( ' ' );
2788
0
    Write( name );
2789
0
    Write( "=\"" );
2790
0
    PrintString( value, false );
2791
0
    Putc ( '\"' );
2792
0
}
2793
2794
2795
void XMLPrinter::PushAttribute( const char* name, int v )
2796
0
{
2797
0
    char buf[BUF_SIZE];
2798
0
    XMLUtil::ToStr( v, buf, BUF_SIZE );
2799
0
    PushAttribute( name, buf );
2800
0
}
2801
2802
2803
void XMLPrinter::PushAttribute( const char* name, unsigned v )
2804
0
{
2805
0
    char buf[BUF_SIZE];
2806
0
    XMLUtil::ToStr( v, buf, BUF_SIZE );
2807
0
    PushAttribute( name, buf );
2808
0
}
2809
2810
2811
void XMLPrinter::PushAttribute(const char* name, int64_t v)
2812
0
{
2813
0
  char buf[BUF_SIZE];
2814
0
  XMLUtil::ToStr(v, buf, BUF_SIZE);
2815
0
  PushAttribute(name, buf);
2816
0
}
2817
2818
2819
void XMLPrinter::PushAttribute(const char* name, uint64_t v)
2820
0
{
2821
0
  char buf[BUF_SIZE];
2822
0
  XMLUtil::ToStr(v, buf, BUF_SIZE);
2823
0
  PushAttribute(name, buf);
2824
0
}
2825
2826
2827
void XMLPrinter::PushAttribute( const char* name, bool v )
2828
0
{
2829
0
    char buf[BUF_SIZE];
2830
0
    XMLUtil::ToStr( v, buf, BUF_SIZE );
2831
0
    PushAttribute( name, buf );
2832
0
}
2833
2834
2835
void XMLPrinter::PushAttribute( const char* name, double v )
2836
0
{
2837
0
    char buf[BUF_SIZE];
2838
0
    XMLUtil::ToStr( v, buf, BUF_SIZE );
2839
0
    PushAttribute( name, buf );
2840
0
}
2841
2842
2843
void XMLPrinter::CloseElement( bool compactMode )
2844
0
{
2845
0
    --_depth;
2846
0
    const char* name = _stack.Pop();
2847
2848
0
    if ( _elementJustOpened ) {
2849
0
        Write( "/>" );
2850
0
    }
2851
0
    else {
2852
0
        if ( _textDepth < 0 && !compactMode) {
2853
0
            Putc( '\n' );
2854
0
            PrintSpace( _depth );
2855
0
        }
2856
0
        Write ( "</" );
2857
0
        Write ( name );
2858
0
        Write ( ">" );
2859
0
    }
2860
2861
0
    if ( _textDepth == _depth ) {
2862
0
        _textDepth = -1;
2863
0
    }
2864
0
    if ( _depth == 0 && !compactMode) {
2865
0
        Putc( '\n' );
2866
0
    }
2867
0
    _elementJustOpened = false;
2868
0
}
2869
2870
2871
void XMLPrinter::SealElementIfJustOpened()
2872
0
{
2873
0
    if ( !_elementJustOpened ) {
2874
0
        return;
2875
0
    }
2876
0
    _elementJustOpened = false;
2877
0
    Putc( '>' );
2878
0
}
2879
2880
2881
void XMLPrinter::PushText( const char* text, bool cdata )
2882
0
{
2883
0
    _textDepth = _depth-1;
2884
2885
0
    SealElementIfJustOpened();
2886
0
    if ( cdata ) {
2887
0
        Write( "<![CDATA[" );
2888
0
        Write( text );
2889
0
        Write( "]]>" );
2890
0
    }
2891
0
    else {
2892
0
        PrintString( text, true );
2893
0
    }
2894
0
}
2895
2896
2897
void XMLPrinter::PushText( int64_t value )
2898
0
{
2899
0
    char buf[BUF_SIZE];
2900
0
    XMLUtil::ToStr( value, buf, BUF_SIZE );
2901
0
    PushText( buf, false );
2902
0
}
2903
2904
2905
void XMLPrinter::PushText( uint64_t value )
2906
0
{
2907
0
  char buf[BUF_SIZE];
2908
0
  XMLUtil::ToStr(value, buf, BUF_SIZE);
2909
0
  PushText(buf, false);
2910
0
}
2911
2912
2913
void XMLPrinter::PushText( int value )
2914
0
{
2915
0
    char buf[BUF_SIZE];
2916
0
    XMLUtil::ToStr( value, buf, BUF_SIZE );
2917
0
    PushText( buf, false );
2918
0
}
2919
2920
2921
void XMLPrinter::PushText( unsigned value )
2922
0
{
2923
0
    char buf[BUF_SIZE];
2924
0
    XMLUtil::ToStr( value, buf, BUF_SIZE );
2925
0
    PushText( buf, false );
2926
0
}
2927
2928
2929
void XMLPrinter::PushText( bool value )
2930
0
{
2931
0
    char buf[BUF_SIZE];
2932
0
    XMLUtil::ToStr( value, buf, BUF_SIZE );
2933
0
    PushText( buf, false );
2934
0
}
2935
2936
2937
void XMLPrinter::PushText( float value )
2938
0
{
2939
0
    char buf[BUF_SIZE];
2940
0
    XMLUtil::ToStr( value, buf, BUF_SIZE );
2941
0
    PushText( buf, false );
2942
0
}
2943
2944
2945
void XMLPrinter::PushText( double value )
2946
0
{
2947
0
    char buf[BUF_SIZE];
2948
0
    XMLUtil::ToStr( value, buf, BUF_SIZE );
2949
0
    PushText( buf, false );
2950
0
}
2951
2952
2953
void XMLPrinter::PushComment( const char* comment )
2954
0
{
2955
0
    PrepareForNewNode( _compactMode );
2956
2957
0
    Write( "<!--" );
2958
0
    Write( comment );
2959
0
    Write( "-->" );
2960
0
}
2961
2962
2963
void XMLPrinter::PushDeclaration( const char* value )
2964
0
{
2965
0
    PrepareForNewNode( _compactMode );
2966
2967
0
    Write( "<?" );
2968
0
    Write( value );
2969
0
    Write( "?>" );
2970
0
}
2971
2972
2973
void XMLPrinter::PushUnknown( const char* value )
2974
0
{
2975
0
    PrepareForNewNode( _compactMode );
2976
2977
0
    Write( "<!" );
2978
0
    Write( value );
2979
0
    Putc( '>' );
2980
0
}
2981
2982
2983
bool XMLPrinter::VisitEnter( const XMLDocument& doc )
2984
0
{
2985
0
    _processEntities = doc.ProcessEntities();
2986
0
    if ( doc.HasBOM() ) {
2987
0
        PushHeader( true, false );
2988
0
    }
2989
0
    return true;
2990
0
}
2991
2992
2993
bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
2994
0
{
2995
0
    const XMLElement* parentElem = 0;
2996
0
    if ( element.Parent() ) {
2997
0
        parentElem = element.Parent()->ToElement();
2998
0
    }
2999
0
    const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode;
3000
0
    OpenElement( element.Name(), compactMode );
3001
0
    while ( attribute ) {
3002
0
        PushAttribute( attribute->Name(), attribute->Value() );
3003
0
        attribute = attribute->Next();
3004
0
    }
3005
0
    return true;
3006
0
}
3007
3008
3009
bool XMLPrinter::VisitExit( const XMLElement& element )
3010
0
{
3011
0
    CloseElement( CompactMode(element) );
3012
0
    return true;
3013
0
}
3014
3015
3016
bool XMLPrinter::Visit( const XMLText& text )
3017
0
{
3018
0
    PushText( text.Value(), text.CData() );
3019
0
    return true;
3020
0
}
3021
3022
3023
bool XMLPrinter::Visit( const XMLComment& comment )
3024
0
{
3025
0
    PushComment( comment.Value() );
3026
0
    return true;
3027
0
}
3028
3029
bool XMLPrinter::Visit( const XMLDeclaration& declaration )
3030
0
{
3031
0
    PushDeclaration( declaration.Value() );
3032
0
    return true;
3033
0
}
3034
3035
3036
bool XMLPrinter::Visit( const XMLUnknown& unknown )
3037
0
{
3038
0
    PushUnknown( unknown.Value() );
3039
0
    return true;
3040
0
}
3041
3042
}   // namespace tinyxml2