corda/sgx-jvm/linux-sgx/external/tinyxml2/tinyxml2.cpp
Chris Rankin 6bb9d0015a Refresh linux-sgx to v1.9+ from upstream. (#42)
commit 2133df2d4ad82119333a003c8f05156998550615
Merge: 0356eb624 f2cf6d18a
Author: yuyuany <yuan.yu@intel.com>
Date:   Wed Sep 13 10:06:33 2017 +0800

    Merge pull request #155 from 01org/revert-151-ocall_exit_enclave

    Revert "Check for crash in sgx_ocall and exit enclave if enclave is crashed"

commit f2cf6d18aa73fdaa916f95b1f3c5ac605a90ff06
Author: yuyuany <yuan.yu@intel.com>
Date:   Thu Sep 7 15:21:45 2017 +0800

    Revert "Check for crash in sgx_ocall and exit enclave if enclave is crashed"

commit 0356eb6249ed93510ca66f057a5ae74dda7b10a2
Merge: 2fe247145 38023b61a
Author: lzha101 <lili.z.zhang@intel.com>
Date:   Wed Sep 6 15:58:16 2017 +0800

    Merge pull request #153 from greglaun/greglaun-typos

    Correct typos in some of the sample apps.

    Signed-off-by: Zhang Lili lili.z.zhang@intel.com

commit 2fe247145943827f04eea8e065a8ad3b0a3d11bd
Merge: df9b03289 2e00f11ed
Author: Li Xun <xun.li@intel.com>
Date:   Wed Sep 6 14:10:42 2017 +0800

    Merge pull request #154 from llly/pull

    Fix build issues with latest OpenSSL and Gcc

commit 2e00f11ed5a2e48445aeabd668105161a55a4502
Author: Vytautas Mickus <vmickus@gmx.com>
Date:   Sat Jul 15 21:00:57 2017 +0300

    Make code compatible with OpenSSL 1.0 and 1.1

commit 95ea2c21a921ceb3851b090f092709c5b08b9b37
Author: Vytautas Mickus <vmickus@gmx.com>
Date:   Sat Jul 15 21:12:03 2017 +0300

    Tell gcc about the fallthroughs (needed because of -Werror in makefiles)

commit 6e0580d110d4c799e0c8aba0cdadee46efe15055
Author: Vytautas Mickus <vmickus@gmx.com>
Date:   Sat Jul 15 21:22:55 2017 +0300

    Use `uname -m` instead of `arch` in installer.

commit 38023b61aaa8f76fbad4391ecafd1342d9a2b4f0
Author: Greg Laun <greg.laun@gmail.com>
Date:   Tue Sep 5 12:33:37 2017 -0700

    Correct typos in some of the sample apps.

    Signed-off-by: Greg Laun <greg.laun@gmail.com>

commit df9b0328986784b603ce0b5af847d15382c795cb
Merge: a2f33d828 a20449f5f
Author: lzha101 <lili.z.zhang@intel.com>
Date:   Tue Sep 5 10:41:22 2017 +0800

    Merge pull request #151 from yuyuany/ocall_exit_enclave

    Check for crash in sgx_ocall and exit enclave if enclave is crashed.

    Signed-off-by: Zhang Lili lili.z.zhang@intel.com

commit a20449f5f85acd4e482c35dd6f8f27f36a3caa60
Author: Yu Yuan <yuan.yu@intel.com>
Date:   Fri Sep 1 22:46:27 2017 +0800

    tRTS check the enclave crash state when doing an OCALL. If the enclave is crashed, tRTS unwind the stack to ECALL, and exit the enclave with SGX_ERROR_ENCLAVE_CRASHED.

    Signed-off-by: Yu Yuan <yuan.yu@intel.com>

commit a2f33d828bc76b49256e9a3128254e3322ef5d52
Merge: aa8e9755a 6979d8180
Author: Andy Zhao <yebin.andy.zhao@intel.com>
Date:   Mon Aug 28 16:30:00 2017 +0800

    Merge pull request #149 from lzha101/add_missing_delete

    Add two missing delete functions

commit 6979d818077c310ec4e75a56937b5aa87b718dae
Author: Zhang Lili <lili.z.zhang@intel.com>
Date:   Mon Aug 28 23:17:47 2017 +0800

    Add two missing delete functions.

    Signed-off-by: Zhang Lili <lili.z.zhang@intel.com>

commit aa8e9755aaecc4c04ed3f3993a1596adb4ba6286
Author: Dionna Glaze <dionnaglaze@google.com>
Date:   Wed Aug 2 10:48:18 2017 -0700

    Move pms pointer validation before its dereference in local_vars definitions.

    Also fix "sgx_status_t" typo in generated comment.

commit 6f0e20cfd992c073aefe76926d28893c51f52445
Author: John Mechalas <john.p.mechalas@intel.com>
Date:   Thu Aug 10 19:00:34 2017 -0700

    Implements most of libsgx_capable for Linux (previously only available in the Windows SDK) (#107)

    * Adds libsgx_capable, which is currently only available in the Windows
    version of the Intel SGX SDK. The Linux implementation of this library
    adds the following functions:

      sgx_status_t sgx_is_capable (int *sgx_capable);
      sgx_status_t sgx_cap_get_status (sgx_device_status_t *sgx_device_status);
      sgx_status_t sgx_cap_enable_device (sgx_device_status_t *sgx_device_status);

    And adds the following error code (also from the Windows version of the
    Intel SGX SDK):

      SGX_ERROR_NO_PRIVILEGE

    Generates both a static and dynamic build of libsgx_capable. This is
    incorporated into the SDK package, not the PSW package, since software
    installers need to check for Intel SGX capability on systems where the PSW
    may not already be installed. This means that installers will either have
    to statically link, or distribute the .so with their installation package.

    ----------------------

    Developer Certificate of Origin
    Version 1.1

    Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
    660 York Street, Suite 102,
    San Francisco, CA 94110 USA

    Everyone is permitted to copy and distribute verbatim copies of this
    license document, but changing it is not allowed.

    Developer's Certificate of Origin 1.1

    By making a contribution to this project, I certify that:

    (a) The contribution was created in whole or in part by me and I
        have the right to submit it under the open source license
        indicated in the file; or

    (b) The contribution is based upon previous work that, to the best
        of my knowledge, is covered under an appropriate open source
        license and I have the right under that license to submit that
        work with modifications, whether created in whole or in part
        by me, under the same open source license (unless I am
        permitted to submit under a different license), as indicated
        in the file; or

    (c) The contribution was provided directly to me by some other
        person who certified (a), (b) or (c) and I have not modified
        it.

    (d) I understand and agree that this project and the contribution
        are public and that a record of the contribution (including all
        personal information I submit with it, including my sign-off) is
        maintained indefinitely and may be redistributed consistent with
        this project or the open source license(s) involved.

    Signed-off-by: John P Mechalas <john.p.mechalas@intel.com>

commit 268b206f15f25671382b95c5650422b335d5c446
Merge: 803250734 136610236
Author: lzha101 <lili.z.zhang@intel.com>
Date:   Fri Jul 28 12:38:05 2017 +0800

    Merge pull request #129 from dingelish/master

    Fix a mis-understood memory alignment in enclave_create().

    Signed-off-by: Zhang Lili <lili.z.zhang@intel.com>

commit 1366102364ce67f8c63eaba9bdae80c08634f07f
Author: Yu Ding <dingyu02@baidu.com>
Date:   Mon Jul 24 20:00:04 2017 -0700

    Fix to a mis-understood in enclave_create(). The memory alignment is mistakenly done twice. Once in urts and once in driver. It is unnecessary. This bug has a great impact on the upper limit of enclave memory.

commit 803250734883037a4a1f69085fb31daa6bd8448b
Author: Li Xun <xun.li@intel.com>
Date:   Tue Jul 25 10:44:47 2017 +0800

    Update server port to 80 (#128)

    Signed-off-by: Li, Xun <xun.li@intel.com>

commit d42cd14bdd01a43c58554a4c73231733426356cd
Author: Jaak Randmets <jaak.ra+github@gmail.com>
Date:   Mon Jul 24 09:04:03 2017 +0300

    Merge pull request #124 from Jaak/master

    Improve const correctness of sgx_fopen and sgx_fimport_auto_key.

    Signed-off-by: Zhang Lili <lili.z.zhang@intel.com>

commit 8383b8c65f49fca017c62eb7efdd97a212829caf
Merge: 0fb9f47e7 768c95174
Author: lzha101 <lili.z.zhang@intel.com>
Date:   Tue Jul 4 09:39:53 2017 +0800

    Merge pull request #120 from aminueza/master

    Fixes sdk and psw version in download_prebuild.sh script

    Zhang Lili <lili.z.zhang@intel.com>

commit 768c95174150e64207b31f5a733bccba25085775
Author: Amanda Souza <amandasouza@quimera.lsd.ufcg.edu.br>
Date:   Mon Jul 3 15:22:45 2017 -0300

    Fixes sdk and psw version in download_prebuild.sh script

    Signed-off-by: Amanda Souza <amandasouza@quimera.lsd.ufcg.edu.br>

commit 0fb9f47e784261369c52c1b49d1484f34409ecaf
Author: Li, Xun <xun.li@intel.com>
Date:   Fri Jun 30 15:51:16 2017 +0800

    Linux 1.9 Open Source Gold release

    Signed-off-by: Li, Xun <xun.li@intel.com>

commit 199911ebaf60d068e4641331a06e71462ba8d324
Merge: 109545891 a2abe4b0d
Author: lzha101 <lili.z.zhang@intel.com>
Date:   Fri Jun 16 12:42:58 2017 +0800

    Merge pull request #115 from danzsmith53/master

    Update README.md

    Signed-off-by: Zhang Lili <lili.z.zhang@intel.com>

commit a2abe4b0d0490112fff7eb9669adf9d0bc866c54
Author: Daniel Smith <daniel.e.smith@intel.com>
Date:   Wed May 31 16:21:21 2017 -0700

    Update README.md

    Updated grammar and style many places throughout file

    Signed-off-by: Daniel Smith <daniel.e.smith@intel.com>

commit 1095458916fafd9e5870403e1d50cd7e1ca379bc
Merge: a4bfecb96 d6acac1d2
Author: Li Xun <xun.li@intel.com>
Date:   Mon Jun 12 09:26:58 2017 +0800

    Merge pull request #113 from mitar/patch-3

    pkg-config is needed for compilation

commit a4bfecb965a7588dc073a295dd842c301bbf075c
Merge: 02b7d490a 6a7ec7a8c
Author: lzha101 <lili.z.zhang@intel.com>
Date:   Thu Jun 8 13:10:03 2017 +0800

    Merge pull request #112 from mitar/patch-2

    Removing sudo

    Signed-off-by: Zhang Lili <lili.z.zhang@intel.com>

commit 6a7ec7a8c14ee2ee8f6977110dae9fe99137064d
Author: Mitar <mitar.github@tnode.com>
Date:   Sun Jun 4 14:18:07 2017 -0700

    Removing sudo.

    The whole script has to be run as root anyway.

    Signed-off-by: Mi Tar <mitar.github@tnode.com>

commit d6acac1d25e67516dc6cb8d1dc7b155416078bb1
Author: Mitar <mitar.github@tnode.com>
Date:   Sun Jun 4 16:47:43 2017 -0700

    pkg-config is needed for compilation.

    See: https://github.com/01org/dynamic-application-loader-host-interface/issues/2#issuecomment-306074645

    Signed-off-by: Mi Tar <mitar.github@tnode.com>

commit 02b7d490aca40af134166221c9b4f332f6ca947a
Author: Li, Xun <xun.li@intel.com>
Date:   Tue May 16 08:40:33 2017 +0800

    fix a aesm_service IPC bug

    aesm_service handles closed client socket incorrectly found in #101

    Signed-off-by: Li, Xun <xun.li@intel.com>

commit f287674fef604f4a6ef8710033f6a091fe644d7a
Merge: ce0f128d2 e7f074381
Author: Li Xun <xun.li@intel.com>
Date:   Fri May 12 16:10:42 2017 +0800

    Merge pull request #91 from sean-jc/docker/merge

    Add support for running the AESM and SGX applications in Docker

commit e7f0743812b065750135220a37d890988633cc64
Author: Sean Christopherson <sean.j.christopherson@intel.com>
Date:   Thu Mar 30 09:26:47 2017 -0700

    Add --no-daemon option to AESM

    Application container runtimes, e.g. Docker and RKT, do not provide a
    program manager, e.g. systemd, inside the container, and so attempting
    to run AESM as a daemon will fail.   Add --no-daemon to allow running
    the AESM in a container as a normal process, i.e. do not call daemon().

    Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>

commit ce0f128d290e116748a78899673709730ac45964
Author: Li, Xun <xun.li@intel.com>
Date:   Mon May 8 09:01:52 2017 +0800

    fix aesm_service compilation with latest openssl

    Signed-off-by: Li, Xun <xun.li@intel.com>

commit fd91609f543ae08991d76d94d28c4ec300bcbc84
Merge: 9e0c32ee5 75336444e
Author: lzha101 <lili.z.zhang@intel.com>
Date:   Wed Apr 19 13:41:12 2017 +0800

    Merge pull request #96 from lzha101/master

    Fix alignment issue for metadata section

    Signed-off-by: Zhang Lili  <lili.z.zhang@intel.com>

commit 75336444e867082159cd7c99bbe0eca57013c806
Author: lzha101 <lili.z.zhang@intel.com>
Date:   Fri Apr 14 16:03:48 2017 +0800

    Update comment

    Signed-off-by: Zhang Lili <lili.z.zhang@intel.com>

commit d455716f9c00265976870d167ee2fae52f354daf
Author: lzha101 <lili.z.zhang@intel.com>
Date:   Fri Apr 14 15:45:41 2017 +0800

    Update ELF parser for metadata alignment

    Signed-off-by: Lili Zhang <lili.z.zhang@intel.com>

commit 2224616cb4675c0225a3e1080059f3360e69f800
Author: lzha101 <lili.z.zhang@intel.com>
Date:   Fri Apr 14 15:43:05 2017 +0800

    Change alignment for metadata section

    Signed-off-by: Lili Zhang <lili.z.zhang@intel.com>

commit 9e0c32ee57f7dbd343cc1d75303e308b58b1fe93
Author: lzha101 <lili.z.zhang@intel.com>
Date:   Thu Apr 13 10:33:35 2017 +0800

    Fix typo flase -> false in urts_internal.cpp

    Fix typo flase -> false in urts_internal.cpp

    Signed-off-by: Zhang Lili <lili.z.zhang@intel.com>

commit 765af6169d80beada705086ec9d5226bbdd96218
Merge: 0cbb1f074 38b3d5f46
Author: lzha101 <lili.z.zhang@intel.com>
Date:   Thu Apr 13 10:30:06 2017 +0800

    Merge pull request #94 from neo9ree/master

    fixed typo flase -> false in psw/urts/linux/urts.cpp:45

    Signed-off-by: Zhang Lili <lili.z.zhang@intel.com>

commit 0cbb1f074d4b805a0492f6a000c5ddd1fda9efd7
Author: Li, Xun <xun.li@intel.com>
Date:   Mon Apr 10 14:24:45 2017 +0800

    Update Linux_SGXEclipsePlugin

    Signed-off-by: Li, Xun <xun.li@intel.com>

commit 38b3d5f4635c617d8181f3b8c1c6aa109a81dc21
Author: Eunchan Kim <eunchan.kim@kaist.ac.kr>
Date:   Thu Apr 6 15:31:02 2017 +0900

    fixed typo flase -> false in psw/urts/linux/urts.cpp:45

    Signed-off-by: Eunchan Kim <eunchan.kim@kaist.ac.kr>

commit 56783b38820f82dcb37f1f48c37a5f705ddf7358
Merge: e5c01189d 885287c13
Author: Li Xun <xun.li@intel.com>
Date:   Wed Apr 5 12:29:14 2017 +0800

    Merge pull request #92 from dcmiddle/fix-readme-pse-install

    fix readme section for PSW install

commit 885287c13df2519ca0318bd908b3827d6e247e17
Author: Dan Middleton <dan.middleton@intel.com>
Date:   Fri Mar 31 22:16:38 2017 -0500

    fix readme section for PSW install

    add missing cmake prerequisite to ubuntu instructions
    correct typos on systemctl

    Signed-off-by: Dan Middleton <dan.middleton@intel.com>

commit 65965c6ab61d103028b8b6a886b57e98ccf7ea9d
Author: Sean Christopherson <sean.j.christopherson@intel.com>
Date:   Tue Feb 7 12:51:44 2017 -0800

    Bind the AESM socket to /var/run/aesmd/aesm.socket

    Exposing sockets to Docker containers is typically done via bind
    mounts, which does not work for abstract sockets since they don't
    exist in the file system.

    Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>

commit e5c01189d318e8c59f576377e231c159ffb71abb
Author: Li, Xun <xun.li@intel.com>
Date:   Thu Mar 30 13:45:20 2017 +0900

    update white list URL

    Signed-off-by: Li, Xun <xun.li@intel.com>

commit 072f4e40c0a5f6b7b27bf77f00872853933bd33a
Author: Li, Xun <xun.li@intel.com>
Date:   Fri Mar 24 11:20:35 2017 +0900

    add missing error code logging in aesm_service

    Signed-off-by: Li, Xun <xun.li@intel.com>

commit c7a3f14de9dbda16fd0fa1edbce1af24b350a678
Author: Li, Xun <xun.li@intel.com>
Date:   Mon Mar 20 14:14:23 2017 +0900

    update instructions in README

    Signed-off-by: Li, Xun <xun.li@intel.com>

commit 525cefa817457d8a412d64677d347ebe64f0f77a
Merge: 1115c195c 157108515
Author: lzha101 <lili.z.zhang@intel.com>
Date:   Fri Mar 17 11:32:05 2017 +0800

    Merge pull request #86 from lzha101/master

    Update README.md

    Signed-off-by: Lili Zhang <lili.z.zhang@intel.com>

commit 157108515551a5b465e7ba5e977df5c3e8df20b9
Author: lzha101 <lili.z.zhang@intel.com>
Date:   Fri Mar 17 11:23:06 2017 +0800

    Update README.md

    Update README.md: fix format, update hardware requiremet, etc.

    Signed-off-by: Lili Zhang <lili.z.zhang@intel.com>

commit 1115c195cd60d5ab2b80c12d07e21663e5aa8030
Author: Li, Xun <xun.li@intel.com>
Date:   Thu Mar 16 15:30:38 2017 +0900

    Linux 1.8 Open Source Gold release

    Signed-off-by: Li, Xun <xun.li@intel.com>

commit 2e379dc79fb58cdf921e36d617e6e4b9d694301c
Merge: 0d8a7d248 24dc91d77
Author: lzha101 <lili.z.zhang@intel.com>
Date:   Mon Feb 27 16:24:11 2017 +0800

    Merge pull request #65 from corda/fix-mallinfo

    Change mallinfo field type to int as per system malloc.h

    Signed-off-by: Lili Zhang <lili.z.zhang@intel.com>

commit 0d8a7d248a8ec6ae08451d78c7588711050d7477
Merge: 2cd3c7309 7e00ad320
Author: lzha101 <lili.z.zhang@intel.com>
Date:   Thu Feb 23 16:30:28 2017 +0800

    Merge pull request #75 from kaimast/master

    Fix simulator under GCC6

    Signed-off-by: Lili Zhang <lili.z.zhang@intel.com>

commit 2cd3c73094637b1fa11740b7b9f51254050a9382
Merge: ddec9f872 171c04e0a
Author: lzha101 <lili.z.zhang@intel.com>
Date:   Thu Feb 23 16:30:04 2017 +0800

    Merge pull request #78 from mitar/patch-1

    Added missing dependencies

    Signed-off-by: Lili Zhang <lili.z.zhang@intel.com>

commit 171c04e0a2d079e75580018f891dd32562963cc4
Author: Mitar <mitar.github@tnode.com>
Date:   Wed Feb 22 17:56:41 2017 -0800

    Added missing dependencies.

    Signed-off-by: Mi Tar <mitar.github@tnode.com>

commit 7e00ad320f7dd2c6070d92bec5bf8f52af8d2af0
Author: Kai Mast <kaimast@cs.cornell.edu>
Date:   Sun Feb 19 20:42:49 2017 -0500

    Fix simulator under GCC6

commit ddec9f8725b597b7b656847b5c7cff1e25cb8018
Merge: dd537b756 75349459a
Author: lzha101 <lili.z.zhang@intel.com>
Date:   Thu Feb 16 13:47:12 2017 +0800

    Merge pull request #71 from sean-jc/master

    Bug fix - mprotect may be called with wrong flags

    Signed-off-by:  lzha101 <lili.z.zhang@intel.com>

commit 75349459a935f2b1a5a794ee8c0a0f45f50c5c1e
Author: Sean Christopherson <sean.j.christopherson@intel.com>
Date:   Mon Feb 6 12:31:30 2017 -0800

    Bug fix - mprotect may be called with wrong flags

    Query si_flags instead of attributes when determining whether a layout
    entry should be readable/writable or not accessible.  The current code
    magically works because all layout entries that should be marked R/W
    will have a non-zero value for attributes since added pages are marked
    with either ADD_EXTEND_PAGE or ADD_EXTEND_PAGE.

    Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>

commit dd537b7562e16a83a1adf863e9e2874d05e2a9f3
Merge: d89c9abfd 91d5e850d
Author: haitaohuang <haitao.huang@intel.com>
Date:   Tue Jan 31 07:55:26 2017 -0600

    Merge pull request #68 from haitaohuang/master

    Clarify versions of the IPP crypto primitives

commit 91d5e850d1fcd1f4ae289639670bd48c67b2c6e1
Author: haitao huang <haitao.huang@intel.com>
Date:   Mon Jan 30 17:17:59 2017 -0600

    Clarify versions of the IPP crypto primitives

    Added statement in README.txt to clarify differences in two versions IPP crypto used for build.

    Signed-off-by: haitao huang <haitao.huang@intel.com>
2017-09-13 14:52:21 +01:00

2540 lines
65 KiB
C++

/*
Original code by Lee Thomason (www.grinninglizard.com)
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#include "tinyxml2.h"
#include <new> // yes, this one new style header, is in the Android SDK.
#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__)
# include <stddef.h>
# include <stdarg.h>
#else
# include <cstddef>
# include <cstdarg>
#endif
#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
// Microsoft Visual Studio, version 2005 and higher. Not WinCE.
/*int _snprintf_s(
char *buffer,
size_t sizeOfBuffer,
size_t count,
const char *format [,
argument] ...
);*/
static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... )
{
va_list va;
va_start( va, format );
int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
va_end( va );
return result;
}
static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va )
{
int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
return result;
}
#define TIXML_VSCPRINTF _vscprintf
#define TIXML_SSCANF sscanf_s
#elif defined _MSC_VER
// Microsoft Visual Studio 2003 and earlier or WinCE
#define TIXML_SNPRINTF _snprintf
#define TIXML_VSNPRINTF _vsnprintf
#define TIXML_SSCANF sscanf
#if (_MSC_VER < 1400 ) && (!defined WINCE)
// Microsoft Visual Studio 2003 and not WinCE.
#define TIXML_VSCPRINTF _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have.
#else
// Microsoft Visual Studio 2003 and earlier or WinCE.
static inline int TIXML_VSCPRINTF( const char* format, va_list va )
{
int len = 512;
for (;;) {
len = len*2;
char* str = new char[len]();
const int required = _vsnprintf(str, len, format, va);
delete[] str;
if ( required != -1 ) {
TIXMLASSERT( required >= 0 );
len = required;
break;
}
}
TIXMLASSERT( len >= 0 );
return len;
}
#endif
#else
// GCC version 3 and higher
//#warning( "Using sn* functions." )
#define TIXML_SNPRINTF snprintf
#define TIXML_VSNPRINTF vsnprintf
static inline int TIXML_VSCPRINTF( const char* format, va_list va )
{
int len = vsnprintf( 0, 0, format, va );
TIXMLASSERT( len >= 0 );
return len;
}
#define TIXML_SSCANF sscanf
#endif
static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
static const char LF = LINE_FEED;
static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
static const char CR = CARRIAGE_RETURN;
static const char SINGLE_QUOTE = '\'';
static const char DOUBLE_QUOTE = '\"';
// Bunch of unicode info at:
// http://www.unicode.org/faq/utf_bom.html
// ef bb bf (Microsoft "lead bytes") - designates UTF-8
static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
namespace tinyxml2
{
struct Entity {
const char* pattern;
int length;
char value;
};
static const int NUM_ENTITIES = 5;
static const Entity entities[NUM_ENTITIES] = {
{ "quot", 4, DOUBLE_QUOTE },
{ "amp", 3, '&' },
{ "apos", 4, SINGLE_QUOTE },
{ "lt", 2, '<' },
{ "gt", 2, '>' }
};
StrPair::~StrPair()
{
Reset();
}
void StrPair::TransferTo( StrPair* other )
{
if ( this == other ) {
return;
}
// This in effect implements the assignment operator by "moving"
// ownership (as in auto_ptr).
TIXMLASSERT( other->_flags == 0 );
TIXMLASSERT( other->_start == 0 );
TIXMLASSERT( other->_end == 0 );
other->Reset();
other->_flags = _flags;
other->_start = _start;
other->_end = _end;
_flags = 0;
_start = 0;
_end = 0;
}
void StrPair::Reset()
{
if ( _flags & NEEDS_DELETE ) {
delete [] _start;
}
_flags = 0;
_start = 0;
_end = 0;
}
void StrPair::SetStr( const char* str, int flags )
{
TIXMLASSERT( str );
Reset();
size_t len = strlen( str );
TIXMLASSERT( _start == 0 );
_start = new char[ len+1 ];
memcpy( _start, str, len+1 );
_end = _start + len;
_flags = flags | NEEDS_DELETE;
}
char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
{
TIXMLASSERT( endTag && *endTag );
char* start = p;
char endChar = *endTag;
size_t length = strlen( endTag );
// Inner loop of text parsing.
while ( *p ) {
if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
Set( start, p, strFlags );
return p + length;
}
++p;
}
return 0;
}
char* StrPair::ParseName( char* p )
{
if ( !p || !(*p) ) {
return 0;
}
if ( !XMLUtil::IsNameStartChar( *p ) ) {
return 0;
}
char* const start = p;
++p;
while ( *p && XMLUtil::IsNameChar( *p ) ) {
++p;
}
Set( start, p, 0 );
return p;
}
void StrPair::CollapseWhitespace()
{
// Adjusting _start would cause undefined behavior on delete[]
TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 );
// Trim leading space.
_start = XMLUtil::SkipWhiteSpace( _start );
if ( *_start ) {
char* p = _start; // the read pointer
char* q = _start; // the write pointer
while( *p ) {
if ( XMLUtil::IsWhiteSpace( *p )) {
p = XMLUtil::SkipWhiteSpace( p );
if ( *p == 0 ) {
break; // don't write to q; this trims the trailing space.
}
*q = ' ';
++q;
}
*q = *p;
++q;
++p;
}
*q = 0;
}
}
const char* StrPair::GetStr()
{
TIXMLASSERT( _start );
TIXMLASSERT( _end );
if ( _flags & NEEDS_FLUSH ) {
*_end = 0;
_flags ^= NEEDS_FLUSH;
if ( _flags ) {
char* p = _start; // the read pointer
char* q = _start; // the write pointer
while( p < _end ) {
if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
// CR-LF pair becomes LF
// CR alone becomes LF
// LF-CR becomes LF
if ( *(p+1) == LF ) {
p += 2;
}
else {
++p;
}
*q++ = LF;
}
else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
if ( *(p+1) == CR ) {
p += 2;
}
else {
++p;
}
*q++ = LF;
}
else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
// Entities handled by tinyXML2:
// - special entities in the entity table [in/out]
// - numeric character reference [in]
// &#20013; or &#x4e2d;
if ( *(p+1) == '#' ) {
const int buflen = 10;
char buf[buflen] = { 0 };
int len = 0;
char* adjusted = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
if ( adjusted == 0 ) {
*q = *p;
++p;
++q;
}
else {
TIXMLASSERT( 0 <= len && len <= buflen );
TIXMLASSERT( q + len <= adjusted );
p = adjusted;
memcpy( q, buf, len );
q += len;
}
}
else {
bool entityFound = false;
for( int i = 0; i < NUM_ENTITIES; ++i ) {
const Entity& entity = entities[i];
if ( strncmp( p + 1, entity.pattern, entity.length ) == 0
&& *( p + entity.length + 1 ) == ';' ) {
// Found an entity - convert.
*q = entity.value;
++q;
p += entity.length + 2;
entityFound = true;
break;
}
}
if ( !entityFound ) {
// fixme: treat as error?
++p;
++q;
}
}
}
else {
*q = *p;
++p;
++q;
}
}
*q = 0;
}
// The loop below has plenty going on, and this
// is a less useful mode. Break it out.
if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) {
CollapseWhitespace();
}
_flags = (_flags & NEEDS_DELETE);
}
TIXMLASSERT( _start );
return _start;
}
// --------- XMLUtil ----------- //
const char* XMLUtil::ReadBOM( const char* p, bool* bom )
{
TIXMLASSERT( p );
TIXMLASSERT( bom );
*bom = false;
const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
// Check for BOM:
if ( *(pu+0) == TIXML_UTF_LEAD_0
&& *(pu+1) == TIXML_UTF_LEAD_1
&& *(pu+2) == TIXML_UTF_LEAD_2 ) {
*bom = true;
p += 3;
}
TIXMLASSERT( p );
return p;
}
void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
{
const unsigned long BYTE_MASK = 0xBF;
const unsigned long BYTE_MARK = 0x80;
const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
if (input < 0x80) {
*length = 1;
}
else if ( input < 0x800 ) {
*length = 2;
}
else if ( input < 0x10000 ) {
*length = 3;
}
else if ( input < 0x200000 ) {
*length = 4;
}
else {
*length = 0; // This code won't convert this correctly anyway.
return;
}
output += *length;
// Scary scary fall throughs.
switch (*length) {
case 4:
--output;
*output = (char)((input | BYTE_MARK) & BYTE_MASK);
input >>= 6;
/* fallthrough */
case 3:
--output;
*output = (char)((input | BYTE_MARK) & BYTE_MASK);
input >>= 6;
/* fallthrough */
case 2:
--output;
*output = (char)((input | BYTE_MARK) & BYTE_MASK);
input >>= 6;
/* fallthrough */
case 1:
--output;
*output = (char)(input | FIRST_BYTE_MARK[*length]);
break;
default:
TIXMLASSERT( false );
}
}
const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
{
// Presume an entity, and pull it out.
*length = 0;
if ( *(p+1) == '#' && *(p+2) ) {
unsigned long ucs = 0;
TIXMLASSERT( sizeof( ucs ) >= 4 );
ptrdiff_t delta = 0;
unsigned mult = 1;
static const char SEMICOLON = ';';
if ( *(p+2) == 'x' ) {
// Hexadecimal.
const char* q = p+3;
if ( !(*q) ) {
return 0;
}
q = strchr( q, SEMICOLON );
if ( !q ) {
return 0;
}
TIXMLASSERT( *q == SEMICOLON );
delta = q-p;
--q;
while ( *q != 'x' ) {
unsigned int digit = 0;
if ( *q >= '0' && *q <= '9' ) {
digit = *q - '0';
}
else if ( *q >= 'a' && *q <= 'f' ) {
digit = *q - 'a' + 10;
}
else if ( *q >= 'A' && *q <= 'F' ) {
digit = *q - 'A' + 10;
}
else {
return 0;
}
TIXMLASSERT( digit < 16 );
TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
const unsigned int digitScaled = mult * digit;
TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
ucs += digitScaled;
TIXMLASSERT( mult <= UINT_MAX / 16 );
mult *= 16;
--q;
}
}
else {
// Decimal.
const char* q = p+2;
if ( !(*q) ) {
return 0;
}
q = strchr( q, SEMICOLON );
if ( !q ) {
return 0;
}
TIXMLASSERT( *q == SEMICOLON );
delta = q-p;
--q;
while ( *q != '#' ) {
if ( *q >= '0' && *q <= '9' ) {
const unsigned int digit = *q - '0';
TIXMLASSERT( digit < 10 );
TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
const unsigned int digitScaled = mult * digit;
TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
ucs += digitScaled;
}
else {
return 0;
}
TIXMLASSERT( mult <= UINT_MAX / 10 );
mult *= 10;
--q;
}
}
// convert the UCS to UTF-8
ConvertUTF32ToUTF8( ucs, value, length );
return p + delta + 1;
}
return p+1;
}
void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
{
TIXML_SNPRINTF( buffer, bufferSize, "%d", v );
}
void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize )
{
TIXML_SNPRINTF( buffer, bufferSize, "%u", v );
}
void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )
{
TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 );
}
/*
ToStr() of a number is a very tricky topic.
https://github.com/leethomason/tinyxml2/issues/106
*/
void XMLUtil::ToStr( float v, char* buffer, int bufferSize )
{
TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v );
}
void XMLUtil::ToStr( double v, char* buffer, int bufferSize )
{
TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v );
}
void XMLUtil::ToStr(int64_t v, char* buffer, int bufferSize)
{
// horrible syntax trick to make the compiler happy about %lld
TIXML_SNPRINTF(buffer, bufferSize, "%lld", (long long)v);
}
bool XMLUtil::ToInt( const char* str, int* value )
{
if ( TIXML_SSCANF( str, "%d", value ) == 1 ) {
return true;
}
return false;
}
bool XMLUtil::ToUnsigned( const char* str, unsigned *value )
{
if ( TIXML_SSCANF( str, "%u", value ) == 1 ) {
return true;
}
return false;
}
bool XMLUtil::ToBool( const char* str, bool* value )
{
int ival = 0;
if ( ToInt( str, &ival )) {
*value = (ival==0) ? false : true;
return true;
}
if ( StringEqual( str, "true" ) ) {
*value = true;
return true;
}
else if ( StringEqual( str, "false" ) ) {
*value = false;
return true;
}
return false;
}
bool XMLUtil::ToFloat( const char* str, float* value )
{
if ( TIXML_SSCANF( str, "%f", value ) == 1 ) {
return true;
}
return false;
}
bool XMLUtil::ToDouble( const char* str, double* value )
{
if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) {
return true;
}
return false;
}
bool XMLUtil::ToInt64(const char* str, int64_t* value)
{
long long v = 0; // horrible syntax trick to make the compiler happy about %lld
if (TIXML_SSCANF(str, "%lld", &v) == 1) {
*value = (int64_t)v;
return true;
}
return false;
}
char* XMLDocument::Identify( char* p, XMLNode** node )
{
TIXMLASSERT( node );
TIXMLASSERT( p );
char* const start = p;
p = XMLUtil::SkipWhiteSpace( p );
if( !*p ) {
*node = 0;
TIXMLASSERT( p );
return p;
}
// These strings define the matching patterns:
static const char* xmlHeader = { "<?" };
static const char* commentHeader = { "<!--" };
static const char* cdataHeader = { "<![CDATA[" };
static const char* dtdHeader = { "<!" };
static const char* elementHeader = { "<" }; // and a header for everything else; check last.
static const int xmlHeaderLen = 2;
static const int commentHeaderLen = 4;
static const int cdataHeaderLen = 9;
static const int dtdHeaderLen = 2;
static const int elementHeaderLen = 1;
TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
XMLNode* returnNode = 0;
if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() );
returnNode = new (_commentPool.Alloc()) XMLDeclaration( this );
returnNode->_memPool = &_commentPool;
p += xmlHeaderLen;
}
else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() );
returnNode = new (_commentPool.Alloc()) XMLComment( this );
returnNode->_memPool = &_commentPool;
p += commentHeaderLen;
}
else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );
XMLText* text = new (_textPool.Alloc()) XMLText( this );
returnNode = text;
returnNode->_memPool = &_textPool;
p += cdataHeaderLen;
text->SetCData( true );
}
else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() );
returnNode = new (_commentPool.Alloc()) XMLUnknown( this );
returnNode->_memPool = &_commentPool;
p += dtdHeaderLen;
}
else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() );
returnNode = new (_elementPool.Alloc()) XMLElement( this );
returnNode->_memPool = &_elementPool;
p += elementHeaderLen;
}
else {
TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );
returnNode = new (_textPool.Alloc()) XMLText( this );
returnNode->_memPool = &_textPool;
p = start; // Back it up, all the text counts.
}
TIXMLASSERT( returnNode );
TIXMLASSERT( p );
*node = returnNode;
return p;
}
bool XMLDocument::Accept( XMLVisitor* visitor ) const
{
TIXMLASSERT( visitor );
if ( visitor->VisitEnter( *this ) ) {
for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
if ( !node->Accept( visitor ) ) {
break;
}
}
}
return visitor->VisitExit( *this );
}
// --------- XMLNode ----------- //
XMLNode::XMLNode( XMLDocument* doc ) :
_document( doc ),
_parent( 0 ),
_firstChild( 0 ), _lastChild( 0 ),
_prev( 0 ), _next( 0 ),
_userData( 0 ),
_memPool( 0 )
{
}
XMLNode::~XMLNode()
{
DeleteChildren();
if ( _parent ) {
_parent->Unlink( this );
}
}
const char* XMLNode::Value() const
{
// Catch an edge case: XMLDocuments don't have a a Value. Carefully return nullptr.
if ( this->ToDocument() )
return 0;
return _value.GetStr();
}
void XMLNode::SetValue( const char* str, bool staticMem )
{
if ( staticMem ) {
_value.SetInternedStr( str );
}
else {
_value.SetStr( str );
}
}
void XMLNode::DeleteChildren()
{
while( _firstChild ) {
TIXMLASSERT( _lastChild );
TIXMLASSERT( _firstChild->_document == _document );
XMLNode* node = _firstChild;
Unlink( node );
DeleteNode( node );
}
_firstChild = _lastChild = 0;
}
void XMLNode::Unlink( XMLNode* child )
{
TIXMLASSERT( child );
TIXMLASSERT( child->_document == _document );
TIXMLASSERT( child->_parent == this );
if ( child == _firstChild ) {
_firstChild = _firstChild->_next;
}
if ( child == _lastChild ) {
_lastChild = _lastChild->_prev;
}
if ( child->_prev ) {
child->_prev->_next = child->_next;
}
if ( child->_next ) {
child->_next->_prev = child->_prev;
}
child->_parent = 0;
}
void XMLNode::DeleteChild( XMLNode* node )
{
TIXMLASSERT( node );
TIXMLASSERT( node->_document == _document );
TIXMLASSERT( node->_parent == this );
Unlink( node );
DeleteNode( node );
}
XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
{
TIXMLASSERT( addThis );
if ( addThis->_document != _document ) {
TIXMLASSERT( false );
return 0;
}
InsertChildPreamble( addThis );
if ( _lastChild ) {
TIXMLASSERT( _firstChild );
TIXMLASSERT( _lastChild->_next == 0 );
_lastChild->_next = addThis;
addThis->_prev = _lastChild;
_lastChild = addThis;
addThis->_next = 0;
}
else {
TIXMLASSERT( _firstChild == 0 );
_firstChild = _lastChild = addThis;
addThis->_prev = 0;
addThis->_next = 0;
}
addThis->_parent = this;
return addThis;
}
XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
{
TIXMLASSERT( addThis );
if ( addThis->_document != _document ) {
TIXMLASSERT( false );
return 0;
}
InsertChildPreamble( addThis );
if ( _firstChild ) {
TIXMLASSERT( _lastChild );
TIXMLASSERT( _firstChild->_prev == 0 );
_firstChild->_prev = addThis;
addThis->_next = _firstChild;
_firstChild = addThis;
addThis->_prev = 0;
}
else {
TIXMLASSERT( _lastChild == 0 );
_firstChild = _lastChild = addThis;
addThis->_prev = 0;
addThis->_next = 0;
}
addThis->_parent = this;
return addThis;
}
XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
{
TIXMLASSERT( addThis );
if ( addThis->_document != _document ) {
TIXMLASSERT( false );
return 0;
}
TIXMLASSERT( afterThis );
if ( afterThis->_parent != this ) {
TIXMLASSERT( false );
return 0;
}
if ( afterThis->_next == 0 ) {
// The last node or the only node.
return InsertEndChild( addThis );
}
InsertChildPreamble( addThis );
addThis->_prev = afterThis;
addThis->_next = afterThis->_next;
afterThis->_next->_prev = addThis;
afterThis->_next = addThis;
addThis->_parent = this;
return addThis;
}
const XMLElement* XMLNode::FirstChildElement( const char* name ) const
{
for( const XMLNode* node = _firstChild; node; node = node->_next ) {
const XMLElement* element = node->ToElement();
if ( element ) {
if ( !name || XMLUtil::StringEqual( element->Name(), name ) ) {
return element;
}
}
}
return 0;
}
const XMLElement* XMLNode::LastChildElement( const char* name ) const
{
for( const XMLNode* node = _lastChild; node; node = node->_prev ) {
const XMLElement* element = node->ToElement();
if ( element ) {
if ( !name || XMLUtil::StringEqual( element->Name(), name ) ) {
return element;
}
}
}
return 0;
}
const XMLElement* XMLNode::NextSiblingElement( const char* name ) const
{
for( const XMLNode* node = _next; node; node = node->_next ) {
const XMLElement* element = node->ToElement();
if ( element
&& (!name || XMLUtil::StringEqual( name, element->Name() ))) {
return element;
}
}
return 0;
}
const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const
{
for( const XMLNode* node = _prev; node; node = node->_prev ) {
const XMLElement* element = node->ToElement();
if ( element
&& (!name || XMLUtil::StringEqual( name, element->Name() ))) {
return element;
}
}
return 0;
}
char* XMLNode::ParseDeep( char* p, StrPair* parentEnd )
{
// This is a recursive method, but thinking about it "at the current level"
// it is a pretty simple flat list:
// <foo/>
// <!-- comment -->
//
// With a special case:
// <foo>
// </foo>
// <!-- comment -->
//
// Where the closing element (/foo) *must* be the next thing after the opening
// element, and the names must match. BUT the tricky bit is that the closing
// element will be read by the child.
//
// 'endTag' is the end tag for this node, it is returned by a call to a child.
// 'parentEnd' is the end tag for the parent, which is filled in and returned.
while( p && *p ) {
XMLNode* node = 0;
p = _document->Identify( p, &node );
if ( node == 0 ) {
break;
}
StrPair endTag;
p = node->ParseDeep( p, &endTag );
if ( !p ) {
DeleteNode( node );
if ( !_document->Error() ) {
_document->SetError( XML_ERROR_PARSING, 0, 0 );
}
break;
}
XMLDeclaration* decl = node->ToDeclaration();
if ( decl ) {
// A declaration can only be the first child of a document.
// Set error, if document already has children.
if ( !_document->NoChildren() ) {
_document->SetError( XML_ERROR_PARSING_DECLARATION, decl->Value(), 0);
DeleteNode( decl );
break;
}
}
XMLElement* ele = node->ToElement();
if ( ele ) {
// We read the end tag. Return it to the parent.
if ( ele->ClosingType() == XMLElement::CLOSING ) {
if ( parentEnd ) {
ele->_value.TransferTo( parentEnd );
}
node->_memPool->SetTracked(); // created and then immediately deleted.
DeleteNode( node );
return p;
}
// Handle an end tag returned to this level.
// And handle a bunch of annoying errors.
bool mismatch = false;
if ( endTag.Empty() ) {
if ( ele->ClosingType() == XMLElement::OPEN ) {
mismatch = true;
}
}
else {
if ( ele->ClosingType() != XMLElement::OPEN ) {
mismatch = true;
}
else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) {
mismatch = true;
}
}
if ( mismatch ) {
_document->SetError( XML_ERROR_MISMATCHED_ELEMENT, ele->Name(), 0 );
DeleteNode( node );
break;
}
}
InsertEndChild( node );
}
return 0;
}
void XMLNode::DeleteNode( XMLNode* node )
{
if ( node == 0 ) {
return;
}
MemPool* pool = node->_memPool;
node->~XMLNode();
pool->Free( node );
}
void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const
{
TIXMLASSERT( insertThis );
TIXMLASSERT( insertThis->_document == _document );
if ( insertThis->_parent )
insertThis->_parent->Unlink( insertThis );
else
insertThis->_memPool->SetTracked();
}
// --------- XMLText ---------- //
char* XMLText::ParseDeep( char* p, StrPair* )
{
const char* start = p;
if ( this->CData() ) {
p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
if ( !p ) {
_document->SetError( XML_ERROR_PARSING_CDATA, start, 0 );
}
return p;
}
else {
int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING;
}
p = _value.ParseText( p, "<", flags );
if ( p && *p ) {
return p-1;
}
if ( !p ) {
_document->SetError( XML_ERROR_PARSING_TEXT, start, 0 );
}
}
return 0;
}
XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
{
if ( !doc ) {
doc = _document;
}
XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
text->SetCData( this->CData() );
return text;
}
bool XMLText::ShallowEqual( const XMLNode* compare ) const
{
const XMLText* text = compare->ToText();
return ( text && XMLUtil::StringEqual( text->Value(), Value() ) );
}
bool XMLText::Accept( XMLVisitor* visitor ) const
{
TIXMLASSERT( visitor );
return visitor->Visit( *this );
}
// --------- XMLComment ---------- //
XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
{
}
XMLComment::~XMLComment()
{
}
char* XMLComment::ParseDeep( char* p, StrPair* )
{
// Comment parses as text.
const char* start = p;
p = _value.ParseText( p, "-->", StrPair::COMMENT );
if ( p == 0 ) {
_document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 );
}
return p;
}
XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
{
if ( !doc ) {
doc = _document;
}
XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
return comment;
}
bool XMLComment::ShallowEqual( const XMLNode* compare ) const
{
TIXMLASSERT( compare );
const XMLComment* comment = compare->ToComment();
return ( comment && XMLUtil::StringEqual( comment->Value(), Value() ));
}
bool XMLComment::Accept( XMLVisitor* visitor ) const
{
TIXMLASSERT( visitor );
return visitor->Visit( *this );
}
// --------- XMLDeclaration ---------- //
XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
{
}
XMLDeclaration::~XMLDeclaration()
{
//printf( "~XMLDeclaration\n" );
}
char* XMLDeclaration::ParseDeep( char* p, StrPair* )
{
// Declaration parses as text.
const char* start = p;
p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
if ( p == 0 ) {
_document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 );
}
return p;
}
XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
{
if ( !doc ) {
doc = _document;
}
XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
return dec;
}
bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
{
TIXMLASSERT( compare );
const XMLDeclaration* declaration = compare->ToDeclaration();
return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() ));
}
bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
{
TIXMLASSERT( visitor );
return visitor->Visit( *this );
}
// --------- XMLUnknown ---------- //
XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
{
}
XMLUnknown::~XMLUnknown()
{
}
char* XMLUnknown::ParseDeep( char* p, StrPair* )
{
// Unknown parses as text.
const char* start = p;
p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
if ( !p ) {
_document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 );
}
return p;
}
XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
{
if ( !doc ) {
doc = _document;
}
XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
return text;
}
bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
{
TIXMLASSERT( compare );
const XMLUnknown* unknown = compare->ToUnknown();
return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() ));
}
bool XMLUnknown::Accept( XMLVisitor* visitor ) const
{
TIXMLASSERT( visitor );
return visitor->Visit( *this );
}
// --------- XMLAttribute ---------- //
const char* XMLAttribute::Name() const
{
return _name.GetStr();
}
const char* XMLAttribute::Value() const
{
return _value.GetStr();
}
char* XMLAttribute::ParseDeep( char* p, bool processEntities )
{
// Parse using the name rules: bug fix, was using ParseText before
p = _name.ParseName( p );
if ( !p || !*p ) {
return 0;
}
// Skip white space before =
p = XMLUtil::SkipWhiteSpace( p );
if ( *p != '=' ) {
return 0;
}
++p; // move up to opening quote
p = XMLUtil::SkipWhiteSpace( p );
if ( *p != '\"' && *p != '\'' ) {
return 0;
}
char endTag[2] = { *p, 0 };
++p; // move past opening quote
p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES );
return p;
}
void XMLAttribute::SetName( const char* n )
{
_name.SetStr( n );
}
XMLError XMLAttribute::QueryIntValue( int* value ) const
{
if ( XMLUtil::ToInt( Value(), value )) {
return XML_SUCCESS;
}
return XML_WRONG_ATTRIBUTE_TYPE;
}
XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
{
if ( XMLUtil::ToUnsigned( Value(), value )) {
return XML_SUCCESS;
}
return XML_WRONG_ATTRIBUTE_TYPE;
}
XMLError XMLAttribute::QueryInt64Value(int64_t* value) const
{
if (XMLUtil::ToInt64(Value(), value)) {
return XML_SUCCESS;
}
return XML_WRONG_ATTRIBUTE_TYPE;
}
XMLError XMLAttribute::QueryBoolValue( bool* value ) const
{
if ( XMLUtil::ToBool( Value(), value )) {
return XML_SUCCESS;
}
return XML_WRONG_ATTRIBUTE_TYPE;
}
XMLError XMLAttribute::QueryFloatValue( float* value ) const
{
if ( XMLUtil::ToFloat( Value(), value )) {
return XML_SUCCESS;
}
return XML_WRONG_ATTRIBUTE_TYPE;
}
XMLError XMLAttribute::QueryDoubleValue( double* value ) const
{
if ( XMLUtil::ToDouble( Value(), value )) {
return XML_SUCCESS;
}
return XML_WRONG_ATTRIBUTE_TYPE;
}
void XMLAttribute::SetAttribute( const char* v )
{
_value.SetStr( v );
}
void XMLAttribute::SetAttribute( int v )
{
char buf[BUF_SIZE];
XMLUtil::ToStr( v, buf, BUF_SIZE );
_value.SetStr( buf );
}
void XMLAttribute::SetAttribute( unsigned v )
{
char buf[BUF_SIZE];
XMLUtil::ToStr( v, buf, BUF_SIZE );
_value.SetStr( buf );
}
void XMLAttribute::SetAttribute(int64_t v)
{
char buf[BUF_SIZE];
XMLUtil::ToStr(v, buf, BUF_SIZE);
_value.SetStr(buf);
}
void XMLAttribute::SetAttribute( bool v )
{
char buf[BUF_SIZE];
XMLUtil::ToStr( v, buf, BUF_SIZE );
_value.SetStr( buf );
}
void XMLAttribute::SetAttribute( double v )
{
char buf[BUF_SIZE];
XMLUtil::ToStr( v, buf, BUF_SIZE );
_value.SetStr( buf );
}
void XMLAttribute::SetAttribute( float v )
{
char buf[BUF_SIZE];
XMLUtil::ToStr( v, buf, BUF_SIZE );
_value.SetStr( buf );
}
// --------- XMLElement ---------- //
XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
_closingType( 0 ),
_rootAttribute( 0 )
{
}
XMLElement::~XMLElement()
{
while( _rootAttribute ) {
XMLAttribute* next = _rootAttribute->_next;
DeleteAttribute( _rootAttribute );
_rootAttribute = next;
}
}
const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
{
for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) {
if ( XMLUtil::StringEqual( a->Name(), name ) ) {
return a;
}
}
return 0;
}
const char* XMLElement::Attribute( const char* name, const char* value ) const
{
const XMLAttribute* a = FindAttribute( name );
if ( !a ) {
return 0;
}
if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
return a->Value();
}
return 0;
}
const char* XMLElement::GetText() const
{
if ( FirstChild() && FirstChild()->ToText() ) {
return FirstChild()->Value();
}
return 0;
}
void XMLElement::SetText( const char* inText )
{
if ( FirstChild() && FirstChild()->ToText() )
FirstChild()->SetValue( inText );
else {
XMLText* theText = GetDocument()->NewText( inText );
InsertFirstChild( theText );
}
}
void XMLElement::SetText( int v )
{
char buf[BUF_SIZE];
XMLUtil::ToStr( v, buf, BUF_SIZE );
SetText( buf );
}
void XMLElement::SetText( unsigned v )
{
char buf[BUF_SIZE];
XMLUtil::ToStr( v, buf, BUF_SIZE );
SetText( buf );
}
void XMLElement::SetText(int64_t v)
{
char buf[BUF_SIZE];
XMLUtil::ToStr(v, buf, BUF_SIZE);
SetText(buf);
}
void XMLElement::SetText( bool v )
{
char buf[BUF_SIZE];
XMLUtil::ToStr( v, buf, BUF_SIZE );
SetText( buf );
}
void XMLElement::SetText( float v )
{
char buf[BUF_SIZE];
XMLUtil::ToStr( v, buf, BUF_SIZE );
SetText( buf );
}
void XMLElement::SetText( double v )
{
char buf[BUF_SIZE];
XMLUtil::ToStr( v, buf, BUF_SIZE );
SetText( buf );
}
XMLError XMLElement::QueryIntText( int* ival ) const
{
if ( FirstChild() && FirstChild()->ToText() ) {
const char* t = FirstChild()->Value();
if ( XMLUtil::ToInt( t, ival ) ) {
return XML_SUCCESS;
}
return XML_CAN_NOT_CONVERT_TEXT;
}
return XML_NO_TEXT_NODE;
}
XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
{
if ( FirstChild() && FirstChild()->ToText() ) {
const char* t = FirstChild()->Value();
if ( XMLUtil::ToUnsigned( t, uval ) ) {
return XML_SUCCESS;
}
return XML_CAN_NOT_CONVERT_TEXT;
}
return XML_NO_TEXT_NODE;
}
XMLError XMLElement::QueryInt64Text(int64_t* ival) const
{
if (FirstChild() && FirstChild()->ToText()) {
const char* t = FirstChild()->Value();
if (XMLUtil::ToInt64(t, ival)) {
return XML_SUCCESS;
}
return XML_CAN_NOT_CONVERT_TEXT;
}
return XML_NO_TEXT_NODE;
}
XMLError XMLElement::QueryBoolText( bool* bval ) const
{
if ( FirstChild() && FirstChild()->ToText() ) {
const char* t = FirstChild()->Value();
if ( XMLUtil::ToBool( t, bval ) ) {
return XML_SUCCESS;
}
return XML_CAN_NOT_CONVERT_TEXT;
}
return XML_NO_TEXT_NODE;
}
XMLError XMLElement::QueryDoubleText( double* dval ) const
{
if ( FirstChild() && FirstChild()->ToText() ) {
const char* t = FirstChild()->Value();
if ( XMLUtil::ToDouble( t, dval ) ) {
return XML_SUCCESS;
}
return XML_CAN_NOT_CONVERT_TEXT;
}
return XML_NO_TEXT_NODE;
}
XMLError XMLElement::QueryFloatText( float* fval ) const
{
if ( FirstChild() && FirstChild()->ToText() ) {
const char* t = FirstChild()->Value();
if ( XMLUtil::ToFloat( t, fval ) ) {
return XML_SUCCESS;
}
return XML_CAN_NOT_CONVERT_TEXT;
}
return XML_NO_TEXT_NODE;
}
XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
{
XMLAttribute* last = 0;
XMLAttribute* attrib = 0;
for( attrib = _rootAttribute;
attrib;
last = attrib, attrib = attrib->_next ) {
if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
break;
}
}
if ( !attrib ) {
TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
attrib->_memPool = &_document->_attributePool;
if ( last ) {
last->_next = attrib;
}
else {
_rootAttribute = attrib;
}
attrib->SetName( name );
attrib->_memPool->SetTracked(); // always created and linked.
}
return attrib;
}
void XMLElement::DeleteAttribute( const char* name )
{
XMLAttribute* prev = 0;
for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
if ( XMLUtil::StringEqual( name, a->Name() ) ) {
if ( prev ) {
prev->_next = a->_next;
}
else {
_rootAttribute = a->_next;
}
DeleteAttribute( a );
break;
}
prev = a;
}
}
char* XMLElement::ParseAttributes( char* p )
{
const char* start = p;
XMLAttribute* prevAttribute = 0;
// Read the attributes.
while( p ) {
p = XMLUtil::SkipWhiteSpace( p );
if ( !(*p) ) {
_document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() );
return 0;
}
// attribute.
if (XMLUtil::IsNameStartChar( *p ) ) {
TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
attrib->_memPool = &_document->_attributePool;
attrib->_memPool->SetTracked();
p = attrib->ParseDeep( p, _document->ProcessEntities() );
if ( !p || Attribute( attrib->Name() ) ) {
DeleteAttribute( attrib );
_document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p );
return 0;
}
// There is a minor bug here: if the attribute in the source xml
// document is duplicated, it will not be detected and the
// attribute will be doubly added. However, tracking the 'prevAttribute'
// avoids re-scanning the attribute list. Preferring performance for
// now, may reconsider in the future.
if ( prevAttribute ) {
prevAttribute->_next = attrib;
}
else {
_rootAttribute = attrib;
}
prevAttribute = attrib;
}
// end of the tag
else if ( *p == '>' ) {
++p;
break;
}
// end of the tag
else if ( *p == '/' && *(p+1) == '>' ) {
_closingType = CLOSED;
return p+2; // done; sealed element.
}
else {
_document->SetError( XML_ERROR_PARSING_ELEMENT, start, p );
return 0;
}
}
return p;
}
void XMLElement::DeleteAttribute( XMLAttribute* attribute )
{
if ( attribute == 0 ) {
return;
}
MemPool* pool = attribute->_memPool;
attribute->~XMLAttribute();
pool->Free( attribute );
}
//
// <ele></ele>
// <ele>foo<b>bar</b></ele>
//
char* XMLElement::ParseDeep( char* p, StrPair* strPair )
{
// Read the element name.
p = XMLUtil::SkipWhiteSpace( p );
// The closing element is the </element> form. It is
// parsed just like a regular element then deleted from
// the DOM.
if ( *p == '/' ) {
_closingType = CLOSING;
++p;
}
p = _value.ParseName( p );
if ( _value.Empty() ) {
return 0;
}
p = ParseAttributes( p );
if ( !p || !*p || _closingType ) {
return p;
}
p = XMLNode::ParseDeep( p, strPair );
return p;
}
XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
{
if ( !doc ) {
doc = _document;
}
XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
}
return element;
}
bool XMLElement::ShallowEqual( const XMLNode* compare ) const
{
TIXMLASSERT( compare );
const XMLElement* other = compare->ToElement();
if ( other && XMLUtil::StringEqual( other->Name(), Name() )) {
const XMLAttribute* a=FirstAttribute();
const XMLAttribute* b=other->FirstAttribute();
while ( a && b ) {
if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
return false;
}
a = a->Next();
b = b->Next();
}
if ( a || b ) {
// different count
return false;
}
return true;
}
return false;
}
bool XMLElement::Accept( XMLVisitor* visitor ) const
{
TIXMLASSERT( visitor );
if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
if ( !node->Accept( visitor ) ) {
break;
}
}
}
return visitor->VisitExit( *this );
}
// --------- XMLDocument ----------- //
// Warning: List must match 'enum XMLError'
const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = {
"XML_SUCCESS",
"XML_NO_ATTRIBUTE",
"XML_WRONG_ATTRIBUTE_TYPE",
"XML_ERROR_FILE_NOT_FOUND",
"XML_ERROR_FILE_COULD_NOT_BE_OPENED",
"XML_ERROR_FILE_READ_ERROR",
"XML_ERROR_ELEMENT_MISMATCH",
"XML_ERROR_PARSING_ELEMENT",
"XML_ERROR_PARSING_ATTRIBUTE",
"XML_ERROR_IDENTIFYING_TAG",
"XML_ERROR_PARSING_TEXT",
"XML_ERROR_PARSING_CDATA",
"XML_ERROR_PARSING_COMMENT",
"XML_ERROR_PARSING_DECLARATION",
"XML_ERROR_PARSING_UNKNOWN",
"XML_ERROR_EMPTY_DOCUMENT",
"XML_ERROR_MISMATCHED_ELEMENT",
"XML_ERROR_PARSING",
"XML_CAN_NOT_CONVERT_TEXT",
"XML_NO_TEXT_NODE"
};
XMLDocument::XMLDocument( bool processEntities, Whitespace whitespace ) :
XMLNode( 0 ),
_writeBOM( false ),
_processEntities( processEntities ),
_errorID(XML_SUCCESS),
_whitespace( whitespace ),
_errorStr1( 0 ),
_errorStr2( 0 ),
_charBuffer( 0 )
{
// avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+)
_document = this;
}
XMLDocument::~XMLDocument()
{
Clear();
}
void XMLDocument::Clear()
{
DeleteChildren();
#ifdef DEBUG
const bool hadError = Error();
#endif
_errorID = XML_SUCCESS;
_errorStr1 = 0;
_errorStr2 = 0;
delete [] _charBuffer;
_charBuffer = 0;
#if 0
_textPool.Trace( "text" );
_elementPool.Trace( "element" );
_commentPool.Trace( "comment" );
_attributePool.Trace( "attribute" );
#endif
#ifdef DEBUG
if ( !hadError ) {
TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() );
TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() );
TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() );
}
#endif
}
XMLElement* XMLDocument::NewElement( const char* name )
{
TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() );
XMLElement* ele = new (_elementPool.Alloc()) XMLElement( this );
ele->_memPool = &_elementPool;
ele->SetName( name );
return ele;
}
XMLComment* XMLDocument::NewComment( const char* str )
{
TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() );
XMLComment* comment = new (_commentPool.Alloc()) XMLComment( this );
comment->_memPool = &_commentPool;
comment->SetValue( str );
return comment;
}
XMLText* XMLDocument::NewText( const char* str )
{
TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );
XMLText* text = new (_textPool.Alloc()) XMLText( this );
text->_memPool = &_textPool;
text->SetValue( str );
return text;
}
XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
{
TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() );
XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration( this );
dec->_memPool = &_commentPool;
dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
return dec;
}
XMLUnknown* XMLDocument::NewUnknown( const char* str )
{
TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() );
XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown( this );
unk->_memPool = &_commentPool;
unk->SetValue( str );
return unk;
}
static FILE* callfopen( const char* filepath, const char* mode )
{
TIXMLASSERT( filepath );
TIXMLASSERT( mode );
#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
FILE* fp = 0;
errno_t err = fopen_s( &fp, filepath, mode );
if ( err ) {
return 0;
}
#else
FILE* fp = fopen( filepath, mode );
#endif
return fp;
}
void XMLDocument::DeleteNode( XMLNode* node ) {
TIXMLASSERT( node );
TIXMLASSERT(node->_document == this );
if (node->_parent) {
node->_parent->DeleteChild( node );
}
else {
// Isn't in the tree.
// Use the parent delete.
// Also, we need to mark it tracked: we 'know'
// it was never used.
node->_memPool->SetTracked();
// Call the static XMLNode version:
XMLNode::DeleteNode(node);
}
}
XMLError XMLDocument::LoadFile( const char* filename )
{
Clear();
FILE* fp = callfopen( filename, "rb" );
if ( !fp ) {
SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 );
return _errorID;
}
LoadFile( fp );
fclose( fp );
return _errorID;
}
// This is likely overengineered template art to have a check that unsigned long value incremented
// by one still fits into size_t. If size_t type is larger than unsigned long type
// (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit
// -Wtype-limits warning. This piece makes the compiler select code with a check when a check
// is useful and code with no check when a check is redundant depending on how size_t and unsigned long
// types sizes relate to each other.
template
<bool = (sizeof(unsigned long) >= sizeof(size_t))>
struct LongFitsIntoSizeTMinusOne {
static bool Fits( unsigned long value )
{
return value < (size_t)-1;
}
};
template <>
struct LongFitsIntoSizeTMinusOne<false> {
static bool Fits( unsigned long )
{
return true;
}
};
XMLError XMLDocument::LoadFile( FILE* fp )
{
Clear();
fseek( fp, 0, SEEK_SET );
if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) {
SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
return _errorID;
}
fseek( fp, 0, SEEK_END );
const long filelength = ftell( fp );
fseek( fp, 0, SEEK_SET );
if ( filelength == -1L ) {
SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
return _errorID;
}
TIXMLASSERT( filelength >= 0 );
if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) {
// Cannot handle files which won't fit in buffer together with null terminator
SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
return _errorID;
}
if ( filelength == 0 ) {
SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
return _errorID;
}
const size_t size = filelength;
TIXMLASSERT( _charBuffer == 0 );
_charBuffer = new char[size+1];
size_t read = fread( _charBuffer, 1, size, fp );
if ( read != size ) {
SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
return _errorID;
}
_charBuffer[size] = 0;
Parse();
return _errorID;
}
XMLError XMLDocument::SaveFile( const char* filename, bool compact )
{
FILE* fp = callfopen( filename, "w" );
if ( !fp ) {
SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 );
return _errorID;
}
SaveFile(fp, compact);
fclose( fp );
return _errorID;
}
XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
{
// Clear any error from the last save, otherwise it will get reported
// for *this* call.
SetError(XML_SUCCESS, 0, 0);
XMLPrinter stream( fp, compact );
Print( &stream );
return _errorID;
}
XMLError XMLDocument::Parse( const char* p, size_t len )
{
Clear();
if ( len == 0 || !p || !*p ) {
SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
return _errorID;
}
if ( len == (size_t)(-1) ) {
len = strlen( p );
}
TIXMLASSERT( _charBuffer == 0 );
_charBuffer = new char[ len+1 ];
memcpy( _charBuffer, p, len );
_charBuffer[len] = 0;
Parse();
if ( Error() ) {
// clean up now essentially dangling memory.
// and the parse fail can put objects in the
// pools that are dead and inaccessible.
DeleteChildren();
_elementPool.Clear();
_attributePool.Clear();
_textPool.Clear();
_commentPool.Clear();
}
return _errorID;
}
void XMLDocument::Print( XMLPrinter* streamer ) const
{
if ( streamer ) {
Accept( streamer );
}
else {
XMLPrinter stdoutStreamer( stdout );
Accept( &stdoutStreamer );
}
}
void XMLDocument::SetError( XMLError error, const char* str1, const char* str2 )
{
TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT );
_errorID = error;
_errorStr1 = str1;
_errorStr2 = str2;
}
const char* XMLDocument::ErrorName() const
{
TIXMLASSERT( _errorID >= 0 && _errorID < XML_ERROR_COUNT );
const char* errorName = _errorNames[_errorID];
TIXMLASSERT( errorName && errorName[0] );
return errorName;
}
void XMLDocument::PrintError() const
{
if ( Error() ) {
static const int LEN = 20;
char buf1[LEN] = { 0 };
char buf2[LEN] = { 0 };
if ( _errorStr1 ) {
TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1 );
}
if ( _errorStr2 ) {
TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2 );
}
// Should check INT_MIN <= _errorID && _errorId <= INT_MAX, but that
// causes a clang "always true" -Wtautological-constant-out-of-range-compare warning
TIXMLASSERT( 0 <= _errorID && XML_ERROR_COUNT - 1 <= INT_MAX );
printf( "XMLDocument error id=%d '%s' str1=%s str2=%s\n",
static_cast<int>( _errorID ), ErrorName(), buf1, buf2 );
}
}
void XMLDocument::Parse()
{
TIXMLASSERT( NoChildren() ); // Clear() must have been called previously
TIXMLASSERT( _charBuffer );
char* p = _charBuffer;
p = XMLUtil::SkipWhiteSpace( p );
p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) );
if ( !*p ) {
SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
return;
}
ParseDeep(p, 0 );
}
XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) :
_elementJustOpened( false ),
_firstElement( true ),
_fp( file ),
_depth( depth ),
_textDepth( -1 ),
_processEntities( true ),
_compactMode( compact )
{
for( int i=0; i<ENTITY_RANGE; ++i ) {
_entityFlag[i] = false;
_restrictedEntityFlag[i] = false;
}
for( int i=0; i<NUM_ENTITIES; ++i ) {
const char entityValue = entities[i].value;
TIXMLASSERT( 0 <= entityValue && entityValue < ENTITY_RANGE );
_entityFlag[ (unsigned char)entityValue ] = true;
}
_restrictedEntityFlag[(unsigned char)'&'] = true;
_restrictedEntityFlag[(unsigned char)'<'] = true;
_restrictedEntityFlag[(unsigned char)'>'] = true; // not required, but consistency is nice
_buffer.Push( 0 );
}
void XMLPrinter::Print( const char* format, ... )
{
va_list va;
va_start( va, format );
if ( _fp ) {
vfprintf( _fp, format, va );
}
else {
const int len = TIXML_VSCPRINTF( format, va );
// Close out and re-start the va-args
va_end( va );
TIXMLASSERT( len >= 0 );
va_start( va, format );
TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 );
char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator.
TIXML_VSNPRINTF( p, len+1, format, va );
}
va_end( va );
}
void XMLPrinter::PrintSpace( int depth )
{
for( int i=0; i<depth; ++i ) {
Print( " " );
}
}
void XMLPrinter::PrintString( const char* p, bool restricted )
{
// Look for runs of bytes between entities to print.
const char* q = p;
if ( _processEntities ) {
const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
while ( *q ) {
TIXMLASSERT( p <= q );
// Remember, char is sometimes signed. (How many times has that bitten me?)
if ( *q > 0 && *q < ENTITY_RANGE ) {
// Check for entities. If one is found, flush
// the stream up until the entity, write the
// entity, and keep looking.
if ( flag[(unsigned char)(*q)] ) {
while ( p < q ) {
const size_t delta = q - p;
// %.*s accepts type int as "precision"
const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
Print( "%.*s", toPrint, p );
p += toPrint;
}
bool entityPatternPrinted = false;
for( int i=0; i<NUM_ENTITIES; ++i ) {
if ( entities[i].value == *q ) {
Print( "&%s;", entities[i].pattern );
entityPatternPrinted = true;
break;
}
}
if ( !entityPatternPrinted ) {
// TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release
TIXMLASSERT( false );
}
++p;
}
}
++q;
TIXMLASSERT( p <= q );
}
}
// Flush the remaining string. This will be the entire
// string if an entity wasn't found.
TIXMLASSERT( p <= q );
if ( !_processEntities || ( p < q ) ) {
Print( "%s", p );
}
}
void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
{
if ( writeBOM ) {
static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
Print( "%s", bom );
}
if ( writeDec ) {
PushDeclaration( "xml version=\"1.0\"" );
}
}
void XMLPrinter::OpenElement( const char* name, bool compactMode )
{
SealElementIfJustOpened();
_stack.Push( name );
if ( _textDepth < 0 && !_firstElement && !compactMode ) {
Print( "\n" );
}
if ( !compactMode ) {
PrintSpace( _depth );
}
Print( "<%s", name );
_elementJustOpened = true;
_firstElement = false;
++_depth;
}
void XMLPrinter::PushAttribute( const char* name, const char* value )
{
TIXMLASSERT( _elementJustOpened );
Print( " %s=\"", name );
PrintString( value, false );
Print( "\"" );
}
void XMLPrinter::PushAttribute( const char* name, int v )
{
char buf[BUF_SIZE];
XMLUtil::ToStr( v, buf, BUF_SIZE );
PushAttribute( name, buf );
}
void XMLPrinter::PushAttribute( const char* name, unsigned v )
{
char buf[BUF_SIZE];
XMLUtil::ToStr( v, buf, BUF_SIZE );
PushAttribute( name, buf );
}
void XMLPrinter::PushAttribute(const char* name, int64_t v)
{
char buf[BUF_SIZE];
XMLUtil::ToStr(v, buf, BUF_SIZE);
PushAttribute(name, buf);
}
void XMLPrinter::PushAttribute( const char* name, bool v )
{
char buf[BUF_SIZE];
XMLUtil::ToStr( v, buf, BUF_SIZE );
PushAttribute( name, buf );
}
void XMLPrinter::PushAttribute( const char* name, double v )
{
char buf[BUF_SIZE];
XMLUtil::ToStr( v, buf, BUF_SIZE );
PushAttribute( name, buf );
}
void XMLPrinter::CloseElement( bool compactMode )
{
--_depth;
const char* name = _stack.Pop();
if ( _elementJustOpened ) {
Print( "/>" );
}
else {
if ( _textDepth < 0 && !compactMode) {
Print( "\n" );
PrintSpace( _depth );
}
Print( "</%s>", name );
}
if ( _textDepth == _depth ) {
_textDepth = -1;
}
if ( _depth == 0 && !compactMode) {
Print( "\n" );
}
_elementJustOpened = false;
}
void XMLPrinter::SealElementIfJustOpened()
{
if ( !_elementJustOpened ) {
return;
}
_elementJustOpened = false;
Print( ">" );
}
void XMLPrinter::PushText( const char* text, bool cdata )
{
_textDepth = _depth-1;
SealElementIfJustOpened();
if ( cdata ) {
Print( "<![CDATA[%s]]>", text );
}
else {
PrintString( text, true );
}
}
void XMLPrinter::PushText( int value )
{
char buf[BUF_SIZE];
XMLUtil::ToStr( value, buf, BUF_SIZE );
PushText( buf, false );
}
void XMLPrinter::PushText( unsigned value )
{
char buf[BUF_SIZE];
XMLUtil::ToStr( value, buf, BUF_SIZE );
PushText( buf, false );
}
void XMLPrinter::PushText( bool value )
{
char buf[BUF_SIZE];
XMLUtil::ToStr( value, buf, BUF_SIZE );
PushText( buf, false );
}
void XMLPrinter::PushText( float value )
{
char buf[BUF_SIZE];
XMLUtil::ToStr( value, buf, BUF_SIZE );
PushText( buf, false );
}
void XMLPrinter::PushText( double value )
{
char buf[BUF_SIZE];
XMLUtil::ToStr( value, buf, BUF_SIZE );
PushText( buf, false );
}
void XMLPrinter::PushComment( const char* comment )
{
SealElementIfJustOpened();
if ( _textDepth < 0 && !_firstElement && !_compactMode) {
Print( "\n" );
PrintSpace( _depth );
}
_firstElement = false;
Print( "<!--%s-->", comment );
}
void XMLPrinter::PushDeclaration( const char* value )
{
SealElementIfJustOpened();
if ( _textDepth < 0 && !_firstElement && !_compactMode) {
Print( "\n" );
PrintSpace( _depth );
}
_firstElement = false;
Print( "<?%s?>", value );
}
void XMLPrinter::PushUnknown( const char* value )
{
SealElementIfJustOpened();
if ( _textDepth < 0 && !_firstElement && !_compactMode) {
Print( "\n" );
PrintSpace( _depth );
}
_firstElement = false;
Print( "<!%s>", value );
}
bool XMLPrinter::VisitEnter( const XMLDocument& doc )
{
_processEntities = doc.ProcessEntities();
if ( doc.HasBOM() ) {
PushHeader( true, false );
}
return true;
}
bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
{
const XMLElement* parentElem = 0;
if ( element.Parent() ) {
parentElem = element.Parent()->ToElement();
}
const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode;
OpenElement( element.Name(), compactMode );
while ( attribute ) {
PushAttribute( attribute->Name(), attribute->Value() );
attribute = attribute->Next();
}
return true;
}
bool XMLPrinter::VisitExit( const XMLElement& element )
{
CloseElement( CompactMode(element) );
return true;
}
bool XMLPrinter::Visit( const XMLText& text )
{
PushText( text.Value(), text.CData() );
return true;
}
bool XMLPrinter::Visit( const XMLComment& comment )
{
PushComment( comment.Value() );
return true;
}
bool XMLPrinter::Visit( const XMLDeclaration& declaration )
{
PushDeclaration( declaration.Value() );
return true;
}
bool XMLPrinter::Visit( const XMLUnknown& unknown )
{
PushUnknown( unknown.Value() );
return true;
}
} // namespace tinyxml2