├── Data ├── .gitkeep ├── diagram │ ├── Cone.png │ ├── Kitti.png │ └── Building.png ├── option.xml ├── option_Cone.xml ├── option_KITTI.xml ├── option_Building.xml └── option2.xml ├── 3rdparty └── .gitkeep ├── library └── FasterStereoCuda │ ├── dll │ ├── .gitkeep │ ├── StereoCuda.dll │ ├── cudart64_110.dll │ └── FasterStereoCuda.dll │ ├── lib │ ├── .gitkeep │ └── FasterStereoCuda.lib │ └── include │ ├── .gitkeep │ └── FasterStereoCuda.h ├── x64 ├── Debug │ └── fasterstereocuda.a └── Release │ └── fasterstereocuda.a ├── FasterStereoConsole ├── resource.h ├── option_manager.h ├── fasterstereocuda.a ├── option_manager.cpp ├── FasterStereoConsole.aps ├── FasterStereoConsole.cpp ├── FasterStereoConsole.rc ├── FasterStereoConsole.vcxproj.administrator.nvuser ├── FasterStereoConsole.vcxproj.lys.nvuser ├── FasterStereoConsole.vcxproj.user ├── FasterStereoConsole.vcxproj.filters ├── FasterStereoConsole.vcxproj ├── tinyxml2.h └── tinyxml2.cpp ├── FasterStereoCuda-v19.sln └── README.md /Data/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /3rdparty/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /library/FasterStereoCuda/dll/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /library/FasterStereoCuda/lib/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /library/FasterStereoCuda/include/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Data/diagram/Cone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethan-li-coding/FasterStereoCuda-Library/HEAD/Data/diagram/Cone.png -------------------------------------------------------------------------------- /Data/diagram/Kitti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethan-li-coding/FasterStereoCuda-Library/HEAD/Data/diagram/Kitti.png -------------------------------------------------------------------------------- /Data/diagram/Building.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethan-li-coding/FasterStereoCuda-Library/HEAD/Data/diagram/Building.png -------------------------------------------------------------------------------- /x64/Debug/fasterstereocuda.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethan-li-coding/FasterStereoCuda-Library/HEAD/x64/Debug/fasterstereocuda.a -------------------------------------------------------------------------------- /FasterStereoConsole/resource.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethan-li-coding/FasterStereoCuda-Library/HEAD/FasterStereoConsole/resource.h -------------------------------------------------------------------------------- /x64/Release/fasterstereocuda.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethan-li-coding/FasterStereoCuda-Library/HEAD/x64/Release/fasterstereocuda.a -------------------------------------------------------------------------------- /FasterStereoConsole/option_manager.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethan-li-coding/FasterStereoCuda-Library/HEAD/FasterStereoConsole/option_manager.h -------------------------------------------------------------------------------- /FasterStereoConsole/fasterstereocuda.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethan-li-coding/FasterStereoCuda-Library/HEAD/FasterStereoConsole/fasterstereocuda.a -------------------------------------------------------------------------------- /FasterStereoConsole/option_manager.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethan-li-coding/FasterStereoCuda-Library/HEAD/FasterStereoConsole/option_manager.cpp -------------------------------------------------------------------------------- /FasterStereoConsole/FasterStereoConsole.aps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethan-li-coding/FasterStereoCuda-Library/HEAD/FasterStereoConsole/FasterStereoConsole.aps -------------------------------------------------------------------------------- /FasterStereoConsole/FasterStereoConsole.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethan-li-coding/FasterStereoCuda-Library/HEAD/FasterStereoConsole/FasterStereoConsole.cpp -------------------------------------------------------------------------------- /FasterStereoConsole/FasterStereoConsole.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethan-li-coding/FasterStereoCuda-Library/HEAD/FasterStereoConsole/FasterStereoConsole.rc -------------------------------------------------------------------------------- /library/FasterStereoCuda/dll/StereoCuda.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethan-li-coding/FasterStereoCuda-Library/HEAD/library/FasterStereoCuda/dll/StereoCuda.dll -------------------------------------------------------------------------------- /library/FasterStereoCuda/dll/cudart64_110.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethan-li-coding/FasterStereoCuda-Library/HEAD/library/FasterStereoCuda/dll/cudart64_110.dll -------------------------------------------------------------------------------- /library/FasterStereoCuda/dll/FasterStereoCuda.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethan-li-coding/FasterStereoCuda-Library/HEAD/library/FasterStereoCuda/dll/FasterStereoCuda.dll -------------------------------------------------------------------------------- /library/FasterStereoCuda/include/FasterStereoCuda.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethan-li-coding/FasterStereoCuda-Library/HEAD/library/FasterStereoCuda/include/FasterStereoCuda.h -------------------------------------------------------------------------------- /library/FasterStereoCuda/lib/FasterStereoCuda.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethan-li-coding/FasterStereoCuda-Library/HEAD/library/FasterStereoCuda/lib/FasterStereoCuda.lib -------------------------------------------------------------------------------- /FasterStereoConsole/FasterStereoConsole.vcxproj.administrator.nvuser: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Data/option.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4800 5 | 3409 6 | 7 | 8 | -776 9 | -520 10 | 2 11 | 1 12 | 1 13 | 0 14 | 15 | 16 | -------------------------------------------------------------------------------- /Data/option_Cone.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 450 5 | 375 6 | 7 | 8 | 0 9 | 64 10 | 1 11 | 1 12 | 0 13 | 0 14 | 15 | 16 | -------------------------------------------------------------------------------- /Data/option_KITTI.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1242 5 | 375 6 | 7 | 8 | 0 9 | 64 10 | 2 11 | 1 12 | 0 13 | 0 14 | 15 | 16 | -------------------------------------------------------------------------------- /Data/option_Building.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4800 5 | 3409 6 | 7 | 8 | -776 9 | -520 10 | 3 11 | 1 12 | 1 13 | 0 14 | 15 | 16 | -------------------------------------------------------------------------------- /FasterStereoConsole/FasterStereoConsole.vcxproj.lys.nvuser: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Data/option2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4800 5 | 3409 6 | 7 | 8 | 34000.0 9 | 96500.0 10 | 2 11 | 1 12 | 1 13 | 0 14 | 15 | 16 | 2627.2746582031250 17 | 1630.0000000000000 18 | 3542.9848632812500 19 | 1630.0000000000000 20 | 4324.5149416122963 21 | 3117.0942850000001 22 | 23 | 24 | -------------------------------------------------------------------------------- /FasterStereoConsole/FasterStereoConsole.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(SolutionDir)Data\Building\left.bmp $(SolutionDir)Data\Building\right.bmp $(SolutionDir)Data\Building\option.xml 5 | WindowsLocalDebugger 6 | E:\ZG-Prog\GScan\Data\Ep-Data2 7 | $(TargetPath) 8 | 9 | 10 | 11 | 12 | WindowsLocalDebugger 13 | 14 | -------------------------------------------------------------------------------- /FasterStereoCuda-v19.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.28803.202 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FasterStereoConsole", "FasterStereoConsole\FasterStereoConsole.vcxproj", "{82B60534-8E5C-4398-8880-6ABDC8FBE24E}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {82B60534-8E5C-4398-8880-6ABDC8FBE24E}.Debug|x64.ActiveCfg = Debug|x64 17 | {82B60534-8E5C-4398-8880-6ABDC8FBE24E}.Debug|x64.Build.0 = Debug|x64 18 | {82B60534-8E5C-4398-8880-6ABDC8FBE24E}.Debug|x86.ActiveCfg = Debug|Win32 19 | {82B60534-8E5C-4398-8880-6ABDC8FBE24E}.Debug|x86.Build.0 = Debug|Win32 20 | {82B60534-8E5C-4398-8880-6ABDC8FBE24E}.Release|x64.ActiveCfg = Release|x64 21 | {82B60534-8E5C-4398-8880-6ABDC8FBE24E}.Release|x64.Build.0 = Release|x64 22 | {82B60534-8E5C-4398-8880-6ABDC8FBE24E}.Release|x86.ActiveCfg = Release|Win32 23 | {82B60534-8E5C-4398-8880-6ABDC8FBE24E}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {AD074331-6078-4D5B-8C81-68A4EDA16ACF} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /FasterStereoConsole/FasterStereoConsole.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | 29 | 30 | Header Files 31 | 32 | 33 | Header Files 34 | 35 | 36 | Header Files 37 | 38 | 39 | 40 | 41 | Resource Files 42 | 43 | 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FasterStereoCuda-Library 2 | 3 | # 必要更新提醒 4 | 1. 试用期更新为12个月,过期了直接contact me! 5 | 2. 修复了你可能不知道的很多bugs! 6 | # 简介 7 | 8 | 这是一个基于CUDA加速的快速立体匹配库,它的核心是SemiglobalMatching(SGM)算法,它不仅在时间效率上要远远优于基于CPU的常规SGM,而且明显占用更少的内存,这意味着它不仅可以在较低分辨率(百万级)图像上达到实时的帧率,且完全具备处理千万级甚至更高量级图像的能力。 9 | 10 | 你可以拉取本测试工程并在自己的数据上体验它的改进效果,也可以在右侧下载已经打包好的压缩包,直接在本地运行控制台程序,或者在你的工程里通过动态库的方式调用它。 11 | 12 | # 环境 13 | 14 | Windows 10
15 | Visual Studio 2019
16 | CUDA v11.0
17 | Opencv3.2(下载地址:[https://download.csdn.net/download/rs_lys/13193887](https://download.csdn.net/download/rs_lys/13193887)) 18 | 19 | # 控制台调用方式 20 | >**单像对:**
21 | >../FasterStereoConsole.exe ../Data/Cone/left.png ../Data/Cone/right.png ../Data/Cone/option.xml
22 | >**多像对:**(KITTI)
23 | >../FasterStereoConsole.exe ../Data/KITTI/image_2 ../Data/KITTI/image_3 png ../Data/KITTI/option.xml

24 | >把../换成你的路径。option.xml是算法参数文件,在Data/文件夹中,附有两类参数文件option.xml和option2.xml,分别对应视差空间和深度空间的参数,二者用其一即可。不同的数据,需要对应修改option.xml文件的参数值。 25 | 26 | >关于视差范围有特殊要求:必须满足64x2n,如64、128、256、512。 27 | 28 | >使用过程中有任何问题,特别是算法BUG,请及时和我交流,感谢! 29 | 30 | # 重要说明 31 | 32 | 算法库为试用版,试用期12个月,联系! 33 | 34 | 微信:EthanYs6 35 | 邮箱:rs_lys@163.com 36 | 37 | # 一些案例图片 38 | ## 概览 39 | | 数据 | Cone(450x375x64) | Kitti(1242x375x64) | Building(4800x3409x256) | 40 | | ------ | ------ | ------ | ------ | 41 | | 帧率 | 341.2 | 154.7 | 6.0 | 42 | | 显存(Mb) | 258.9 | 325.3 | 4185.9 | 43 | 44 | 案例数据下载地址:[https://download.csdn.net/download/rs_lys/13074343](https://download.csdn.net/download/rs_lys/13074343)
45 | 测试平台:NVIDIA GTX1080 46 | 47 | ## 数据1:Cone(450*375) 48 |
49 | 50 |
51 | 52 | ## 数据2:Kitti(1242*375) 53 |
54 | 55 |
56 | 57 | ## 数据3:Building(4800*3409) 58 |
59 | 60 |
61 | 62 | ## Github图片不显示的解决办法 63 | 修改hosts 64 | 65 | C:\Windows\System32\drivers\etc\hosts 66 | 67 | 在文件末尾添加: 68 | ```cpp 69 | # GitHub Start 70 | 192.30.253.119 gist.github.com 71 | 151.101.184.133 assets-cdn.github.com 72 | 151.101.184.133 raw.githubusercontent.com 73 | 151.101.184.133 gist.githubusercontent.com 74 | 151.101.184.133 cloud.githubusercontent.com 75 | 151.101.184.133 camo.githubusercontent.com 76 | 151.101.184.133 avatars0.githubusercontent.com 77 | 151.101.184.133 avatars1.githubusercontent.com 78 | 151.101.184.133 avatars2.githubusercontent.com 79 | 151.101.184.133 avatars3.githubusercontent.com 80 | 151.101.184.133 avatars4.githubusercontent.com 81 | 151.101.184.133 avatars5.githubusercontent.com 82 | 151.101.184.133 avatars6.githubusercontent.com 83 | 151.101.184.133 avatars7.githubusercontent.com 84 | 151.101.184.133 avatars8.githubusercontent.com 85 | # GitHub End 86 | 87 | ``` 88 | -------------------------------------------------------------------------------- /FasterStereoConsole/FasterStereoConsole.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {82B60534-8E5C-4398-8880-6ABDC8FBE24E} 23 | Win32Proj 24 | SpeckleConsoleTest 25 | 10.0.19041.0 26 | FasterStereoConsole 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | MultiByte 47 | 48 | 49 | Application 50 | false 51 | v140 52 | true 53 | MultiByte 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | true 78 | 79 | 80 | false 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Use 88 | Level3 89 | Disabled 90 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 91 | true 92 | 93 | 94 | Console 95 | true 96 | 97 | 98 | 99 | 100 | Use 101 | Level3 102 | Disabled 103 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 104 | true 105 | $(SolutionDir)3rdparty\opencv\include;$(SolutionDir)3rdparty\opencv\include\opencv;$(SolutionDir)3rdparty\opencv\include\opencv2;$(SolutionDir)library\FasterStereoCuda\include\ 106 | /D _CRT_SECURE_NO_WARNINGS %(AdditionalOptions) 107 | 108 | 109 | Console 110 | true 111 | $(SolutionDir)3rdparty\opencv\lib\;$(SolutionDir)library\FasterStereoCuda\lib\;%(AdditionalLibraryDirectories) 112 | 113 | 114 | 115 | 116 | Level3 117 | Use 118 | MaxSpeed 119 | true 120 | true 121 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 122 | true 123 | 124 | 125 | Console 126 | true 127 | true 128 | true 129 | 130 | 131 | 132 | 133 | Level3 134 | Use 135 | MaxSpeed 136 | true 137 | true 138 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 139 | true 140 | $(SolutionDir)3rdparty\opencv\include;$(SolutionDir)3rdparty\opencv\include\opencv;$(SolutionDir)3rdparty\opencv\include\opencv2;$(SolutionDir)library\FasterStereoCuda\include\ 141 | /D _CRT_SECURE_NO_WARNINGS %(AdditionalOptions) 142 | true 143 | 144 | 145 | Console 146 | true 147 | true 148 | true 149 | $(SolutionDir)3rdparty\opencv\lib\;$(SolutionDir)library\FasterStereoCuda\lib\;%(AdditionalLibraryDirectories) 150 | 151 | 152 | 153 | 154 | NotUsing 155 | NotUsing 156 | 157 | 158 | NotUsing 159 | NotUsing 160 | 161 | 162 | NotUsing 163 | NotUsing 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /FasterStereoConsole/tinyxml2.h: -------------------------------------------------------------------------------- 1 | /* 2 | Original code by Lee Thomason (www.grinninglizard.com) 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any 5 | damages arising from the use of this software. 6 | Permission is granted to anyone to use this software for any 7 | purpose, including commercial applications, and to alter it and 8 | redistribute it freely, subject to the following restrictions: 9 | 1. The origin of this software must not be misrepresented; you must 10 | not claim that you wrote the original software. If you use this 11 | software in a product, an acknowledgment in the product documentation 12 | would be appreciated but is not required. 13 | 2. Altered source versions must be plainly marked as such, and 14 | must not be misrepresented as being the original software. 15 | 3. This notice may not be removed or altered from any source 16 | distribution. 17 | */ 18 | 19 | #ifndef TINYXML2_INCLUDED 20 | #define TINYXML2_INCLUDED 21 | 22 | #if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) 23 | # include 24 | # include 25 | # include 26 | # include 27 | # include 28 | # if defined(__PS3__) 29 | # include 30 | # endif 31 | #else 32 | # include 33 | # include 34 | # include 35 | # include 36 | # include 37 | #endif 38 | #include 39 | 40 | /* 41 | TODO: intern strings instead of allocation. 42 | */ 43 | /* 44 | gcc: 45 | g++ -Wall -DDEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe 46 | Formatting, Artistic Style: 47 | AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h 48 | */ 49 | 50 | #if defined( _DEBUG ) || defined( DEBUG ) || defined (__DEBUG__) 51 | # ifndef DEBUG 52 | # define DEBUG 53 | # endif 54 | #endif 55 | 56 | #ifdef _MSC_VER 57 | # pragma warning(push) 58 | # pragma warning(disable: 4251) 59 | #endif 60 | 61 | #ifdef _WIN32 62 | # ifdef TINYXML2_EXPORT 63 | # define TINYXML2_LIB __declspec(dllexport) 64 | # elif defined(TINYXML2_IMPORT) 65 | # define TINYXML2_LIB __declspec(dllimport) 66 | # else 67 | # define TINYXML2_LIB 68 | # endif 69 | #elif __GNUC__ >= 4 70 | # define TINYXML2_LIB __attribute__((visibility("default"))) 71 | #else 72 | # define TINYXML2_LIB 73 | #endif 74 | 75 | 76 | #if defined(DEBUG) 77 | # if defined(_MSC_VER) 78 | # // "(void)0," is for suppressing C4127 warning in "assert(false)", "assert(true)" and the like 79 | # define TIXMLASSERT( x ) if ( !((void)0,(x))) { __debugbreak(); } 80 | # elif defined (ANDROID_NDK) 81 | # include 82 | # define TIXMLASSERT( x ) if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); } 83 | # else 84 | # include 85 | # define TIXMLASSERT assert 86 | # endif 87 | #else 88 | # define TIXMLASSERT( x ) {} 89 | #endif 90 | 91 | 92 | /* Versioning, past 1.0.14: 93 | http://semver.org/ 94 | */ 95 | static const int TIXML2_MAJOR_VERSION = 4; 96 | static const int TIXML2_MINOR_VERSION = 0; 97 | static const int TIXML2_PATCH_VERSION = 1; 98 | 99 | namespace tinyxml2 100 | { 101 | class XMLDocument; 102 | class XMLElement; 103 | class XMLAttribute; 104 | class XMLComment; 105 | class XMLText; 106 | class XMLDeclaration; 107 | class XMLUnknown; 108 | class XMLPrinter; 109 | 110 | /* 111 | A class that wraps strings. Normally stores the start and end 112 | pointers into the XML file itself, and will apply normalization 113 | and entity translation if actually read. Can also store (and memory 114 | manage) a traditional char[] 115 | */ 116 | class StrPair 117 | { 118 | public: 119 | enum { 120 | NEEDS_ENTITY_PROCESSING = 0x01, 121 | NEEDS_NEWLINE_NORMALIZATION = 0x02, 122 | NEEDS_WHITESPACE_COLLAPSING = 0x04, 123 | 124 | TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, 125 | TEXT_ELEMENT_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, 126 | ATTRIBUTE_NAME = 0, 127 | ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, 128 | ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, 129 | COMMENT = NEEDS_NEWLINE_NORMALIZATION 130 | }; 131 | 132 | StrPair() : _flags(0), _start(0), _end(0) {} 133 | ~StrPair(); 134 | 135 | void Set(char* start, char* end, int flags) { 136 | TIXMLASSERT(start); 137 | TIXMLASSERT(end); 138 | Reset(); 139 | _start = start; 140 | _end = end; 141 | _flags = flags | NEEDS_FLUSH; 142 | } 143 | 144 | const char* GetStr(); 145 | 146 | bool Empty() const { 147 | return _start == _end; 148 | } 149 | 150 | void SetInternedStr(const char* str) { 151 | Reset(); 152 | _start = const_cast(str); 153 | } 154 | 155 | void SetStr(const char* str, int flags = 0); 156 | 157 | char* ParseText(char* in, const char* endTag, int strFlags); 158 | char* ParseName(char* in); 159 | 160 | void TransferTo(StrPair* other); 161 | void Reset(); 162 | 163 | private: 164 | void CollapseWhitespace(); 165 | 166 | enum { 167 | NEEDS_FLUSH = 0x100, 168 | NEEDS_DELETE = 0x200 169 | }; 170 | 171 | int _flags; 172 | char* _start; 173 | char* _end; 174 | 175 | StrPair(const StrPair& other); // not supported 176 | void operator=(StrPair& other); // not supported, use TransferTo() 177 | }; 178 | 179 | 180 | /* 181 | A dynamic array of Plain Old Data. Doesn't support constructors, etc. 182 | Has a small initial memory pool, so that low or no usage will not 183 | cause a call to new/delete 184 | */ 185 | template 186 | class DynArray 187 | { 188 | public: 189 | DynArray() { 190 | _mem = _pool; 191 | _allocated = INITIAL_SIZE; 192 | _size = 0; 193 | } 194 | 195 | ~DynArray() { 196 | if (_mem != _pool) { 197 | delete[] _mem; 198 | } 199 | } 200 | 201 | void Clear() { 202 | _size = 0; 203 | } 204 | 205 | void Push(T t) { 206 | TIXMLASSERT(_size < INT_MAX); 207 | EnsureCapacity(_size + 1); 208 | _mem[_size] = t; 209 | ++_size; 210 | } 211 | 212 | T* PushArr(int count) { 213 | TIXMLASSERT(count >= 0); 214 | TIXMLASSERT(_size <= INT_MAX - count); 215 | EnsureCapacity(_size + count); 216 | T* ret = &_mem[_size]; 217 | _size += count; 218 | return ret; 219 | } 220 | 221 | T Pop() { 222 | TIXMLASSERT(_size > 0); 223 | --_size; 224 | return _mem[_size]; 225 | } 226 | 227 | void PopArr(int count) { 228 | TIXMLASSERT(_size >= count); 229 | _size -= count; 230 | } 231 | 232 | bool Empty() const { 233 | return _size == 0; 234 | } 235 | 236 | T& operator[](int i) { 237 | TIXMLASSERT(i >= 0 && i < _size); 238 | return _mem[i]; 239 | } 240 | 241 | const T& operator[](int i) const { 242 | TIXMLASSERT(i >= 0 && i < _size); 243 | return _mem[i]; 244 | } 245 | 246 | const T& PeekTop() const { 247 | TIXMLASSERT(_size > 0); 248 | return _mem[_size - 1]; 249 | } 250 | 251 | int Size() const { 252 | TIXMLASSERT(_size >= 0); 253 | return _size; 254 | } 255 | 256 | int Capacity() const { 257 | TIXMLASSERT(_allocated >= INITIAL_SIZE); 258 | return _allocated; 259 | } 260 | 261 | const T* Mem() const { 262 | TIXMLASSERT(_mem); 263 | return _mem; 264 | } 265 | 266 | T* Mem() { 267 | TIXMLASSERT(_mem); 268 | return _mem; 269 | } 270 | 271 | private: 272 | DynArray(const DynArray&); // not supported 273 | void operator=(const DynArray&); // not supported 274 | 275 | void EnsureCapacity(int cap) { 276 | TIXMLASSERT(cap > 0); 277 | if (cap > _allocated) { 278 | TIXMLASSERT(cap <= INT_MAX / 2); 279 | int newAllocated = cap * 2; 280 | T* newMem = new T[newAllocated]; 281 | memcpy(newMem, _mem, sizeof(T)*_size); // warning: not using constructors, only works for PODs 282 | if (_mem != _pool) { 283 | delete[] _mem; 284 | } 285 | _mem = newMem; 286 | _allocated = newAllocated; 287 | } 288 | } 289 | 290 | T* _mem; 291 | T _pool[INITIAL_SIZE]; 292 | int _allocated; // objects allocated 293 | int _size; // number objects in use 294 | }; 295 | 296 | 297 | /* 298 | Parent virtual class of a pool for fast allocation 299 | and deallocation of objects. 300 | */ 301 | class MemPool 302 | { 303 | public: 304 | MemPool() {} 305 | virtual ~MemPool() {} 306 | 307 | virtual int ItemSize() const = 0; 308 | virtual void* Alloc() = 0; 309 | virtual void Free(void*) = 0; 310 | virtual void SetTracked() = 0; 311 | virtual void Clear() = 0; 312 | }; 313 | 314 | 315 | /* 316 | Template child class to create pools of the correct type. 317 | */ 318 | template< int ITEM_SIZE > 319 | class MemPoolT : public MemPool 320 | { 321 | public: 322 | MemPoolT() : _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0) {} 323 | ~MemPoolT() { 324 | Clear(); 325 | } 326 | 327 | void Clear() { 328 | // Delete the blocks. 329 | while (!_blockPtrs.Empty()) { 330 | Block* b = _blockPtrs.Pop(); 331 | delete b; 332 | } 333 | _root = 0; 334 | _currentAllocs = 0; 335 | _nAllocs = 0; 336 | _maxAllocs = 0; 337 | _nUntracked = 0; 338 | } 339 | 340 | virtual int ItemSize() const { 341 | return ITEM_SIZE; 342 | } 343 | int CurrentAllocs() const { 344 | return _currentAllocs; 345 | } 346 | 347 | virtual void* Alloc() { 348 | if (!_root) { 349 | // Need a new block. 350 | Block* block = new Block(); 351 | _blockPtrs.Push(block); 352 | 353 | Item* blockItems = block->items; 354 | for (int i = 0; i < ITEMS_PER_BLOCK - 1; ++i) { 355 | blockItems[i].next = &(blockItems[i + 1]); 356 | } 357 | blockItems[ITEMS_PER_BLOCK - 1].next = 0; 358 | _root = blockItems; 359 | } 360 | Item* const result = _root; 361 | TIXMLASSERT(result != 0); 362 | _root = _root->next; 363 | 364 | ++_currentAllocs; 365 | if (_currentAllocs > _maxAllocs) { 366 | _maxAllocs = _currentAllocs; 367 | } 368 | ++_nAllocs; 369 | ++_nUntracked; 370 | return result; 371 | } 372 | 373 | virtual void Free(void* mem) { 374 | if (!mem) { 375 | return; 376 | } 377 | --_currentAllocs; 378 | Item* item = static_cast(mem); 379 | #ifdef DEBUG 380 | memset(item, 0xfe, sizeof(*item)); 381 | #endif 382 | item->next = _root; 383 | _root = item; 384 | } 385 | void Trace(const char* name) { 386 | printf("Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n", 387 | name, _maxAllocs, _maxAllocs * ITEM_SIZE / 1024, _currentAllocs, 388 | ITEM_SIZE, _nAllocs, _blockPtrs.Size()); 389 | } 390 | 391 | void SetTracked() { 392 | --_nUntracked; 393 | } 394 | 395 | int Untracked() const { 396 | return _nUntracked; 397 | } 398 | 399 | // This number is perf sensitive. 4k seems like a good tradeoff on my machine. 400 | // The test file is large, 170k. 401 | // Release: VS2010 gcc(no opt) 402 | // 1k: 4000 403 | // 2k: 4000 404 | // 4k: 3900 21000 405 | // 16k: 5200 406 | // 32k: 4300 407 | // 64k: 4000 21000 408 | // Declared public because some compilers do not accept to use ITEMS_PER_BLOCK 409 | // in private part if ITEMS_PER_BLOCK is private 410 | enum { ITEMS_PER_BLOCK = (4 * 1024) / ITEM_SIZE }; 411 | 412 | private: 413 | MemPoolT(const MemPoolT&); // not supported 414 | void operator=(const MemPoolT&); // not supported 415 | 416 | union Item { 417 | Item* next; 418 | char itemData[ITEM_SIZE]; 419 | }; 420 | struct Block { 421 | Item items[ITEMS_PER_BLOCK]; 422 | }; 423 | DynArray< Block*, 10 > _blockPtrs; 424 | Item* _root; 425 | 426 | int _currentAllocs; 427 | int _nAllocs; 428 | int _maxAllocs; 429 | int _nUntracked; 430 | }; 431 | 432 | 433 | 434 | /** 435 | Implements the interface to the "Visitor pattern" (see the Accept() method.) 436 | If you call the Accept() method, it requires being passed a XMLVisitor 437 | class to handle callbacks. For nodes that contain other nodes (Document, Element) 438 | you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs 439 | are simply called with Visit(). 440 | If you return 'true' from a Visit method, recursive parsing will continue. If you return 441 | false, no children of this node or its siblings will be visited. 442 | All flavors of Visit methods have a default implementation that returns 'true' (continue 443 | visiting). You need to only override methods that are interesting to you. 444 | Generally Accept() is called on the XMLDocument, although all nodes support visiting. 445 | You should never change the document from a callback. 446 | @sa XMLNode::Accept() 447 | */ 448 | class TINYXML2_LIB XMLVisitor 449 | { 450 | public: 451 | virtual ~XMLVisitor() {} 452 | 453 | /// Visit a document. 454 | virtual bool VisitEnter(const XMLDocument& /*doc*/) { 455 | return true; 456 | } 457 | /// Visit a document. 458 | virtual bool VisitExit(const XMLDocument& /*doc*/) { 459 | return true; 460 | } 461 | 462 | /// Visit an element. 463 | virtual bool VisitEnter(const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/) { 464 | return true; 465 | } 466 | /// Visit an element. 467 | virtual bool VisitExit(const XMLElement& /*element*/) { 468 | return true; 469 | } 470 | 471 | /// Visit a declaration. 472 | virtual bool Visit(const XMLDeclaration& /*declaration*/) { 473 | return true; 474 | } 475 | /// Visit a text node. 476 | virtual bool Visit(const XMLText& /*text*/) { 477 | return true; 478 | } 479 | /// Visit a comment node. 480 | virtual bool Visit(const XMLComment& /*comment*/) { 481 | return true; 482 | } 483 | /// Visit an unknown node. 484 | virtual bool Visit(const XMLUnknown& /*unknown*/) { 485 | return true; 486 | } 487 | }; 488 | 489 | // WARNING: must match XMLDocument::_errorNames[] 490 | enum XMLError { 491 | XML_SUCCESS = 0, 492 | XML_NO_ATTRIBUTE, 493 | XML_WRONG_ATTRIBUTE_TYPE, 494 | XML_ERROR_FILE_NOT_FOUND, 495 | XML_ERROR_FILE_COULD_NOT_BE_OPENED, 496 | XML_ERROR_FILE_READ_ERROR, 497 | XML_ERROR_ELEMENT_MISMATCH, 498 | XML_ERROR_PARSING_ELEMENT, 499 | XML_ERROR_PARSING_ATTRIBUTE, 500 | XML_ERROR_IDENTIFYING_TAG, 501 | XML_ERROR_PARSING_TEXT, 502 | XML_ERROR_PARSING_CDATA, 503 | XML_ERROR_PARSING_COMMENT, 504 | XML_ERROR_PARSING_DECLARATION, 505 | XML_ERROR_PARSING_UNKNOWN, 506 | XML_ERROR_EMPTY_DOCUMENT, 507 | XML_ERROR_MISMATCHED_ELEMENT, 508 | XML_ERROR_PARSING, 509 | XML_CAN_NOT_CONVERT_TEXT, 510 | XML_NO_TEXT_NODE, 511 | 512 | XML_ERROR_COUNT 513 | }; 514 | 515 | 516 | /* 517 | Utility functionality. 518 | */ 519 | class XMLUtil 520 | { 521 | public: 522 | static const char* SkipWhiteSpace(const char* p) { 523 | TIXMLASSERT(p); 524 | while (IsWhiteSpace(*p)) { 525 | ++p; 526 | } 527 | TIXMLASSERT(p); 528 | return p; 529 | } 530 | static char* SkipWhiteSpace(char* p) { 531 | return const_cast(SkipWhiteSpace(const_cast(p))); 532 | } 533 | 534 | // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't 535 | // correct, but simple, and usually works. 536 | static bool IsWhiteSpace(char p) { 537 | return !IsUTF8Continuation(p) && isspace(static_cast(p)); 538 | } 539 | 540 | inline static bool IsNameStartChar(unsigned char ch) { 541 | if (ch >= 128) { 542 | // This is a heuristic guess in attempt to not implement Unicode-aware isalpha() 543 | return true; 544 | } 545 | if (isalpha(ch)) { 546 | return true; 547 | } 548 | return ch == ':' || ch == '_'; 549 | } 550 | 551 | inline static bool IsNameChar(unsigned char ch) { 552 | return IsNameStartChar(ch) 553 | || isdigit(ch) 554 | || ch == '.' 555 | || ch == '-'; 556 | } 557 | 558 | inline static bool StringEqual(const char* p, const char* q, int nChar = INT_MAX) { 559 | if (p == q) { 560 | return true; 561 | } 562 | TIXMLASSERT(p); 563 | TIXMLASSERT(q); 564 | TIXMLASSERT(nChar >= 0); 565 | return strncmp(p, q, nChar) == 0; 566 | } 567 | 568 | inline static bool IsUTF8Continuation(char p) { 569 | return (p & 0x80) != 0; 570 | } 571 | 572 | static const char* ReadBOM(const char* p, bool* hasBOM); 573 | // p is the starting location, 574 | // the UTF-8 value of the entity will be placed in value, and length filled in. 575 | static const char* GetCharacterRef(const char* p, char* value, int* length); 576 | static void ConvertUTF32ToUTF8(unsigned long input, char* output, int* length); 577 | 578 | // converts primitive types to strings 579 | static void ToStr(int v, char* buffer, int bufferSize); 580 | static void ToStr(unsigned v, char* buffer, int bufferSize); 581 | static void ToStr(bool v, char* buffer, int bufferSize); 582 | static void ToStr(float v, char* buffer, int bufferSize); 583 | static void ToStr(double v, char* buffer, int bufferSize); 584 | static void ToStr(int64_t v, char* buffer, int bufferSize); 585 | 586 | // converts strings to primitive types 587 | static bool ToInt(const char* str, int* value); 588 | static bool ToUnsigned(const char* str, unsigned* value); 589 | static bool ToBool(const char* str, bool* value); 590 | static bool ToFloat(const char* str, float* value); 591 | static bool ToDouble(const char* str, double* value); 592 | static bool ToInt64(const char* str, int64_t* value); 593 | }; 594 | 595 | 596 | /** XMLNode is a base class for every object that is in the 597 | XML Document Object Model (DOM), except XMLAttributes. 598 | Nodes have siblings, a parent, and children which can 599 | be navigated. A node is always in a XMLDocument. 600 | The type of a XMLNode can be queried, and it can 601 | be cast to its more defined type. 602 | A XMLDocument allocates memory for all its Nodes. 603 | When the XMLDocument gets deleted, all its Nodes 604 | will also be deleted. 605 | @verbatim 606 | A Document can contain: Element (container or leaf) 607 | Comment (leaf) 608 | Unknown (leaf) 609 | Declaration( leaf ) 610 | An Element can contain: Element (container or leaf) 611 | Text (leaf) 612 | Attributes (not on tree) 613 | Comment (leaf) 614 | Unknown (leaf) 615 | @endverbatim 616 | */ 617 | class TINYXML2_LIB XMLNode 618 | { 619 | friend class XMLDocument; 620 | friend class XMLElement; 621 | public: 622 | 623 | /// Get the XMLDocument that owns this XMLNode. 624 | const XMLDocument* GetDocument() const { 625 | TIXMLASSERT(_document); 626 | return _document; 627 | } 628 | /// Get the XMLDocument that owns this XMLNode. 629 | XMLDocument* GetDocument() { 630 | TIXMLASSERT(_document); 631 | return _document; 632 | } 633 | 634 | /// Safely cast to an Element, or null. 635 | virtual XMLElement* ToElement() { 636 | return 0; 637 | } 638 | /// Safely cast to Text, or null. 639 | virtual XMLText* ToText() { 640 | return 0; 641 | } 642 | /// Safely cast to a Comment, or null. 643 | virtual XMLComment* ToComment() { 644 | return 0; 645 | } 646 | /// Safely cast to a Document, or null. 647 | virtual XMLDocument* ToDocument() { 648 | return 0; 649 | } 650 | /// Safely cast to a Declaration, or null. 651 | virtual XMLDeclaration* ToDeclaration() { 652 | return 0; 653 | } 654 | /// Safely cast to an Unknown, or null. 655 | virtual XMLUnknown* ToUnknown() { 656 | return 0; 657 | } 658 | 659 | virtual const XMLElement* ToElement() const { 660 | return 0; 661 | } 662 | virtual const XMLText* ToText() const { 663 | return 0; 664 | } 665 | virtual const XMLComment* ToComment() const { 666 | return 0; 667 | } 668 | virtual const XMLDocument* ToDocument() const { 669 | return 0; 670 | } 671 | virtual const XMLDeclaration* ToDeclaration() const { 672 | return 0; 673 | } 674 | virtual const XMLUnknown* ToUnknown() const { 675 | return 0; 676 | } 677 | 678 | /** The meaning of 'value' changes for the specific type. 679 | @verbatim 680 | Document: empty (NULL is returned, not an empty string) 681 | Element: name of the element 682 | Comment: the comment text 683 | Unknown: the tag contents 684 | Text: the text string 685 | @endverbatim 686 | */ 687 | const char* Value() const; 688 | 689 | /** Set the Value of an XML node. 690 | @sa Value() 691 | */ 692 | void SetValue(const char* val, bool staticMem = false); 693 | 694 | /// Get the parent of this node on the DOM. 695 | const XMLNode* Parent() const { 696 | return _parent; 697 | } 698 | 699 | XMLNode* Parent() { 700 | return _parent; 701 | } 702 | 703 | /// Returns true if this node has no children. 704 | bool NoChildren() const { 705 | return !_firstChild; 706 | } 707 | 708 | /// Get the first child node, or null if none exists. 709 | const XMLNode* FirstChild() const { 710 | return _firstChild; 711 | } 712 | 713 | XMLNode* FirstChild() { 714 | return _firstChild; 715 | } 716 | 717 | /** Get the first child element, or optionally the first child 718 | element with the specified name. 719 | */ 720 | const XMLElement* FirstChildElement(const char* name = 0) const; 721 | 722 | XMLElement* FirstChildElement(const char* name = 0) { 723 | return const_cast(const_cast(this)->FirstChildElement(name)); 724 | } 725 | 726 | /// Get the last child node, or null if none exists. 727 | const XMLNode* LastChild() const { 728 | return _lastChild; 729 | } 730 | 731 | XMLNode* LastChild() { 732 | return _lastChild; 733 | } 734 | 735 | /** Get the last child element or optionally the last child 736 | element with the specified name. 737 | */ 738 | const XMLElement* LastChildElement(const char* name = 0) const; 739 | 740 | XMLElement* LastChildElement(const char* name = 0) { 741 | return const_cast(const_cast(this)->LastChildElement(name)); 742 | } 743 | 744 | /// Get the previous (left) sibling node of this node. 745 | const XMLNode* PreviousSibling() const { 746 | return _prev; 747 | } 748 | 749 | XMLNode* PreviousSibling() { 750 | return _prev; 751 | } 752 | 753 | /// Get the previous (left) sibling element of this node, with an optionally supplied name. 754 | const XMLElement* PreviousSiblingElement(const char* name = 0) const; 755 | 756 | XMLElement* PreviousSiblingElement(const char* name = 0) { 757 | return const_cast(const_cast(this)->PreviousSiblingElement(name)); 758 | } 759 | 760 | /// Get the next (right) sibling node of this node. 761 | const XMLNode* NextSibling() const { 762 | return _next; 763 | } 764 | 765 | XMLNode* NextSibling() { 766 | return _next; 767 | } 768 | 769 | /// Get the next (right) sibling element of this node, with an optionally supplied name. 770 | const XMLElement* NextSiblingElement(const char* name = 0) const; 771 | 772 | XMLElement* NextSiblingElement(const char* name = 0) { 773 | return const_cast(const_cast(this)->NextSiblingElement(name)); 774 | } 775 | 776 | /** 777 | Add a child node as the last (right) child. 778 | If the child node is already part of the document, 779 | it is moved from its old location to the new location. 780 | Returns the addThis argument or 0 if the node does not 781 | belong to the same document. 782 | */ 783 | XMLNode* InsertEndChild(XMLNode* addThis); 784 | 785 | XMLNode* LinkEndChild(XMLNode* addThis) { 786 | return InsertEndChild(addThis); 787 | } 788 | /** 789 | Add a child node as the first (left) child. 790 | If the child node is already part of the document, 791 | it is moved from its old location to the new location. 792 | Returns the addThis argument or 0 if the node does not 793 | belong to the same document. 794 | */ 795 | XMLNode* InsertFirstChild(XMLNode* addThis); 796 | /** 797 | Add a node after the specified child node. 798 | If the child node is already part of the document, 799 | it is moved from its old location to the new location. 800 | Returns the addThis argument or 0 if the afterThis node 801 | is not a child of this node, or if the node does not 802 | belong to the same document. 803 | */ 804 | XMLNode* InsertAfterChild(XMLNode* afterThis, XMLNode* addThis); 805 | 806 | /** 807 | Delete all the children of this node. 808 | */ 809 | void DeleteChildren(); 810 | 811 | /** 812 | Delete a child of this node. 813 | */ 814 | void DeleteChild(XMLNode* node); 815 | 816 | /** 817 | Make a copy of this node, but not its children. 818 | You may pass in a Document pointer that will be 819 | the owner of the new Node. If the 'document' is 820 | null, then the node returned will be allocated 821 | from the current Document. (this->GetDocument()) 822 | Note: if called on a XMLDocument, this will return null. 823 | */ 824 | virtual XMLNode* ShallowClone(XMLDocument* document) const = 0; 825 | 826 | /** 827 | Test if 2 nodes are the same, but don't test children. 828 | The 2 nodes do not need to be in the same Document. 829 | Note: if called on a XMLDocument, this will return false. 830 | */ 831 | virtual bool ShallowEqual(const XMLNode* compare) const = 0; 832 | 833 | /** Accept a hierarchical visit of the nodes in the TinyXML-2 DOM. Every node in the 834 | XML tree will be conditionally visited and the host will be called back 835 | via the XMLVisitor interface. 836 | This is essentially a SAX interface for TinyXML-2. (Note however it doesn't re-parse 837 | the XML for the callbacks, so the performance of TinyXML-2 is unchanged by using this 838 | interface versus any other.) 839 | The interface has been based on ideas from: 840 | - http://www.saxproject.org/ 841 | - http://c2.com/cgi/wiki?HierarchicalVisitorPattern 842 | Which are both good references for "visiting". 843 | An example of using Accept(): 844 | @verbatim 845 | XMLPrinter printer; 846 | tinyxmlDoc.Accept( &printer ); 847 | const char* xmlcstr = printer.CStr(); 848 | @endverbatim 849 | */ 850 | virtual bool Accept(XMLVisitor* visitor) const = 0; 851 | 852 | /** 853 | Set user data into the XMLNode. TinyXML-2 in 854 | no way processes or interprets user data. 855 | It is initially 0. 856 | */ 857 | void SetUserData(void* userData) { _userData = userData; } 858 | 859 | /** 860 | Get user data set into the XMLNode. TinyXML-2 in 861 | no way processes or interprets user data. 862 | It is initially 0. 863 | */ 864 | void* GetUserData() const { return _userData; } 865 | 866 | protected: 867 | XMLNode(XMLDocument*); 868 | virtual ~XMLNode(); 869 | 870 | virtual char* ParseDeep(char*, StrPair*); 871 | 872 | XMLDocument* _document; 873 | XMLNode* _parent; 874 | mutable StrPair _value; 875 | 876 | XMLNode* _firstChild; 877 | XMLNode* _lastChild; 878 | 879 | XMLNode* _prev; 880 | XMLNode* _next; 881 | 882 | void* _userData; 883 | 884 | private: 885 | MemPool* _memPool; 886 | void Unlink(XMLNode* child); 887 | static void DeleteNode(XMLNode* node); 888 | void InsertChildPreamble(XMLNode* insertThis) const; 889 | const XMLElement* ToElementWithName(const char* name) const; 890 | 891 | XMLNode(const XMLNode&); // not supported 892 | XMLNode& operator=(const XMLNode&); // not supported 893 | }; 894 | 895 | 896 | /** XML text. 897 | Note that a text node can have child element nodes, for example: 898 | @verbatim 899 | This is bold 900 | @endverbatim 901 | A text node can have 2 ways to output the next. "normal" output 902 | and CDATA. It will default to the mode it was parsed from the XML file and 903 | you generally want to leave it alone, but you can change the output mode with 904 | SetCData() and query it with CData(). 905 | */ 906 | class TINYXML2_LIB XMLText : public XMLNode 907 | { 908 | friend class XMLDocument; 909 | public: 910 | virtual bool Accept(XMLVisitor* visitor) const; 911 | 912 | virtual XMLText* ToText() { 913 | return this; 914 | } 915 | virtual const XMLText* ToText() const { 916 | return this; 917 | } 918 | 919 | /// Declare whether this should be CDATA or standard text. 920 | void SetCData(bool isCData) { 921 | _isCData = isCData; 922 | } 923 | /// Returns true if this is a CDATA text element. 924 | bool CData() const { 925 | return _isCData; 926 | } 927 | 928 | virtual XMLNode* ShallowClone(XMLDocument* document) const; 929 | virtual bool ShallowEqual(const XMLNode* compare) const; 930 | 931 | protected: 932 | XMLText(XMLDocument* doc) : XMLNode(doc), _isCData(false) {} 933 | virtual ~XMLText() {} 934 | 935 | char* ParseDeep(char*, StrPair* endTag); 936 | 937 | private: 938 | bool _isCData; 939 | 940 | XMLText(const XMLText&); // not supported 941 | XMLText& operator=(const XMLText&); // not supported 942 | }; 943 | 944 | 945 | /** An XML Comment. */ 946 | class TINYXML2_LIB XMLComment : public XMLNode 947 | { 948 | friend class XMLDocument; 949 | public: 950 | virtual XMLComment* ToComment() { 951 | return this; 952 | } 953 | virtual const XMLComment* ToComment() const { 954 | return this; 955 | } 956 | 957 | virtual bool Accept(XMLVisitor* visitor) const; 958 | 959 | virtual XMLNode* ShallowClone(XMLDocument* document) const; 960 | virtual bool ShallowEqual(const XMLNode* compare) const; 961 | 962 | protected: 963 | XMLComment(XMLDocument* doc); 964 | virtual ~XMLComment(); 965 | 966 | char* ParseDeep(char*, StrPair* endTag); 967 | 968 | private: 969 | XMLComment(const XMLComment&); // not supported 970 | XMLComment& operator=(const XMLComment&); // not supported 971 | }; 972 | 973 | 974 | /** In correct XML the declaration is the first entry in the file. 975 | @verbatim 976 | 977 | @endverbatim 978 | TinyXML-2 will happily read or write files without a declaration, 979 | however. 980 | The text of the declaration isn't interpreted. It is parsed 981 | and written as a string. 982 | */ 983 | class TINYXML2_LIB XMLDeclaration : public XMLNode 984 | { 985 | friend class XMLDocument; 986 | public: 987 | virtual XMLDeclaration* ToDeclaration() { 988 | return this; 989 | } 990 | virtual const XMLDeclaration* ToDeclaration() const { 991 | return this; 992 | } 993 | 994 | virtual bool Accept(XMLVisitor* visitor) const; 995 | 996 | virtual XMLNode* ShallowClone(XMLDocument* document) const; 997 | virtual bool ShallowEqual(const XMLNode* compare) const; 998 | 999 | protected: 1000 | XMLDeclaration(XMLDocument* doc); 1001 | virtual ~XMLDeclaration(); 1002 | 1003 | char* ParseDeep(char*, StrPair* endTag); 1004 | 1005 | private: 1006 | XMLDeclaration(const XMLDeclaration&); // not supported 1007 | XMLDeclaration& operator=(const XMLDeclaration&); // not supported 1008 | }; 1009 | 1010 | 1011 | /** Any tag that TinyXML-2 doesn't recognize is saved as an 1012 | unknown. It is a tag of text, but should not be modified. 1013 | It will be written back to the XML, unchanged, when the file 1014 | is saved. 1015 | DTD tags get thrown into XMLUnknowns. 1016 | */ 1017 | class TINYXML2_LIB XMLUnknown : public XMLNode 1018 | { 1019 | friend class XMLDocument; 1020 | public: 1021 | virtual XMLUnknown* ToUnknown() { 1022 | return this; 1023 | } 1024 | virtual const XMLUnknown* ToUnknown() const { 1025 | return this; 1026 | } 1027 | 1028 | virtual bool Accept(XMLVisitor* visitor) const; 1029 | 1030 | virtual XMLNode* ShallowClone(XMLDocument* document) const; 1031 | virtual bool ShallowEqual(const XMLNode* compare) const; 1032 | 1033 | protected: 1034 | XMLUnknown(XMLDocument* doc); 1035 | virtual ~XMLUnknown(); 1036 | 1037 | char* ParseDeep(char*, StrPair* endTag); 1038 | 1039 | private: 1040 | XMLUnknown(const XMLUnknown&); // not supported 1041 | XMLUnknown& operator=(const XMLUnknown&); // not supported 1042 | }; 1043 | 1044 | 1045 | 1046 | /** An attribute is a name-value pair. Elements have an arbitrary 1047 | number of attributes, each with a unique name. 1048 | @note The attributes are not XMLNodes. You may only query the 1049 | Next() attribute in a list. 1050 | */ 1051 | class TINYXML2_LIB XMLAttribute 1052 | { 1053 | friend class XMLElement; 1054 | public: 1055 | /// The name of the attribute. 1056 | const char* Name() const; 1057 | 1058 | /// The value of the attribute. 1059 | const char* Value() const; 1060 | 1061 | /// The next attribute in the list. 1062 | const XMLAttribute* Next() const { 1063 | return _next; 1064 | } 1065 | 1066 | /** IntValue interprets the attribute as an integer, and returns the value. 1067 | If the value isn't an integer, 0 will be returned. There is no error checking; 1068 | use QueryIntValue() if you need error checking. 1069 | */ 1070 | int IntValue() const { 1071 | int i = 0; 1072 | QueryIntValue(&i); 1073 | return i; 1074 | } 1075 | 1076 | int64_t Int64Value() const { 1077 | int64_t i = 0; 1078 | QueryInt64Value(&i); 1079 | return i; 1080 | } 1081 | 1082 | /// Query as an unsigned integer. See IntValue() 1083 | unsigned UnsignedValue() const { 1084 | unsigned i = 0; 1085 | QueryUnsignedValue(&i); 1086 | return i; 1087 | } 1088 | /// Query as a boolean. See IntValue() 1089 | bool BoolValue() const { 1090 | bool b = false; 1091 | QueryBoolValue(&b); 1092 | return b; 1093 | } 1094 | /// Query as a double. See IntValue() 1095 | double DoubleValue() const { 1096 | double d = 0; 1097 | QueryDoubleValue(&d); 1098 | return d; 1099 | } 1100 | /// Query as a float. See IntValue() 1101 | float FloatValue() const { 1102 | float f = 0; 1103 | QueryFloatValue(&f); 1104 | return f; 1105 | } 1106 | 1107 | /** QueryIntValue interprets the attribute as an integer, and returns the value 1108 | in the provided parameter. The function will return XML_SUCCESS on success, 1109 | and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful. 1110 | */ 1111 | XMLError QueryIntValue(int* value) const; 1112 | /// See QueryIntValue 1113 | XMLError QueryUnsignedValue(unsigned int* value) const; 1114 | /// See QueryIntValue 1115 | XMLError QueryInt64Value(int64_t* value) const; 1116 | /// See QueryIntValue 1117 | XMLError QueryBoolValue(bool* value) const; 1118 | /// See QueryIntValue 1119 | XMLError QueryDoubleValue(double* value) const; 1120 | /// See QueryIntValue 1121 | XMLError QueryFloatValue(float* value) const; 1122 | 1123 | /// Set the attribute to a string value. 1124 | void SetAttribute(const char* value); 1125 | /// Set the attribute to value. 1126 | void SetAttribute(int value); 1127 | /// Set the attribute to value. 1128 | void SetAttribute(unsigned value); 1129 | /// Set the attribute to value. 1130 | void SetAttribute(int64_t value); 1131 | /// Set the attribute to value. 1132 | void SetAttribute(bool value); 1133 | /// Set the attribute to value. 1134 | void SetAttribute(double value); 1135 | /// Set the attribute to value. 1136 | void SetAttribute(float value); 1137 | 1138 | private: 1139 | enum { BUF_SIZE = 200 }; 1140 | 1141 | XMLAttribute() : _next(0), _memPool(0) {} 1142 | virtual ~XMLAttribute() {} 1143 | 1144 | XMLAttribute(const XMLAttribute&); // not supported 1145 | void operator=(const XMLAttribute&); // not supported 1146 | void SetName(const char* name); 1147 | 1148 | char* ParseDeep(char* p, bool processEntities); 1149 | 1150 | mutable StrPair _name; 1151 | mutable StrPair _value; 1152 | XMLAttribute* _next; 1153 | MemPool* _memPool; 1154 | }; 1155 | 1156 | 1157 | /** The element is a container class. It has a value, the element name, 1158 | and can contain other elements, text, comments, and unknowns. 1159 | Elements also contain an arbitrary number of attributes. 1160 | */ 1161 | class TINYXML2_LIB XMLElement : public XMLNode 1162 | { 1163 | friend class XMLDocument; 1164 | public: 1165 | /// Get the name of an element (which is the Value() of the node.) 1166 | const char* Name() const { 1167 | return Value(); 1168 | } 1169 | /// Set the name of the element. 1170 | void SetName(const char* str, bool staticMem = false) { 1171 | SetValue(str, staticMem); 1172 | } 1173 | 1174 | virtual XMLElement* ToElement() { 1175 | return this; 1176 | } 1177 | virtual const XMLElement* ToElement() const { 1178 | return this; 1179 | } 1180 | virtual bool Accept(XMLVisitor* visitor) const; 1181 | 1182 | /** Given an attribute name, Attribute() returns the value 1183 | for the attribute of that name, or null if none 1184 | exists. For example: 1185 | @verbatim 1186 | const char* value = ele->Attribute( "foo" ); 1187 | @endverbatim 1188 | The 'value' parameter is normally null. However, if specified, 1189 | the attribute will only be returned if the 'name' and 'value' 1190 | match. This allow you to write code: 1191 | @verbatim 1192 | if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar(); 1193 | @endverbatim 1194 | rather than: 1195 | @verbatim 1196 | if ( ele->Attribute( "foo" ) ) { 1197 | if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar(); 1198 | } 1199 | @endverbatim 1200 | */ 1201 | const char* Attribute(const char* name, const char* value = 0) const; 1202 | 1203 | /** Given an attribute name, IntAttribute() returns the value 1204 | of the attribute interpreted as an integer. The default 1205 | value will be returned if the attribute isn't present, 1206 | or if there is an error. (For a method with error 1207 | checking, see QueryIntAttribute()). 1208 | */ 1209 | int IntAttribute(const char* name, int defaultValue = 0) const; 1210 | /// See IntAttribute() 1211 | unsigned UnsignedAttribute(const char* name, unsigned defaultValue = 0) const; 1212 | /// See IntAttribute() 1213 | int64_t Int64Attribute(const char* name, int64_t defaultValue = 0) const; 1214 | /// See IntAttribute() 1215 | bool BoolAttribute(const char* name, bool defaultValue = false) const; 1216 | /// See IntAttribute() 1217 | double DoubleAttribute(const char* name, double defaultValue = 0) const; 1218 | /// See IntAttribute() 1219 | float FloatAttribute(const char* name, float defaultValue = 0) const; 1220 | 1221 | /** Given an attribute name, QueryIntAttribute() returns 1222 | XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion 1223 | can't be performed, or XML_NO_ATTRIBUTE if the attribute 1224 | doesn't exist. If successful, the result of the conversion 1225 | will be written to 'value'. If not successful, nothing will 1226 | be written to 'value'. This allows you to provide default 1227 | value: 1228 | @verbatim 1229 | int value = 10; 1230 | QueryIntAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 1231 | @endverbatim 1232 | */ 1233 | XMLError QueryIntAttribute(const char* name, int* value) const { 1234 | const XMLAttribute* a = FindAttribute(name); 1235 | if (!a) { 1236 | return XML_NO_ATTRIBUTE; 1237 | } 1238 | return a->QueryIntValue(value); 1239 | } 1240 | 1241 | /// See QueryIntAttribute() 1242 | XMLError QueryUnsignedAttribute(const char* name, unsigned int* value) const { 1243 | const XMLAttribute* a = FindAttribute(name); 1244 | if (!a) { 1245 | return XML_NO_ATTRIBUTE; 1246 | } 1247 | return a->QueryUnsignedValue(value); 1248 | } 1249 | 1250 | /// See QueryIntAttribute() 1251 | XMLError QueryInt64Attribute(const char* name, int64_t* value) const { 1252 | const XMLAttribute* a = FindAttribute(name); 1253 | if (!a) { 1254 | return XML_NO_ATTRIBUTE; 1255 | } 1256 | return a->QueryInt64Value(value); 1257 | } 1258 | 1259 | /// See QueryIntAttribute() 1260 | XMLError QueryBoolAttribute(const char* name, bool* value) const { 1261 | const XMLAttribute* a = FindAttribute(name); 1262 | if (!a) { 1263 | return XML_NO_ATTRIBUTE; 1264 | } 1265 | return a->QueryBoolValue(value); 1266 | } 1267 | /// See QueryIntAttribute() 1268 | XMLError QueryDoubleAttribute(const char* name, double* value) const { 1269 | const XMLAttribute* a = FindAttribute(name); 1270 | if (!a) { 1271 | return XML_NO_ATTRIBUTE; 1272 | } 1273 | return a->QueryDoubleValue(value); 1274 | } 1275 | /// See QueryIntAttribute() 1276 | XMLError QueryFloatAttribute(const char* name, float* value) const { 1277 | const XMLAttribute* a = FindAttribute(name); 1278 | if (!a) { 1279 | return XML_NO_ATTRIBUTE; 1280 | } 1281 | return a->QueryFloatValue(value); 1282 | } 1283 | 1284 | 1285 | /** Given an attribute name, QueryAttribute() returns 1286 | XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion 1287 | can't be performed, or XML_NO_ATTRIBUTE if the attribute 1288 | doesn't exist. It is overloaded for the primitive types, 1289 | and is a generally more convenient replacement of 1290 | QueryIntAttribute() and related functions. 1291 | 1292 | If successful, the result of the conversion 1293 | will be written to 'value'. If not successful, nothing will 1294 | be written to 'value'. This allows you to provide default 1295 | value: 1296 | @verbatim 1297 | int value = 10; 1298 | QueryAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 1299 | @endverbatim 1300 | */ 1301 | int QueryAttribute(const char* name, int* value) const { 1302 | return QueryIntAttribute(name, value); 1303 | } 1304 | 1305 | int QueryAttribute(const char* name, unsigned int* value) const { 1306 | return QueryUnsignedAttribute(name, value); 1307 | } 1308 | 1309 | int QueryAttribute(const char* name, int64_t* value) const { 1310 | return QueryInt64Attribute(name, value); 1311 | } 1312 | 1313 | int QueryAttribute(const char* name, bool* value) const { 1314 | return QueryBoolAttribute(name, value); 1315 | } 1316 | 1317 | int QueryAttribute(const char* name, double* value) const { 1318 | return QueryDoubleAttribute(name, value); 1319 | } 1320 | 1321 | int QueryAttribute(const char* name, float* value) const { 1322 | return QueryFloatAttribute(name, value); 1323 | } 1324 | 1325 | /// Sets the named attribute to value. 1326 | void SetAttribute(const char* name, const char* value) { 1327 | XMLAttribute* a = FindOrCreateAttribute(name); 1328 | a->SetAttribute(value); 1329 | } 1330 | /// Sets the named attribute to value. 1331 | void SetAttribute(const char* name, int value) { 1332 | XMLAttribute* a = FindOrCreateAttribute(name); 1333 | a->SetAttribute(value); 1334 | } 1335 | /// Sets the named attribute to value. 1336 | void SetAttribute(const char* name, unsigned value) { 1337 | XMLAttribute* a = FindOrCreateAttribute(name); 1338 | a->SetAttribute(value); 1339 | } 1340 | 1341 | /// Sets the named attribute to value. 1342 | void SetAttribute(const char* name, int64_t value) { 1343 | XMLAttribute* a = FindOrCreateAttribute(name); 1344 | a->SetAttribute(value); 1345 | } 1346 | 1347 | /// Sets the named attribute to value. 1348 | void SetAttribute(const char* name, bool value) { 1349 | XMLAttribute* a = FindOrCreateAttribute(name); 1350 | a->SetAttribute(value); 1351 | } 1352 | /// Sets the named attribute to value. 1353 | void SetAttribute(const char* name, double value) { 1354 | XMLAttribute* a = FindOrCreateAttribute(name); 1355 | a->SetAttribute(value); 1356 | } 1357 | /// Sets the named attribute to value. 1358 | void SetAttribute(const char* name, float value) { 1359 | XMLAttribute* a = FindOrCreateAttribute(name); 1360 | a->SetAttribute(value); 1361 | } 1362 | 1363 | /** 1364 | Delete an attribute. 1365 | */ 1366 | void DeleteAttribute(const char* name); 1367 | 1368 | /// Return the first attribute in the list. 1369 | const XMLAttribute* FirstAttribute() const { 1370 | return _rootAttribute; 1371 | } 1372 | /// Query a specific attribute in the list. 1373 | const XMLAttribute* FindAttribute(const char* name) const; 1374 | 1375 | /** Convenience function for easy access to the text inside an element. Although easy 1376 | and concise, GetText() is limited compared to getting the XMLText child 1377 | and accessing it directly. 1378 | If the first child of 'this' is a XMLText, the GetText() 1379 | returns the character string of the Text node, else null is returned. 1380 | This is a convenient method for getting the text of simple contained text: 1381 | @verbatim 1382 | This is text 1383 | const char* str = fooElement->GetText(); 1384 | @endverbatim 1385 | 'str' will be a pointer to "This is text". 1386 | Note that this function can be misleading. If the element foo was created from 1387 | this XML: 1388 | @verbatim 1389 | This is text 1390 | @endverbatim 1391 | then the value of str would be null. The first child node isn't a text node, it is 1392 | another element. From this XML: 1393 | @verbatim 1394 | This is text 1395 | @endverbatim 1396 | GetText() will return "This is ". 1397 | */ 1398 | const char* GetText() const; 1399 | 1400 | /** Convenience function for easy access to the text inside an element. Although easy 1401 | and concise, SetText() is limited compared to creating an XMLText child 1402 | and mutating it directly. 1403 | If the first child of 'this' is a XMLText, SetText() sets its value to 1404 | the given string, otherwise it will create a first child that is an XMLText. 1405 | This is a convenient method for setting the text of simple contained text: 1406 | @verbatim 1407 | This is text 1408 | fooElement->SetText( "Hullaballoo!" ); 1409 | Hullaballoo! 1410 | @endverbatim 1411 | Note that this function can be misleading. If the element foo was created from 1412 | this XML: 1413 | @verbatim 1414 | This is text 1415 | @endverbatim 1416 | then it will not change "This is text", but rather prefix it with a text element: 1417 | @verbatim 1418 | Hullaballoo!This is text 1419 | @endverbatim 1420 | 1421 | For this XML: 1422 | @verbatim 1423 | 1424 | @endverbatim 1425 | SetText() will generate 1426 | @verbatim 1427 | Hullaballoo! 1428 | @endverbatim 1429 | */ 1430 | void SetText(const char* inText); 1431 | /// Convenience method for setting text inside an element. See SetText() for important limitations. 1432 | void SetText(int value); 1433 | /// Convenience method for setting text inside an element. See SetText() for important limitations. 1434 | void SetText(unsigned value); 1435 | /// Convenience method for setting text inside an element. See SetText() for important limitations. 1436 | void SetText(int64_t value); 1437 | /// Convenience method for setting text inside an element. See SetText() for important limitations. 1438 | void SetText(bool value); 1439 | /// Convenience method for setting text inside an element. See SetText() for important limitations. 1440 | void SetText(double value); 1441 | /// Convenience method for setting text inside an element. See SetText() for important limitations. 1442 | void SetText(float value); 1443 | 1444 | /** 1445 | Convenience method to query the value of a child text node. This is probably best 1446 | shown by example. Given you have a document is this form: 1447 | @verbatim 1448 | 1449 | 1 1450 | 1.4 1451 | 1452 | @endverbatim 1453 | The QueryIntText() and similar functions provide a safe and easier way to get to the 1454 | "value" of x and y. 1455 | @verbatim 1456 | int x = 0; 1457 | float y = 0; // types of x and y are contrived for example 1458 | const XMLElement* xElement = pointElement->FirstChildElement( "x" ); 1459 | const XMLElement* yElement = pointElement->FirstChildElement( "y" ); 1460 | xElement->QueryIntText( &x ); 1461 | yElement->QueryFloatText( &y ); 1462 | @endverbatim 1463 | @returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted 1464 | to the requested type, and XML_NO_TEXT_NODE if there is no child text to query. 1465 | */ 1466 | XMLError QueryIntText(int* ival) const; 1467 | /// See QueryIntText() 1468 | XMLError QueryUnsignedText(unsigned* uval) const; 1469 | /// See QueryIntText() 1470 | XMLError QueryInt64Text(int64_t* uval) const; 1471 | /// See QueryIntText() 1472 | XMLError QueryBoolText(bool* bval) const; 1473 | /// See QueryIntText() 1474 | XMLError QueryDoubleText(double* dval) const; 1475 | /// See QueryIntText() 1476 | XMLError QueryFloatText(float* fval) const; 1477 | 1478 | int IntText(int defaultValue = 0) const; 1479 | 1480 | /// See QueryIntText() 1481 | unsigned UnsignedText(unsigned defaultValue = 0) const; 1482 | /// See QueryIntText() 1483 | int64_t Int64Text(int64_t defaultValue = 0) const; 1484 | /// See QueryIntText() 1485 | bool BoolText(bool defaultValue = false) const; 1486 | /// See QueryIntText() 1487 | double DoubleText(double defaultValue = 0) const; 1488 | /// See QueryIntText() 1489 | float FloatText(float defaultValue = 0) const; 1490 | 1491 | // internal: 1492 | enum { 1493 | OPEN, // 1494 | CLOSED, // 1495 | CLOSING // 1496 | }; 1497 | int ClosingType() const { 1498 | return _closingType; 1499 | } 1500 | virtual XMLNode* ShallowClone(XMLDocument* document) const; 1501 | virtual bool ShallowEqual(const XMLNode* compare) const; 1502 | 1503 | protected: 1504 | char* ParseDeep(char* p, StrPair* endTag); 1505 | 1506 | private: 1507 | XMLElement(XMLDocument* doc); 1508 | virtual ~XMLElement(); 1509 | XMLElement(const XMLElement&); // not supported 1510 | void operator=(const XMLElement&); // not supported 1511 | 1512 | XMLAttribute* FindAttribute(const char* name) { 1513 | return const_cast(const_cast(this)->FindAttribute(name)); 1514 | } 1515 | XMLAttribute* FindOrCreateAttribute(const char* name); 1516 | //void LinkAttribute( XMLAttribute* attrib ); 1517 | char* ParseAttributes(char* p); 1518 | static void DeleteAttribute(XMLAttribute* attribute); 1519 | XMLAttribute* CreateAttribute(); 1520 | 1521 | enum { BUF_SIZE = 200 }; 1522 | int _closingType; 1523 | // The attribute list is ordered; there is no 'lastAttribute' 1524 | // because the list needs to be scanned for dupes before adding 1525 | // a new attribute. 1526 | XMLAttribute* _rootAttribute; 1527 | }; 1528 | 1529 | 1530 | enum Whitespace { 1531 | PRESERVE_WHITESPACE, 1532 | COLLAPSE_WHITESPACE 1533 | }; 1534 | 1535 | 1536 | /** A Document binds together all the functionality. 1537 | It can be saved, loaded, and printed to the screen. 1538 | All Nodes are connected and allocated to a Document. 1539 | If the Document is deleted, all its Nodes are also deleted. 1540 | */ 1541 | class TINYXML2_LIB XMLDocument : public XMLNode 1542 | { 1543 | friend class XMLElement; 1544 | public: 1545 | /// constructor 1546 | XMLDocument(bool processEntities = true, Whitespace = PRESERVE_WHITESPACE); 1547 | ~XMLDocument(); 1548 | 1549 | virtual XMLDocument* ToDocument() { 1550 | TIXMLASSERT(this == _document); 1551 | return this; 1552 | } 1553 | virtual const XMLDocument* ToDocument() const { 1554 | TIXMLASSERT(this == _document); 1555 | return this; 1556 | } 1557 | 1558 | /** 1559 | Parse an XML file from a character string. 1560 | Returns XML_SUCCESS (0) on success, or 1561 | an errorID. 1562 | You may optionally pass in the 'nBytes', which is 1563 | the number of bytes which will be parsed. If not 1564 | specified, TinyXML-2 will assume 'xml' points to a 1565 | null terminated string. 1566 | */ 1567 | XMLError Parse(const char* xml, size_t nBytes = (size_t)(-1)); 1568 | 1569 | /** 1570 | Load an XML file from disk. 1571 | Returns XML_SUCCESS (0) on success, or 1572 | an errorID. 1573 | */ 1574 | XMLError LoadFile(const char* filename); 1575 | 1576 | /** 1577 | Load an XML file from disk. You are responsible 1578 | for providing and closing the FILE*. 1579 | 1580 | NOTE: The file should be opened as binary ("rb") 1581 | not text in order for TinyXML-2 to correctly 1582 | do newline normalization. 1583 | Returns XML_SUCCESS (0) on success, or 1584 | an errorID. 1585 | */ 1586 | XMLError LoadFile(FILE*); 1587 | 1588 | /** 1589 | Save the XML file to disk. 1590 | Returns XML_SUCCESS (0) on success, or 1591 | an errorID. 1592 | */ 1593 | XMLError SaveFile(const char* filename, bool compact = false); 1594 | 1595 | /** 1596 | Save the XML file to disk. You are responsible 1597 | for providing and closing the FILE*. 1598 | Returns XML_SUCCESS (0) on success, or 1599 | an errorID. 1600 | */ 1601 | XMLError SaveFile(FILE* fp, bool compact = false); 1602 | 1603 | bool ProcessEntities() const { 1604 | return _processEntities; 1605 | } 1606 | Whitespace WhitespaceMode() const { 1607 | return _whitespace; 1608 | } 1609 | 1610 | /** 1611 | Returns true if this document has a leading Byte Order Mark of UTF8. 1612 | */ 1613 | bool HasBOM() const { 1614 | return _writeBOM; 1615 | } 1616 | /** Sets whether to write the BOM when writing the file. 1617 | */ 1618 | void SetBOM(bool useBOM) { 1619 | _writeBOM = useBOM; 1620 | } 1621 | 1622 | /** Return the root element of DOM. Equivalent to FirstChildElement(). 1623 | To get the first node, use FirstChild(). 1624 | */ 1625 | XMLElement* RootElement() { 1626 | return FirstChildElement(); 1627 | } 1628 | const XMLElement* RootElement() const { 1629 | return FirstChildElement(); 1630 | } 1631 | 1632 | /** Print the Document. If the Printer is not provided, it will 1633 | print to stdout. If you provide Printer, this can print to a file: 1634 | @verbatim 1635 | XMLPrinter printer( fp ); 1636 | doc.Print( &printer ); 1637 | @endverbatim 1638 | Or you can use a printer to print to memory: 1639 | @verbatim 1640 | XMLPrinter printer; 1641 | doc.Print( &printer ); 1642 | // printer.CStr() has a const char* to the XML 1643 | @endverbatim 1644 | */ 1645 | void Print(XMLPrinter* streamer = 0) const; 1646 | virtual bool Accept(XMLVisitor* visitor) const; 1647 | 1648 | /** 1649 | Create a new Element associated with 1650 | this Document. The memory for the Element 1651 | is managed by the Document. 1652 | */ 1653 | XMLElement* NewElement(const char* name); 1654 | /** 1655 | Create a new Comment associated with 1656 | this Document. The memory for the Comment 1657 | is managed by the Document. 1658 | */ 1659 | XMLComment* NewComment(const char* comment); 1660 | /** 1661 | Create a new Text associated with 1662 | this Document. The memory for the Text 1663 | is managed by the Document. 1664 | */ 1665 | XMLText* NewText(const char* text); 1666 | /** 1667 | Create a new Declaration associated with 1668 | this Document. The memory for the object 1669 | is managed by the Document. 1670 | If the 'text' param is null, the standard 1671 | declaration is used.: 1672 | @verbatim 1673 | 1674 | @endverbatim 1675 | */ 1676 | XMLDeclaration* NewDeclaration(const char* text = 0); 1677 | /** 1678 | Create a new Unknown associated with 1679 | this Document. The memory for the object 1680 | is managed by the Document. 1681 | */ 1682 | XMLUnknown* NewUnknown(const char* text); 1683 | 1684 | /** 1685 | Delete a node associated with this document. 1686 | It will be unlinked from the DOM. 1687 | */ 1688 | void DeleteNode(XMLNode* node); 1689 | 1690 | void SetError(XMLError error, const char* str1, const char* str2); 1691 | 1692 | void ClearError() { 1693 | SetError(XML_SUCCESS, 0, 0); 1694 | } 1695 | 1696 | /// Return true if there was an error parsing the document. 1697 | bool Error() const { 1698 | return _errorID != XML_SUCCESS; 1699 | } 1700 | /// Return the errorID. 1701 | XMLError ErrorID() const { 1702 | return _errorID; 1703 | } 1704 | const char* ErrorName() const; 1705 | 1706 | /// Return a possibly helpful diagnostic location or string. 1707 | const char* GetErrorStr1() const { 1708 | return _errorStr1.GetStr(); 1709 | } 1710 | /// Return a possibly helpful secondary diagnostic location or string. 1711 | const char* GetErrorStr2() const { 1712 | return _errorStr2.GetStr(); 1713 | } 1714 | /// If there is an error, print it to stdout. 1715 | void PrintError() const; 1716 | 1717 | /// Clear the document, resetting it to the initial state. 1718 | void Clear(); 1719 | 1720 | // internal 1721 | char* Identify(char* p, XMLNode** node); 1722 | 1723 | virtual XMLNode* ShallowClone(XMLDocument* /*document*/) const { 1724 | return 0; 1725 | } 1726 | virtual bool ShallowEqual(const XMLNode* /*compare*/) const { 1727 | return false; 1728 | } 1729 | 1730 | private: 1731 | XMLDocument(const XMLDocument&); // not supported 1732 | void operator=(const XMLDocument&); // not supported 1733 | 1734 | bool _writeBOM; 1735 | bool _processEntities; 1736 | XMLError _errorID; 1737 | Whitespace _whitespace; 1738 | mutable StrPair _errorStr1; 1739 | mutable StrPair _errorStr2; 1740 | char* _charBuffer; 1741 | 1742 | MemPoolT< sizeof(XMLElement) > _elementPool; 1743 | MemPoolT< sizeof(XMLAttribute) > _attributePool; 1744 | MemPoolT< sizeof(XMLText) > _textPool; 1745 | MemPoolT< sizeof(XMLComment) > _commentPool; 1746 | 1747 | static const char* _errorNames[XML_ERROR_COUNT]; 1748 | 1749 | void Parse(); 1750 | }; 1751 | 1752 | 1753 | /** 1754 | A XMLHandle is a class that wraps a node pointer with null checks; this is 1755 | an incredibly useful thing. Note that XMLHandle is not part of the TinyXML-2 1756 | DOM structure. It is a separate utility class. 1757 | Take an example: 1758 | @verbatim 1759 | 1760 | 1761 | 1762 | 1763 | 1764 | 1765 | @endverbatim 1766 | Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very 1767 | easy to write a *lot* of code that looks like: 1768 | @verbatim 1769 | XMLElement* root = document.FirstChildElement( "Document" ); 1770 | if ( root ) 1771 | { 1772 | XMLElement* element = root->FirstChildElement( "Element" ); 1773 | if ( element ) 1774 | { 1775 | XMLElement* child = element->FirstChildElement( "Child" ); 1776 | if ( child ) 1777 | { 1778 | XMLElement* child2 = child->NextSiblingElement( "Child" ); 1779 | if ( child2 ) 1780 | { 1781 | // Finally do something useful. 1782 | @endverbatim 1783 | And that doesn't even cover "else" cases. XMLHandle addresses the verbosity 1784 | of such code. A XMLHandle checks for null pointers so it is perfectly safe 1785 | and correct to use: 1786 | @verbatim 1787 | XMLHandle docHandle( &document ); 1788 | XMLElement* child2 = docHandle.FirstChildElement( "Document" ).FirstChildElement( "Element" ).FirstChildElement().NextSiblingElement(); 1789 | if ( child2 ) 1790 | { 1791 | // do something useful 1792 | @endverbatim 1793 | Which is MUCH more concise and useful. 1794 | It is also safe to copy handles - internally they are nothing more than node pointers. 1795 | @verbatim 1796 | XMLHandle handleCopy = handle; 1797 | @endverbatim 1798 | See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects. 1799 | */ 1800 | class TINYXML2_LIB XMLHandle 1801 | { 1802 | public: 1803 | /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. 1804 | XMLHandle(XMLNode* node) { 1805 | _node = node; 1806 | } 1807 | /// Create a handle from a node. 1808 | XMLHandle(XMLNode& node) { 1809 | _node = &node; 1810 | } 1811 | /// Copy constructor 1812 | XMLHandle(const XMLHandle& ref) { 1813 | _node = ref._node; 1814 | } 1815 | /// Assignment 1816 | XMLHandle& operator=(const XMLHandle& ref) { 1817 | _node = ref._node; 1818 | return *this; 1819 | } 1820 | 1821 | /// Get the first child of this handle. 1822 | XMLHandle FirstChild() { 1823 | return XMLHandle(_node ? _node->FirstChild() : 0); 1824 | } 1825 | /// Get the first child element of this handle. 1826 | XMLHandle FirstChildElement(const char* name = 0) { 1827 | return XMLHandle(_node ? _node->FirstChildElement(name) : 0); 1828 | } 1829 | /// Get the last child of this handle. 1830 | XMLHandle LastChild() { 1831 | return XMLHandle(_node ? _node->LastChild() : 0); 1832 | } 1833 | /// Get the last child element of this handle. 1834 | XMLHandle LastChildElement(const char* name = 0) { 1835 | return XMLHandle(_node ? _node->LastChildElement(name) : 0); 1836 | } 1837 | /// Get the previous sibling of this handle. 1838 | XMLHandle PreviousSibling() { 1839 | return XMLHandle(_node ? _node->PreviousSibling() : 0); 1840 | } 1841 | /// Get the previous sibling element of this handle. 1842 | XMLHandle PreviousSiblingElement(const char* name = 0) { 1843 | return XMLHandle(_node ? _node->PreviousSiblingElement(name) : 0); 1844 | } 1845 | /// Get the next sibling of this handle. 1846 | XMLHandle NextSibling() { 1847 | return XMLHandle(_node ? _node->NextSibling() : 0); 1848 | } 1849 | /// Get the next sibling element of this handle. 1850 | XMLHandle NextSiblingElement(const char* name = 0) { 1851 | return XMLHandle(_node ? _node->NextSiblingElement(name) : 0); 1852 | } 1853 | 1854 | /// Safe cast to XMLNode. This can return null. 1855 | XMLNode* ToNode() { 1856 | return _node; 1857 | } 1858 | /// Safe cast to XMLElement. This can return null. 1859 | XMLElement* ToElement() { 1860 | return (_node ? _node->ToElement() : 0); 1861 | } 1862 | /// Safe cast to XMLText. This can return null. 1863 | XMLText* ToText() { 1864 | return (_node ? _node->ToText() : 0); 1865 | } 1866 | /// Safe cast to XMLUnknown. This can return null. 1867 | XMLUnknown* ToUnknown() { 1868 | return (_node ? _node->ToUnknown() : 0); 1869 | } 1870 | /// Safe cast to XMLDeclaration. This can return null. 1871 | XMLDeclaration* ToDeclaration() { 1872 | return (_node ? _node->ToDeclaration() : 0); 1873 | } 1874 | 1875 | private: 1876 | XMLNode* _node; 1877 | }; 1878 | 1879 | 1880 | /** 1881 | A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the 1882 | same in all regards, except for the 'const' qualifiers. See XMLHandle for API. 1883 | */ 1884 | class TINYXML2_LIB XMLConstHandle 1885 | { 1886 | public: 1887 | XMLConstHandle(const XMLNode* node) { 1888 | _node = node; 1889 | } 1890 | XMLConstHandle(const XMLNode& node) { 1891 | _node = &node; 1892 | } 1893 | XMLConstHandle(const XMLConstHandle& ref) { 1894 | _node = ref._node; 1895 | } 1896 | 1897 | XMLConstHandle& operator=(const XMLConstHandle& ref) { 1898 | _node = ref._node; 1899 | return *this; 1900 | } 1901 | 1902 | const XMLConstHandle FirstChild() const { 1903 | return XMLConstHandle(_node ? _node->FirstChild() : 0); 1904 | } 1905 | const XMLConstHandle FirstChildElement(const char* name = 0) const { 1906 | return XMLConstHandle(_node ? _node->FirstChildElement(name) : 0); 1907 | } 1908 | const XMLConstHandle LastChild() const { 1909 | return XMLConstHandle(_node ? _node->LastChild() : 0); 1910 | } 1911 | const XMLConstHandle LastChildElement(const char* name = 0) const { 1912 | return XMLConstHandle(_node ? _node->LastChildElement(name) : 0); 1913 | } 1914 | const XMLConstHandle PreviousSibling() const { 1915 | return XMLConstHandle(_node ? _node->PreviousSibling() : 0); 1916 | } 1917 | const XMLConstHandle PreviousSiblingElement(const char* name = 0) const { 1918 | return XMLConstHandle(_node ? _node->PreviousSiblingElement(name) : 0); 1919 | } 1920 | const XMLConstHandle NextSibling() const { 1921 | return XMLConstHandle(_node ? _node->NextSibling() : 0); 1922 | } 1923 | const XMLConstHandle NextSiblingElement(const char* name = 0) const { 1924 | return XMLConstHandle(_node ? _node->NextSiblingElement(name) : 0); 1925 | } 1926 | 1927 | 1928 | const XMLNode* ToNode() const { 1929 | return _node; 1930 | } 1931 | const XMLElement* ToElement() const { 1932 | return (_node ? _node->ToElement() : 0); 1933 | } 1934 | const XMLText* ToText() const { 1935 | return (_node ? _node->ToText() : 0); 1936 | } 1937 | const XMLUnknown* ToUnknown() const { 1938 | return (_node ? _node->ToUnknown() : 0); 1939 | } 1940 | const XMLDeclaration* ToDeclaration() const { 1941 | return (_node ? _node->ToDeclaration() : 0); 1942 | } 1943 | 1944 | private: 1945 | const XMLNode* _node; 1946 | }; 1947 | 1948 | 1949 | /** 1950 | Printing functionality. The XMLPrinter gives you more 1951 | options than the XMLDocument::Print() method. 1952 | It can: 1953 | -# Print to memory. 1954 | -# Print to a file you provide. 1955 | -# Print XML without a XMLDocument. 1956 | Print to Memory 1957 | @verbatim 1958 | XMLPrinter printer; 1959 | doc.Print( &printer ); 1960 | SomeFunction( printer.CStr() ); 1961 | @endverbatim 1962 | Print to a File 1963 | You provide the file pointer. 1964 | @verbatim 1965 | XMLPrinter printer( fp ); 1966 | doc.Print( &printer ); 1967 | @endverbatim 1968 | Print without a XMLDocument 1969 | When loading, an XML parser is very useful. However, sometimes 1970 | when saving, it just gets in the way. The code is often set up 1971 | for streaming, and constructing the DOM is just overhead. 1972 | The Printer supports the streaming case. The following code 1973 | prints out a trivially simple XML file without ever creating 1974 | an XML document. 1975 | @verbatim 1976 | XMLPrinter printer( fp ); 1977 | printer.OpenElement( "foo" ); 1978 | printer.PushAttribute( "foo", "bar" ); 1979 | printer.CloseElement(); 1980 | @endverbatim 1981 | */ 1982 | class TINYXML2_LIB XMLPrinter : public XMLVisitor 1983 | { 1984 | public: 1985 | /** Construct the printer. If the FILE* is specified, 1986 | this will print to the FILE. Else it will print 1987 | to memory, and the result is available in CStr(). 1988 | If 'compact' is set to true, then output is created 1989 | with only required whitespace and newlines. 1990 | */ 1991 | XMLPrinter(FILE* file = 0, bool compact = false, int depth = 0); 1992 | virtual ~XMLPrinter() {} 1993 | 1994 | /** If streaming, write the BOM and declaration. */ 1995 | void PushHeader(bool writeBOM, bool writeDeclaration); 1996 | /** If streaming, start writing an element. 1997 | The element must be closed with CloseElement() 1998 | */ 1999 | void OpenElement(const char* name, bool compactMode = false); 2000 | /// If streaming, add an attribute to an open element. 2001 | void PushAttribute(const char* name, const char* value); 2002 | void PushAttribute(const char* name, int value); 2003 | void PushAttribute(const char* name, unsigned value); 2004 | void PushAttribute(const char* name, int64_t value); 2005 | void PushAttribute(const char* name, bool value); 2006 | void PushAttribute(const char* name, double value); 2007 | /// If streaming, close the Element. 2008 | virtual void CloseElement(bool compactMode = false); 2009 | 2010 | /// Add a text node. 2011 | void PushText(const char* text, bool cdata = false); 2012 | /// Add a text node from an integer. 2013 | void PushText(int value); 2014 | /// Add a text node from an unsigned. 2015 | void PushText(unsigned value); 2016 | /// Add a text node from an unsigned. 2017 | void PushText(int64_t value); 2018 | /// Add a text node from a bool. 2019 | void PushText(bool value); 2020 | /// Add a text node from a float. 2021 | void PushText(float value); 2022 | /// Add a text node from a double. 2023 | void PushText(double value); 2024 | 2025 | /// Add a comment 2026 | void PushComment(const char* comment); 2027 | 2028 | void PushDeclaration(const char* value); 2029 | void PushUnknown(const char* value); 2030 | 2031 | virtual bool VisitEnter(const XMLDocument& /*doc*/); 2032 | virtual bool VisitExit(const XMLDocument& /*doc*/) { 2033 | return true; 2034 | } 2035 | 2036 | virtual bool VisitEnter(const XMLElement& element, const XMLAttribute* attribute); 2037 | virtual bool VisitExit(const XMLElement& element); 2038 | 2039 | virtual bool Visit(const XMLText& text); 2040 | virtual bool Visit(const XMLComment& comment); 2041 | virtual bool Visit(const XMLDeclaration& declaration); 2042 | virtual bool Visit(const XMLUnknown& unknown); 2043 | 2044 | /** 2045 | If in print to memory mode, return a pointer to 2046 | the XML file in memory. 2047 | */ 2048 | const char* CStr() const { 2049 | return _buffer.Mem(); 2050 | } 2051 | /** 2052 | If in print to memory mode, return the size 2053 | of the XML file in memory. (Note the size returned 2054 | includes the terminating null.) 2055 | */ 2056 | int CStrSize() const { 2057 | return _buffer.Size(); 2058 | } 2059 | /** 2060 | If in print to memory mode, reset the buffer to the 2061 | beginning. 2062 | */ 2063 | void ClearBuffer() { 2064 | _buffer.Clear(); 2065 | _buffer.Push(0); 2066 | } 2067 | 2068 | protected: 2069 | virtual bool CompactMode(const XMLElement&) { return _compactMode; } 2070 | 2071 | /** Prints out the space before an element. You may override to change 2072 | the space and tabs used. A PrintSpace() override should call Print(). 2073 | */ 2074 | virtual void PrintSpace(int depth); 2075 | void Print(const char* format, ...); 2076 | 2077 | void SealElementIfJustOpened(); 2078 | bool _elementJustOpened; 2079 | DynArray< const char*, 10 > _stack; 2080 | 2081 | private: 2082 | void PrintString(const char*, bool restrictedEntitySet); // prints out, after detecting entities. 2083 | 2084 | bool _firstElement; 2085 | FILE* _fp; 2086 | int _depth; 2087 | int _textDepth; 2088 | bool _processEntities; 2089 | bool _compactMode; 2090 | 2091 | enum { 2092 | ENTITY_RANGE = 64, 2093 | BUF_SIZE = 200 2094 | }; 2095 | bool _entityFlag[ENTITY_RANGE]; 2096 | bool _restrictedEntityFlag[ENTITY_RANGE]; 2097 | 2098 | DynArray< char, 20 > _buffer; 2099 | }; 2100 | 2101 | 2102 | } // tinyxml2 2103 | 2104 | #if defined(_MSC_VER) 2105 | # pragma warning(pop) 2106 | #endif 2107 | 2108 | #endif // TINYXML2_INCLUDED -------------------------------------------------------------------------------- /FasterStereoConsole/tinyxml2.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Original code by Lee Thomason (www.grinninglizard.com) 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any 5 | damages arising from the use of this software. 6 | Permission is granted to anyone to use this software for any 7 | purpose, including commercial applications, and to alter it and 8 | redistribute it freely, subject to the following restrictions: 9 | 1. The origin of this software must not be misrepresented; you must 10 | not claim that you wrote the original software. If you use this 11 | software in a product, an acknowledgment in the product documentation 12 | would be appreciated but is not required. 13 | 2. Altered source versions must be plainly marked as such, and 14 | must not be misrepresented as being the original software. 15 | 3. This notice may not be removed or altered from any source 16 | distribution. 17 | */ 18 | #include "tinyxml2.h" 19 | 20 | #include // yes, this one new style header, is in the Android SDK. 21 | #if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) 22 | # include 23 | # include 24 | #else 25 | # include 26 | # include 27 | #endif 28 | 29 | #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) 30 | // Microsoft Visual Studio, version 2005 and higher. Not WinCE. 31 | /*int _snprintf_s( 32 | char *buffer, 33 | size_t sizeOfBuffer, 34 | size_t count, 35 | const char *format [, 36 | argument] ... 37 | );*/ 38 | static inline int TIXML_SNPRINTF(char* buffer, size_t size, const char* format, ...) 39 | { 40 | va_list va; 41 | va_start(va, format); 42 | int result = vsnprintf_s(buffer, size, _TRUNCATE, format, va); 43 | va_end(va); 44 | return result; 45 | } 46 | 47 | static inline int TIXML_VSNPRINTF(char* buffer, size_t size, const char* format, va_list va) 48 | { 49 | int result = vsnprintf_s(buffer, size, _TRUNCATE, format, va); 50 | return result; 51 | } 52 | 53 | #define TIXML_VSCPRINTF _vscprintf 54 | #define TIXML_SSCANF sscanf_s 55 | #elif defined _MSC_VER 56 | // Microsoft Visual Studio 2003 and earlier or WinCE 57 | #define TIXML_SNPRINTF _snprintf 58 | #define TIXML_VSNPRINTF _vsnprintf 59 | #define TIXML_SSCANF sscanf 60 | #if (_MSC_VER < 1400 ) && (!defined WINCE) 61 | // Microsoft Visual Studio 2003 and not WinCE. 62 | #define TIXML_VSCPRINTF _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have. 63 | #else 64 | // Microsoft Visual Studio 2003 and earlier or WinCE. 65 | static inline int TIXML_VSCPRINTF(const char* format, va_list va) 66 | { 67 | int len = 512; 68 | for (;;) { 69 | len = len * 2; 70 | char* str = new char[len](); 71 | const int required = _vsnprintf(str, len, format, va); 72 | delete[] str; 73 | if (required != -1) { 74 | TIXMLASSERT(required >= 0); 75 | len = required; 76 | break; 77 | } 78 | } 79 | TIXMLASSERT(len >= 0); 80 | return len; 81 | } 82 | #endif 83 | #else 84 | // GCC version 3 and higher 85 | //#warning( "Using sn* functions." ) 86 | #define TIXML_SNPRINTF snprintf 87 | #define TIXML_VSNPRINTF vsnprintf 88 | static inline int TIXML_VSCPRINTF(const char* format, va_list va) 89 | { 90 | int len = vsnprintf(0, 0, format, va); 91 | TIXMLASSERT(len >= 0); 92 | return len; 93 | } 94 | #define TIXML_SSCANF sscanf 95 | #endif 96 | 97 | 98 | static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF 99 | static const char LF = LINE_FEED; 100 | static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out 101 | static const char CR = CARRIAGE_RETURN; 102 | static const char SINGLE_QUOTE = '\''; 103 | static const char DOUBLE_QUOTE = '\"'; 104 | 105 | // Bunch of unicode info at: 106 | // http://www.unicode.org/faq/utf_bom.html 107 | // ef bb bf (Microsoft "lead bytes") - designates UTF-8 108 | 109 | static const unsigned char TIXML_UTF_LEAD_0 = 0xefU; 110 | static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; 111 | static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; 112 | 113 | namespace tinyxml2 114 | { 115 | 116 | struct Entity { 117 | const char* pattern; 118 | int length; 119 | char value; 120 | }; 121 | 122 | static const int NUM_ENTITIES = 5; 123 | static const Entity entities[NUM_ENTITIES] = { 124 | { "quot", 4, DOUBLE_QUOTE }, 125 | { "amp", 3, '&' }, 126 | { "apos", 4, SINGLE_QUOTE }, 127 | { "lt", 2, '<' }, 128 | { "gt", 2, '>' } 129 | }; 130 | 131 | 132 | StrPair::~StrPair() 133 | { 134 | Reset(); 135 | } 136 | 137 | 138 | void StrPair::TransferTo(StrPair* other) 139 | { 140 | if (this == other) { 141 | return; 142 | } 143 | // This in effect implements the assignment operator by "moving" 144 | // ownership (as in auto_ptr). 145 | 146 | TIXMLASSERT(other != 0); 147 | TIXMLASSERT(other->_flags == 0); 148 | TIXMLASSERT(other->_start == 0); 149 | TIXMLASSERT(other->_end == 0); 150 | 151 | other->Reset(); 152 | 153 | other->_flags = _flags; 154 | other->_start = _start; 155 | other->_end = _end; 156 | 157 | _flags = 0; 158 | _start = 0; 159 | _end = 0; 160 | } 161 | 162 | void StrPair::Reset() 163 | { 164 | if (_flags & NEEDS_DELETE) { 165 | delete[] _start; 166 | } 167 | _flags = 0; 168 | _start = 0; 169 | _end = 0; 170 | } 171 | 172 | 173 | void StrPair::SetStr(const char* str, int flags) 174 | { 175 | TIXMLASSERT(str); 176 | Reset(); 177 | size_t len = strlen(str); 178 | TIXMLASSERT(_start == 0); 179 | _start = new char[len + 1]; 180 | memcpy(_start, str, len + 1); 181 | _end = _start + len; 182 | _flags = flags | NEEDS_DELETE; 183 | } 184 | 185 | 186 | char* StrPair::ParseText(char* p, const char* endTag, int strFlags) 187 | { 188 | TIXMLASSERT(p); 189 | TIXMLASSERT(endTag && *endTag); 190 | 191 | char* start = p; 192 | char endChar = *endTag; 193 | size_t length = strlen(endTag); 194 | 195 | // Inner loop of text parsing. 196 | while (*p) { 197 | if (*p == endChar && strncmp(p, endTag, length) == 0) { 198 | Set(start, p, strFlags); 199 | return p + length; 200 | } 201 | ++p; 202 | TIXMLASSERT(p); 203 | } 204 | return 0; 205 | } 206 | 207 | 208 | char* StrPair::ParseName(char* p) 209 | { 210 | if (!p || !(*p)) { 211 | return 0; 212 | } 213 | if (!XMLUtil::IsNameStartChar(*p)) { 214 | return 0; 215 | } 216 | 217 | char* const start = p; 218 | ++p; 219 | while (*p && XMLUtil::IsNameChar(*p)) { 220 | ++p; 221 | } 222 | 223 | Set(start, p, 0); 224 | return p; 225 | } 226 | 227 | 228 | void StrPair::CollapseWhitespace() 229 | { 230 | // Adjusting _start would cause undefined behavior on delete[] 231 | TIXMLASSERT((_flags & NEEDS_DELETE) == 0); 232 | // Trim leading space. 233 | _start = XMLUtil::SkipWhiteSpace(_start); 234 | 235 | if (*_start) { 236 | const char* p = _start; // the read pointer 237 | char* q = _start; // the write pointer 238 | 239 | while (*p) { 240 | if (XMLUtil::IsWhiteSpace(*p)) { 241 | p = XMLUtil::SkipWhiteSpace(p); 242 | if (*p == 0) { 243 | break; // don't write to q; this trims the trailing space. 244 | } 245 | *q = ' '; 246 | ++q; 247 | } 248 | *q = *p; 249 | ++q; 250 | ++p; 251 | } 252 | *q = 0; 253 | } 254 | } 255 | 256 | 257 | const char* StrPair::GetStr() 258 | { 259 | TIXMLASSERT(_start); 260 | TIXMLASSERT(_end); 261 | if (_flags & NEEDS_FLUSH) { 262 | *_end = 0; 263 | _flags ^= NEEDS_FLUSH; 264 | 265 | if (_flags) { 266 | const char* p = _start; // the read pointer 267 | char* q = _start; // the write pointer 268 | 269 | while (p < _end) { 270 | if ((_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR) { 271 | // CR-LF pair becomes LF 272 | // CR alone becomes LF 273 | // LF-CR becomes LF 274 | if (*(p + 1) == LF) { 275 | p += 2; 276 | } 277 | else { 278 | ++p; 279 | } 280 | *q = LF; 281 | ++q; 282 | } 283 | else if ((_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF) { 284 | if (*(p + 1) == CR) { 285 | p += 2; 286 | } 287 | else { 288 | ++p; 289 | } 290 | *q = LF; 291 | ++q; 292 | } 293 | else if ((_flags & NEEDS_ENTITY_PROCESSING) && *p == '&') { 294 | // Entities handled by tinyXML2: 295 | // - special entities in the entity table [in/out] 296 | // - numeric character reference [in] 297 | // 中 or 中 298 | 299 | if (*(p + 1) == '#') { 300 | const int buflen = 10; 301 | char buf[buflen] = { 0 }; 302 | int len = 0; 303 | char* adjusted = const_cast(XMLUtil::GetCharacterRef(p, buf, &len)); 304 | if (adjusted == 0) { 305 | *q = *p; 306 | ++p; 307 | ++q; 308 | } 309 | else { 310 | TIXMLASSERT(0 <= len && len <= buflen); 311 | TIXMLASSERT(q + len <= adjusted); 312 | p = adjusted; 313 | memcpy(q, buf, len); 314 | q += len; 315 | } 316 | } 317 | else { 318 | bool entityFound = false; 319 | for (int i = 0; i < NUM_ENTITIES; ++i) { 320 | const Entity& entity = entities[i]; 321 | if (strncmp(p + 1, entity.pattern, entity.length) == 0 322 | && *(p + entity.length + 1) == ';') { 323 | // Found an entity - convert. 324 | *q = entity.value; 325 | ++q; 326 | p += entity.length + 2; 327 | entityFound = true; 328 | break; 329 | } 330 | } 331 | if (!entityFound) { 332 | // fixme: treat as error? 333 | ++p; 334 | ++q; 335 | } 336 | } 337 | } 338 | else { 339 | *q = *p; 340 | ++p; 341 | ++q; 342 | } 343 | } 344 | *q = 0; 345 | } 346 | // The loop below has plenty going on, and this 347 | // is a less useful mode. Break it out. 348 | if (_flags & NEEDS_WHITESPACE_COLLAPSING) { 349 | CollapseWhitespace(); 350 | } 351 | _flags = (_flags & NEEDS_DELETE); 352 | } 353 | TIXMLASSERT(_start); 354 | return _start; 355 | } 356 | 357 | 358 | 359 | 360 | // --------- XMLUtil ----------- // 361 | 362 | const char* XMLUtil::ReadBOM(const char* p, bool* bom) 363 | { 364 | TIXMLASSERT(p); 365 | TIXMLASSERT(bom); 366 | *bom = false; 367 | const unsigned char* pu = reinterpret_cast(p); 368 | // Check for BOM: 369 | if (*(pu + 0) == TIXML_UTF_LEAD_0 370 | && *(pu + 1) == TIXML_UTF_LEAD_1 371 | && *(pu + 2) == TIXML_UTF_LEAD_2) { 372 | *bom = true; 373 | p += 3; 374 | } 375 | TIXMLASSERT(p); 376 | return p; 377 | } 378 | 379 | 380 | void XMLUtil::ConvertUTF32ToUTF8(unsigned long input, char* output, int* length) 381 | { 382 | const unsigned long BYTE_MASK = 0xBF; 383 | const unsigned long BYTE_MARK = 0x80; 384 | const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; 385 | 386 | if (input < 0x80) { 387 | *length = 1; 388 | } 389 | else if (input < 0x800) { 390 | *length = 2; 391 | } 392 | else if (input < 0x10000) { 393 | *length = 3; 394 | } 395 | else if (input < 0x200000) { 396 | *length = 4; 397 | } 398 | else { 399 | *length = 0; // This code won't convert this correctly anyway. 400 | return; 401 | } 402 | 403 | output += *length; 404 | 405 | // Scary scary fall throughs. 406 | switch (*length) { 407 | case 4: 408 | --output; 409 | *output = (char)((input | BYTE_MARK) & BYTE_MASK); 410 | input >>= 6; 411 | case 3: 412 | --output; 413 | *output = (char)((input | BYTE_MARK) & BYTE_MASK); 414 | input >>= 6; 415 | case 2: 416 | --output; 417 | *output = (char)((input | BYTE_MARK) & BYTE_MASK); 418 | input >>= 6; 419 | case 1: 420 | --output; 421 | *output = (char)(input | FIRST_BYTE_MARK[*length]); 422 | break; 423 | default: 424 | TIXMLASSERT(false); 425 | } 426 | } 427 | 428 | 429 | const char* XMLUtil::GetCharacterRef(const char* p, char* value, int* length) 430 | { 431 | // Presume an entity, and pull it out. 432 | *length = 0; 433 | 434 | if (*(p + 1) == '#' && *(p + 2)) { 435 | unsigned long ucs = 0; 436 | TIXMLASSERT(sizeof(ucs) >= 4); 437 | ptrdiff_t delta = 0; 438 | unsigned mult = 1; 439 | static const char SEMICOLON = ';'; 440 | 441 | if (*(p + 2) == 'x') { 442 | // Hexadecimal. 443 | const char* q = p + 3; 444 | if (!(*q)) { 445 | return 0; 446 | } 447 | 448 | q = strchr(q, SEMICOLON); 449 | 450 | if (!q) { 451 | return 0; 452 | } 453 | TIXMLASSERT(*q == SEMICOLON); 454 | 455 | delta = q - p; 456 | --q; 457 | 458 | while (*q != 'x') { 459 | unsigned int digit = 0; 460 | 461 | if (*q >= '0' && *q <= '9') { 462 | digit = *q - '0'; 463 | } 464 | else if (*q >= 'a' && *q <= 'f') { 465 | digit = *q - 'a' + 10; 466 | } 467 | else if (*q >= 'A' && *q <= 'F') { 468 | digit = *q - 'A' + 10; 469 | } 470 | else { 471 | return 0; 472 | } 473 | TIXMLASSERT(digit < 16); 474 | TIXMLASSERT(digit == 0 || mult <= UINT_MAX / digit); 475 | const unsigned int digitScaled = mult * digit; 476 | TIXMLASSERT(ucs <= ULONG_MAX - digitScaled); 477 | ucs += digitScaled; 478 | TIXMLASSERT(mult <= UINT_MAX / 16); 479 | mult *= 16; 480 | --q; 481 | } 482 | } 483 | else { 484 | // Decimal. 485 | const char* q = p + 2; 486 | if (!(*q)) { 487 | return 0; 488 | } 489 | 490 | q = strchr(q, SEMICOLON); 491 | 492 | if (!q) { 493 | return 0; 494 | } 495 | TIXMLASSERT(*q == SEMICOLON); 496 | 497 | delta = q - p; 498 | --q; 499 | 500 | while (*q != '#') { 501 | if (*q >= '0' && *q <= '9') { 502 | const unsigned int digit = *q - '0'; 503 | TIXMLASSERT(digit < 10); 504 | TIXMLASSERT(digit == 0 || mult <= UINT_MAX / digit); 505 | const unsigned int digitScaled = mult * digit; 506 | TIXMLASSERT(ucs <= ULONG_MAX - digitScaled); 507 | ucs += digitScaled; 508 | } 509 | else { 510 | return 0; 511 | } 512 | TIXMLASSERT(mult <= UINT_MAX / 10); 513 | mult *= 10; 514 | --q; 515 | } 516 | } 517 | // convert the UCS to UTF-8 518 | ConvertUTF32ToUTF8(ucs, value, length); 519 | return p + delta + 1; 520 | } 521 | return p + 1; 522 | } 523 | 524 | 525 | void XMLUtil::ToStr(int v, char* buffer, int bufferSize) 526 | { 527 | TIXML_SNPRINTF(buffer, bufferSize, "%d", v); 528 | } 529 | 530 | 531 | void XMLUtil::ToStr(unsigned v, char* buffer, int bufferSize) 532 | { 533 | TIXML_SNPRINTF(buffer, bufferSize, "%u", v); 534 | } 535 | 536 | 537 | void XMLUtil::ToStr(bool v, char* buffer, int bufferSize) 538 | { 539 | TIXML_SNPRINTF(buffer, bufferSize, "%s", v ? "true" : "false"); 540 | } 541 | 542 | /* 543 | ToStr() of a number is a very tricky topic. 544 | https://github.com/leethomason/tinyxml2/issues/106 545 | */ 546 | void XMLUtil::ToStr(float v, char* buffer, int bufferSize) 547 | { 548 | TIXML_SNPRINTF(buffer, bufferSize, "%.8g", v); 549 | } 550 | 551 | 552 | void XMLUtil::ToStr(double v, char* buffer, int bufferSize) 553 | { 554 | TIXML_SNPRINTF(buffer, bufferSize, "%.17g", v); 555 | } 556 | 557 | 558 | void XMLUtil::ToStr(int64_t v, char* buffer, int bufferSize) 559 | { 560 | // horrible syntax trick to make the compiler happy about %lld 561 | TIXML_SNPRINTF(buffer, bufferSize, "%lld", (long long)v); 562 | } 563 | 564 | 565 | bool XMLUtil::ToInt(const char* str, int* value) 566 | { 567 | if (TIXML_SSCANF(str, "%d", value) == 1) { 568 | return true; 569 | } 570 | return false; 571 | } 572 | 573 | bool XMLUtil::ToUnsigned(const char* str, unsigned *value) 574 | { 575 | if (TIXML_SSCANF(str, "%u", value) == 1) { 576 | return true; 577 | } 578 | return false; 579 | } 580 | 581 | bool XMLUtil::ToBool(const char* str, bool* value) 582 | { 583 | int ival = 0; 584 | if (ToInt(str, &ival)) { 585 | *value = (ival == 0) ? false : true; 586 | return true; 587 | } 588 | if (StringEqual(str, "true")) { 589 | *value = true; 590 | return true; 591 | } 592 | else if (StringEqual(str, "false")) { 593 | *value = false; 594 | return true; 595 | } 596 | return false; 597 | } 598 | 599 | 600 | bool XMLUtil::ToFloat(const char* str, float* value) 601 | { 602 | if (TIXML_SSCANF(str, "%f", value) == 1) { 603 | return true; 604 | } 605 | return false; 606 | } 607 | 608 | 609 | bool XMLUtil::ToDouble(const char* str, double* value) 610 | { 611 | if (TIXML_SSCANF(str, "%lf", value) == 1) { 612 | return true; 613 | } 614 | return false; 615 | } 616 | 617 | 618 | bool XMLUtil::ToInt64(const char* str, int64_t* value) 619 | { 620 | long long v = 0; // horrible syntax trick to make the compiler happy about %lld 621 | if (TIXML_SSCANF(str, "%lld", &v) == 1) { 622 | *value = (int64_t)v; 623 | return true; 624 | } 625 | return false; 626 | } 627 | 628 | 629 | char* XMLDocument::Identify(char* p, XMLNode** node) 630 | { 631 | TIXMLASSERT(node); 632 | TIXMLASSERT(p); 633 | char* const start = p; 634 | p = XMLUtil::SkipWhiteSpace(p); 635 | if (!*p) { 636 | *node = 0; 637 | TIXMLASSERT(p); 638 | return p; 639 | } 640 | 641 | // These strings define the matching patterns: 642 | static const char* xmlHeader = { "_memPool = &_commentPool; 661 | p += xmlHeaderLen; 662 | } 663 | else if (XMLUtil::StringEqual(p, commentHeader, commentHeaderLen)) { 664 | TIXMLASSERT(sizeof(XMLComment) == _commentPool.ItemSize()); 665 | returnNode = new (_commentPool.Alloc()) XMLComment(this); 666 | returnNode->_memPool = &_commentPool; 667 | p += commentHeaderLen; 668 | } 669 | else if (XMLUtil::StringEqual(p, cdataHeader, cdataHeaderLen)) { 670 | TIXMLASSERT(sizeof(XMLText) == _textPool.ItemSize()); 671 | XMLText* text = new (_textPool.Alloc()) XMLText(this); 672 | returnNode = text; 673 | returnNode->_memPool = &_textPool; 674 | p += cdataHeaderLen; 675 | text->SetCData(true); 676 | } 677 | else if (XMLUtil::StringEqual(p, dtdHeader, dtdHeaderLen)) { 678 | TIXMLASSERT(sizeof(XMLUnknown) == _commentPool.ItemSize()); 679 | returnNode = new (_commentPool.Alloc()) XMLUnknown(this); 680 | returnNode->_memPool = &_commentPool; 681 | p += dtdHeaderLen; 682 | } 683 | else if (XMLUtil::StringEqual(p, elementHeader, elementHeaderLen)) { 684 | TIXMLASSERT(sizeof(XMLElement) == _elementPool.ItemSize()); 685 | returnNode = new (_elementPool.Alloc()) XMLElement(this); 686 | returnNode->_memPool = &_elementPool; 687 | p += elementHeaderLen; 688 | } 689 | else { 690 | TIXMLASSERT(sizeof(XMLText) == _textPool.ItemSize()); 691 | returnNode = new (_textPool.Alloc()) XMLText(this); 692 | returnNode->_memPool = &_textPool; 693 | p = start; // Back it up, all the text counts. 694 | } 695 | 696 | TIXMLASSERT(returnNode); 697 | TIXMLASSERT(p); 698 | *node = returnNode; 699 | return p; 700 | } 701 | 702 | 703 | bool XMLDocument::Accept(XMLVisitor* visitor) const 704 | { 705 | TIXMLASSERT(visitor); 706 | if (visitor->VisitEnter(*this)) { 707 | for (const XMLNode* node = FirstChild(); node; node = node->NextSibling()) { 708 | if (!node->Accept(visitor)) { 709 | break; 710 | } 711 | } 712 | } 713 | return visitor->VisitExit(*this); 714 | } 715 | 716 | 717 | // --------- XMLNode ----------- // 718 | 719 | XMLNode::XMLNode(XMLDocument* doc) : 720 | _document(doc), 721 | _parent(0), 722 | _firstChild(0), _lastChild(0), 723 | _prev(0), _next(0), 724 | _userData(0), 725 | _memPool(0) 726 | { 727 | } 728 | 729 | 730 | XMLNode::~XMLNode() 731 | { 732 | DeleteChildren(); 733 | if (_parent) { 734 | _parent->Unlink(this); 735 | } 736 | } 737 | 738 | const char* XMLNode::Value() const 739 | { 740 | // Edge case: XMLDocuments don't have a Value. Return null. 741 | if (this->ToDocument()) 742 | return 0; 743 | return _value.GetStr(); 744 | } 745 | 746 | void XMLNode::SetValue(const char* str, bool staticMem) 747 | { 748 | if (staticMem) { 749 | _value.SetInternedStr(str); 750 | } 751 | else { 752 | _value.SetStr(str); 753 | } 754 | } 755 | 756 | 757 | void XMLNode::DeleteChildren() 758 | { 759 | while (_firstChild) { 760 | TIXMLASSERT(_lastChild); 761 | DeleteChild(_firstChild); 762 | } 763 | _firstChild = _lastChild = 0; 764 | } 765 | 766 | 767 | void XMLNode::Unlink(XMLNode* child) 768 | { 769 | TIXMLASSERT(child); 770 | TIXMLASSERT(child->_document == _document); 771 | TIXMLASSERT(child->_parent == this); 772 | if (child == _firstChild) { 773 | _firstChild = _firstChild->_next; 774 | } 775 | if (child == _lastChild) { 776 | _lastChild = _lastChild->_prev; 777 | } 778 | 779 | if (child->_prev) { 780 | child->_prev->_next = child->_next; 781 | } 782 | if (child->_next) { 783 | child->_next->_prev = child->_prev; 784 | } 785 | child->_parent = 0; 786 | } 787 | 788 | 789 | void XMLNode::DeleteChild(XMLNode* node) 790 | { 791 | TIXMLASSERT(node); 792 | TIXMLASSERT(node->_document == _document); 793 | TIXMLASSERT(node->_parent == this); 794 | Unlink(node); 795 | DeleteNode(node); 796 | } 797 | 798 | 799 | XMLNode* XMLNode::InsertEndChild(XMLNode* addThis) 800 | { 801 | TIXMLASSERT(addThis); 802 | if (addThis->_document != _document) { 803 | TIXMLASSERT(false); 804 | return 0; 805 | } 806 | InsertChildPreamble(addThis); 807 | 808 | if (_lastChild) { 809 | TIXMLASSERT(_firstChild); 810 | TIXMLASSERT(_lastChild->_next == 0); 811 | _lastChild->_next = addThis; 812 | addThis->_prev = _lastChild; 813 | _lastChild = addThis; 814 | 815 | addThis->_next = 0; 816 | } 817 | else { 818 | TIXMLASSERT(_firstChild == 0); 819 | _firstChild = _lastChild = addThis; 820 | 821 | addThis->_prev = 0; 822 | addThis->_next = 0; 823 | } 824 | addThis->_parent = this; 825 | return addThis; 826 | } 827 | 828 | 829 | XMLNode* XMLNode::InsertFirstChild(XMLNode* addThis) 830 | { 831 | TIXMLASSERT(addThis); 832 | if (addThis->_document != _document) { 833 | TIXMLASSERT(false); 834 | return 0; 835 | } 836 | InsertChildPreamble(addThis); 837 | 838 | if (_firstChild) { 839 | TIXMLASSERT(_lastChild); 840 | TIXMLASSERT(_firstChild->_prev == 0); 841 | 842 | _firstChild->_prev = addThis; 843 | addThis->_next = _firstChild; 844 | _firstChild = addThis; 845 | 846 | addThis->_prev = 0; 847 | } 848 | else { 849 | TIXMLASSERT(_lastChild == 0); 850 | _firstChild = _lastChild = addThis; 851 | 852 | addThis->_prev = 0; 853 | addThis->_next = 0; 854 | } 855 | addThis->_parent = this; 856 | return addThis; 857 | } 858 | 859 | 860 | XMLNode* XMLNode::InsertAfterChild(XMLNode* afterThis, XMLNode* addThis) 861 | { 862 | TIXMLASSERT(addThis); 863 | if (addThis->_document != _document) { 864 | TIXMLASSERT(false); 865 | return 0; 866 | } 867 | 868 | TIXMLASSERT(afterThis); 869 | 870 | if (afterThis->_parent != this) { 871 | TIXMLASSERT(false); 872 | return 0; 873 | } 874 | 875 | if (afterThis->_next == 0) { 876 | // The last node or the only node. 877 | return InsertEndChild(addThis); 878 | } 879 | InsertChildPreamble(addThis); 880 | addThis->_prev = afterThis; 881 | addThis->_next = afterThis->_next; 882 | afterThis->_next->_prev = addThis; 883 | afterThis->_next = addThis; 884 | addThis->_parent = this; 885 | return addThis; 886 | } 887 | 888 | 889 | 890 | 891 | const XMLElement* XMLNode::FirstChildElement(const char* name) const 892 | { 893 | for (const XMLNode* node = _firstChild; node; node = node->_next) { 894 | const XMLElement* element = node->ToElementWithName(name); 895 | if (element) { 896 | return element; 897 | } 898 | } 899 | return 0; 900 | } 901 | 902 | 903 | const XMLElement* XMLNode::LastChildElement(const char* name) const 904 | { 905 | for (const XMLNode* node = _lastChild; node; node = node->_prev) { 906 | const XMLElement* element = node->ToElementWithName(name); 907 | if (element) { 908 | return element; 909 | } 910 | } 911 | return 0; 912 | } 913 | 914 | 915 | const XMLElement* XMLNode::NextSiblingElement(const char* name) const 916 | { 917 | for (const XMLNode* node = _next; node; node = node->_next) { 918 | const XMLElement* element = node->ToElementWithName(name); 919 | if (element) { 920 | return element; 921 | } 922 | } 923 | return 0; 924 | } 925 | 926 | 927 | const XMLElement* XMLNode::PreviousSiblingElement(const char* name) const 928 | { 929 | for (const XMLNode* node = _prev; node; node = node->_prev) { 930 | const XMLElement* element = node->ToElementWithName(name); 931 | if (element) { 932 | return element; 933 | } 934 | } 935 | return 0; 936 | } 937 | 938 | 939 | char* XMLNode::ParseDeep(char* p, StrPair* parentEnd) 940 | { 941 | // This is a recursive method, but thinking about it "at the current level" 942 | // it is a pretty simple flat list: 943 | // 944 | // 945 | // 946 | // With a special case: 947 | // 948 | // 949 | // 950 | // 951 | // Where the closing element (/foo) *must* be the next thing after the opening 952 | // element, and the names must match. BUT the tricky bit is that the closing 953 | // element will be read by the child. 954 | // 955 | // 'endTag' is the end tag for this node, it is returned by a call to a child. 956 | // 'parentEnd' is the end tag for the parent, which is filled in and returned. 957 | 958 | while (p && *p) { 959 | XMLNode* node = 0; 960 | 961 | p = _document->Identify(p, &node); 962 | TIXMLASSERT(p); 963 | if (node == 0) { 964 | break; 965 | } 966 | 967 | StrPair endTag; 968 | p = node->ParseDeep(p, &endTag); 969 | if (!p) { 970 | DeleteNode(node); 971 | if (!_document->Error()) { 972 | _document->SetError(XML_ERROR_PARSING, 0, 0); 973 | } 974 | break; 975 | } 976 | 977 | XMLDeclaration* decl = node->ToDeclaration(); 978 | if (decl) { 979 | // Declarations are only allowed at document level 980 | bool wellLocated = (ToDocument() != 0); 981 | if (wellLocated) { 982 | // Multiple declarations are allowed but all declarations 983 | // must occur before anything else 984 | for (const XMLNode* existingNode = _document->FirstChild(); existingNode; existingNode = existingNode->NextSibling()) { 985 | if (!existingNode->ToDeclaration()) { 986 | wellLocated = false; 987 | break; 988 | } 989 | } 990 | } 991 | if (!wellLocated) { 992 | _document->SetError(XML_ERROR_PARSING_DECLARATION, decl->Value(), 0); 993 | DeleteNode(node); 994 | break; 995 | } 996 | } 997 | 998 | XMLElement* ele = node->ToElement(); 999 | if (ele) { 1000 | // We read the end tag. Return it to the parent. 1001 | if (ele->ClosingType() == XMLElement::CLOSING) { 1002 | if (parentEnd) { 1003 | ele->_value.TransferTo(parentEnd); 1004 | } 1005 | node->_memPool->SetTracked(); // created and then immediately deleted. 1006 | DeleteNode(node); 1007 | return p; 1008 | } 1009 | 1010 | // Handle an end tag returned to this level. 1011 | // And handle a bunch of annoying errors. 1012 | bool mismatch = false; 1013 | if (endTag.Empty()) { 1014 | if (ele->ClosingType() == XMLElement::OPEN) { 1015 | mismatch = true; 1016 | } 1017 | } 1018 | else { 1019 | if (ele->ClosingType() != XMLElement::OPEN) { 1020 | mismatch = true; 1021 | } 1022 | else if (!XMLUtil::StringEqual(endTag.GetStr(), ele->Name())) { 1023 | mismatch = true; 1024 | } 1025 | } 1026 | if (mismatch) { 1027 | _document->SetError(XML_ERROR_MISMATCHED_ELEMENT, ele->Name(), 0); 1028 | DeleteNode(node); 1029 | break; 1030 | } 1031 | } 1032 | InsertEndChild(node); 1033 | } 1034 | return 0; 1035 | } 1036 | 1037 | void XMLNode::DeleteNode(XMLNode* node) 1038 | { 1039 | if (node == 0) { 1040 | return; 1041 | } 1042 | MemPool* pool = node->_memPool; 1043 | node->~XMLNode(); 1044 | pool->Free(node); 1045 | } 1046 | 1047 | void XMLNode::InsertChildPreamble(XMLNode* insertThis) const 1048 | { 1049 | TIXMLASSERT(insertThis); 1050 | TIXMLASSERT(insertThis->_document == _document); 1051 | 1052 | if (insertThis->_parent) 1053 | insertThis->_parent->Unlink(insertThis); 1054 | else 1055 | insertThis->_memPool->SetTracked(); 1056 | } 1057 | 1058 | const XMLElement* XMLNode::ToElementWithName(const char* name) const 1059 | { 1060 | const XMLElement* element = this->ToElement(); 1061 | if (element == 0) { 1062 | return 0; 1063 | } 1064 | if (name == 0) { 1065 | return element; 1066 | } 1067 | if (XMLUtil::StringEqual(element->Name(), name)) { 1068 | return element; 1069 | } 1070 | return 0; 1071 | } 1072 | 1073 | // --------- XMLText ---------- // 1074 | char* XMLText::ParseDeep(char* p, StrPair*) 1075 | { 1076 | const char* start = p; 1077 | if (this->CData()) { 1078 | p = _value.ParseText(p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION); 1079 | if (!p) { 1080 | _document->SetError(XML_ERROR_PARSING_CDATA, start, 0); 1081 | } 1082 | return p; 1083 | } 1084 | else { 1085 | int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES; 1086 | if (_document->WhitespaceMode() == COLLAPSE_WHITESPACE) { 1087 | flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING; 1088 | } 1089 | 1090 | p = _value.ParseText(p, "<", flags); 1091 | if (p && *p) { 1092 | return p - 1; 1093 | } 1094 | if (!p) { 1095 | _document->SetError(XML_ERROR_PARSING_TEXT, start, 0); 1096 | } 1097 | } 1098 | return 0; 1099 | } 1100 | 1101 | 1102 | XMLNode* XMLText::ShallowClone(XMLDocument* doc) const 1103 | { 1104 | if (!doc) { 1105 | doc = _document; 1106 | } 1107 | XMLText* text = doc->NewText(Value()); // fixme: this will always allocate memory. Intern? 1108 | text->SetCData(this->CData()); 1109 | return text; 1110 | } 1111 | 1112 | 1113 | bool XMLText::ShallowEqual(const XMLNode* compare) const 1114 | { 1115 | const XMLText* text = compare->ToText(); 1116 | return (text && XMLUtil::StringEqual(text->Value(), Value())); 1117 | } 1118 | 1119 | 1120 | bool XMLText::Accept(XMLVisitor* visitor) const 1121 | { 1122 | TIXMLASSERT(visitor); 1123 | return visitor->Visit(*this); 1124 | } 1125 | 1126 | 1127 | // --------- XMLComment ---------- // 1128 | 1129 | XMLComment::XMLComment(XMLDocument* doc) : XMLNode(doc) 1130 | { 1131 | } 1132 | 1133 | 1134 | XMLComment::~XMLComment() 1135 | { 1136 | } 1137 | 1138 | 1139 | char* XMLComment::ParseDeep(char* p, StrPair*) 1140 | { 1141 | // Comment parses as text. 1142 | const char* start = p; 1143 | p = _value.ParseText(p, "-->", StrPair::COMMENT); 1144 | if (p == 0) { 1145 | _document->SetError(XML_ERROR_PARSING_COMMENT, start, 0); 1146 | } 1147 | return p; 1148 | } 1149 | 1150 | 1151 | XMLNode* XMLComment::ShallowClone(XMLDocument* doc) const 1152 | { 1153 | if (!doc) { 1154 | doc = _document; 1155 | } 1156 | XMLComment* comment = doc->NewComment(Value()); // fixme: this will always allocate memory. Intern? 1157 | return comment; 1158 | } 1159 | 1160 | 1161 | bool XMLComment::ShallowEqual(const XMLNode* compare) const 1162 | { 1163 | TIXMLASSERT(compare); 1164 | const XMLComment* comment = compare->ToComment(); 1165 | return (comment && XMLUtil::StringEqual(comment->Value(), Value())); 1166 | } 1167 | 1168 | 1169 | bool XMLComment::Accept(XMLVisitor* visitor) const 1170 | { 1171 | TIXMLASSERT(visitor); 1172 | return visitor->Visit(*this); 1173 | } 1174 | 1175 | 1176 | // --------- XMLDeclaration ---------- // 1177 | 1178 | XMLDeclaration::XMLDeclaration(XMLDocument* doc) : XMLNode(doc) 1179 | { 1180 | } 1181 | 1182 | 1183 | XMLDeclaration::~XMLDeclaration() 1184 | { 1185 | //printf( "~XMLDeclaration\n" ); 1186 | } 1187 | 1188 | 1189 | char* XMLDeclaration::ParseDeep(char* p, StrPair*) 1190 | { 1191 | // Declaration parses as text. 1192 | const char* start = p; 1193 | p = _value.ParseText(p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION); 1194 | if (p == 0) { 1195 | _document->SetError(XML_ERROR_PARSING_DECLARATION, start, 0); 1196 | } 1197 | return p; 1198 | } 1199 | 1200 | 1201 | XMLNode* XMLDeclaration::ShallowClone(XMLDocument* doc) const 1202 | { 1203 | if (!doc) { 1204 | doc = _document; 1205 | } 1206 | XMLDeclaration* dec = doc->NewDeclaration(Value()); // fixme: this will always allocate memory. Intern? 1207 | return dec; 1208 | } 1209 | 1210 | 1211 | bool XMLDeclaration::ShallowEqual(const XMLNode* compare) const 1212 | { 1213 | TIXMLASSERT(compare); 1214 | const XMLDeclaration* declaration = compare->ToDeclaration(); 1215 | return (declaration && XMLUtil::StringEqual(declaration->Value(), Value())); 1216 | } 1217 | 1218 | 1219 | 1220 | bool XMLDeclaration::Accept(XMLVisitor* visitor) const 1221 | { 1222 | TIXMLASSERT(visitor); 1223 | return visitor->Visit(*this); 1224 | } 1225 | 1226 | // --------- XMLUnknown ---------- // 1227 | 1228 | XMLUnknown::XMLUnknown(XMLDocument* doc) : XMLNode(doc) 1229 | { 1230 | } 1231 | 1232 | 1233 | XMLUnknown::~XMLUnknown() 1234 | { 1235 | } 1236 | 1237 | 1238 | char* XMLUnknown::ParseDeep(char* p, StrPair*) 1239 | { 1240 | // Unknown parses as text. 1241 | const char* start = p; 1242 | 1243 | p = _value.ParseText(p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION); 1244 | if (!p) { 1245 | _document->SetError(XML_ERROR_PARSING_UNKNOWN, start, 0); 1246 | } 1247 | return p; 1248 | } 1249 | 1250 | 1251 | XMLNode* XMLUnknown::ShallowClone(XMLDocument* doc) const 1252 | { 1253 | if (!doc) { 1254 | doc = _document; 1255 | } 1256 | XMLUnknown* text = doc->NewUnknown(Value()); // fixme: this will always allocate memory. Intern? 1257 | return text; 1258 | } 1259 | 1260 | 1261 | bool XMLUnknown::ShallowEqual(const XMLNode* compare) const 1262 | { 1263 | TIXMLASSERT(compare); 1264 | const XMLUnknown* unknown = compare->ToUnknown(); 1265 | return (unknown && XMLUtil::StringEqual(unknown->Value(), Value())); 1266 | } 1267 | 1268 | 1269 | bool XMLUnknown::Accept(XMLVisitor* visitor) const 1270 | { 1271 | TIXMLASSERT(visitor); 1272 | return visitor->Visit(*this); 1273 | } 1274 | 1275 | // --------- XMLAttribute ---------- // 1276 | 1277 | const char* XMLAttribute::Name() const 1278 | { 1279 | return _name.GetStr(); 1280 | } 1281 | 1282 | const char* XMLAttribute::Value() const 1283 | { 1284 | return _value.GetStr(); 1285 | } 1286 | 1287 | char* XMLAttribute::ParseDeep(char* p, bool processEntities) 1288 | { 1289 | // Parse using the name rules: bug fix, was using ParseText before 1290 | p = _name.ParseName(p); 1291 | if (!p || !*p) { 1292 | return 0; 1293 | } 1294 | 1295 | // Skip white space before = 1296 | p = XMLUtil::SkipWhiteSpace(p); 1297 | if (*p != '=') { 1298 | return 0; 1299 | } 1300 | 1301 | ++p; // move up to opening quote 1302 | p = XMLUtil::SkipWhiteSpace(p); 1303 | if (*p != '\"' && *p != '\'') { 1304 | return 0; 1305 | } 1306 | 1307 | char endTag[2] = { *p, 0 }; 1308 | ++p; // move past opening quote 1309 | 1310 | p = _value.ParseText(p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES); 1311 | return p; 1312 | } 1313 | 1314 | 1315 | void XMLAttribute::SetName(const char* n) 1316 | { 1317 | _name.SetStr(n); 1318 | } 1319 | 1320 | 1321 | XMLError XMLAttribute::QueryIntValue(int* value) const 1322 | { 1323 | if (XMLUtil::ToInt(Value(), value)) { 1324 | return XML_SUCCESS; 1325 | } 1326 | return XML_WRONG_ATTRIBUTE_TYPE; 1327 | } 1328 | 1329 | 1330 | XMLError XMLAttribute::QueryUnsignedValue(unsigned int* value) const 1331 | { 1332 | if (XMLUtil::ToUnsigned(Value(), value)) { 1333 | return XML_SUCCESS; 1334 | } 1335 | return XML_WRONG_ATTRIBUTE_TYPE; 1336 | } 1337 | 1338 | 1339 | XMLError XMLAttribute::QueryInt64Value(int64_t* value) const 1340 | { 1341 | if (XMLUtil::ToInt64(Value(), value)) { 1342 | return XML_SUCCESS; 1343 | } 1344 | return XML_WRONG_ATTRIBUTE_TYPE; 1345 | } 1346 | 1347 | 1348 | XMLError XMLAttribute::QueryBoolValue(bool* value) const 1349 | { 1350 | if (XMLUtil::ToBool(Value(), value)) { 1351 | return XML_SUCCESS; 1352 | } 1353 | return XML_WRONG_ATTRIBUTE_TYPE; 1354 | } 1355 | 1356 | 1357 | XMLError XMLAttribute::QueryFloatValue(float* value) const 1358 | { 1359 | if (XMLUtil::ToFloat(Value(), value)) { 1360 | return XML_SUCCESS; 1361 | } 1362 | return XML_WRONG_ATTRIBUTE_TYPE; 1363 | } 1364 | 1365 | 1366 | XMLError XMLAttribute::QueryDoubleValue(double* value) const 1367 | { 1368 | if (XMLUtil::ToDouble(Value(), value)) { 1369 | return XML_SUCCESS; 1370 | } 1371 | return XML_WRONG_ATTRIBUTE_TYPE; 1372 | } 1373 | 1374 | 1375 | void XMLAttribute::SetAttribute(const char* v) 1376 | { 1377 | _value.SetStr(v); 1378 | } 1379 | 1380 | 1381 | void XMLAttribute::SetAttribute(int v) 1382 | { 1383 | char buf[BUF_SIZE]; 1384 | XMLUtil::ToStr(v, buf, BUF_SIZE); 1385 | _value.SetStr(buf); 1386 | } 1387 | 1388 | 1389 | void XMLAttribute::SetAttribute(unsigned v) 1390 | { 1391 | char buf[BUF_SIZE]; 1392 | XMLUtil::ToStr(v, buf, BUF_SIZE); 1393 | _value.SetStr(buf); 1394 | } 1395 | 1396 | 1397 | void XMLAttribute::SetAttribute(int64_t v) 1398 | { 1399 | char buf[BUF_SIZE]; 1400 | XMLUtil::ToStr(v, buf, BUF_SIZE); 1401 | _value.SetStr(buf); 1402 | } 1403 | 1404 | 1405 | 1406 | void XMLAttribute::SetAttribute(bool v) 1407 | { 1408 | char buf[BUF_SIZE]; 1409 | XMLUtil::ToStr(v, buf, BUF_SIZE); 1410 | _value.SetStr(buf); 1411 | } 1412 | 1413 | void XMLAttribute::SetAttribute(double v) 1414 | { 1415 | char buf[BUF_SIZE]; 1416 | XMLUtil::ToStr(v, buf, BUF_SIZE); 1417 | _value.SetStr(buf); 1418 | } 1419 | 1420 | void XMLAttribute::SetAttribute(float v) 1421 | { 1422 | char buf[BUF_SIZE]; 1423 | XMLUtil::ToStr(v, buf, BUF_SIZE); 1424 | _value.SetStr(buf); 1425 | } 1426 | 1427 | 1428 | // --------- XMLElement ---------- // 1429 | XMLElement::XMLElement(XMLDocument* doc) : XMLNode(doc), 1430 | _closingType(0), 1431 | _rootAttribute(0) 1432 | { 1433 | } 1434 | 1435 | 1436 | XMLElement::~XMLElement() 1437 | { 1438 | while (_rootAttribute) { 1439 | XMLAttribute* next = _rootAttribute->_next; 1440 | DeleteAttribute(_rootAttribute); 1441 | _rootAttribute = next; 1442 | } 1443 | } 1444 | 1445 | 1446 | const XMLAttribute* XMLElement::FindAttribute(const char* name) const 1447 | { 1448 | for (XMLAttribute* a = _rootAttribute; a; a = a->_next) { 1449 | if (XMLUtil::StringEqual(a->Name(), name)) { 1450 | return a; 1451 | } 1452 | } 1453 | return 0; 1454 | } 1455 | 1456 | 1457 | const char* XMLElement::Attribute(const char* name, const char* value) const 1458 | { 1459 | const XMLAttribute* a = FindAttribute(name); 1460 | if (!a) { 1461 | return 0; 1462 | } 1463 | if (!value || XMLUtil::StringEqual(a->Value(), value)) { 1464 | return a->Value(); 1465 | } 1466 | return 0; 1467 | } 1468 | 1469 | int XMLElement::IntAttribute(const char* name, int defaultValue) const 1470 | { 1471 | int i = defaultValue; 1472 | QueryIntAttribute(name, &i); 1473 | return i; 1474 | } 1475 | 1476 | unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const 1477 | { 1478 | unsigned i = defaultValue; 1479 | QueryUnsignedAttribute(name, &i); 1480 | return i; 1481 | } 1482 | 1483 | int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const 1484 | { 1485 | int64_t i = defaultValue; 1486 | QueryInt64Attribute(name, &i); 1487 | return i; 1488 | } 1489 | 1490 | bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const 1491 | { 1492 | bool b = defaultValue; 1493 | QueryBoolAttribute(name, &b); 1494 | return b; 1495 | } 1496 | 1497 | double XMLElement::DoubleAttribute(const char* name, double defaultValue) const 1498 | { 1499 | double d = defaultValue; 1500 | QueryDoubleAttribute(name, &d); 1501 | return d; 1502 | } 1503 | 1504 | float XMLElement::FloatAttribute(const char* name, float defaultValue) const 1505 | { 1506 | float f = defaultValue; 1507 | QueryFloatAttribute(name, &f); 1508 | return f; 1509 | } 1510 | 1511 | const char* XMLElement::GetText() const 1512 | { 1513 | if (FirstChild() && FirstChild()->ToText()) { 1514 | return FirstChild()->Value(); 1515 | } 1516 | return 0; 1517 | } 1518 | 1519 | 1520 | void XMLElement::SetText(const char* inText) 1521 | { 1522 | if (FirstChild() && FirstChild()->ToText()) 1523 | FirstChild()->SetValue(inText); 1524 | else { 1525 | XMLText* theText = GetDocument()->NewText(inText); 1526 | InsertFirstChild(theText); 1527 | } 1528 | } 1529 | 1530 | 1531 | void XMLElement::SetText(int v) 1532 | { 1533 | char buf[BUF_SIZE]; 1534 | XMLUtil::ToStr(v, buf, BUF_SIZE); 1535 | SetText(buf); 1536 | } 1537 | 1538 | 1539 | void XMLElement::SetText(unsigned v) 1540 | { 1541 | char buf[BUF_SIZE]; 1542 | XMLUtil::ToStr(v, buf, BUF_SIZE); 1543 | SetText(buf); 1544 | } 1545 | 1546 | 1547 | void XMLElement::SetText(int64_t v) 1548 | { 1549 | char buf[BUF_SIZE]; 1550 | XMLUtil::ToStr(v, buf, BUF_SIZE); 1551 | SetText(buf); 1552 | } 1553 | 1554 | 1555 | void XMLElement::SetText(bool v) 1556 | { 1557 | char buf[BUF_SIZE]; 1558 | XMLUtil::ToStr(v, buf, BUF_SIZE); 1559 | SetText(buf); 1560 | } 1561 | 1562 | 1563 | void XMLElement::SetText(float v) 1564 | { 1565 | char buf[BUF_SIZE]; 1566 | XMLUtil::ToStr(v, buf, BUF_SIZE); 1567 | SetText(buf); 1568 | } 1569 | 1570 | 1571 | void XMLElement::SetText(double v) 1572 | { 1573 | char buf[BUF_SIZE]; 1574 | XMLUtil::ToStr(v, buf, BUF_SIZE); 1575 | SetText(buf); 1576 | } 1577 | 1578 | 1579 | XMLError XMLElement::QueryIntText(int* ival) const 1580 | { 1581 | if (FirstChild() && FirstChild()->ToText()) { 1582 | const char* t = FirstChild()->Value(); 1583 | if (XMLUtil::ToInt(t, ival)) { 1584 | return XML_SUCCESS; 1585 | } 1586 | return XML_CAN_NOT_CONVERT_TEXT; 1587 | } 1588 | return XML_NO_TEXT_NODE; 1589 | } 1590 | 1591 | 1592 | XMLError XMLElement::QueryUnsignedText(unsigned* uval) const 1593 | { 1594 | if (FirstChild() && FirstChild()->ToText()) { 1595 | const char* t = FirstChild()->Value(); 1596 | if (XMLUtil::ToUnsigned(t, uval)) { 1597 | return XML_SUCCESS; 1598 | } 1599 | return XML_CAN_NOT_CONVERT_TEXT; 1600 | } 1601 | return XML_NO_TEXT_NODE; 1602 | } 1603 | 1604 | 1605 | XMLError XMLElement::QueryInt64Text(int64_t* ival) const 1606 | { 1607 | if (FirstChild() && FirstChild()->ToText()) { 1608 | const char* t = FirstChild()->Value(); 1609 | if (XMLUtil::ToInt64(t, ival)) { 1610 | return XML_SUCCESS; 1611 | } 1612 | return XML_CAN_NOT_CONVERT_TEXT; 1613 | } 1614 | return XML_NO_TEXT_NODE; 1615 | } 1616 | 1617 | 1618 | XMLError XMLElement::QueryBoolText(bool* bval) const 1619 | { 1620 | if (FirstChild() && FirstChild()->ToText()) { 1621 | const char* t = FirstChild()->Value(); 1622 | if (XMLUtil::ToBool(t, bval)) { 1623 | return XML_SUCCESS; 1624 | } 1625 | return XML_CAN_NOT_CONVERT_TEXT; 1626 | } 1627 | return XML_NO_TEXT_NODE; 1628 | } 1629 | 1630 | 1631 | XMLError XMLElement::QueryDoubleText(double* dval) const 1632 | { 1633 | if (FirstChild() && FirstChild()->ToText()) { 1634 | const char* t = FirstChild()->Value(); 1635 | if (XMLUtil::ToDouble(t, dval)) { 1636 | return XML_SUCCESS; 1637 | } 1638 | return XML_CAN_NOT_CONVERT_TEXT; 1639 | } 1640 | return XML_NO_TEXT_NODE; 1641 | } 1642 | 1643 | 1644 | XMLError XMLElement::QueryFloatText(float* fval) const 1645 | { 1646 | if (FirstChild() && FirstChild()->ToText()) { 1647 | const char* t = FirstChild()->Value(); 1648 | if (XMLUtil::ToFloat(t, fval)) { 1649 | return XML_SUCCESS; 1650 | } 1651 | return XML_CAN_NOT_CONVERT_TEXT; 1652 | } 1653 | return XML_NO_TEXT_NODE; 1654 | } 1655 | 1656 | int XMLElement::IntText(int defaultValue) const 1657 | { 1658 | int i = defaultValue; 1659 | QueryIntText(&i); 1660 | return i; 1661 | } 1662 | 1663 | unsigned XMLElement::UnsignedText(unsigned defaultValue) const 1664 | { 1665 | unsigned i = defaultValue; 1666 | QueryUnsignedText(&i); 1667 | return i; 1668 | } 1669 | 1670 | int64_t XMLElement::Int64Text(int64_t defaultValue) const 1671 | { 1672 | int64_t i = defaultValue; 1673 | QueryInt64Text(&i); 1674 | return i; 1675 | } 1676 | 1677 | bool XMLElement::BoolText(bool defaultValue) const 1678 | { 1679 | bool b = defaultValue; 1680 | QueryBoolText(&b); 1681 | return b; 1682 | } 1683 | 1684 | double XMLElement::DoubleText(double defaultValue) const 1685 | { 1686 | double d = defaultValue; 1687 | QueryDoubleText(&d); 1688 | return d; 1689 | } 1690 | 1691 | float XMLElement::FloatText(float defaultValue) const 1692 | { 1693 | float f = defaultValue; 1694 | QueryFloatText(&f); 1695 | return f; 1696 | } 1697 | 1698 | 1699 | XMLAttribute* XMLElement::FindOrCreateAttribute(const char* name) 1700 | { 1701 | XMLAttribute* last = 0; 1702 | XMLAttribute* attrib = 0; 1703 | for (attrib = _rootAttribute; 1704 | attrib; 1705 | last = attrib, attrib = attrib->_next) { 1706 | if (XMLUtil::StringEqual(attrib->Name(), name)) { 1707 | break; 1708 | } 1709 | } 1710 | if (!attrib) { 1711 | attrib = CreateAttribute(); 1712 | TIXMLASSERT(attrib); 1713 | if (last) { 1714 | last->_next = attrib; 1715 | } 1716 | else { 1717 | _rootAttribute = attrib; 1718 | } 1719 | attrib->SetName(name); 1720 | } 1721 | return attrib; 1722 | } 1723 | 1724 | 1725 | void XMLElement::DeleteAttribute(const char* name) 1726 | { 1727 | XMLAttribute* prev = 0; 1728 | for (XMLAttribute* a = _rootAttribute; a; a = a->_next) { 1729 | if (XMLUtil::StringEqual(name, a->Name())) { 1730 | if (prev) { 1731 | prev->_next = a->_next; 1732 | } 1733 | else { 1734 | _rootAttribute = a->_next; 1735 | } 1736 | DeleteAttribute(a); 1737 | break; 1738 | } 1739 | prev = a; 1740 | } 1741 | } 1742 | 1743 | 1744 | char* XMLElement::ParseAttributes(char* p) 1745 | { 1746 | const char* start = p; 1747 | XMLAttribute* prevAttribute = 0; 1748 | 1749 | // Read the attributes. 1750 | while (p) { 1751 | p = XMLUtil::SkipWhiteSpace(p); 1752 | if (!(*p)) { 1753 | _document->SetError(XML_ERROR_PARSING_ELEMENT, start, Name()); 1754 | return 0; 1755 | } 1756 | 1757 | // attribute. 1758 | if (XMLUtil::IsNameStartChar(*p)) { 1759 | XMLAttribute* attrib = CreateAttribute(); 1760 | TIXMLASSERT(attrib); 1761 | 1762 | p = attrib->ParseDeep(p, _document->ProcessEntities()); 1763 | if (!p || Attribute(attrib->Name())) { 1764 | DeleteAttribute(attrib); 1765 | _document->SetError(XML_ERROR_PARSING_ATTRIBUTE, start, p); 1766 | return 0; 1767 | } 1768 | // There is a minor bug here: if the attribute in the source xml 1769 | // document is duplicated, it will not be detected and the 1770 | // attribute will be doubly added. However, tracking the 'prevAttribute' 1771 | // avoids re-scanning the attribute list. Preferring performance for 1772 | // now, may reconsider in the future. 1773 | if (prevAttribute) { 1774 | prevAttribute->_next = attrib; 1775 | } 1776 | else { 1777 | _rootAttribute = attrib; 1778 | } 1779 | prevAttribute = attrib; 1780 | } 1781 | // end of the tag 1782 | else if (*p == '>') { 1783 | ++p; 1784 | break; 1785 | } 1786 | // end of the tag 1787 | else if (*p == '/' && *(p + 1) == '>') { 1788 | _closingType = CLOSED; 1789 | return p + 2; // done; sealed element. 1790 | } 1791 | else { 1792 | _document->SetError(XML_ERROR_PARSING_ELEMENT, start, p); 1793 | return 0; 1794 | } 1795 | } 1796 | return p; 1797 | } 1798 | 1799 | void XMLElement::DeleteAttribute(XMLAttribute* attribute) 1800 | { 1801 | if (attribute == 0) { 1802 | return; 1803 | } 1804 | MemPool* pool = attribute->_memPool; 1805 | attribute->~XMLAttribute(); 1806 | pool->Free(attribute); 1807 | } 1808 | 1809 | XMLAttribute* XMLElement::CreateAttribute() 1810 | { 1811 | TIXMLASSERT(sizeof(XMLAttribute) == _document->_attributePool.ItemSize()); 1812 | XMLAttribute* attrib = new (_document->_attributePool.Alloc()) XMLAttribute(); 1813 | attrib->_memPool = &_document->_attributePool; 1814 | attrib->_memPool->SetTracked(); 1815 | return attrib; 1816 | } 1817 | 1818 | // 1819 | // 1820 | // foobar 1821 | // 1822 | char* XMLElement::ParseDeep(char* p, StrPair* strPair) 1823 | { 1824 | // Read the element name. 1825 | p = XMLUtil::SkipWhiteSpace(p); 1826 | 1827 | // The closing element is the form. It is 1828 | // parsed just like a regular element then deleted from 1829 | // the DOM. 1830 | if (*p == '/') { 1831 | _closingType = CLOSING; 1832 | ++p; 1833 | } 1834 | 1835 | p = _value.ParseName(p); 1836 | if (_value.Empty()) { 1837 | return 0; 1838 | } 1839 | 1840 | p = ParseAttributes(p); 1841 | if (!p || !*p || _closingType) { 1842 | return p; 1843 | } 1844 | 1845 | p = XMLNode::ParseDeep(p, strPair); 1846 | return p; 1847 | } 1848 | 1849 | 1850 | 1851 | XMLNode* XMLElement::ShallowClone(XMLDocument* doc) const 1852 | { 1853 | if (!doc) { 1854 | doc = _document; 1855 | } 1856 | XMLElement* element = doc->NewElement(Value()); // fixme: this will always allocate memory. Intern? 1857 | for (const XMLAttribute* a = FirstAttribute(); a; a = a->Next()) { 1858 | element->SetAttribute(a->Name(), a->Value()); // fixme: this will always allocate memory. Intern? 1859 | } 1860 | return element; 1861 | } 1862 | 1863 | 1864 | bool XMLElement::ShallowEqual(const XMLNode* compare) const 1865 | { 1866 | TIXMLASSERT(compare); 1867 | const XMLElement* other = compare->ToElement(); 1868 | if (other && XMLUtil::StringEqual(other->Name(), Name())) { 1869 | 1870 | const XMLAttribute* a = FirstAttribute(); 1871 | const XMLAttribute* b = other->FirstAttribute(); 1872 | 1873 | while (a && b) { 1874 | if (!XMLUtil::StringEqual(a->Value(), b->Value())) { 1875 | return false; 1876 | } 1877 | a = a->Next(); 1878 | b = b->Next(); 1879 | } 1880 | if (a || b) { 1881 | // different count 1882 | return false; 1883 | } 1884 | return true; 1885 | } 1886 | return false; 1887 | } 1888 | 1889 | 1890 | bool XMLElement::Accept(XMLVisitor* visitor) const 1891 | { 1892 | TIXMLASSERT(visitor); 1893 | if (visitor->VisitEnter(*this, _rootAttribute)) { 1894 | for (const XMLNode* node = FirstChild(); node; node = node->NextSibling()) { 1895 | if (!node->Accept(visitor)) { 1896 | break; 1897 | } 1898 | } 1899 | } 1900 | return visitor->VisitExit(*this); 1901 | } 1902 | 1903 | 1904 | // --------- XMLDocument ----------- // 1905 | 1906 | // Warning: List must match 'enum XMLError' 1907 | const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = { 1908 | "XML_SUCCESS", 1909 | "XML_NO_ATTRIBUTE", 1910 | "XML_WRONG_ATTRIBUTE_TYPE", 1911 | "XML_ERROR_FILE_NOT_FOUND", 1912 | "XML_ERROR_FILE_COULD_NOT_BE_OPENED", 1913 | "XML_ERROR_FILE_READ_ERROR", 1914 | "XML_ERROR_ELEMENT_MISMATCH", 1915 | "XML_ERROR_PARSING_ELEMENT", 1916 | "XML_ERROR_PARSING_ATTRIBUTE", 1917 | "XML_ERROR_IDENTIFYING_TAG", 1918 | "XML_ERROR_PARSING_TEXT", 1919 | "XML_ERROR_PARSING_CDATA", 1920 | "XML_ERROR_PARSING_COMMENT", 1921 | "XML_ERROR_PARSING_DECLARATION", 1922 | "XML_ERROR_PARSING_UNKNOWN", 1923 | "XML_ERROR_EMPTY_DOCUMENT", 1924 | "XML_ERROR_MISMATCHED_ELEMENT", 1925 | "XML_ERROR_PARSING", 1926 | "XML_CAN_NOT_CONVERT_TEXT", 1927 | "XML_NO_TEXT_NODE" 1928 | }; 1929 | 1930 | 1931 | XMLDocument::XMLDocument(bool processEntities, Whitespace whitespace) : 1932 | XMLNode(0), 1933 | _writeBOM(false), 1934 | _processEntities(processEntities), 1935 | _errorID(XML_SUCCESS), 1936 | _whitespace(whitespace), 1937 | _charBuffer(0) 1938 | { 1939 | // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+) 1940 | _document = this; 1941 | } 1942 | 1943 | 1944 | XMLDocument::~XMLDocument() 1945 | { 1946 | Clear(); 1947 | } 1948 | 1949 | 1950 | void XMLDocument::Clear() 1951 | { 1952 | DeleteChildren(); 1953 | 1954 | #ifdef DEBUG 1955 | const bool hadError = Error(); 1956 | #endif 1957 | ClearError(); 1958 | 1959 | delete[] _charBuffer; 1960 | _charBuffer = 0; 1961 | 1962 | #if 0 1963 | _textPool.Trace("text"); 1964 | _elementPool.Trace("element"); 1965 | _commentPool.Trace("comment"); 1966 | _attributePool.Trace("attribute"); 1967 | #endif 1968 | 1969 | #ifdef DEBUG 1970 | if (!hadError) { 1971 | TIXMLASSERT(_elementPool.CurrentAllocs() == _elementPool.Untracked()); 1972 | TIXMLASSERT(_attributePool.CurrentAllocs() == _attributePool.Untracked()); 1973 | TIXMLASSERT(_textPool.CurrentAllocs() == _textPool.Untracked()); 1974 | TIXMLASSERT(_commentPool.CurrentAllocs() == _commentPool.Untracked()); 1975 | } 1976 | #endif 1977 | } 1978 | 1979 | 1980 | XMLElement* XMLDocument::NewElement(const char* name) 1981 | { 1982 | TIXMLASSERT(sizeof(XMLElement) == _elementPool.ItemSize()); 1983 | XMLElement* ele = new (_elementPool.Alloc()) XMLElement(this); 1984 | ele->_memPool = &_elementPool; 1985 | ele->SetName(name); 1986 | return ele; 1987 | } 1988 | 1989 | 1990 | XMLComment* XMLDocument::NewComment(const char* str) 1991 | { 1992 | TIXMLASSERT(sizeof(XMLComment) == _commentPool.ItemSize()); 1993 | XMLComment* comment = new (_commentPool.Alloc()) XMLComment(this); 1994 | comment->_memPool = &_commentPool; 1995 | comment->SetValue(str); 1996 | return comment; 1997 | } 1998 | 1999 | 2000 | XMLText* XMLDocument::NewText(const char* str) 2001 | { 2002 | TIXMLASSERT(sizeof(XMLText) == _textPool.ItemSize()); 2003 | XMLText* text = new (_textPool.Alloc()) XMLText(this); 2004 | text->_memPool = &_textPool; 2005 | text->SetValue(str); 2006 | return text; 2007 | } 2008 | 2009 | 2010 | XMLDeclaration* XMLDocument::NewDeclaration(const char* str) 2011 | { 2012 | TIXMLASSERT(sizeof(XMLDeclaration) == _commentPool.ItemSize()); 2013 | XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration(this); 2014 | dec->_memPool = &_commentPool; 2015 | dec->SetValue(str ? str : "xml version=\"1.0\" encoding=\"UTF-8\""); 2016 | return dec; 2017 | } 2018 | 2019 | 2020 | XMLUnknown* XMLDocument::NewUnknown(const char* str) 2021 | { 2022 | TIXMLASSERT(sizeof(XMLUnknown) == _commentPool.ItemSize()); 2023 | XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown(this); 2024 | unk->_memPool = &_commentPool; 2025 | unk->SetValue(str); 2026 | return unk; 2027 | } 2028 | 2029 | static FILE* callfopen(const char* filepath, const char* mode) 2030 | { 2031 | TIXMLASSERT(filepath); 2032 | TIXMLASSERT(mode); 2033 | #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) 2034 | FILE* fp = 0; 2035 | errno_t err = fopen_s(&fp, filepath, mode); 2036 | if (err) { 2037 | return 0; 2038 | } 2039 | #else 2040 | FILE* fp = fopen(filepath, mode); 2041 | #endif 2042 | return fp; 2043 | } 2044 | 2045 | void XMLDocument::DeleteNode(XMLNode* node) { 2046 | TIXMLASSERT(node); 2047 | TIXMLASSERT(node->_document == this); 2048 | if (node->_parent) { 2049 | node->_parent->DeleteChild(node); 2050 | } 2051 | else { 2052 | // Isn't in the tree. 2053 | // Use the parent delete. 2054 | // Also, we need to mark it tracked: we 'know' 2055 | // it was never used. 2056 | node->_memPool->SetTracked(); 2057 | // Call the static XMLNode version: 2058 | XMLNode::DeleteNode(node); 2059 | } 2060 | } 2061 | 2062 | 2063 | XMLError XMLDocument::LoadFile(const char* filename) 2064 | { 2065 | Clear(); 2066 | FILE* fp = callfopen(filename, "rb"); 2067 | if (!fp) { 2068 | SetError(XML_ERROR_FILE_NOT_FOUND, filename, 0); 2069 | return _errorID; 2070 | } 2071 | LoadFile(fp); 2072 | fclose(fp); 2073 | return _errorID; 2074 | } 2075 | 2076 | // This is likely overengineered template art to have a check that unsigned long value incremented 2077 | // by one still fits into size_t. If size_t type is larger than unsigned long type 2078 | // (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit 2079 | // -Wtype-limits warning. This piece makes the compiler select code with a check when a check 2080 | // is useful and code with no check when a check is redundant depending on how size_t and unsigned long 2081 | // types sizes relate to each other. 2082 | template 2083 | = sizeof(size_t))> 2084 | struct LongFitsIntoSizeTMinusOne { 2085 | static bool Fits(unsigned long value) 2086 | { 2087 | return value < (size_t)-1; 2088 | } 2089 | }; 2090 | 2091 | template <> 2092 | struct LongFitsIntoSizeTMinusOne { 2093 | static bool Fits(unsigned long) 2094 | { 2095 | return true; 2096 | } 2097 | }; 2098 | 2099 | XMLError XMLDocument::LoadFile(FILE* fp) 2100 | { 2101 | Clear(); 2102 | 2103 | fseek(fp, 0, SEEK_SET); 2104 | if (fgetc(fp) == EOF && ferror(fp) != 0) { 2105 | SetError(XML_ERROR_FILE_READ_ERROR, 0, 0); 2106 | return _errorID; 2107 | } 2108 | 2109 | fseek(fp, 0, SEEK_END); 2110 | const long filelength = ftell(fp); 2111 | fseek(fp, 0, SEEK_SET); 2112 | if (filelength == -1L) { 2113 | SetError(XML_ERROR_FILE_READ_ERROR, 0, 0); 2114 | return _errorID; 2115 | } 2116 | TIXMLASSERT(filelength >= 0); 2117 | 2118 | if (!LongFitsIntoSizeTMinusOne<>::Fits(filelength)) { 2119 | // Cannot handle files which won't fit in buffer together with null terminator 2120 | SetError(XML_ERROR_FILE_READ_ERROR, 0, 0); 2121 | return _errorID; 2122 | } 2123 | 2124 | if (filelength == 0) { 2125 | SetError(XML_ERROR_EMPTY_DOCUMENT, 0, 0); 2126 | return _errorID; 2127 | } 2128 | 2129 | const size_t size = filelength; 2130 | TIXMLASSERT(_charBuffer == 0); 2131 | _charBuffer = new char[size + 1]; 2132 | size_t read = fread(_charBuffer, 1, size, fp); 2133 | if (read != size) { 2134 | SetError(XML_ERROR_FILE_READ_ERROR, 0, 0); 2135 | return _errorID; 2136 | } 2137 | 2138 | _charBuffer[size] = 0; 2139 | 2140 | Parse(); 2141 | return _errorID; 2142 | } 2143 | 2144 | 2145 | XMLError XMLDocument::SaveFile(const char* filename, bool compact) 2146 | { 2147 | FILE* fp = callfopen(filename, "w"); 2148 | if (!fp) { 2149 | SetError(XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0); 2150 | return _errorID; 2151 | } 2152 | SaveFile(fp, compact); 2153 | fclose(fp); 2154 | return _errorID; 2155 | } 2156 | 2157 | 2158 | XMLError XMLDocument::SaveFile(FILE* fp, bool compact) 2159 | { 2160 | // Clear any error from the last save, otherwise it will get reported 2161 | // for *this* call. 2162 | ClearError(); 2163 | XMLPrinter stream(fp, compact); 2164 | Print(&stream); 2165 | return _errorID; 2166 | } 2167 | 2168 | 2169 | XMLError XMLDocument::Parse(const char* p, size_t len) 2170 | { 2171 | Clear(); 2172 | 2173 | if (len == 0 || !p || !*p) { 2174 | SetError(XML_ERROR_EMPTY_DOCUMENT, 0, 0); 2175 | return _errorID; 2176 | } 2177 | if (len == (size_t)(-1)) { 2178 | len = strlen(p); 2179 | } 2180 | TIXMLASSERT(_charBuffer == 0); 2181 | _charBuffer = new char[len + 1]; 2182 | memcpy(_charBuffer, p, len); 2183 | _charBuffer[len] = 0; 2184 | 2185 | Parse(); 2186 | if (Error()) { 2187 | // clean up now essentially dangling memory. 2188 | // and the parse fail can put objects in the 2189 | // pools that are dead and inaccessible. 2190 | DeleteChildren(); 2191 | _elementPool.Clear(); 2192 | _attributePool.Clear(); 2193 | _textPool.Clear(); 2194 | _commentPool.Clear(); 2195 | } 2196 | return _errorID; 2197 | } 2198 | 2199 | 2200 | void XMLDocument::Print(XMLPrinter* streamer) const 2201 | { 2202 | if (streamer) { 2203 | Accept(streamer); 2204 | } 2205 | else { 2206 | XMLPrinter stdoutStreamer(stdout); 2207 | Accept(&stdoutStreamer); 2208 | } 2209 | } 2210 | 2211 | 2212 | void XMLDocument::SetError(XMLError error, const char* str1, const char* str2) 2213 | { 2214 | TIXMLASSERT(error >= 0 && error < XML_ERROR_COUNT); 2215 | _errorID = error; 2216 | 2217 | _errorStr1.Reset(); 2218 | _errorStr2.Reset(); 2219 | 2220 | if (str1) 2221 | _errorStr1.SetStr(str1); 2222 | if (str2) 2223 | _errorStr2.SetStr(str2); 2224 | } 2225 | 2226 | const char* XMLDocument::ErrorName() const 2227 | { 2228 | TIXMLASSERT(_errorID >= 0 && _errorID < XML_ERROR_COUNT); 2229 | const char* errorName = _errorNames[_errorID]; 2230 | TIXMLASSERT(errorName && errorName[0]); 2231 | return errorName; 2232 | } 2233 | 2234 | void XMLDocument::PrintError() const 2235 | { 2236 | if (Error()) { 2237 | static const int LEN = 20; 2238 | char buf1[LEN] = { 0 }; 2239 | char buf2[LEN] = { 0 }; 2240 | 2241 | if (!_errorStr1.Empty()) { 2242 | TIXML_SNPRINTF(buf1, LEN, "%s", _errorStr1.GetStr()); 2243 | } 2244 | if (!_errorStr2.Empty()) { 2245 | TIXML_SNPRINTF(buf2, LEN, "%s", _errorStr2.GetStr()); 2246 | } 2247 | 2248 | // Should check INT_MIN <= _errorID && _errorId <= INT_MAX, but that 2249 | // causes a clang "always true" -Wtautological-constant-out-of-range-compare warning 2250 | TIXMLASSERT(0 <= _errorID && XML_ERROR_COUNT - 1 <= INT_MAX); 2251 | printf("XMLDocument error id=%d '%s' str1=%s str2=%s\n", 2252 | static_cast(_errorID), ErrorName(), buf1, buf2); 2253 | } 2254 | } 2255 | 2256 | void XMLDocument::Parse() 2257 | { 2258 | TIXMLASSERT(NoChildren()); // Clear() must have been called previously 2259 | TIXMLASSERT(_charBuffer); 2260 | char* p = _charBuffer; 2261 | p = XMLUtil::SkipWhiteSpace(p); 2262 | p = const_cast(XMLUtil::ReadBOM(p, &_writeBOM)); 2263 | if (!*p) { 2264 | SetError(XML_ERROR_EMPTY_DOCUMENT, 0, 0); 2265 | return; 2266 | } 2267 | ParseDeep(p, 0); 2268 | } 2269 | 2270 | XMLPrinter::XMLPrinter(FILE* file, bool compact, int depth) : 2271 | _elementJustOpened(false), 2272 | _firstElement(true), 2273 | _fp(file), 2274 | _depth(depth), 2275 | _textDepth(-1), 2276 | _processEntities(true), 2277 | _compactMode(compact) 2278 | { 2279 | for (int i = 0; i'] = true; // not required, but consistency is nice 2291 | _buffer.Push(0); 2292 | } 2293 | 2294 | 2295 | void XMLPrinter::Print(const char* format, ...) 2296 | { 2297 | va_list va; 2298 | va_start(va, format); 2299 | 2300 | if (_fp) { 2301 | vfprintf(_fp, format, va); 2302 | } 2303 | else { 2304 | const int len = TIXML_VSCPRINTF(format, va); 2305 | // Close out and re-start the va-args 2306 | va_end(va); 2307 | TIXMLASSERT(len >= 0); 2308 | va_start(va, format); 2309 | TIXMLASSERT(_buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0); 2310 | char* p = _buffer.PushArr(len) - 1; // back up over the null terminator. 2311 | TIXML_VSNPRINTF(p, len + 1, format, va); 2312 | } 2313 | va_end(va); 2314 | } 2315 | 2316 | 2317 | void XMLPrinter::PrintSpace(int depth) 2318 | { 2319 | for (int i = 0; i 0 && *q < ENTITY_RANGE) { 2336 | // Check for entities. If one is found, flush 2337 | // the stream up until the entity, write the 2338 | // entity, and keep looking. 2339 | if (flag[(unsigned char)(*q)]) { 2340 | while (p < q) { 2341 | const size_t delta = q - p; 2342 | // %.*s accepts type int as "precision" 2343 | const int toPrint = (INT_MAX < delta) ? INT_MAX : (int)delta; 2344 | Print("%.*s", toPrint, p); 2345 | p += toPrint; 2346 | } 2347 | bool entityPatternPrinted = false; 2348 | for (int i = 0; i"); 2462 | } 2463 | else { 2464 | if (_textDepth < 0 && !compactMode) { 2465 | Print("\n"); 2466 | PrintSpace(_depth); 2467 | } 2468 | Print("", name); 2469 | } 2470 | 2471 | if (_textDepth == _depth) { 2472 | _textDepth = -1; 2473 | } 2474 | if (_depth == 0 && !compactMode) { 2475 | Print("\n"); 2476 | } 2477 | _elementJustOpened = false; 2478 | } 2479 | 2480 | 2481 | void XMLPrinter::SealElementIfJustOpened() 2482 | { 2483 | if (!_elementJustOpened) { 2484 | return; 2485 | } 2486 | _elementJustOpened = false; 2487 | Print(">"); 2488 | } 2489 | 2490 | 2491 | void XMLPrinter::PushText(const char* text, bool cdata) 2492 | { 2493 | _textDepth = _depth - 1; 2494 | 2495 | SealElementIfJustOpened(); 2496 | if (cdata) { 2497 | Print("", text); 2498 | } 2499 | else { 2500 | PrintString(text, true); 2501 | } 2502 | } 2503 | 2504 | void XMLPrinter::PushText(int64_t value) 2505 | { 2506 | char buf[BUF_SIZE]; 2507 | XMLUtil::ToStr(value, buf, BUF_SIZE); 2508 | PushText(buf, false); 2509 | } 2510 | 2511 | void XMLPrinter::PushText(int value) 2512 | { 2513 | char buf[BUF_SIZE]; 2514 | XMLUtil::ToStr(value, buf, BUF_SIZE); 2515 | PushText(buf, false); 2516 | } 2517 | 2518 | 2519 | void XMLPrinter::PushText(unsigned value) 2520 | { 2521 | char buf[BUF_SIZE]; 2522 | XMLUtil::ToStr(value, buf, BUF_SIZE); 2523 | PushText(buf, false); 2524 | } 2525 | 2526 | 2527 | void XMLPrinter::PushText(bool value) 2528 | { 2529 | char buf[BUF_SIZE]; 2530 | XMLUtil::ToStr(value, buf, BUF_SIZE); 2531 | PushText(buf, false); 2532 | } 2533 | 2534 | 2535 | void XMLPrinter::PushText(float value) 2536 | { 2537 | char buf[BUF_SIZE]; 2538 | XMLUtil::ToStr(value, buf, BUF_SIZE); 2539 | PushText(buf, false); 2540 | } 2541 | 2542 | 2543 | void XMLPrinter::PushText(double value) 2544 | { 2545 | char buf[BUF_SIZE]; 2546 | XMLUtil::ToStr(value, buf, BUF_SIZE); 2547 | PushText(buf, false); 2548 | } 2549 | 2550 | 2551 | void XMLPrinter::PushComment(const char* comment) 2552 | { 2553 | SealElementIfJustOpened(); 2554 | if (_textDepth < 0 && !_firstElement && !_compactMode) { 2555 | Print("\n"); 2556 | PrintSpace(_depth); 2557 | } 2558 | _firstElement = false; 2559 | Print("", comment); 2560 | } 2561 | 2562 | 2563 | void XMLPrinter::PushDeclaration(const char* value) 2564 | { 2565 | SealElementIfJustOpened(); 2566 | if (_textDepth < 0 && !_firstElement && !_compactMode) { 2567 | Print("\n"); 2568 | PrintSpace(_depth); 2569 | } 2570 | _firstElement = false; 2571 | Print("", value); 2572 | } 2573 | 2574 | 2575 | void XMLPrinter::PushUnknown(const char* value) 2576 | { 2577 | SealElementIfJustOpened(); 2578 | if (_textDepth < 0 && !_firstElement && !_compactMode) { 2579 | Print("\n"); 2580 | PrintSpace(_depth); 2581 | } 2582 | _firstElement = false; 2583 | Print("", value); 2584 | } 2585 | 2586 | 2587 | bool XMLPrinter::VisitEnter(const XMLDocument& doc) 2588 | { 2589 | _processEntities = doc.ProcessEntities(); 2590 | if (doc.HasBOM()) { 2591 | PushHeader(true, false); 2592 | } 2593 | return true; 2594 | } 2595 | 2596 | 2597 | bool XMLPrinter::VisitEnter(const XMLElement& element, const XMLAttribute* attribute) 2598 | { 2599 | const XMLElement* parentElem = 0; 2600 | if (element.Parent()) { 2601 | parentElem = element.Parent()->ToElement(); 2602 | } 2603 | const bool compactMode = parentElem ? CompactMode(*parentElem) : _compactMode; 2604 | OpenElement(element.Name(), compactMode); 2605 | while (attribute) { 2606 | PushAttribute(attribute->Name(), attribute->Value()); 2607 | attribute = attribute->Next(); 2608 | } 2609 | return true; 2610 | } 2611 | 2612 | 2613 | bool XMLPrinter::VisitExit(const XMLElement& element) 2614 | { 2615 | CloseElement(CompactMode(element)); 2616 | return true; 2617 | } 2618 | 2619 | 2620 | bool XMLPrinter::Visit(const XMLText& text) 2621 | { 2622 | PushText(text.Value(), text.CData()); 2623 | return true; 2624 | } 2625 | 2626 | 2627 | bool XMLPrinter::Visit(const XMLComment& comment) 2628 | { 2629 | PushComment(comment.Value()); 2630 | return true; 2631 | } 2632 | 2633 | bool XMLPrinter::Visit(const XMLDeclaration& declaration) 2634 | { 2635 | PushDeclaration(declaration.Value()); 2636 | return true; 2637 | } 2638 | 2639 | 2640 | bool XMLPrinter::Visit(const XMLUnknown& unknown) 2641 | { 2642 | PushUnknown(unknown.Value()); 2643 | return true; 2644 | } 2645 | 2646 | } // namespace tinyxml2 --------------------------------------------------------------------------------