Coverage Report

Created: 2026-08-14 06:06

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