mirror of
https://github.com/corda/corda.git
synced 2025-01-01 10:46:46 +00:00
6bb9d0015a
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: 0d8a7d24824dc91d77
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>
2540 lines
65 KiB
C++
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]
|
|
// 中 or 中
|
|
|
|
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
|
|
|