diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 00000000..2d02d396 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,56 @@ +version: '{build}' + +environment: + WINFLEXBISON_ARCHIVE: win_flex_bison-2.5.14.zip + matrix: + - CMAKE_GENERATOR: Visual Studio 14 2015 Win64 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + +matrix: + fast_finish: true + +install: + - appveyor DownloadFile "https://downloads.sourceforge.net/project/winflexbison/%WINFLEXBISON_ARCHIVE%" + - 7z x -y -owinflexbison\ "%WINFLEXBISON_ARCHIVE%" > nul + - set Path=%CD%\winflexbison;%Path% + +before_build: + - cmd: mkdir build + - cmd: cd build + - cmd: cmake -DCMAKE_CXX_FLAGS_RELEASE="/Od -DNDEBUG" -G"%CMAKE_GENERATOR%" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="install" -DRETDEC_TESTS=ON -DRETDEC_DEV_TOOLS=ON .. + +build_script: + - cmd: cmake --build . --config Release -- /m + # Get wget, etc. needed for install step. If we do this earlier, build step fails. + # Otherwise, we could use `cmake --build . --config Release --target install -- -m` and save a few seconds. + - set Path=C:\msys64\usr\bin;C:\msys64\mingw64\bin;%Path% + - cmd: cmake --build . --config Release --target install + +test_script: + # Test that install is movable and that it does not need build directory. + - cmd: mv install ../retdec-install + - cmd: cd .. + # Run unit tests. + - cmd: C:\msys64\usr\bin\bash.exe retdec-install\bin\retdec-tests-runner.sh + # Run decompilation script. + - cmd: C:\msys64\usr\bin\bash.exe retdec-install\bin\retdec-decompiler.sh --help + # Run simple decompilation. + - cmd: C:\msys64\usr\bin\echo.exe -e "#include <stdio.h>\n#include <stdlib.h>\nint main()\n{\n printf(\"hello world\\\n\");\n return 0;\n}\n" > hello-orig.c + - cmd: cat hello-orig.c + # Make sure 32-bit gcc will be used. + - set Path=C:\MinGW\bin;%Path% + - cmd: gcc.exe -o hello.exe hello-orig.c + # Prefer msys64 after we used 32-bit compiler. + - set Path=C:\msys64\usr\bin;C:\msys64\mingw64\bin;%Path% + - cmd: hello.exe + - cmd: C:\msys64\usr\bin\bash.exe retdec-install\bin\retdec-decompiler.sh hello.exe + - cmd: cat hello.c + - cmd: grep "int main(int argc, char \*\* argv)" hello.c + +notifications: + - provider: Email + to: + - '{{commitAuthorEmail}}' + on_build_success: false + on_build_failure: true + on_build_status_changed: true diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..5ce865bb --- /dev/null +++ b/.travis.yml @@ -0,0 +1,107 @@ +language: cpp + +cache: ccache + +matrix: + fast_finish: true + include: + - os: linux + dist: trusty + compiler: gcc-4.9 + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - build-essential + - gcc-4.8-multilib + - gcc-4.9 + - g++-4.9 + - cmake + - perl + - python3 + - flex + - bison + - autoconf + - automake + - libtool + - pkg-config + - m4 + - coreutils + - zlib1g-dev + - libtinfo-dev + - wget + - bc + - upx + - openssl + env: + - MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9 && NPROC=$(nproc)" + # We need this so that ccache does not cause compilation errors. + # e.g. retdec/tests/utils/string_tests.cpp:276:2: error: stray '\' in program + - CCACHE_CPP2=true + + - os: osx + osx_image: xcode8.3 + env: + - MATRIX_EVAL="NPROC=$(sysctl -n hw.physicalcpu)" + - CCACHE_CPP2=true + +install: + # We need to install newer versions of Flex and Bison on MacOS X. + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew install flex bison; fi + # ccache is not installed on OS X. + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew install ccache; fi + # gnu-getopt + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew install gnu-getopt; fi + # bash 4 + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew install bash; fi + +before_script: + - eval "${MATRIX_EVAL}" + # We need to use newer versions of Flex and Bison on MacOS X (the ones from Homebrew). + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export CMAKE_INCLUDE_PATH="/usr/local/opt/flex/include"; fi + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export CMAKE_LIBRARY_PATH="/usr/local/opt/flex/lib;/usr/local/opt/bison/lib"; fi + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export PATH="/usr/local/opt/flex/bin:/usr/local/opt/bison/bin:$PATH"; fi + # Coreutils. + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export PATH="$(brew --prefix coreutils)/libexec/gnubin:$PATH"; fi + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then ln -s /usr/local/bin/greadlink /usr/local/bin/readlink; fi + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export PATH="/usr/local/opt/gnu-getopt/bin:$PATH"; fi + # We need to add ccache before everything else in PATH. + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export PATH="/usr/local/opt/ccache/libexec:$PATH"; fi + +script: + - mkdir build && cd build + # We use "-O0" to speed-up build. + # "-O0" causes segfaults in LLVM if we do not use "-DNDEBUG" as well. + - cmake -DCMAKE_CXX_FLAGS_RELEASE="-O0 -DNDEBUG" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$(pwd)/install" -DRETDEC_TESTS=ON -DRETDEC_DEV_TOOLS=ON .. + - time make install -j $NPROC + # Test that install is movable and that it does not need build directory. + - mv install ../retdec-install + - cd .. + - rm -rf build + # Run unit tests. + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then /usr/local/bin/bash retdec-install/bin/retdec-tests-runner.sh; fi + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then ./retdec-install/bin/retdec-tests-runner.sh; fi + # Run decompilation script. + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then /usr/local/bin/bash retdec-install/bin/retdec-decompiler.sh --help; fi + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then ./retdec-install/bin/retdec-decompiler.sh --help; fi + # Run simple decompilation. + - echo -e '#include <stdio.h>\n#include <stdlib.h>\nint main()\n{\n printf("hello world\\n");\n return 0;\n}\n' > hello-orig.c + - cat hello-orig.c + - gcc -m32 -o hello hello-orig.c + - ./hello + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then /usr/local/bin/bash retdec-install/bin/retdec-decompiler.sh hello; fi + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then ./retdec-install/bin/retdec-decompiler.sh hello; fi + - cat hello.c + - grep "int main(int argc, char \*\* argv)" hello.c + +branches: + only: + # Pushes and PRs to the master branch. + - master + # Version tags. + - /^v?\d+\.\d+.*$/ + +notifications: + email: + on_success: never diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d1f2773..1735813e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,12 @@ # dev +* Enhancement: Speeded up build by skipping compilation of unnecessary dependencies (e.g. unused LLVM libraries, tools, and examples). +* Enhancement: OpenSSL is now automatically built only if it is not found in your system. * Enhancement: Added support for a system-wide installation ([#94](https://github.com/avast-tl/retdec/issues/94)). * Enhancement: Prefixed all the installed binaries and scripts with `retdec-` ([#70](https://github.com/avast-tl/retdec/issues/70)). Also, some tools were renamed to make their names more uniform. * Enhancement: Got rid of all git submodules ([#92](https://github.com/avast-tl/retdec/issues/92), [#93](https://github.com/avast-tl/retdec/issues/93)). Moved sources of all RetDec-related repositories to this main repository. Third-party dependencies are downloaded and built via CMake's external projects. This allows us to have e.g. only a single copy of LLVM ([#14](https://github.com/avast-tl/retdec/issues/14)) and not require a recursive clone ([#48](https://github.com/avast-tl/retdec/issues/48), [#68](https://github.com/avast-tl/retdec/issues/68), [#72](https://github.com/avast-tl/retdec/issues/72)). -* Enhancement: Set a proper `rpath` during installation on Linux and macOS ([#77](https://github.com/avast-tl/retdec/issues/77)). This allows us to move the installation directory after the installation into another location. +* Enhancement: Set a proper `rpath` during installation on Linux and macOS ([#77](https://github.com/avast-tl/retdec/issues/77), [#100](https://github.com/avast-tl/retdec/issues/100)). This allows us to move the installation directory after the installation into another location. * Enhancement: Added community support for building and running RetDec inside Docker ([#60](https://github.com/avast-tl/retdec/pull/60)). * Enhancement: Decrease the default timeout when downloading the support package during installation ([#6](https://github.com/avast-tl/retdec/pull/6)). * Enhancement: Any shell can be used to install the decompiler, not just Bash. @@ -22,6 +24,7 @@ * Fix: When installing the decompiler, do not remove the entire `share` directory ([#12](https://github.com/avast-tl/retdec/issues/12)). * Fix: Improve OS type detection when installing the decompiler. * Fix: Remove useless OS type detection when running decompilations ([#10](https://github.com/avast-tl/retdec/issues/10)). +* Fix: Filesystem path in utils now returns correct information when it is appended with another path. # v3.0 (2017-12-13) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c7cdf61..5ceb30ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,34 @@ endif() set(EP_PREFIX "${PROJECT_BINARY_DIR}/external") set_directory_properties(PROPERTIES EP_PREFIX "${EP_PREFIX}") -set(DEPS_TESTS ${RETDEC_TESTS}) +# Compilation warnings. +if(MSVC) + # For the moment, suppress all warnings when building with MSVC on Windows + # because there are too many warnings that clutter the build output (#106). + # We should investigate the warnings, fix them, and then enable their + # emission (e.g. by replacing /W0 with /W3 in the code below). + if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") + string(REGEX REPLACE "/W[0-4]" "/W0" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W0") + endif() +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR + CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR + CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + # Enable standard warnings. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") + + # Enable additional warnings that are not included in -Wall and -Wextra. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wcast-align") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wcast-qual") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wswitch-default") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wuninitialized") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wold-style-cast") + + # Disable warnings that produce more headaches than use. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter") +endif() + include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/install-external.cmake) add_subdirectory(deps) diff --git a/README.md b/README.md index 9f50b51e..4afd7b85 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # RetDec +[![Travis CI build status](https://travis-ci.org/avast-tl/retdec.svg?branch=master)](https://travis-ci.org/avast-tl/retdec) +[![AppVeyor build status](https://ci.appveyor.com/api/projects/status/github/avast-tl/retdec?branch=master&svg=true)](https://ci.appveyor.com/project/avast-tl/retdec) + [RetDec](https://retdec.com/) is a retargetable machine-code decompiler based on [LLVM](https://llvm.org/). The decompiler is not limited to any particular target architecture, operating system, or executable file format: diff --git a/deps/capstone/CMakeLists.txt b/deps/capstone/CMakeLists.txt index c193d83d..3b53ebb8 100644 --- a/deps/capstone/CMakeLists.txt +++ b/deps/capstone/CMakeLists.txt @@ -1,13 +1,42 @@ include(ExternalProject) +if(CMAKE_C_COMPILER) + set(CMAKE_C_COMPILER_OPTION "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}") +endif() +if(CMAKE_CXX_COMPILER) + set(CMAKE_CXX_COMPILER_OPTION "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}") +endif() + ExternalProject_Add(capstone-project URL https://github.com/avast-tl/capstone/archive/27c713fe4f6eaf9721785932d850b6291a6073fe.zip URL_HASH SHA256=4d8d0461d7d5737893253698cd0b6d0d64545c1a74b166e8b1d823156a3109cb + DOWNLOAD_NAME capstone.zip CMAKE_ARGS # This does not work on MSVC, but may be useful on Linux. -DCMAKE_BUILD_TYPE=Release + -DCAPSTONE_BUILD_STATIC=ON -DCAPSTONE_BUILD_SHARED=OFF -DCAPSTONE_BUILD_STATIC_RUNTIME=OFF + -DCAPSTONE_BUILD_TESTS=OFF + -DCAPSTONE_X86_ATT_DISABLE=OFF + # Enabled architectures. + -DCAPSTONE_ARM_SUPPORT=ON + -DCAPSTONE_MIPS_SUPPORT=ON + -DCAPSTONE_PPC_SUPPORT=ON + -DCAPSTONE_X86_SUPPORT=ON + # Disabled architectures. + -DCAPSTONE_ARM64_SUPPORT=OFF + -DCAPSTONE_M68K_SUPPORT=OFF + -DCAPSTONE_SPARC_SUPPORT=OFF + -DCAPSTONE_SYSZ_SUPPORT=OFF + -DCAPSTONE_XCORE_SUPPORT=OFF + -DCAPSTONE_TMS320C64X_SUPPORT=OFF + -DCAPSTONE_M680X_SUPPORT=OFF + # Force the use of the same compiler as used to build the top-level + # project. Otherwise, the external project may pick up a different + # compiler, which may result in link errors. + "${CMAKE_C_COMPILER_OPTION}" + "${CMAKE_CXX_COMPILER_OPTION}" # Disable the update step. UPDATE_COMMAND "" # Disable the install step. diff --git a/deps/elfio/CMakeLists.txt b/deps/elfio/CMakeLists.txt index 1648d119..0b28863d 100644 --- a/deps/elfio/CMakeLists.txt +++ b/deps/elfio/CMakeLists.txt @@ -1,11 +1,26 @@ include(ExternalProject) +if(CMAKE_C_COMPILER) + set(CMAKE_C_COMPILER_OPTION "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}") +endif() +if(CMAKE_CXX_COMPILER) + set(CMAKE_CXX_COMPILER_OPTION "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}") +endif() + ExternalProject_Add(elfio-project URL https://github.com/avast-tl/elfio/archive/1677a88d902d0fa6a5a7a31dc4da45748bba0bde.zip URL_HASH SHA256=a446b2751b177bde6c5bff5a3716a978918daba6f4953aeef3850036cb43fde7 + DOWNLOAD_NAME elfio.zip CMAKE_ARGS # This does not work on MSVC, but may be useful on Linux. -DCMAKE_BUILD_TYPE=Release + # Force the use of the same compiler as used to build the top-level + # project. Otherwise, the external project may pick up a different + # compiler, which may result in link errors. + "${CMAKE_C_COMPILER_OPTION}" + "${CMAKE_CXX_COMPILER_OPTION}" + # Disable the configure step - header only. + CONFIGURE_COMMAND "" # Disable the build step. BUILD_COMMAND "" # Disable the update step. diff --git a/deps/googletest/CMakeLists.txt b/deps/googletest/CMakeLists.txt index 7f931939..597b3c6e 100644 --- a/deps/googletest/CMakeLists.txt +++ b/deps/googletest/CMakeLists.txt @@ -10,8 +10,9 @@ if(CMAKE_CXX_COMPILER) endif() ExternalProject_Add(googletest - URL https://github.com/google/googletest/archive/5490beb0602eab560fa3969a4410e11d94bf12af.zip - URL_HASH SHA256=fb94ea2c653f239fb57e423afef372ebb5e019418af58f8bb2ce946679a6187b + URL https://github.com/google/googletest/archive/83fa0cb17dad47a1d905526dcdddb5b96ed189d2.zip + URL_HASH SHA256=3b7cf6bfd1fdec3204933b4c0419c010e89b2409dcd8cbc1ac6a78aab058e2b0 + DOWNLOAD_NAME googletest.zip CMAKE_ARGS # This does not work on MSVC, but is useful on Linux. -DCMAKE_BUILD_TYPE=Release @@ -44,28 +45,32 @@ ExternalProject_Get_Property(googletest binary_dir) if(MSVC) set(DEBUG_DIR "Debug/") set(RELEASE_DIR "Release/") + set(DEBUG_LIB_SUFFIX "d") endif() add_library(gtest INTERFACE) -target_link_libraries(gtest INTERFACE debug ${binary_dir}/googlemock/gtest/${DEBUG_DIR}${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(gtest INTERFACE debug ${binary_dir}/googlemock/gtest/${DEBUG_DIR}${CMAKE_STATIC_LIBRARY_PREFIX}gtest${DEBUG_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(gtest INTERFACE optimized ${binary_dir}/googlemock/gtest/${RELEASE_DIR}${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_THREAD_LIBS_INIT}) target_include_directories(gtest SYSTEM INTERFACE ${GTEST_INCLUDE_DIR}) add_dependencies(gtest googletest) add_library(gtest_main INTERFACE) -target_link_libraries(gtest_main INTERFACE debug ${binary_dir}/googlemock/gtest/${DEBUG_DIR}${CMAKE_STATIC_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(gtest_main INTERFACE debug ${binary_dir}/googlemock/gtest/${DEBUG_DIR}${CMAKE_STATIC_LIBRARY_PREFIX}gtest_main${DEBUG_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(gtest_main INTERFACE optimized ${binary_dir}/googlemock/gtest/${RELEASE_DIR}${CMAKE_STATIC_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(gtest_main INTERFACE gtest) target_include_directories(gtest_main SYSTEM INTERFACE ${GTEST_INCLUDE_DIR}) add_dependencies(gtest_main googletest) add_library(gmock INTERFACE) -target_link_libraries(gmock INTERFACE debug ${binary_dir}/googlemock/${DEBUG_DIR}${CMAKE_STATIC_LIBRARY_PREFIX}gmock${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(gmock INTERFACE debug ${binary_dir}/googlemock/${DEBUG_DIR}${CMAKE_STATIC_LIBRARY_PREFIX}gmock${DEBUG_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(gmock INTERFACE optimized ${binary_dir}/googlemock/${RELEASE_DIR}${CMAKE_STATIC_LIBRARY_PREFIX}gmock${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(gmock INTERFACE gtest) target_include_directories(gmock SYSTEM INTERFACE ${GTEST_INCLUDE_DIR} ${GMOCK_INCLUDE_DIR}) add_dependencies(gmock googletest) add_library(gmock_main INTERFACE) -target_link_libraries(gmock_main INTERFACE debug ${binary_dir}/googlemock/${DEBUG_DIR}${CMAKE_STATIC_LIBRARY_PREFIX}gmock_main${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(gmock_main INTERFACE debug ${binary_dir}/googlemock/${DEBUG_DIR}${CMAKE_STATIC_LIBRARY_PREFIX}gmock_main${DEBUG_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(gmock_main INTERFACE optimized ${binary_dir}/googlemock/${RELEASE_DIR}${CMAKE_STATIC_LIBRARY_PREFIX}gmock_main${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(gmock_main INTERFACE gmock) target_include_directories(gmock_main SYSTEM INTERFACE ${GTEST_INCLUDE_DIR} ${GMOCK_INCLUDE_DIR}) add_dependencies(gmock_main googletest) diff --git a/deps/jsoncpp/CMakeLists.txt b/deps/jsoncpp/CMakeLists.txt index 656074cd..274eddac 100644 --- a/deps/jsoncpp/CMakeLists.txt +++ b/deps/jsoncpp/CMakeLists.txt @@ -10,6 +10,7 @@ endif() ExternalProject_Add(jsoncpp-project URL https://github.com/open-source-parsers/jsoncpp/archive/1.8.4.zip URL_HASH SHA256=2979436dbd4c48a3284dca9fa8f212298425ba3920ed6bacdda8905a94b111a8 + DOWNLOAD_NAME jsoncpp.zip CMAKE_ARGS # This does not work on MSVC, but may be useful on Linux. -DCMAKE_BUILD_TYPE=Release @@ -18,6 +19,11 @@ ExternalProject_Add(jsoncpp-project # compiler, which may result in link errors. "${CMAKE_C_COMPILER_OPTION}" "${CMAKE_CXX_COMPILER_OPTION}" + # Disable unused options. + -DJSONCPP_WITH_TESTS=OFF + -DJSONCPP_WITH_POST_BUILD_UNITTEST=OFF + -DJSONCPP_WITH_PKGCONFIG_SUPPORT=OFF + -DBUILD_SHARED_LIBS=OFF # Disable the update step. UPDATE_COMMAND "" # Disable the install step. diff --git a/deps/keystone/CMakeLists.txt b/deps/keystone/CMakeLists.txt index d034c7a6..d248004e 100644 --- a/deps/keystone/CMakeLists.txt +++ b/deps/keystone/CMakeLists.txt @@ -1,5 +1,12 @@ include(ExternalProject) +if(CMAKE_C_COMPILER) + set(CMAKE_C_COMPILER_OPTION "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}") +endif() +if(CMAKE_CXX_COMPILER) + set(CMAKE_CXX_COMPILER_OPTION "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}") +endif() + # Keystone requires python3, so we first check whether python3 is installed as # "python3", and if not, we suppose that `python` runs python3. # Python is used to generate LLVMBuild.cmake file. Python2 generates this file @@ -12,12 +19,18 @@ endif() ExternalProject_Add(keystone-project URL https://github.com/avast-tl/keystone/archive/4722974f58dbe878c805ce2b7c0a41cf6153903c.zip URL_HASH SHA256=145c8bb836d2f06f845b5015b3eab350ae59d35a54052f00f5d26445172415fd + DOWNLOAD_NAME keystone.zip CMAKE_ARGS # This does not work on MSVC, but may be useful on Linux. -DCMAKE_BUILD_TYPE=Release # Force python version set in this cmake file. -DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE} -DKEYSTONE_BUILD_STATIC_RUNTIME=OFF + # Force the use of the same compiler as used to build the top-level + # project. Otherwise, the external project may pick up a different + # compiler, which may result in link errors. + "${CMAKE_C_COMPILER_OPTION}" + "${CMAKE_CXX_COMPILER_OPTION}" # Disable the update step. UPDATE_COMMAND "" # Disable the install step. diff --git a/deps/libdwarf/CMakeLists.txt b/deps/libdwarf/CMakeLists.txt index ec9e6164..397c8daf 100644 --- a/deps/libdwarf/CMakeLists.txt +++ b/deps/libdwarf/CMakeLists.txt @@ -1,15 +1,28 @@ include(ExternalProject) +if(CMAKE_C_COMPILER) + set(CMAKE_C_COMPILER_OPTION "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}") +endif() +if(CMAKE_CXX_COMPILER) + set(CMAKE_CXX_COMPILER_OPTION "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}") +endif() + set(LIBDWARF_INSTALL_DIR "${PROJECT_BINARY_DIR}/external/src/libdwarf-project-install") ExternalProject_Add(libdwarf-project - URL https://github.com/avast-tl/libdwarf/archive/bb920ecb8b6172359b4d57955a2670194d1f1f43.zip - URL_HASH SHA256=b28a6f9fc854d7e1dce84c892b522b9ae98b2d600f82d9f1cc10e470f926ab98 + URL https://github.com/avast-tl/libdwarf/archive/85465d5e235cc2d2f90d04016d6aca1a452d0e73.zip + URL_HASH SHA256=2864aa7b46529778476190e90359669eb35799ad273233c4df2203bec7db0738 + DOWNLOAD_NAME libdwarf.zip CMAKE_ARGS # This does not work on MSVC, but may be useful on Linux. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${LIBDWARF_INSTALL_DIR} -DLIBDWARF_INSTALL_TO_UNITTESTS=${RETDEC_TESTS} + # Force the use of the same compiler as used to build the top-level + # project. Otherwise, the external project may pick up a different + # compiler, which may result in link errors. + "${CMAKE_C_COMPILER_OPTION}" + "${CMAKE_CXX_COMPILER_OPTION}" # Disable the update step. UPDATE_COMMAND "" LOG_DOWNLOAD ON diff --git a/deps/llvm/CMakeLists.txt b/deps/llvm/CMakeLists.txt index 4129f87d..bf8ff7fe 100644 --- a/deps/llvm/CMakeLists.txt +++ b/deps/llvm/CMakeLists.txt @@ -3,6 +3,13 @@ if(UNIX) find_package(ZLIB REQUIRED) endif() +if(CMAKE_C_COMPILER) + set(CMAKE_C_COMPILER_OPTION "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}") +endif() +if(CMAKE_CXX_COMPILER) + set(CMAKE_CXX_COMPILER_OPTION "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}") +endif() + # Set the default build type to 'Release' if (NOT CMAKE_BUILD_TYPE) set(default_build_type "Release") @@ -28,8 +35,9 @@ if(NOT PYTHON_EXECUTABLE) endif() ExternalProject_Add(llvm-project - URL https://github.com/avast-tl/llvm/archive/e7431aa1980227e15caf296bd1a8da85aa5706f4.zip - URL_HASH SHA256=d30752a7cd23597cf54513b6ea69fa8c1311c631bd7bbfc816737249664443e9 + URL https://github.com/avast-tl/llvm/archive/257248365d034351e164aae04060f0d8f4c5b1f1.zip + URL_HASH SHA256=0088e715fd92d9670398514791563f1efc727d5a81b5cac8830c7e746ab26c06 + DOWNLOAD_NAME llvm.zip CMAKE_ARGS # Force a release build (we don't need to debug LLVM). # This has no effect on Windows with MSVC, but is useful on Linux. @@ -66,6 +74,17 @@ ExternalProject_Add(llvm-project # need to also link the tinfo library. -DLLVM_ENABLE_TERMINFO=YES + # Disable build of unnecessary LLVM parts. + -DLLVM_BUILD_TOOLS=OFF + -DLLVM_BUILD_UTILS=OFF + -DLLVM_BUILD_RUNTIME=OFF + + # Force the use of the same compiler as used to build the top-level + # project. Otherwise, the external project may pick up a different + # compiler, which may result in link errors. + "${CMAKE_C_COMPILER_OPTION}" + "${CMAKE_CXX_COMPILER_OPTION}" + # Disable the installation right after build (we want to install the # needed libraries and tools manually). INSTALL_COMMAND "" @@ -97,47 +116,24 @@ add_library(llvm INTERFACE) add_dependencies(llvm llvm-project) set(LLVM_LIB_LIST - LLVMInterpreter - LLVMMCJIT - LLVMRuntimeDyld - LLVMOrcJIT - LLVMExecutionEngine - LLVMRuntimeDyld - LLVMX86CodeGen - LLVMX86AsmParser - LLVMX86Disassembler LLVMBitWriter LLVMIRReader - LLVMInstrumentation LLVMObject + LLVMInstCombine LLVMSupport LLVMipo - LLVMAsmPrinter - LLVMSelectionDAG - LLVMX86Desc LLVMAsmParser LLVMBitReader - LLVMVectorize LLVMMCParser LLVMCodeGen - LLVMX86AsmPrinter - LLVMX86Info - LLVMObjCARCOpts LLVMScalarOpts - LLVMX86Utils LLVMTransformUtils LLVMAnalysis LLVMTarget LLVMCore LLVMMC LLVMObject - LLVMMCDisassembler - LLVMProfileData - LLVMDebugInfoCodeView LLVMPasses - LLVMLinker - LLVMInstCombine - LLVMSupport ) if(MSVC) diff --git a/deps/openssl/CMakeLists.txt b/deps/openssl/CMakeLists.txt index 60415674..2ac11457 100644 --- a/deps/openssl/CMakeLists.txt +++ b/deps/openssl/CMakeLists.txt @@ -1,3 +1,12 @@ + +find_package(OpenSSL 1.0.1) +if(OPENSSL_FOUND) + add_library(openssl-crypto INTERFACE) + target_link_libraries(openssl-crypto INTERFACE OpenSSL::Crypto) + return() +endif() +message(STATUS " --> OpenSSL will be built as an external project") + find_package(Perl REQUIRED) include(ExternalProject) @@ -82,6 +91,7 @@ set(OPENSSL_INSTALL_DIR "${OPENSSL_PREFIX}/src/openssl-install") set(OPENSSL_VERSION "1.1.0f") set(OPENSSL_URL "https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz") set(OPENSSL_ARCHIVE_SHA256 "12f746f3f2493b2f39da7ecf63d7ee19c6ac9ec6a4fcd8c229da8a522cb12765") +set(OPENSSL_DOWNLOAD_NAME "openssl.tar.gz") set(OPENSSL_CONFIGURE_OPTIONS no-shared @@ -96,6 +106,7 @@ if (NOT TARGET openssl) PREFIX ${OPENSSL_PREFIX} URL ${OPENSSL_URL} URL_HASH SHA256=${OPENSSL_ARCHIVE_SHA256} + DOWNLOAD_NAME ${OPENSSL_DOWNLOAD_NAME} CONFIGURE_COMMAND ${PERL_EXECUTABLE} ${OPENSSL_SRC_DIR}/Configure @@ -105,7 +116,9 @@ if (NOT TARGET openssl) BUILD_COMMAND ${OPENSSL_MAKE} INSTALL_COMMAND - ${OPENSSL_MAKE} install + # Install only the software parts (libraries and includes), not + # e.g. documentation and manual pages. + ${OPENSSL_MAKE} install_sw LOG_DOWNLOAD 1 LOG_BUILD 1 LOG_CONFIGURE 1 diff --git a/deps/pelib/CMakeLists.txt b/deps/pelib/CMakeLists.txt index 09a57976..39ff2ed4 100644 --- a/deps/pelib/CMakeLists.txt +++ b/deps/pelib/CMakeLists.txt @@ -1,11 +1,24 @@ include(ExternalProject) +if(CMAKE_C_COMPILER) + set(CMAKE_C_COMPILER_OPTION "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}") +endif() +if(CMAKE_CXX_COMPILER) + set(CMAKE_CXX_COMPILER_OPTION "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}") +endif() + ExternalProject_Add(pelib-project URL https://github.com/avast-tl/pelib/archive/e93eaa7c150f4608a5d02a67f5edc9e54456fe24.zip URL_HASH SHA256=2ffd7e89451c980a1af6d24d4f6dfbb69a660b06ad5de44c481f6431e21de394 + DOWNLOAD_NAME pelib.zip CMAKE_ARGS # This does not work on MSVC, but may be useful on Linux. -DCMAKE_BUILD_TYPE=Release + # Force the use of the same compiler as used to build the top-level + # project. Otherwise, the external project may pick up a different + # compiler, which may result in link errors. + "${CMAKE_C_COMPILER_OPTION}" + "${CMAKE_CXX_COMPILER_OPTION}" # Disable the update step. UPDATE_COMMAND "" # Disable the install step. diff --git a/deps/rapidjson/CMakeLists.txt b/deps/rapidjson/CMakeLists.txt index c6e6af38..4ae43d04 100644 --- a/deps/rapidjson/CMakeLists.txt +++ b/deps/rapidjson/CMakeLists.txt @@ -1,14 +1,27 @@ include(ExternalProject) +if(CMAKE_C_COMPILER) + set(CMAKE_C_COMPILER_OPTION "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}") +endif() +if(CMAKE_CXX_COMPILER) + set(CMAKE_CXX_COMPILER_OPTION "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}") +endif() + ExternalProject_Add(rapidjson-project URL https://github.com/Tencent/rapidjson/archive/v1.1.0.zip URL_HASH SHA256=8e00c38829d6785a2dfb951bb87c6974fa07dfe488aa5b25deec4b8bc0f6a3ab + DOWNLOAD_NAME rapidjson.zip CMAKE_ARGS # This does not work on MSVC, but may be useful on Linux. -DCMAKE_BUILD_TYPE=Release -DRAPIDJSON_BUILD_DOC=OFF -DRAPIDJSON_BUILD_EXAMPLES=OFF -DRAPIDJSON_BUILD_TESTS=OFF + # Force the use of the same compiler as used to build the top-level + # project. Otherwise, the external project may pick up a different + # compiler, which may result in link errors. + "${CMAKE_C_COMPILER_OPTION}" + "${CMAKE_CXX_COMPILER_OPTION}" # Disable the build step. BUILD_COMMAND "" # Disable the update step. diff --git a/deps/tinyxml2/CMakeLists.txt b/deps/tinyxml2/CMakeLists.txt index 425265bb..bf42f81f 100644 --- a/deps/tinyxml2/CMakeLists.txt +++ b/deps/tinyxml2/CMakeLists.txt @@ -1,2 +1,53 @@ -add_library(tinyxml2 STATIC tinyxml2.cpp) -target_include_directories(tinyxml2 PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") +include(ExternalProject) + +if(CMAKE_C_COMPILER) + set(CMAKE_C_COMPILER_OPTION "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}") +endif() +if(CMAKE_CXX_COMPILER) + set(CMAKE_CXX_COMPILER_OPTION "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}") +endif() + +ExternalProject_Add(tinyxml2-project + URL https://github.com/leethomason/tinyxml2/archive/cc1745b552dd12bb1297a99f82044f83b06729e0.zip + URL_HASH SHA256=de255ce39ba1efe20cbffdebd3eea2d08f1269d03440a4fd60e142b0481c3f00 + DOWNLOAD_NAME tinyxml2.zip + CMAKE_ARGS + # This does not work on MSVC, but may be useful on Linux. + -DCMAKE_BUILD_TYPE=Release + # Build only static library. + -DBUILD_STATIC_LIBS:BOOL=ON + -DBUILD_SHARED_LIBS:BOOL=OFF + # Do not build tests. + -DBUILD_TESTS:BOOL=OFF + # Force the use of the same compiler as used to build the top-level + # project. Otherwise, the external project may pick up a different + # compiler, which may result in link errors. + "${CMAKE_C_COMPILER_OPTION}" + "${CMAKE_CXX_COMPILER_OPTION}" + # Disable the update step. + UPDATE_COMMAND "" + # Disable the install step. + INSTALL_COMMAND "" + LOG_DOWNLOAD ON + LOG_CONFIGURE ON + LOG_BUILD ON +) + +# Set include directories. +ExternalProject_Get_Property(tinyxml2-project source_dir) +set(TINYXML2_INCLUDE_DIR ${source_dir}) + +# Add libraries. +ExternalProject_Get_Property(tinyxml2-project binary_dir) + +if(MSVC) + set(DEBUG_DIR "Debug/") + set(RELEASE_DIR "Release/") + set(DEBUG_LIB_SUFFIX "d") +endif() + +add_library(tinyxml2 INTERFACE) +target_link_libraries(tinyxml2 INTERFACE debug ${binary_dir}/${DEBUG_DIR}${CMAKE_STATIC_LIBRARY_PREFIX}tinyxml2${DEBUG_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}) +target_link_libraries(tinyxml2 INTERFACE optimized ${binary_dir}/${RELEASE_DIR}${CMAKE_STATIC_LIBRARY_PREFIX}tinyxml2${CMAKE_STATIC_LIBRARY_SUFFIX}) +target_include_directories(tinyxml2 SYSTEM INTERFACE ${TINYXML2_INCLUDE_DIR}) +add_dependencies(tinyxml2 tinyxml2-project) diff --git a/deps/tinyxml2/tinyxml2.cpp b/deps/tinyxml2/tinyxml2.cpp deleted file mode 100644 index c7116c9e..00000000 --- a/deps/tinyxml2/tinyxml2.cpp +++ /dev/null @@ -1,2741 +0,0 @@ -/* -Original code by Lee Thomason (www.grinninglizard.com) - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - -#include "tinyxml2.h" - -#include <new> // yes, this one new style header, is in the Android SDK. -#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) -# include <stddef.h> -# include <stdarg.h> -#else -# include <cstddef> -# include <cstdarg> -#endif - -#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) - // Microsoft Visual Studio, version 2005 and higher. Not WinCE. - /*int _snprintf_s( - char *buffer, - size_t sizeOfBuffer, - size_t count, - const char *format [, - argument] ... - );*/ - static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... ) - { - va_list va; - va_start( va, format ); - int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); - va_end( va ); - return result; - } - - static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va ) - { - int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); - return result; - } - - #define TIXML_VSCPRINTF _vscprintf - #define TIXML_SSCANF sscanf_s -#elif defined _MSC_VER - // Microsoft Visual Studio 2003 and earlier or WinCE - #define TIXML_SNPRINTF _snprintf - #define TIXML_VSNPRINTF _vsnprintf - #define TIXML_SSCANF sscanf - #if (_MSC_VER < 1400 ) && (!defined WINCE) - // Microsoft Visual Studio 2003 and not WinCE. - #define TIXML_VSCPRINTF _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have. - #else - // Microsoft Visual Studio 2003 and earlier or WinCE. - static inline int TIXML_VSCPRINTF( const char* format, va_list va ) - { - int len = 512; - for (;;) { - len = len*2; - char* str = new char[len](); - const int required = _vsnprintf(str, len, format, va); - delete[] str; - if ( required != -1 ) { - TIXMLASSERT( required >= 0 ); - len = required; - break; - } - } - TIXMLASSERT( len >= 0 ); - return len; - } - #endif -#else - // GCC version 3 and higher - //#warning( "Using sn* functions." ) - #define TIXML_SNPRINTF snprintf - #define TIXML_VSNPRINTF vsnprintf - static inline int TIXML_VSCPRINTF( const char* format, va_list va ) - { - int len = vsnprintf( 0, 0, format, va ); - TIXMLASSERT( len >= 0 ); - return len; - } - #define TIXML_SSCANF sscanf -#endif - - -static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF -static const char LF = LINE_FEED; -static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out -static const char CR = CARRIAGE_RETURN; -static const char SINGLE_QUOTE = '\''; -static const char DOUBLE_QUOTE = '\"'; - -// Bunch of unicode info at: -// http://www.unicode.org/faq/utf_bom.html -// ef bb bf (Microsoft "lead bytes") - designates UTF-8 - -static const unsigned char TIXML_UTF_LEAD_0 = 0xefU; -static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; -static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; - -namespace tinyxml2 -{ - -struct Entity { - const char* pattern; - int length; - char value; -}; - -static const int NUM_ENTITIES = 5; -static const Entity entities[NUM_ENTITIES] = { - { "quot", 4, DOUBLE_QUOTE }, - { "amp", 3, '&' }, - { "apos", 4, SINGLE_QUOTE }, - { "lt", 2, '<' }, - { "gt", 2, '>' } -}; - - -StrPair::~StrPair() -{ - Reset(); -} - - -void StrPair::TransferTo( StrPair* other ) -{ - if ( this == other ) { - return; - } - // This in effect implements the assignment operator by "moving" - // ownership (as in auto_ptr). - - TIXMLASSERT( other != 0 ); - TIXMLASSERT( other->_flags == 0 ); - TIXMLASSERT( other->_start == 0 ); - TIXMLASSERT( other->_end == 0 ); - - other->Reset(); - - other->_flags = _flags; - other->_start = _start; - other->_end = _end; - - _flags = 0; - _start = 0; - _end = 0; -} - -void StrPair::Reset() -{ - if ( _flags & NEEDS_DELETE ) { - delete [] _start; - } - _flags = 0; - _start = 0; - _end = 0; -} - - -void StrPair::SetStr( const char* str, int flags ) -{ - TIXMLASSERT( str ); - Reset(); - size_t len = strlen( str ); - TIXMLASSERT( _start == 0 ); - _start = new char[ len+1 ]; - memcpy( _start, str, len+1 ); - _end = _start + len; - _flags = flags | NEEDS_DELETE; -} - - -char* StrPair::ParseText( char* p, const char* endTag, int strFlags, int* curLineNumPtr ) -{ - TIXMLASSERT( p ); - TIXMLASSERT( endTag && *endTag ); - TIXMLASSERT(curLineNumPtr); - - char* start = p; - char endChar = *endTag; - size_t length = strlen( endTag ); - - // Inner loop of text parsing. - while ( *p ) { - if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) { - Set( start, p, strFlags ); - return p + length; - } else if (*p == '\n') { - ++(*curLineNumPtr); - } - ++p; - TIXMLASSERT( p ); - } - return 0; -} - - -char* StrPair::ParseName( char* p ) -{ - if ( !p || !(*p) ) { - return 0; - } - if ( !XMLUtil::IsNameStartChar( *p ) ) { - return 0; - } - - char* const start = p; - ++p; - while ( *p && XMLUtil::IsNameChar( *p ) ) { - ++p; - } - - Set( start, p, 0 ); - return p; -} - - -void StrPair::CollapseWhitespace() -{ - // Adjusting _start would cause undefined behavior on delete[] - TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 ); - // Trim leading space. - _start = XMLUtil::SkipWhiteSpace( _start, 0 ); - - if ( *_start ) { - const char* p = _start; // the read pointer - char* q = _start; // the write pointer - - while( *p ) { - if ( XMLUtil::IsWhiteSpace( *p )) { - p = XMLUtil::SkipWhiteSpace( p, 0 ); - if ( *p == 0 ) { - break; // don't write to q; this trims the trailing space. - } - *q = ' '; - ++q; - } - *q = *p; - ++q; - ++p; - } - *q = 0; - } -} - - -const char* StrPair::GetStr() -{ - TIXMLASSERT( _start ); - TIXMLASSERT( _end ); - if ( _flags & NEEDS_FLUSH ) { - *_end = 0; - _flags ^= NEEDS_FLUSH; - - if ( _flags ) { - const char* p = _start; // the read pointer - char* q = _start; // the write pointer - - while( p < _end ) { - if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) { - // CR-LF pair becomes LF - // CR alone becomes LF - // LF-CR becomes LF - if ( *(p+1) == LF ) { - p += 2; - } - else { - ++p; - } - *q = LF; - ++q; - } - else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) { - if ( *(p+1) == CR ) { - p += 2; - } - else { - ++p; - } - *q = LF; - ++q; - } - else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) { - // Entities handled by tinyXML2: - // - special entities in the entity table [in/out] - // - numeric character reference [in] - // 中 or 中 - - if ( *(p+1) == '#' ) { - const int buflen = 10; - char buf[buflen] = { 0 }; - int len = 0; - char* adjusted = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) ); - if ( adjusted == 0 ) { - *q = *p; - ++p; - ++q; - } - else { - TIXMLASSERT( 0 <= len && len <= buflen ); - TIXMLASSERT( q + len <= adjusted ); - p = adjusted; - memcpy( q, buf, len ); - q += len; - } - } - else { - bool entityFound = false; - for( int i = 0; i < NUM_ENTITIES; ++i ) { - const Entity& entity = entities[i]; - if ( strncmp( p + 1, entity.pattern, entity.length ) == 0 - && *( p + entity.length + 1 ) == ';' ) { - // Found an entity - convert. - *q = entity.value; - ++q; - p += entity.length + 2; - entityFound = true; - break; - } - } - if ( !entityFound ) { - // fixme: treat as error? - ++p; - ++q; - } - } - } - else { - *q = *p; - ++p; - ++q; - } - } - *q = 0; - } - // The loop below has plenty going on, and this - // is a less useful mode. Break it out. - if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) { - CollapseWhitespace(); - } - _flags = (_flags & NEEDS_DELETE); - } - TIXMLASSERT( _start ); - return _start; -} - - - - -// --------- XMLUtil ----------- // - -const char* XMLUtil::writeBoolTrue = "true"; -const char* XMLUtil::writeBoolFalse = "false"; - -void XMLUtil::SetBoolSerialization(const char* writeTrue, const char* writeFalse) -{ - static const char* defTrue = "true"; - static const char* defFalse = "false"; - - writeBoolTrue = (writeTrue) ? writeTrue : defTrue; - writeBoolFalse = (writeFalse) ? writeFalse : defFalse; -} - - -const char* XMLUtil::ReadBOM( const char* p, bool* bom ) -{ - TIXMLASSERT( p ); - TIXMLASSERT( bom ); - *bom = false; - const unsigned char* pu = reinterpret_cast<const unsigned char*>(p); - // Check for BOM: - if ( *(pu+0) == TIXML_UTF_LEAD_0 - && *(pu+1) == TIXML_UTF_LEAD_1 - && *(pu+2) == TIXML_UTF_LEAD_2 ) { - *bom = true; - p += 3; - } - TIXMLASSERT( p ); - return p; -} - - -void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) -{ - const unsigned long BYTE_MASK = 0xBF; - const unsigned long BYTE_MARK = 0x80; - const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; - - if (input < 0x80) { - *length = 1; - } - else if ( input < 0x800 ) { - *length = 2; - } - else if ( input < 0x10000 ) { - *length = 3; - } - else if ( input < 0x200000 ) { - *length = 4; - } - else { - *length = 0; // This code won't convert this correctly anyway. - return; - } - - output += *length; - - // Scary scary fall throughs. - switch (*length) { - case 4: - --output; - *output = (char)((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - case 3: - --output; - *output = (char)((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - case 2: - --output; - *output = (char)((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - case 1: - --output; - *output = (char)(input | FIRST_BYTE_MARK[*length]); - break; - default: - TIXMLASSERT( false ); - } -} - - -const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length ) -{ - // Presume an entity, and pull it out. - *length = 0; - - if ( *(p+1) == '#' && *(p+2) ) { - unsigned long ucs = 0; - TIXMLASSERT( sizeof( ucs ) >= 4 ); - ptrdiff_t delta = 0; - unsigned mult = 1; - static const char SEMICOLON = ';'; - - if ( *(p+2) == 'x' ) { - // Hexadecimal. - const char* q = p+3; - if ( !(*q) ) { - return 0; - } - - q = strchr( q, SEMICOLON ); - - if ( !q ) { - return 0; - } - TIXMLASSERT( *q == SEMICOLON ); - - delta = q-p; - --q; - - while ( *q != 'x' ) { - unsigned int digit = 0; - - if ( *q >= '0' && *q <= '9' ) { - digit = *q - '0'; - } - else if ( *q >= 'a' && *q <= 'f' ) { - digit = *q - 'a' + 10; - } - else if ( *q >= 'A' && *q <= 'F' ) { - digit = *q - 'A' + 10; - } - else { - return 0; - } - TIXMLASSERT( digit < 16 ); - TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); - const unsigned int digitScaled = mult * digit; - TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); - ucs += digitScaled; - TIXMLASSERT( mult <= UINT_MAX / 16 ); - mult *= 16; - --q; - } - } - else { - // Decimal. - const char* q = p+2; - if ( !(*q) ) { - return 0; - } - - q = strchr( q, SEMICOLON ); - - if ( !q ) { - return 0; - } - TIXMLASSERT( *q == SEMICOLON ); - - delta = q-p; - --q; - - while ( *q != '#' ) { - if ( *q >= '0' && *q <= '9' ) { - const unsigned int digit = *q - '0'; - TIXMLASSERT( digit < 10 ); - TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); - const unsigned int digitScaled = mult * digit; - TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); - ucs += digitScaled; - } - else { - return 0; - } - TIXMLASSERT( mult <= UINT_MAX / 10 ); - mult *= 10; - --q; - } - } - // convert the UCS to UTF-8 - ConvertUTF32ToUTF8( ucs, value, length ); - return p + delta + 1; - } - return p+1; -} - - -void XMLUtil::ToStr( int v, char* buffer, int bufferSize ) -{ - TIXML_SNPRINTF( buffer, bufferSize, "%d", v ); -} - - -void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize ) -{ - TIXML_SNPRINTF( buffer, bufferSize, "%u", v ); -} - - -void XMLUtil::ToStr( bool v, char* buffer, int bufferSize ) -{ - TIXML_SNPRINTF( buffer, bufferSize, "%s", v ? writeBoolTrue : writeBoolFalse); -} - -/* - ToStr() of a number is a very tricky topic. - https://github.com/leethomason/tinyxml2/issues/106 -*/ -void XMLUtil::ToStr( float v, char* buffer, int bufferSize ) -{ - TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v ); -} - - -void XMLUtil::ToStr( double v, char* buffer, int bufferSize ) -{ - TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v ); -} - - -void XMLUtil::ToStr(int64_t v, char* buffer, int bufferSize) -{ - // horrible syntax trick to make the compiler happy about %lld - TIXML_SNPRINTF(buffer, bufferSize, "%lld", (long long)v); -} - - -bool XMLUtil::ToInt( const char* str, int* value ) -{ - if ( TIXML_SSCANF( str, "%d", value ) == 1 ) { - return true; - } - return false; -} - -bool XMLUtil::ToUnsigned( const char* str, unsigned *value ) -{ - if ( TIXML_SSCANF( str, "%u", value ) == 1 ) { - return true; - } - return false; -} - -bool XMLUtil::ToBool( const char* str, bool* value ) -{ - int ival = 0; - if ( ToInt( str, &ival )) { - *value = (ival==0) ? false : true; - return true; - } - if ( StringEqual( str, "true" ) ) { - *value = true; - return true; - } - else if ( StringEqual( str, "false" ) ) { - *value = false; - return true; - } - return false; -} - - -bool XMLUtil::ToFloat( const char* str, float* value ) -{ - if ( TIXML_SSCANF( str, "%f", value ) == 1 ) { - return true; - } - return false; -} - - -bool XMLUtil::ToDouble( const char* str, double* value ) -{ - if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) { - return true; - } - return false; -} - - -bool XMLUtil::ToInt64(const char* str, int64_t* value) -{ - long long v = 0; // horrible syntax trick to make the compiler happy about %lld - if (TIXML_SSCANF(str, "%lld", &v) == 1) { - *value = (int64_t)v; - return true; - } - return false; -} - - -char* XMLDocument::Identify( char* p, XMLNode** node ) -{ - TIXMLASSERT( node ); - TIXMLASSERT( p ); - char* const start = p; - int const startLine = _parseCurLineNum; - p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum ); - if( !*p ) { - *node = 0; - TIXMLASSERT( p ); - return p; - } - - // These strings define the matching patterns: - static const char* xmlHeader = { "<?" }; - static const char* commentHeader = { "<!--" }; - static const char* cdataHeader = { "<![CDATA[" }; - static const char* dtdHeader = { "<!" }; - static const char* elementHeader = { "<" }; // and a header for everything else; check last. - - static const int xmlHeaderLen = 2; - static const int commentHeaderLen = 4; - static const int cdataHeaderLen = 9; - static const int dtdHeaderLen = 2; - static const int elementHeaderLen = 1; - - TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool - TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool - XMLNode* returnNode = 0; - if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) { - returnNode = CreateUnlinkedNode<XMLDeclaration>( _commentPool ); - returnNode->_parseLineNum = _parseCurLineNum; - p += xmlHeaderLen; - } - else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) { - returnNode = CreateUnlinkedNode<XMLComment>( _commentPool ); - returnNode->_parseLineNum = _parseCurLineNum; - p += commentHeaderLen; - } - else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) { - XMLText* text = CreateUnlinkedNode<XMLText>( _textPool ); - returnNode = text; - returnNode->_parseLineNum = _parseCurLineNum; - p += cdataHeaderLen; - text->SetCData( true ); - } - else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) { - returnNode = CreateUnlinkedNode<XMLUnknown>( _commentPool ); - returnNode->_parseLineNum = _parseCurLineNum; - p += dtdHeaderLen; - } - else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) { - returnNode = CreateUnlinkedNode<XMLElement>( _elementPool ); - returnNode->_parseLineNum = _parseCurLineNum; - p += elementHeaderLen; - } - else { - returnNode = CreateUnlinkedNode<XMLText>( _textPool ); - returnNode->_parseLineNum = _parseCurLineNum; // Report line of first non-whitespace character - p = start; // Back it up, all the text counts. - _parseCurLineNum = startLine; - } - - TIXMLASSERT( returnNode ); - TIXMLASSERT( p ); - *node = returnNode; - return p; -} - - -bool XMLDocument::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - if ( visitor->VisitEnter( *this ) ) { - for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { - if ( !node->Accept( visitor ) ) { - break; - } - } - } - return visitor->VisitExit( *this ); -} - - -// --------- XMLNode ----------- // - -XMLNode::XMLNode( XMLDocument* doc ) : - _document( doc ), - _parent( 0 ), - _parseLineNum( 0 ), - _firstChild( 0 ), _lastChild( 0 ), - _prev( 0 ), _next( 0 ), - _userData( 0 ), - _memPool( 0 ) -{ -} - - -XMLNode::~XMLNode() -{ - DeleteChildren(); - if ( _parent ) { - _parent->Unlink( this ); - } -} - -const char* XMLNode::Value() const -{ - // Edge case: XMLDocuments don't have a Value. Return null. - if ( this->ToDocument() ) - return 0; - return _value.GetStr(); -} - -void XMLNode::SetValue( const char* str, bool staticMem ) -{ - if ( staticMem ) { - _value.SetInternedStr( str ); - } - else { - _value.SetStr( str ); - } -} - -XMLNode* XMLNode::DeepClone(XMLDocument* target) const -{ - XMLNode* clone = this->ShallowClone(target); - if (!clone) return 0; - - for (const XMLNode* child = this->FirstChild(); child; child = child->NextSibling()) { - XMLNode* childClone = child->DeepClone(target); - TIXMLASSERT(childClone); - clone->InsertEndChild(childClone); - } - return clone; -} - -void XMLNode::DeleteChildren() -{ - while( _firstChild ) { - TIXMLASSERT( _lastChild ); - DeleteChild( _firstChild ); - } - _firstChild = _lastChild = 0; -} - - -void XMLNode::Unlink( XMLNode* child ) -{ - TIXMLASSERT( child ); - TIXMLASSERT( child->_document == _document ); - TIXMLASSERT( child->_parent == this ); - if ( child == _firstChild ) { - _firstChild = _firstChild->_next; - } - if ( child == _lastChild ) { - _lastChild = _lastChild->_prev; - } - - if ( child->_prev ) { - child->_prev->_next = child->_next; - } - if ( child->_next ) { - child->_next->_prev = child->_prev; - } - child->_next = 0; - child->_prev = 0; - child->_parent = 0; -} - - -void XMLNode::DeleteChild( XMLNode* node ) -{ - TIXMLASSERT( node ); - TIXMLASSERT( node->_document == _document ); - TIXMLASSERT( node->_parent == this ); - Unlink( node ); - TIXMLASSERT(node->_prev == 0); - TIXMLASSERT(node->_next == 0); - TIXMLASSERT(node->_parent == 0); - DeleteNode( node ); -} - - -XMLNode* XMLNode::InsertEndChild( XMLNode* addThis ) -{ - TIXMLASSERT( addThis ); - if ( addThis->_document != _document ) { - TIXMLASSERT( false ); - return 0; - } - InsertChildPreamble( addThis ); - - if ( _lastChild ) { - TIXMLASSERT( _firstChild ); - TIXMLASSERT( _lastChild->_next == 0 ); - _lastChild->_next = addThis; - addThis->_prev = _lastChild; - _lastChild = addThis; - - addThis->_next = 0; - } - else { - TIXMLASSERT( _firstChild == 0 ); - _firstChild = _lastChild = addThis; - - addThis->_prev = 0; - addThis->_next = 0; - } - addThis->_parent = this; - return addThis; -} - - -XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis ) -{ - TIXMLASSERT( addThis ); - if ( addThis->_document != _document ) { - TIXMLASSERT( false ); - return 0; - } - InsertChildPreamble( addThis ); - - if ( _firstChild ) { - TIXMLASSERT( _lastChild ); - TIXMLASSERT( _firstChild->_prev == 0 ); - - _firstChild->_prev = addThis; - addThis->_next = _firstChild; - _firstChild = addThis; - - addThis->_prev = 0; - } - else { - TIXMLASSERT( _lastChild == 0 ); - _firstChild = _lastChild = addThis; - - addThis->_prev = 0; - addThis->_next = 0; - } - addThis->_parent = this; - return addThis; -} - - -XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ) -{ - TIXMLASSERT( addThis ); - if ( addThis->_document != _document ) { - TIXMLASSERT( false ); - return 0; - } - - TIXMLASSERT( afterThis ); - - if ( afterThis->_parent != this ) { - TIXMLASSERT( false ); - return 0; - } - - if ( afterThis->_next == 0 ) { - // The last node or the only node. - return InsertEndChild( addThis ); - } - InsertChildPreamble( addThis ); - addThis->_prev = afterThis; - addThis->_next = afterThis->_next; - afterThis->_next->_prev = addThis; - afterThis->_next = addThis; - addThis->_parent = this; - return addThis; -} - - - - -const XMLElement* XMLNode::FirstChildElement( const char* name ) const -{ - for( const XMLNode* node = _firstChild; node; node = node->_next ) { - const XMLElement* element = node->ToElementWithName( name ); - if ( element ) { - return element; - } - } - return 0; -} - - -const XMLElement* XMLNode::LastChildElement( const char* name ) const -{ - for( const XMLNode* node = _lastChild; node; node = node->_prev ) { - const XMLElement* element = node->ToElementWithName( name ); - if ( element ) { - return element; - } - } - return 0; -} - - -const XMLElement* XMLNode::NextSiblingElement( const char* name ) const -{ - for( const XMLNode* node = _next; node; node = node->_next ) { - const XMLElement* element = node->ToElementWithName( name ); - if ( element ) { - return element; - } - } - return 0; -} - - -const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const -{ - for( const XMLNode* node = _prev; node; node = node->_prev ) { - const XMLElement* element = node->ToElementWithName( name ); - if ( element ) { - return element; - } - } - return 0; -} - - -char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) -{ - // This is a recursive method, but thinking about it "at the current level" - // it is a pretty simple flat list: - // <foo/> - // <!-- comment --> - // - // With a special case: - // <foo> - // </foo> - // <!-- comment --> - // - // Where the closing element (/foo) *must* be the next thing after the opening - // element, and the names must match. BUT the tricky bit is that the closing - // element will be read by the child. - // - // 'endTag' is the end tag for this node, it is returned by a call to a child. - // 'parentEnd' is the end tag for the parent, which is filled in and returned. - - while( p && *p ) { - XMLNode* node = 0; - - p = _document->Identify( p, &node ); - TIXMLASSERT( p ); - if ( node == 0 ) { - break; - } - - int initialLineNum = node->_parseLineNum; - - StrPair endTag; - p = node->ParseDeep( p, &endTag, curLineNumPtr ); - if ( !p ) { - DeleteNode( node ); - if ( !_document->Error() ) { - _document->SetError( XML_ERROR_PARSING, 0, 0, initialLineNum); - } - break; - } - - XMLDeclaration* decl = node->ToDeclaration(); - if ( decl ) { - // Declarations are only allowed at document level - bool wellLocated = ( ToDocument() != 0 ); - if ( wellLocated ) { - // Multiple declarations are allowed but all declarations - // must occur before anything else - for ( const XMLNode* existingNode = _document->FirstChild(); existingNode; existingNode = existingNode->NextSibling() ) { - if ( !existingNode->ToDeclaration() ) { - wellLocated = false; - break; - } - } - } - if ( !wellLocated ) { - _document->SetError( XML_ERROR_PARSING_DECLARATION, decl->Value(), 0, initialLineNum); - DeleteNode( node ); - break; - } - } - - XMLElement* ele = node->ToElement(); - if ( ele ) { - // We read the end tag. Return it to the parent. - if ( ele->ClosingType() == XMLElement::CLOSING ) { - if ( parentEndTag ) { - ele->_value.TransferTo( parentEndTag ); - } - node->_memPool->SetTracked(); // created and then immediately deleted. - DeleteNode( node ); - return p; - } - - // Handle an end tag returned to this level. - // And handle a bunch of annoying errors. - bool mismatch = false; - if ( endTag.Empty() ) { - if ( ele->ClosingType() == XMLElement::OPEN ) { - mismatch = true; - } - } - else { - if ( ele->ClosingType() != XMLElement::OPEN ) { - mismatch = true; - } - else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) { - mismatch = true; - } - } - if ( mismatch ) { - _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, ele->Name(), 0, initialLineNum); - DeleteNode( node ); - break; - } - } - InsertEndChild( node ); - } - return 0; -} - -/*static*/ void XMLNode::DeleteNode( XMLNode* node ) -{ - if ( node == 0 ) { - return; - } - TIXMLASSERT(node->_document); - if (!node->ToDocument()) { - node->_document->MarkInUse(node); - } - - MemPool* pool = node->_memPool; - node->~XMLNode(); - pool->Free( node ); -} - -void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const -{ - TIXMLASSERT( insertThis ); - TIXMLASSERT( insertThis->_document == _document ); - - if (insertThis->_parent) { - insertThis->_parent->Unlink( insertThis ); - } - else { - insertThis->_document->MarkInUse(insertThis); - insertThis->_memPool->SetTracked(); - } -} - -const XMLElement* XMLNode::ToElementWithName( const char* name ) const -{ - const XMLElement* element = this->ToElement(); - if ( element == 0 ) { - return 0; - } - if ( name == 0 ) { - return element; - } - if ( XMLUtil::StringEqual( element->Name(), name ) ) { - return element; - } - return 0; -} - -// --------- XMLText ---------- // -char* XMLText::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) -{ - const char* start = p; - if ( this->CData() ) { - p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); - if ( !p ) { - _document->SetError( XML_ERROR_PARSING_CDATA, start, 0, _parseLineNum ); - } - return p; - } - else { - int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES; - if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) { - flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING; - } - - p = _value.ParseText( p, "<", flags, curLineNumPtr ); - if ( p && *p ) { - return p-1; - } - if ( !p ) { - _document->SetError( XML_ERROR_PARSING_TEXT, start, 0, _parseLineNum ); - } - } - return 0; -} - - -XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const -{ - if ( !doc ) { - doc = _document; - } - XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern? - text->SetCData( this->CData() ); - return text; -} - - -bool XMLText::ShallowEqual( const XMLNode* compare ) const -{ - TIXMLASSERT( compare ); - const XMLText* text = compare->ToText(); - return ( text && XMLUtil::StringEqual( text->Value(), Value() ) ); -} - - -bool XMLText::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - return visitor->Visit( *this ); -} - - -// --------- XMLComment ---------- // - -XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc ) -{ -} - - -XMLComment::~XMLComment() -{ -} - - -char* XMLComment::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) -{ - // Comment parses as text. - const char* start = p; - p = _value.ParseText( p, "-->", StrPair::COMMENT, curLineNumPtr ); - if ( p == 0 ) { - _document->SetError( XML_ERROR_PARSING_COMMENT, start, 0, _parseLineNum ); - } - return p; -} - - -XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const -{ - if ( !doc ) { - doc = _document; - } - XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern? - return comment; -} - - -bool XMLComment::ShallowEqual( const XMLNode* compare ) const -{ - TIXMLASSERT( compare ); - const XMLComment* comment = compare->ToComment(); - return ( comment && XMLUtil::StringEqual( comment->Value(), Value() )); -} - - -bool XMLComment::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - return visitor->Visit( *this ); -} - - -// --------- XMLDeclaration ---------- // - -XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc ) -{ -} - - -XMLDeclaration::~XMLDeclaration() -{ - //printf( "~XMLDeclaration\n" ); -} - - -char* XMLDeclaration::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) -{ - // Declaration parses as text. - const char* start = p; - p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); - if ( p == 0 ) { - _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0, _parseLineNum ); - } - return p; -} - - -XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const -{ - if ( !doc ) { - doc = _document; - } - XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern? - return dec; -} - - -bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const -{ - TIXMLASSERT( compare ); - const XMLDeclaration* declaration = compare->ToDeclaration(); - return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() )); -} - - - -bool XMLDeclaration::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - return visitor->Visit( *this ); -} - -// --------- XMLUnknown ---------- // - -XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc ) -{ -} - - -XMLUnknown::~XMLUnknown() -{ -} - - -char* XMLUnknown::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) -{ - // Unknown parses as text. - const char* start = p; - - p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); - if ( !p ) { - _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0, _parseLineNum ); - } - return p; -} - - -XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const -{ - if ( !doc ) { - doc = _document; - } - XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern? - return text; -} - - -bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const -{ - TIXMLASSERT( compare ); - const XMLUnknown* unknown = compare->ToUnknown(); - return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() )); -} - - -bool XMLUnknown::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - return visitor->Visit( *this ); -} - -// --------- XMLAttribute ---------- // - -const char* XMLAttribute::Name() const -{ - return _name.GetStr(); -} - -const char* XMLAttribute::Value() const -{ - return _value.GetStr(); -} - -char* XMLAttribute::ParseDeep( char* p, bool processEntities, int* curLineNumPtr ) -{ - // Parse using the name rules: bug fix, was using ParseText before - p = _name.ParseName( p ); - if ( !p || !*p ) { - return 0; - } - - // Skip white space before = - p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); - if ( *p != '=' ) { - return 0; - } - - ++p; // move up to opening quote - p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); - if ( *p != '\"' && *p != '\'' ) { - return 0; - } - - char endTag[2] = { *p, 0 }; - ++p; // move past opening quote - - p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr ); - return p; -} - - -void XMLAttribute::SetName( const char* n ) -{ - _name.SetStr( n ); -} - - -XMLError XMLAttribute::QueryIntValue( int* value ) const -{ - if ( XMLUtil::ToInt( Value(), value )) { - return XML_SUCCESS; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const -{ - if ( XMLUtil::ToUnsigned( Value(), value )) { - return XML_SUCCESS; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -XMLError XMLAttribute::QueryInt64Value(int64_t* value) const -{ - if (XMLUtil::ToInt64(Value(), value)) { - return XML_SUCCESS; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -XMLError XMLAttribute::QueryBoolValue( bool* value ) const -{ - if ( XMLUtil::ToBool( Value(), value )) { - return XML_SUCCESS; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -XMLError XMLAttribute::QueryFloatValue( float* value ) const -{ - if ( XMLUtil::ToFloat( Value(), value )) { - return XML_SUCCESS; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -XMLError XMLAttribute::QueryDoubleValue( double* value ) const -{ - if ( XMLUtil::ToDouble( Value(), value )) { - return XML_SUCCESS; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -void XMLAttribute::SetAttribute( const char* v ) -{ - _value.SetStr( v ); -} - - -void XMLAttribute::SetAttribute( int v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - _value.SetStr( buf ); -} - - -void XMLAttribute::SetAttribute( unsigned v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - _value.SetStr( buf ); -} - - -void XMLAttribute::SetAttribute(int64_t v) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr(v, buf, BUF_SIZE); - _value.SetStr(buf); -} - - - -void XMLAttribute::SetAttribute( bool v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - _value.SetStr( buf ); -} - -void XMLAttribute::SetAttribute( double v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - _value.SetStr( buf ); -} - -void XMLAttribute::SetAttribute( float v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - _value.SetStr( buf ); -} - - -// --------- XMLElement ---------- // -XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ), - _closingType( OPEN ), - _rootAttribute( 0 ) -{ -} - - -XMLElement::~XMLElement() -{ - while( _rootAttribute ) { - XMLAttribute* next = _rootAttribute->_next; - DeleteAttribute( _rootAttribute ); - _rootAttribute = next; - } -} - - -const XMLAttribute* XMLElement::FindAttribute( const char* name ) const -{ - for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) { - if ( XMLUtil::StringEqual( a->Name(), name ) ) { - return a; - } - } - return 0; -} - - -const char* XMLElement::Attribute( const char* name, const char* value ) const -{ - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return 0; - } - if ( !value || XMLUtil::StringEqual( a->Value(), value )) { - return a->Value(); - } - return 0; -} - -int XMLElement::IntAttribute(const char* name, int defaultValue) const -{ - int i = defaultValue; - QueryIntAttribute(name, &i); - return i; -} - -unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const -{ - unsigned i = defaultValue; - QueryUnsignedAttribute(name, &i); - return i; -} - -int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const -{ - int64_t i = defaultValue; - QueryInt64Attribute(name, &i); - return i; -} - -bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const -{ - bool b = defaultValue; - QueryBoolAttribute(name, &b); - return b; -} - -double XMLElement::DoubleAttribute(const char* name, double defaultValue) const -{ - double d = defaultValue; - QueryDoubleAttribute(name, &d); - return d; -} - -float XMLElement::FloatAttribute(const char* name, float defaultValue) const -{ - float f = defaultValue; - QueryFloatAttribute(name, &f); - return f; -} - -const char* XMLElement::GetText() const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - return FirstChild()->Value(); - } - return 0; -} - - -void XMLElement::SetText( const char* inText ) -{ - if ( FirstChild() && FirstChild()->ToText() ) - FirstChild()->SetValue( inText ); - else { - XMLText* theText = GetDocument()->NewText( inText ); - InsertFirstChild( theText ); - } -} - - -void XMLElement::SetText( int v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - SetText( buf ); -} - - -void XMLElement::SetText( unsigned v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - SetText( buf ); -} - - -void XMLElement::SetText(int64_t v) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr(v, buf, BUF_SIZE); - SetText(buf); -} - - -void XMLElement::SetText( bool v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - SetText( buf ); -} - - -void XMLElement::SetText( float v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - SetText( buf ); -} - - -void XMLElement::SetText( double v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - SetText( buf ); -} - - -XMLError XMLElement::QueryIntText( int* ival ) const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - const char* t = FirstChild()->Value(); - if ( XMLUtil::ToInt( t, ival ) ) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - - -XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - const char* t = FirstChild()->Value(); - if ( XMLUtil::ToUnsigned( t, uval ) ) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - - -XMLError XMLElement::QueryInt64Text(int64_t* ival) const -{ - if (FirstChild() && FirstChild()->ToText()) { - const char* t = FirstChild()->Value(); - if (XMLUtil::ToInt64(t, ival)) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - - -XMLError XMLElement::QueryBoolText( bool* bval ) const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - const char* t = FirstChild()->Value(); - if ( XMLUtil::ToBool( t, bval ) ) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - - -XMLError XMLElement::QueryDoubleText( double* dval ) const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - const char* t = FirstChild()->Value(); - if ( XMLUtil::ToDouble( t, dval ) ) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - - -XMLError XMLElement::QueryFloatText( float* fval ) const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - const char* t = FirstChild()->Value(); - if ( XMLUtil::ToFloat( t, fval ) ) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - -int XMLElement::IntText(int defaultValue) const -{ - int i = defaultValue; - QueryIntText(&i); - return i; -} - -unsigned XMLElement::UnsignedText(unsigned defaultValue) const -{ - unsigned i = defaultValue; - QueryUnsignedText(&i); - return i; -} - -int64_t XMLElement::Int64Text(int64_t defaultValue) const -{ - int64_t i = defaultValue; - QueryInt64Text(&i); - return i; -} - -bool XMLElement::BoolText(bool defaultValue) const -{ - bool b = defaultValue; - QueryBoolText(&b); - return b; -} - -double XMLElement::DoubleText(double defaultValue) const -{ - double d = defaultValue; - QueryDoubleText(&d); - return d; -} - -float XMLElement::FloatText(float defaultValue) const -{ - float f = defaultValue; - QueryFloatText(&f); - return f; -} - - -XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name ) -{ - XMLAttribute* last = 0; - XMLAttribute* attrib = 0; - for( attrib = _rootAttribute; - attrib; - last = attrib, attrib = attrib->_next ) { - if ( XMLUtil::StringEqual( attrib->Name(), name ) ) { - break; - } - } - if ( !attrib ) { - attrib = CreateAttribute(); - TIXMLASSERT( attrib ); - if ( last ) { - TIXMLASSERT( last->_next == 0 ); - last->_next = attrib; - } - else { - TIXMLASSERT( _rootAttribute == 0 ); - _rootAttribute = attrib; - } - attrib->SetName( name ); - } - return attrib; -} - - -void XMLElement::DeleteAttribute( const char* name ) -{ - XMLAttribute* prev = 0; - for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) { - if ( XMLUtil::StringEqual( name, a->Name() ) ) { - if ( prev ) { - prev->_next = a->_next; - } - else { - _rootAttribute = a->_next; - } - DeleteAttribute( a ); - break; - } - prev = a; - } -} - - -char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr ) -{ - const char* start = p; - XMLAttribute* prevAttribute = 0; - - // Read the attributes. - while( p ) { - p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); - if ( !(*p) ) { - _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name(), _parseLineNum ); - return 0; - } - - // attribute. - if (XMLUtil::IsNameStartChar( *p ) ) { - XMLAttribute* attrib = CreateAttribute(); - TIXMLASSERT( attrib ); - attrib->_parseLineNum = _document->_parseCurLineNum; - - int attrLineNum = attrib->_parseLineNum; - - p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr ); - if ( !p || Attribute( attrib->Name() ) ) { - DeleteAttribute( attrib ); - _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p, attrLineNum ); - return 0; - } - // There is a minor bug here: if the attribute in the source xml - // document is duplicated, it will not be detected and the - // attribute will be doubly added. However, tracking the 'prevAttribute' - // avoids re-scanning the attribute list. Preferring performance for - // now, may reconsider in the future. - if ( prevAttribute ) { - TIXMLASSERT( prevAttribute->_next == 0 ); - prevAttribute->_next = attrib; - } - else { - TIXMLASSERT( _rootAttribute == 0 ); - _rootAttribute = attrib; - } - prevAttribute = attrib; - } - // end of the tag - else if ( *p == '>' ) { - ++p; - break; - } - // end of the tag - else if ( *p == '/' && *(p+1) == '>' ) { - _closingType = CLOSED; - return p+2; // done; sealed element. - } - else { - _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p, _parseLineNum ); - return 0; - } - } - return p; -} - -void XMLElement::DeleteAttribute( XMLAttribute* attribute ) -{ - if ( attribute == 0 ) { - return; - } - MemPool* pool = attribute->_memPool; - attribute->~XMLAttribute(); - pool->Free( attribute ); -} - -XMLAttribute* XMLElement::CreateAttribute() -{ - TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() ); - XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); - TIXMLASSERT( attrib ); - attrib->_memPool = &_document->_attributePool; - attrib->_memPool->SetTracked(); - return attrib; -} - -// -// <ele></ele> -// <ele>foo<b>bar</b></ele> -// -char* XMLElement::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) -{ - // Read the element name. - p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); - - // The closing element is the </element> form. It is - // parsed just like a regular element then deleted from - // the DOM. - if ( *p == '/' ) { - _closingType = CLOSING; - ++p; - } - - p = _value.ParseName( p ); - if ( _value.Empty() ) { - return 0; - } - - p = ParseAttributes( p, curLineNumPtr ); - if ( !p || !*p || _closingType != OPEN ) { - return p; - } - - p = XMLNode::ParseDeep( p, parentEndTag, curLineNumPtr ); - return p; -} - - - -XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const -{ - if ( !doc ) { - doc = _document; - } - XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern? - for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) { - element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern? - } - return element; -} - - -bool XMLElement::ShallowEqual( const XMLNode* compare ) const -{ - TIXMLASSERT( compare ); - const XMLElement* other = compare->ToElement(); - if ( other && XMLUtil::StringEqual( other->Name(), Name() )) { - - const XMLAttribute* a=FirstAttribute(); - const XMLAttribute* b=other->FirstAttribute(); - - while ( a && b ) { - if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) { - return false; - } - a = a->Next(); - b = b->Next(); - } - if ( a || b ) { - // different count - return false; - } - return true; - } - return false; -} - - -bool XMLElement::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - if ( visitor->VisitEnter( *this, _rootAttribute ) ) { - for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { - if ( !node->Accept( visitor ) ) { - break; - } - } - } - return visitor->VisitExit( *this ); -} - - -// --------- XMLDocument ----------- // - -// Warning: List must match 'enum XMLError' -const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = { - "XML_SUCCESS", - "XML_NO_ATTRIBUTE", - "XML_WRONG_ATTRIBUTE_TYPE", - "XML_ERROR_FILE_NOT_FOUND", - "XML_ERROR_FILE_COULD_NOT_BE_OPENED", - "XML_ERROR_FILE_READ_ERROR", - "UNUSED_XML_ERROR_ELEMENT_MISMATCH", - "XML_ERROR_PARSING_ELEMENT", - "XML_ERROR_PARSING_ATTRIBUTE", - "UNUSED_XML_ERROR_IDENTIFYING_TAG", - "XML_ERROR_PARSING_TEXT", - "XML_ERROR_PARSING_CDATA", - "XML_ERROR_PARSING_COMMENT", - "XML_ERROR_PARSING_DECLARATION", - "XML_ERROR_PARSING_UNKNOWN", - "XML_ERROR_EMPTY_DOCUMENT", - "XML_ERROR_MISMATCHED_ELEMENT", - "XML_ERROR_PARSING", - "XML_CAN_NOT_CONVERT_TEXT", - "XML_NO_TEXT_NODE" -}; - - -XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) : - XMLNode( 0 ), - _writeBOM( false ), - _processEntities( processEntities ), - _errorID(XML_SUCCESS), - _whitespaceMode( whitespaceMode ), - _errorLineNum( 0 ), - _charBuffer( 0 ), - _parseCurLineNum( 0 ) -{ - // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+) - _document = this; -} - - -XMLDocument::~XMLDocument() -{ - Clear(); -} - - -void XMLDocument::MarkInUse(XMLNode* node) -{ - TIXMLASSERT(node); - TIXMLASSERT(node->_parent == 0); - - for (int i = 0; i < _unlinked.Size(); ++i) { - if (node == _unlinked[i]) { - _unlinked.SwapRemove(i); - break; - } - } -} - -void XMLDocument::Clear() -{ - DeleteChildren(); - while( _unlinked.Size()) { - DeleteNode(_unlinked[0]); // Will remove from _unlinked as part of delete. - } - -#ifdef TINYXML2_DEBUG - const bool hadError = Error(); -#endif - ClearError(); - - delete [] _charBuffer; - _charBuffer = 0; - -#if 0 - _textPool.Trace( "text" ); - _elementPool.Trace( "element" ); - _commentPool.Trace( "comment" ); - _attributePool.Trace( "attribute" ); -#endif - -#ifdef TINYXML2_DEBUG - if ( !hadError ) { - TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() ); - TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() ); - TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() ); - TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() ); - } -#endif -} - - -void XMLDocument::DeepCopy(XMLDocument* target) -{ - TIXMLASSERT(target); - if (target == this) { - return; // technically success - a no-op. - } - - target->Clear(); - for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) { - target->InsertEndChild(node->DeepClone(target)); - } -} - -XMLElement* XMLDocument::NewElement( const char* name ) -{ - XMLElement* ele = CreateUnlinkedNode<XMLElement>( _elementPool ); - ele->SetName( name ); - return ele; -} - - -XMLComment* XMLDocument::NewComment( const char* str ) -{ - XMLComment* comment = CreateUnlinkedNode<XMLComment>( _commentPool ); - comment->SetValue( str ); - return comment; -} - - -XMLText* XMLDocument::NewText( const char* str ) -{ - XMLText* text = CreateUnlinkedNode<XMLText>( _textPool ); - text->SetValue( str ); - return text; -} - - -XMLDeclaration* XMLDocument::NewDeclaration( const char* str ) -{ - XMLDeclaration* dec = CreateUnlinkedNode<XMLDeclaration>( _commentPool ); - dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" ); - return dec; -} - - -XMLUnknown* XMLDocument::NewUnknown( const char* str ) -{ - XMLUnknown* unk = CreateUnlinkedNode<XMLUnknown>( _commentPool ); - unk->SetValue( str ); - return unk; -} - -static FILE* callfopen( const char* filepath, const char* mode ) -{ - TIXMLASSERT( filepath ); - TIXMLASSERT( mode ); -#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) - FILE* fp = 0; - errno_t err = fopen_s( &fp, filepath, mode ); - if ( err ) { - return 0; - } -#else - FILE* fp = fopen( filepath, mode ); -#endif - return fp; -} - -void XMLDocument::DeleteNode( XMLNode* node ) { - TIXMLASSERT( node ); - TIXMLASSERT(node->_document == this ); - if (node->_parent) { - node->_parent->DeleteChild( node ); - } - else { - // Isn't in the tree. - // Use the parent delete. - // Also, we need to mark it tracked: we 'know' - // it was never used. - node->_memPool->SetTracked(); - // Call the static XMLNode version: - XMLNode::DeleteNode(node); - } -} - - -XMLError XMLDocument::LoadFile( const char* filename ) -{ - Clear(); - FILE* fp = callfopen( filename, "rb" ); - if ( !fp ) { - SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0, 0 ); - return _errorID; - } - LoadFile( fp ); - fclose( fp ); - return _errorID; -} - -// This is likely overengineered template art to have a check that unsigned long value incremented -// by one still fits into size_t. If size_t type is larger than unsigned long type -// (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit -// -Wtype-limits warning. This piece makes the compiler select code with a check when a check -// is useful and code with no check when a check is redundant depending on how size_t and unsigned long -// types sizes relate to each other. -template -<bool = (sizeof(unsigned long) >= sizeof(size_t))> -struct LongFitsIntoSizeTMinusOne { - static bool Fits( unsigned long value ) - { - return value < (size_t)-1; - } -}; - -template <> -struct LongFitsIntoSizeTMinusOne<false> { - static bool Fits( unsigned long ) - { - return true; - } -}; - -XMLError XMLDocument::LoadFile( FILE* fp ) -{ - Clear(); - - fseek( fp, 0, SEEK_SET ); - if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) { - SetError( XML_ERROR_FILE_READ_ERROR, 0, 0, 0 ); - return _errorID; - } - - fseek( fp, 0, SEEK_END ); - const long filelength = ftell( fp ); - fseek( fp, 0, SEEK_SET ); - if ( filelength == -1L ) { - SetError( XML_ERROR_FILE_READ_ERROR, 0, 0, 0 ); - return _errorID; - } - TIXMLASSERT( filelength >= 0 ); - - if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) { - // Cannot handle files which won't fit in buffer together with null terminator - SetError( XML_ERROR_FILE_READ_ERROR, 0, 0, 0 ); - return _errorID; - } - - if ( filelength == 0 ) { - SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0, 0 ); - return _errorID; - } - - const size_t size = filelength; - TIXMLASSERT( _charBuffer == 0 ); - _charBuffer = new char[size+1]; - size_t read = fread( _charBuffer, 1, size, fp ); - if ( read != size ) { - SetError( XML_ERROR_FILE_READ_ERROR, 0, 0, 0 ); - return _errorID; - } - - _charBuffer[size] = 0; - - Parse(); - return _errorID; -} - - -XMLError XMLDocument::SaveFile( const char* filename, bool compact ) -{ - FILE* fp = callfopen( filename, "w" ); - if ( !fp ) { - SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0, 0 ); - return _errorID; - } - SaveFile(fp, compact); - fclose( fp ); - return _errorID; -} - - -XMLError XMLDocument::SaveFile( FILE* fp, bool compact ) -{ - // Clear any error from the last save, otherwise it will get reported - // for *this* call. - ClearError(); - XMLPrinter stream( fp, compact ); - Print( &stream ); - return _errorID; -} - - -XMLError XMLDocument::Parse( const char* p, size_t len ) -{ - Clear(); - - if ( len == 0 || !p || !*p ) { - SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0, 0 ); - return _errorID; - } - if ( len == (size_t)(-1) ) { - len = strlen( p ); - } - TIXMLASSERT( _charBuffer == 0 ); - _charBuffer = new char[ len+1 ]; - memcpy( _charBuffer, p, len ); - _charBuffer[len] = 0; - - Parse(); - if ( Error() ) { - // clean up now essentially dangling memory. - // and the parse fail can put objects in the - // pools that are dead and inaccessible. - DeleteChildren(); - _elementPool.Clear(); - _attributePool.Clear(); - _textPool.Clear(); - _commentPool.Clear(); - } - return _errorID; -} - - -void XMLDocument::Print( XMLPrinter* streamer ) const -{ - if ( streamer ) { - Accept( streamer ); - } - else { - XMLPrinter stdoutStreamer( stdout ); - Accept( &stdoutStreamer ); - } -} - - -void XMLDocument::SetError( XMLError error, const char* str1, const char* str2, int lineNum ) -{ - TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT ); - _errorID = error; - - _errorStr1.Reset(); - _errorStr2.Reset(); - _errorLineNum = lineNum; - - if (str1) - _errorStr1.SetStr(str1); - if (str2) - _errorStr2.SetStr(str2); -} - -/*static*/ const char* XMLDocument::ErrorIDToName(XMLError errorID) -{ - TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT ); - const char* errorName = _errorNames[errorID]; - TIXMLASSERT( errorName && errorName[0] ); - return errorName; -} - -const char* XMLDocument::GetErrorStr1() const -{ - return _errorStr1.GetStr(); -} - -const char* XMLDocument::GetErrorStr2() const -{ - return _errorStr2.GetStr(); -} - -const char* XMLDocument::ErrorName() const -{ - return ErrorIDToName(_errorID); -} - -void XMLDocument::PrintError() const -{ - if ( Error() ) { - static const int LEN = 20; - char buf1[LEN] = { 0 }; - char buf2[LEN] = { 0 }; - - if ( !_errorStr1.Empty() ) { - TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1.GetStr() ); - } - if ( !_errorStr2.Empty() ) { - TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2.GetStr() ); - } - - // Should check INT_MIN <= _errorID && _errorId <= INT_MAX, but that - // causes a clang "always true" -Wtautological-constant-out-of-range-compare warning - TIXMLASSERT( 0 <= _errorID && XML_ERROR_COUNT - 1 <= INT_MAX ); - printf( "XMLDocument error id=%d '%s' str1=%s str2=%s line=%d\n", - static_cast<int>( _errorID ), ErrorName(), buf1, buf2, _errorLineNum ); - } -} - -void XMLDocument::Parse() -{ - TIXMLASSERT( NoChildren() ); // Clear() must have been called previously - TIXMLASSERT( _charBuffer ); - _parseCurLineNum = 1; - _parseLineNum = 1; - char* p = _charBuffer; - p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum ); - p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) ); - if ( !*p ) { - SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0, 0 ); - return; - } - ParseDeep(p, 0, &_parseCurLineNum ); -} - -XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) : - _elementJustOpened( false ), - _firstElement( true ), - _fp( file ), - _depth( depth ), - _textDepth( -1 ), - _processEntities( true ), - _compactMode( compact ) -{ - for( int i=0; i<ENTITY_RANGE; ++i ) { - _entityFlag[i] = false; - _restrictedEntityFlag[i] = false; - } - for( int i=0; i<NUM_ENTITIES; ++i ) { - const char entityValue = entities[i].value; - TIXMLASSERT( ((unsigned char)entityValue) < ENTITY_RANGE ); - _entityFlag[ (unsigned char)entityValue ] = true; - } - _restrictedEntityFlag[(unsigned char)'&'] = true; - _restrictedEntityFlag[(unsigned char)'<'] = true; - _restrictedEntityFlag[(unsigned char)'>'] = true; // not required, but consistency is nice - _buffer.Push( 0 ); -} - - -void XMLPrinter::Print( const char* format, ... ) -{ - va_list va; - va_start( va, format ); - - if ( _fp ) { - vfprintf( _fp, format, va ); - } - else { - const int len = TIXML_VSCPRINTF( format, va ); - // Close out and re-start the va-args - va_end( va ); - TIXMLASSERT( len >= 0 ); - va_start( va, format ); - TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 ); - char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator. - TIXML_VSNPRINTF( p, len+1, format, va ); - } - va_end( va ); -} - - -void XMLPrinter::PrintSpace( int depth ) -{ - for( int i=0; i<depth; ++i ) { - Print( " " ); - } -} - - -void XMLPrinter::PrintString( const char* p, bool restricted ) -{ - // Look for runs of bytes between entities to print. - const char* q = p; - - if ( _processEntities ) { - const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag; - while ( *q ) { - TIXMLASSERT( p <= q ); - // Remember, char is sometimes signed. (How many times has that bitten me?) - if ( *q > 0 && *q < ENTITY_RANGE ) { - // Check for entities. If one is found, flush - // the stream up until the entity, write the - // entity, and keep looking. - if ( flag[(unsigned char)(*q)] ) { - while ( p < q ) { - const size_t delta = q - p; - // %.*s accepts type int as "precision" - const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta; - Print( "%.*s", toPrint, p ); - p += toPrint; - } - bool entityPatternPrinted = false; - for( int i=0; i<NUM_ENTITIES; ++i ) { - if ( entities[i].value == *q ) { - Print( "&%s;", entities[i].pattern ); - entityPatternPrinted = true; - break; - } - } - if ( !entityPatternPrinted ) { - // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release - TIXMLASSERT( false ); - } - ++p; - } - } - ++q; - TIXMLASSERT( p <= q ); - } - } - // Flush the remaining string. This will be the entire - // string if an entity wasn't found. - TIXMLASSERT( p <= q ); - if ( !_processEntities || ( p < q ) ) { - Print( "%s", p ); - } -} - - -void XMLPrinter::PushHeader( bool writeBOM, bool writeDec ) -{ - if ( writeBOM ) { - static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 }; - Print( "%s", bom ); - } - if ( writeDec ) { - PushDeclaration( "xml version=\"1.0\"" ); - } -} - - -void XMLPrinter::OpenElement( const char* name, bool compactMode ) -{ - SealElementIfJustOpened(); - _stack.Push( name ); - - if ( _textDepth < 0 && !_firstElement && !compactMode ) { - Print( "\n" ); - } - if ( !compactMode ) { - PrintSpace( _depth ); - } - - Print( "<%s", name ); - _elementJustOpened = true; - _firstElement = false; - ++_depth; -} - - -void XMLPrinter::PushAttribute( const char* name, const char* value ) -{ - TIXMLASSERT( _elementJustOpened ); - Print( " %s=\"", name ); - PrintString( value, false ); - Print( "\"" ); -} - - -void XMLPrinter::PushAttribute( const char* name, int v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - PushAttribute( name, buf ); -} - - -void XMLPrinter::PushAttribute( const char* name, unsigned v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - PushAttribute( name, buf ); -} - - -void XMLPrinter::PushAttribute(const char* name, int64_t v) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr(v, buf, BUF_SIZE); - PushAttribute(name, buf); -} - - -void XMLPrinter::PushAttribute( const char* name, bool v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - PushAttribute( name, buf ); -} - - -void XMLPrinter::PushAttribute( const char* name, double v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - PushAttribute( name, buf ); -} - - -void XMLPrinter::CloseElement( bool compactMode ) -{ - --_depth; - const char* name = _stack.Pop(); - - if ( _elementJustOpened ) { - Print( "/>" ); - } - else { - if ( _textDepth < 0 && !compactMode) { - Print( "\n" ); - PrintSpace( _depth ); - } - Print( "</%s>", name ); - } - - if ( _textDepth == _depth ) { - _textDepth = -1; - } - if ( _depth == 0 && !compactMode) { - Print( "\n" ); - } - _elementJustOpened = false; -} - - -void XMLPrinter::SealElementIfJustOpened() -{ - if ( !_elementJustOpened ) { - return; - } - _elementJustOpened = false; - Print( ">" ); -} - - -void XMLPrinter::PushText( const char* text, bool cdata ) -{ - _textDepth = _depth-1; - - SealElementIfJustOpened(); - if ( cdata ) { - Print( "<![CDATA[%s]]>", text ); - } - else { - PrintString( text, true ); - } -} - -void XMLPrinter::PushText( int64_t value ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - -void XMLPrinter::PushText( int value ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - - -void XMLPrinter::PushText( unsigned value ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - - -void XMLPrinter::PushText( bool value ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - - -void XMLPrinter::PushText( float value ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - - -void XMLPrinter::PushText( double value ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - - -void XMLPrinter::PushComment( const char* comment ) -{ - SealElementIfJustOpened(); - if ( _textDepth < 0 && !_firstElement && !_compactMode) { - Print( "\n" ); - PrintSpace( _depth ); - } - _firstElement = false; - Print( "<!--%s-->", comment ); -} - - -void XMLPrinter::PushDeclaration( const char* value ) -{ - SealElementIfJustOpened(); - if ( _textDepth < 0 && !_firstElement && !_compactMode) { - Print( "\n" ); - PrintSpace( _depth ); - } - _firstElement = false; - Print( "<?%s?>", value ); -} - - -void XMLPrinter::PushUnknown( const char* value ) -{ - SealElementIfJustOpened(); - if ( _textDepth < 0 && !_firstElement && !_compactMode) { - Print( "\n" ); - PrintSpace( _depth ); - } - _firstElement = false; - Print( "<!%s>", value ); -} - - -bool XMLPrinter::VisitEnter( const XMLDocument& doc ) -{ - _processEntities = doc.ProcessEntities(); - if ( doc.HasBOM() ) { - PushHeader( true, false ); - } - return true; -} - - -bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute ) -{ - const XMLElement* parentElem = 0; - if ( element.Parent() ) { - parentElem = element.Parent()->ToElement(); - } - const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode; - OpenElement( element.Name(), compactMode ); - while ( attribute ) { - PushAttribute( attribute->Name(), attribute->Value() ); - attribute = attribute->Next(); - } - return true; -} - - -bool XMLPrinter::VisitExit( const XMLElement& element ) -{ - CloseElement( CompactMode(element) ); - return true; -} - - -bool XMLPrinter::Visit( const XMLText& text ) -{ - PushText( text.Value(), text.CData() ); - return true; -} - - -bool XMLPrinter::Visit( const XMLComment& comment ) -{ - PushComment( comment.Value() ); - return true; -} - -bool XMLPrinter::Visit( const XMLDeclaration& declaration ) -{ - PushDeclaration( declaration.Value() ); - return true; -} - - -bool XMLPrinter::Visit( const XMLUnknown& unknown ) -{ - PushUnknown( unknown.Value() ); - return true; -} - -} // namespace tinyxml2 - diff --git a/deps/tinyxml2/tinyxml2.h b/deps/tinyxml2/tinyxml2.h deleted file mode 100644 index ffe5c4b9..00000000 --- a/deps/tinyxml2/tinyxml2.h +++ /dev/null @@ -1,2264 +0,0 @@ -/* -Original code by Lee Thomason (www.grinninglizard.com) - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - -#ifndef TINYXML2_INCLUDED -#define TINYXML2_INCLUDED - -#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) -# include <ctype.h> -# include <limits.h> -# include <stdio.h> -# include <stdlib.h> -# include <string.h> -# if defined(__PS3__) -# include <stddef.h> -# endif -#else -# include <cctype> -# include <climits> -# include <cstdio> -# include <cstdlib> -# include <cstring> -#endif -#include <stdint.h> - -/* - TODO: intern strings instead of allocation. -*/ -/* - gcc: - g++ -Wall -DDEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe - - Formatting, Artistic Style: - AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h -*/ - -#if defined( _DEBUG ) || defined (__DEBUG__) -# ifndef TINYXML2_DEBUG -# define TINYXML2_DEBUG -# endif -#endif - -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable: 4251) -#endif - -#ifdef _WIN32 -# ifdef TINYXML2_EXPORT -# define TINYXML2_LIB __declspec(dllexport) -# elif defined(TINYXML2_IMPORT) -# define TINYXML2_LIB __declspec(dllimport) -# else -# define TINYXML2_LIB -# endif -#elif __GNUC__ >= 4 -# define TINYXML2_LIB __attribute__((visibility("default"))) -#else -# define TINYXML2_LIB -#endif - - -#if defined(TINYXML2_DEBUG) -# if defined(_MSC_VER) -# // "(void)0," is for suppressing C4127 warning in "assert(false)", "assert(true)" and the like -# define TIXMLASSERT( x ) if ( !((void)0,(x))) { __debugbreak(); } -# elif defined (ANDROID_NDK) -# include <android/log.h> -# define TIXMLASSERT( x ) if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); } -# else -# include <assert.h> -# define TIXMLASSERT assert -# endif -#else -# define TIXMLASSERT( x ) {} -#endif - - -/* Versioning, past 1.0.14: - http://semver.org/ -*/ -static const int TIXML2_MAJOR_VERSION = 5; -static const int TIXML2_MINOR_VERSION = 0; -static const int TIXML2_PATCH_VERSION = 1; - -namespace tinyxml2 -{ -class XMLDocument; -class XMLElement; -class XMLAttribute; -class XMLComment; -class XMLText; -class XMLDeclaration; -class XMLUnknown; -class XMLPrinter; - -/* - A class that wraps strings. Normally stores the start and end - pointers into the XML file itself, and will apply normalization - and entity translation if actually read. Can also store (and memory - manage) a traditional char[] -*/ -class StrPair -{ -public: - enum { - NEEDS_ENTITY_PROCESSING = 0x01, - NEEDS_NEWLINE_NORMALIZATION = 0x02, - NEEDS_WHITESPACE_COLLAPSING = 0x04, - - TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, - TEXT_ELEMENT_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, - ATTRIBUTE_NAME = 0, - ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, - ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, - COMMENT = NEEDS_NEWLINE_NORMALIZATION - }; - - StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {} - ~StrPair(); - - void Set( char* start, char* end, int flags ) { - TIXMLASSERT( start ); - TIXMLASSERT( end ); - Reset(); - _start = start; - _end = end; - _flags = flags | NEEDS_FLUSH; - } - - const char* GetStr(); - - bool Empty() const { - return _start == _end; - } - - void SetInternedStr( const char* str ) { - Reset(); - _start = const_cast<char*>(str); - } - - void SetStr( const char* str, int flags=0 ); - - char* ParseText( char* in, const char* endTag, int strFlags, int* curLineNumPtr ); - char* ParseName( char* in ); - - void TransferTo( StrPair* other ); - void Reset(); - -private: - void CollapseWhitespace(); - - enum { - NEEDS_FLUSH = 0x100, - NEEDS_DELETE = 0x200 - }; - - int _flags; - char* _start; - char* _end; - - StrPair( const StrPair& other ); // not supported - void operator=( StrPair& other ); // not supported, use TransferTo() -}; - - -/* - A dynamic array of Plain Old Data. Doesn't support constructors, etc. - Has a small initial memory pool, so that low or no usage will not - cause a call to new/delete -*/ -template <class T, int INITIAL_SIZE> -class DynArray -{ -public: - DynArray() { - _mem = _pool; - _allocated = INITIAL_SIZE; - _size = 0; - } - - ~DynArray() { - if ( _mem != _pool ) { - delete [] _mem; - } - } - - void Clear() { - _size = 0; - } - - void Push( T t ) { - TIXMLASSERT( _size < INT_MAX ); - EnsureCapacity( _size+1 ); - _mem[_size] = t; - ++_size; - } - - T* PushArr( int count ) { - TIXMLASSERT( count >= 0 ); - TIXMLASSERT( _size <= INT_MAX - count ); - EnsureCapacity( _size+count ); - T* ret = &_mem[_size]; - _size += count; - return ret; - } - - T Pop() { - TIXMLASSERT( _size > 0 ); - --_size; - return _mem[_size]; - } - - void PopArr( int count ) { - TIXMLASSERT( _size >= count ); - _size -= count; - } - - bool Empty() const { - return _size == 0; - } - - T& operator[](int i) { - TIXMLASSERT( i>= 0 && i < _size ); - return _mem[i]; - } - - const T& operator[](int i) const { - TIXMLASSERT( i>= 0 && i < _size ); - return _mem[i]; - } - - const T& PeekTop() const { - TIXMLASSERT( _size > 0 ); - return _mem[ _size - 1]; - } - - int Size() const { - TIXMLASSERT( _size >= 0 ); - return _size; - } - - int Capacity() const { - TIXMLASSERT( _allocated >= INITIAL_SIZE ); - return _allocated; - } - - void SwapRemove(int i) { - TIXMLASSERT(i >= 0 && i < _size); - TIXMLASSERT(_size > 0); - _mem[i] = _mem[_size - 1]; - --_size; - } - - const T* Mem() const { - TIXMLASSERT( _mem ); - return _mem; - } - - T* Mem() { - TIXMLASSERT( _mem ); - return _mem; - } - -private: - DynArray( const DynArray& ); // not supported - void operator=( const DynArray& ); // not supported - - void EnsureCapacity( int cap ) { - TIXMLASSERT( cap > 0 ); - if ( cap > _allocated ) { - TIXMLASSERT( cap <= INT_MAX / 2 ); - int newAllocated = cap * 2; - T* newMem = new T[newAllocated]; - TIXMLASSERT( newAllocated >= _size ); - memcpy( newMem, _mem, sizeof(T)*_size ); // warning: not using constructors, only works for PODs - if ( _mem != _pool ) { - delete [] _mem; - } - _mem = newMem; - _allocated = newAllocated; - } - } - - T* _mem; - T _pool[INITIAL_SIZE]; - int _allocated; // objects allocated - int _size; // number objects in use -}; - - -/* - Parent virtual class of a pool for fast allocation - and deallocation of objects. -*/ -class MemPool -{ -public: - MemPool() {} - virtual ~MemPool() {} - - virtual int ItemSize() const = 0; - virtual void* Alloc() = 0; - virtual void Free( void* ) = 0; - virtual void SetTracked() = 0; - virtual void Clear() = 0; -}; - - -/* - Template child class to create pools of the correct type. -*/ -template< int ITEM_SIZE > -class MemPoolT : public MemPool -{ -public: - MemPoolT() : _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0) {} - ~MemPoolT() { - Clear(); - } - - void Clear() { - // Delete the blocks. - while( !_blockPtrs.Empty()) { - Block* b = _blockPtrs.Pop(); - delete b; - } - _root = 0; - _currentAllocs = 0; - _nAllocs = 0; - _maxAllocs = 0; - _nUntracked = 0; - } - - virtual int ItemSize() const { - return ITEM_SIZE; - } - int CurrentAllocs() const { - return _currentAllocs; - } - - virtual void* Alloc() { - if ( !_root ) { - // Need a new block. - Block* block = new Block(); - _blockPtrs.Push( block ); - - Item* blockItems = block->items; - for( int i = 0; i < ITEMS_PER_BLOCK - 1; ++i ) { - blockItems[i].next = &(blockItems[i + 1]); - } - blockItems[ITEMS_PER_BLOCK - 1].next = 0; - _root = blockItems; - } - Item* const result = _root; - TIXMLASSERT( result != 0 ); - _root = _root->next; - - ++_currentAllocs; - if ( _currentAllocs > _maxAllocs ) { - _maxAllocs = _currentAllocs; - } - ++_nAllocs; - ++_nUntracked; - return result; - } - - virtual void Free( void* mem ) { - if ( !mem ) { - return; - } - --_currentAllocs; - Item* item = static_cast<Item*>( mem ); -#ifdef TINYXML2_DEBUG - memset( item, 0xfe, sizeof( *item ) ); -#endif - item->next = _root; - _root = item; - } - void Trace( const char* name ) { - printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n", - name, _maxAllocs, _maxAllocs * ITEM_SIZE / 1024, _currentAllocs, - ITEM_SIZE, _nAllocs, _blockPtrs.Size() ); - } - - void SetTracked() { - --_nUntracked; - } - - int Untracked() const { - return _nUntracked; - } - - // This number is perf sensitive. 4k seems like a good tradeoff on my machine. - // The test file is large, 170k. - // Release: VS2010 gcc(no opt) - // 1k: 4000 - // 2k: 4000 - // 4k: 3900 21000 - // 16k: 5200 - // 32k: 4300 - // 64k: 4000 21000 - // Declared public because some compilers do not accept to use ITEMS_PER_BLOCK - // in private part if ITEMS_PER_BLOCK is private - enum { ITEMS_PER_BLOCK = (4 * 1024) / ITEM_SIZE }; - -private: - MemPoolT( const MemPoolT& ); // not supported - void operator=( const MemPoolT& ); // not supported - - union Item { - Item* next; - char itemData[ITEM_SIZE]; - }; - struct Block { - Item items[ITEMS_PER_BLOCK]; - }; - DynArray< Block*, 10 > _blockPtrs; - Item* _root; - - int _currentAllocs; - int _nAllocs; - int _maxAllocs; - int _nUntracked; -}; - - - -/** - Implements the interface to the "Visitor pattern" (see the Accept() method.) - If you call the Accept() method, it requires being passed a XMLVisitor - class to handle callbacks. For nodes that contain other nodes (Document, Element) - you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs - are simply called with Visit(). - - If you return 'true' from a Visit method, recursive parsing will continue. If you return - false, <b>no children of this node or its siblings</b> will be visited. - - All flavors of Visit methods have a default implementation that returns 'true' (continue - visiting). You need to only override methods that are interesting to you. - - Generally Accept() is called on the XMLDocument, although all nodes support visiting. - - You should never change the document from a callback. - - @sa XMLNode::Accept() -*/ -class TINYXML2_LIB XMLVisitor -{ -public: - virtual ~XMLVisitor() {} - - /// Visit a document. - virtual bool VisitEnter( const XMLDocument& /*doc*/ ) { - return true; - } - /// Visit a document. - virtual bool VisitExit( const XMLDocument& /*doc*/ ) { - return true; - } - - /// Visit an element. - virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ ) { - return true; - } - /// Visit an element. - virtual bool VisitExit( const XMLElement& /*element*/ ) { - return true; - } - - /// Visit a declaration. - virtual bool Visit( const XMLDeclaration& /*declaration*/ ) { - return true; - } - /// Visit a text node. - virtual bool Visit( const XMLText& /*text*/ ) { - return true; - } - /// Visit a comment node. - virtual bool Visit( const XMLComment& /*comment*/ ) { - return true; - } - /// Visit an unknown node. - virtual bool Visit( const XMLUnknown& /*unknown*/ ) { - return true; - } -}; - -// WARNING: must match XMLDocument::_errorNames[] -enum XMLError { - XML_SUCCESS = 0, - XML_NO_ATTRIBUTE, - XML_WRONG_ATTRIBUTE_TYPE, - XML_ERROR_FILE_NOT_FOUND, - XML_ERROR_FILE_COULD_NOT_BE_OPENED, - XML_ERROR_FILE_READ_ERROR, - UNUSED_XML_ERROR_ELEMENT_MISMATCH, // remove at next major version - XML_ERROR_PARSING_ELEMENT, - XML_ERROR_PARSING_ATTRIBUTE, - UNUSED_XML_ERROR_IDENTIFYING_TAG, // remove at next major version - XML_ERROR_PARSING_TEXT, - XML_ERROR_PARSING_CDATA, - XML_ERROR_PARSING_COMMENT, - XML_ERROR_PARSING_DECLARATION, - XML_ERROR_PARSING_UNKNOWN, - XML_ERROR_EMPTY_DOCUMENT, - XML_ERROR_MISMATCHED_ELEMENT, - XML_ERROR_PARSING, - XML_CAN_NOT_CONVERT_TEXT, - XML_NO_TEXT_NODE, - - XML_ERROR_COUNT -}; - - -/* - Utility functionality. -*/ -class TINYXML2_LIB XMLUtil -{ -public: - static const char* SkipWhiteSpace( const char* p, int* curLineNumPtr ) { - TIXMLASSERT( p ); - - while( IsWhiteSpace(*p) ) { - if (curLineNumPtr && *p == '\n') { - ++(*curLineNumPtr); - } - ++p; - } - TIXMLASSERT( p ); - return p; - } - static char* SkipWhiteSpace( char* p, int* curLineNumPtr ) { - return const_cast<char*>( SkipWhiteSpace( const_cast<const char*>(p), curLineNumPtr ) ); - } - - // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't - // correct, but simple, and usually works. - static bool IsWhiteSpace( char p ) { - return !IsUTF8Continuation(p) && isspace( static_cast<unsigned char>(p) ); - } - - inline static bool IsNameStartChar( unsigned char ch ) { - if ( ch >= 128 ) { - // This is a heuristic guess in attempt to not implement Unicode-aware isalpha() - return true; - } - if ( isalpha( ch ) ) { - return true; - } - return ch == ':' || ch == '_'; - } - - inline static bool IsNameChar( unsigned char ch ) { - return IsNameStartChar( ch ) - || isdigit( ch ) - || ch == '.' - || ch == '-'; - } - - inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX ) { - if ( p == q ) { - return true; - } - TIXMLASSERT( p ); - TIXMLASSERT( q ); - TIXMLASSERT( nChar >= 0 ); - return strncmp( p, q, nChar ) == 0; - } - - inline static bool IsUTF8Continuation( char p ) { - return ( p & 0x80 ) != 0; - } - - static const char* ReadBOM( const char* p, bool* hasBOM ); - // p is the starting location, - // the UTF-8 value of the entity will be placed in value, and length filled in. - static const char* GetCharacterRef( const char* p, char* value, int* length ); - static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); - - // converts primitive types to strings - static void ToStr( int v, char* buffer, int bufferSize ); - static void ToStr( unsigned v, char* buffer, int bufferSize ); - static void ToStr( bool v, char* buffer, int bufferSize ); - static void ToStr( float v, char* buffer, int bufferSize ); - static void ToStr( double v, char* buffer, int bufferSize ); - static void ToStr(int64_t v, char* buffer, int bufferSize); - - // converts strings to primitive types - static bool ToInt( const char* str, int* value ); - static bool ToUnsigned( const char* str, unsigned* value ); - static bool ToBool( const char* str, bool* value ); - static bool ToFloat( const char* str, float* value ); - static bool ToDouble( const char* str, double* value ); - static bool ToInt64(const char* str, int64_t* value); - - // Changes what is serialized for a boolean value. - // Default to "true" and "false". Shouldn't be changed - // unless you have a special testing or compatibility need. - // Be careful: static, global, & not thread safe. - // Be sure to set static const memory as parameters. - static void SetBoolSerialization(const char* writeTrue, const char* writeFalse); - -private: - static const char* writeBoolTrue; - static const char* writeBoolFalse; -}; - - -/** XMLNode is a base class for every object that is in the - XML Document Object Model (DOM), except XMLAttributes. - Nodes have siblings, a parent, and children which can - be navigated. A node is always in a XMLDocument. - The type of a XMLNode can be queried, and it can - be cast to its more defined type. - - A XMLDocument allocates memory for all its Nodes. - When the XMLDocument gets deleted, all its Nodes - will also be deleted. - - @verbatim - A Document can contain: Element (container or leaf) - Comment (leaf) - Unknown (leaf) - Declaration( leaf ) - - An Element can contain: Element (container or leaf) - Text (leaf) - Attributes (not on tree) - Comment (leaf) - Unknown (leaf) - - @endverbatim -*/ -class TINYXML2_LIB XMLNode -{ - friend class XMLDocument; - friend class XMLElement; -public: - - /// Get the XMLDocument that owns this XMLNode. - const XMLDocument* GetDocument() const { - TIXMLASSERT( _document ); - return _document; - } - /// Get the XMLDocument that owns this XMLNode. - XMLDocument* GetDocument() { - TIXMLASSERT( _document ); - return _document; - } - - /// Safely cast to an Element, or null. - virtual XMLElement* ToElement() { - return 0; - } - /// Safely cast to Text, or null. - virtual XMLText* ToText() { - return 0; - } - /// Safely cast to a Comment, or null. - virtual XMLComment* ToComment() { - return 0; - } - /// Safely cast to a Document, or null. - virtual XMLDocument* ToDocument() { - return 0; - } - /// Safely cast to a Declaration, or null. - virtual XMLDeclaration* ToDeclaration() { - return 0; - } - /// Safely cast to an Unknown, or null. - virtual XMLUnknown* ToUnknown() { - return 0; - } - - virtual const XMLElement* ToElement() const { - return 0; - } - virtual const XMLText* ToText() const { - return 0; - } - virtual const XMLComment* ToComment() const { - return 0; - } - virtual const XMLDocument* ToDocument() const { - return 0; - } - virtual const XMLDeclaration* ToDeclaration() const { - return 0; - } - virtual const XMLUnknown* ToUnknown() const { - return 0; - } - - /** The meaning of 'value' changes for the specific type. - @verbatim - Document: empty (NULL is returned, not an empty string) - Element: name of the element - Comment: the comment text - Unknown: the tag contents - Text: the text string - @endverbatim - */ - const char* Value() const; - - /** Set the Value of an XML node. - @sa Value() - */ - void SetValue( const char* val, bool staticMem=false ); - - /// Gets the line number the node is in, if the document was parsed from a file. - int GetLineNum() const { return _parseLineNum; } - - /// Get the parent of this node on the DOM. - const XMLNode* Parent() const { - return _parent; - } - - XMLNode* Parent() { - return _parent; - } - - /// Returns true if this node has no children. - bool NoChildren() const { - return !_firstChild; - } - - /// Get the first child node, or null if none exists. - const XMLNode* FirstChild() const { - return _firstChild; - } - - XMLNode* FirstChild() { - return _firstChild; - } - - /** Get the first child element, or optionally the first child - element with the specified name. - */ - const XMLElement* FirstChildElement( const char* name = 0 ) const; - - XMLElement* FirstChildElement( const char* name = 0 ) { - return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->FirstChildElement( name )); - } - - /// Get the last child node, or null if none exists. - const XMLNode* LastChild() const { - return _lastChild; - } - - XMLNode* LastChild() { - return _lastChild; - } - - /** Get the last child element or optionally the last child - element with the specified name. - */ - const XMLElement* LastChildElement( const char* name = 0 ) const; - - XMLElement* LastChildElement( const char* name = 0 ) { - return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->LastChildElement(name) ); - } - - /// Get the previous (left) sibling node of this node. - const XMLNode* PreviousSibling() const { - return _prev; - } - - XMLNode* PreviousSibling() { - return _prev; - } - - /// Get the previous (left) sibling element of this node, with an optionally supplied name. - const XMLElement* PreviousSiblingElement( const char* name = 0 ) const ; - - XMLElement* PreviousSiblingElement( const char* name = 0 ) { - return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->PreviousSiblingElement( name ) ); - } - - /// Get the next (right) sibling node of this node. - const XMLNode* NextSibling() const { - return _next; - } - - XMLNode* NextSibling() { - return _next; - } - - /// Get the next (right) sibling element of this node, with an optionally supplied name. - const XMLElement* NextSiblingElement( const char* name = 0 ) const; - - XMLElement* NextSiblingElement( const char* name = 0 ) { - return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->NextSiblingElement( name ) ); - } - - /** - Add a child node as the last (right) child. - If the child node is already part of the document, - it is moved from its old location to the new location. - Returns the addThis argument or 0 if the node does not - belong to the same document. - */ - XMLNode* InsertEndChild( XMLNode* addThis ); - - XMLNode* LinkEndChild( XMLNode* addThis ) { - return InsertEndChild( addThis ); - } - /** - Add a child node as the first (left) child. - If the child node is already part of the document, - it is moved from its old location to the new location. - Returns the addThis argument or 0 if the node does not - belong to the same document. - */ - XMLNode* InsertFirstChild( XMLNode* addThis ); - /** - Add a node after the specified child node. - If the child node is already part of the document, - it is moved from its old location to the new location. - Returns the addThis argument or 0 if the afterThis node - is not a child of this node, or if the node does not - belong to the same document. - */ - XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ); - - /** - Delete all the children of this node. - */ - void DeleteChildren(); - - /** - Delete a child of this node. - */ - void DeleteChild( XMLNode* node ); - - /** - Make a copy of this node, but not its children. - You may pass in a Document pointer that will be - the owner of the new Node. If the 'document' is - null, then the node returned will be allocated - from the current Document. (this->GetDocument()) - - Note: if called on a XMLDocument, this will return null. - */ - virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0; - - /** - Make a copy of this node and all its children. - - If the 'target' is null, then the nodes will - be allocated in the current document. If 'target' - is specified, the memory will be allocated is the - specified XMLDocument. - - NOTE: This is probably not the correct tool to - copy a document, since XMLDocuments can have multiple - top level XMLNodes. You probably want to use - XMLDocument::DeepCopy() - */ - XMLNode* DeepClone( XMLDocument* target ) const; - - /** - Test if 2 nodes are the same, but don't test children. - The 2 nodes do not need to be in the same Document. - - Note: if called on a XMLDocument, this will return false. - */ - virtual bool ShallowEqual( const XMLNode* compare ) const = 0; - - /** Accept a hierarchical visit of the nodes in the TinyXML-2 DOM. Every node in the - XML tree will be conditionally visited and the host will be called back - via the XMLVisitor interface. - - This is essentially a SAX interface for TinyXML-2. (Note however it doesn't re-parse - the XML for the callbacks, so the performance of TinyXML-2 is unchanged by using this - interface versus any other.) - - The interface has been based on ideas from: - - - http://www.saxproject.org/ - - http://c2.com/cgi/wiki?HierarchicalVisitorPattern - - Which are both good references for "visiting". - - An example of using Accept(): - @verbatim - XMLPrinter printer; - tinyxmlDoc.Accept( &printer ); - const char* xmlcstr = printer.CStr(); - @endverbatim - */ - virtual bool Accept( XMLVisitor* visitor ) const = 0; - - /** - Set user data into the XMLNode. TinyXML-2 in - no way processes or interprets user data. - It is initially 0. - */ - void SetUserData(void* userData) { _userData = userData; } - - /** - Get user data set into the XMLNode. TinyXML-2 in - no way processes or interprets user data. - It is initially 0. - */ - void* GetUserData() const { return _userData; } - -protected: - XMLNode( XMLDocument* ); - virtual ~XMLNode(); - - virtual char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr); - - XMLDocument* _document; - XMLNode* _parent; - mutable StrPair _value; - int _parseLineNum; - - XMLNode* _firstChild; - XMLNode* _lastChild; - - XMLNode* _prev; - XMLNode* _next; - - void* _userData; - -private: - MemPool* _memPool; - void Unlink( XMLNode* child ); - static void DeleteNode( XMLNode* node ); - void InsertChildPreamble( XMLNode* insertThis ) const; - const XMLElement* ToElementWithName( const char* name ) const; - - XMLNode( const XMLNode& ); // not supported - XMLNode& operator=( const XMLNode& ); // not supported -}; - - -/** XML text. - - Note that a text node can have child element nodes, for example: - @verbatim - <root>This is <b>bold</b></root> - @endverbatim - - A text node can have 2 ways to output the next. "normal" output - and CDATA. It will default to the mode it was parsed from the XML file and - you generally want to leave it alone, but you can change the output mode with - SetCData() and query it with CData(). -*/ -class TINYXML2_LIB XMLText : public XMLNode -{ - friend class XMLDocument; -public: - virtual bool Accept( XMLVisitor* visitor ) const; - - virtual XMLText* ToText() { - return this; - } - virtual const XMLText* ToText() const { - return this; - } - - /// Declare whether this should be CDATA or standard text. - void SetCData( bool isCData ) { - _isCData = isCData; - } - /// Returns true if this is a CDATA text element. - bool CData() const { - return _isCData; - } - - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; - -protected: - XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {} - virtual ~XMLText() {} - - char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); - -private: - bool _isCData; - - XMLText( const XMLText& ); // not supported - XMLText& operator=( const XMLText& ); // not supported -}; - - -/** An XML Comment. */ -class TINYXML2_LIB XMLComment : public XMLNode -{ - friend class XMLDocument; -public: - virtual XMLComment* ToComment() { - return this; - } - virtual const XMLComment* ToComment() const { - return this; - } - - virtual bool Accept( XMLVisitor* visitor ) const; - - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; - -protected: - XMLComment( XMLDocument* doc ); - virtual ~XMLComment(); - - char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr); - -private: - XMLComment( const XMLComment& ); // not supported - XMLComment& operator=( const XMLComment& ); // not supported -}; - - -/** In correct XML the declaration is the first entry in the file. - @verbatim - <?xml version="1.0" standalone="yes"?> - @endverbatim - - TinyXML-2 will happily read or write files without a declaration, - however. - - The text of the declaration isn't interpreted. It is parsed - and written as a string. -*/ -class TINYXML2_LIB XMLDeclaration : public XMLNode -{ - friend class XMLDocument; -public: - virtual XMLDeclaration* ToDeclaration() { - return this; - } - virtual const XMLDeclaration* ToDeclaration() const { - return this; - } - - virtual bool Accept( XMLVisitor* visitor ) const; - - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; - -protected: - XMLDeclaration( XMLDocument* doc ); - virtual ~XMLDeclaration(); - - char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); - -private: - XMLDeclaration( const XMLDeclaration& ); // not supported - XMLDeclaration& operator=( const XMLDeclaration& ); // not supported -}; - - -/** Any tag that TinyXML-2 doesn't recognize is saved as an - unknown. It is a tag of text, but should not be modified. - It will be written back to the XML, unchanged, when the file - is saved. - - DTD tags get thrown into XMLUnknowns. -*/ -class TINYXML2_LIB XMLUnknown : public XMLNode -{ - friend class XMLDocument; -public: - virtual XMLUnknown* ToUnknown() { - return this; - } - virtual const XMLUnknown* ToUnknown() const { - return this; - } - - virtual bool Accept( XMLVisitor* visitor ) const; - - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; - -protected: - XMLUnknown( XMLDocument* doc ); - virtual ~XMLUnknown(); - - char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); - -private: - XMLUnknown( const XMLUnknown& ); // not supported - XMLUnknown& operator=( const XMLUnknown& ); // not supported -}; - - - -/** An attribute is a name-value pair. Elements have an arbitrary - number of attributes, each with a unique name. - - @note The attributes are not XMLNodes. You may only query the - Next() attribute in a list. -*/ -class TINYXML2_LIB XMLAttribute -{ - friend class XMLElement; -public: - /// The name of the attribute. - const char* Name() const; - - /// The value of the attribute. - const char* Value() const; - - /// Gets the line number the attribute is in, if the document was parsed from a file. - int GetLineNum() const { return _parseLineNum; } - - /// The next attribute in the list. - const XMLAttribute* Next() const { - return _next; - } - - /** IntValue interprets the attribute as an integer, and returns the value. - If the value isn't an integer, 0 will be returned. There is no error checking; - use QueryIntValue() if you need error checking. - */ - int IntValue() const { - int i = 0; - QueryIntValue(&i); - return i; - } - - int64_t Int64Value() const { - int64_t i = 0; - QueryInt64Value(&i); - return i; - } - - /// Query as an unsigned integer. See IntValue() - unsigned UnsignedValue() const { - unsigned i=0; - QueryUnsignedValue( &i ); - return i; - } - /// Query as a boolean. See IntValue() - bool BoolValue() const { - bool b=false; - QueryBoolValue( &b ); - return b; - } - /// Query as a double. See IntValue() - double DoubleValue() const { - double d=0; - QueryDoubleValue( &d ); - return d; - } - /// Query as a float. See IntValue() - float FloatValue() const { - float f=0; - QueryFloatValue( &f ); - return f; - } - - /** QueryIntValue interprets the attribute as an integer, and returns the value - in the provided parameter. The function will return XML_SUCCESS on success, - and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful. - */ - XMLError QueryIntValue( int* value ) const; - /// See QueryIntValue - XMLError QueryUnsignedValue( unsigned int* value ) const; - /// See QueryIntValue - XMLError QueryInt64Value(int64_t* value) const; - /// See QueryIntValue - XMLError QueryBoolValue( bool* value ) const; - /// See QueryIntValue - XMLError QueryDoubleValue( double* value ) const; - /// See QueryIntValue - XMLError QueryFloatValue( float* value ) const; - - /// Set the attribute to a string value. - void SetAttribute( const char* value ); - /// Set the attribute to value. - void SetAttribute( int value ); - /// Set the attribute to value. - void SetAttribute( unsigned value ); - /// Set the attribute to value. - void SetAttribute(int64_t value); - /// Set the attribute to value. - void SetAttribute( bool value ); - /// Set the attribute to value. - void SetAttribute( double value ); - /// Set the attribute to value. - void SetAttribute( float value ); - -private: - enum { BUF_SIZE = 200 }; - - XMLAttribute() : _parseLineNum( 0 ), _next( 0 ), _memPool( 0 ) {} - virtual ~XMLAttribute() {} - - XMLAttribute( const XMLAttribute& ); // not supported - void operator=( const XMLAttribute& ); // not supported - void SetName( const char* name ); - - char* ParseDeep( char* p, bool processEntities, int* curLineNumPtr ); - - mutable StrPair _name; - mutable StrPair _value; - int _parseLineNum; - XMLAttribute* _next; - MemPool* _memPool; -}; - - -/** The element is a container class. It has a value, the element name, - and can contain other elements, text, comments, and unknowns. - Elements also contain an arbitrary number of attributes. -*/ -class TINYXML2_LIB XMLElement : public XMLNode -{ - friend class XMLDocument; -public: - /// Get the name of an element (which is the Value() of the node.) - const char* Name() const { - return Value(); - } - /// Set the name of the element. - void SetName( const char* str, bool staticMem=false ) { - SetValue( str, staticMem ); - } - - virtual XMLElement* ToElement() { - return this; - } - virtual const XMLElement* ToElement() const { - return this; - } - virtual bool Accept( XMLVisitor* visitor ) const; - - /** Given an attribute name, Attribute() returns the value - for the attribute of that name, or null if none - exists. For example: - - @verbatim - const char* value = ele->Attribute( "foo" ); - @endverbatim - - The 'value' parameter is normally null. However, if specified, - the attribute will only be returned if the 'name' and 'value' - match. This allow you to write code: - - @verbatim - if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar(); - @endverbatim - - rather than: - @verbatim - if ( ele->Attribute( "foo" ) ) { - if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar(); - } - @endverbatim - */ - const char* Attribute( const char* name, const char* value=0 ) const; - - /** Given an attribute name, IntAttribute() returns the value - of the attribute interpreted as an integer. The default - value will be returned if the attribute isn't present, - or if there is an error. (For a method with error - checking, see QueryIntAttribute()). - */ - int IntAttribute(const char* name, int defaultValue = 0) const; - /// See IntAttribute() - unsigned UnsignedAttribute(const char* name, unsigned defaultValue = 0) const; - /// See IntAttribute() - int64_t Int64Attribute(const char* name, int64_t defaultValue = 0) const; - /// See IntAttribute() - bool BoolAttribute(const char* name, bool defaultValue = false) const; - /// See IntAttribute() - double DoubleAttribute(const char* name, double defaultValue = 0) const; - /// See IntAttribute() - float FloatAttribute(const char* name, float defaultValue = 0) const; - - /** Given an attribute name, QueryIntAttribute() returns - XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion - can't be performed, or XML_NO_ATTRIBUTE if the attribute - doesn't exist. If successful, the result of the conversion - will be written to 'value'. If not successful, nothing will - be written to 'value'. This allows you to provide default - value: - - @verbatim - int value = 10; - QueryIntAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 - @endverbatim - */ - XMLError QueryIntAttribute( const char* name, int* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryIntValue( value ); - } - - /// See QueryIntAttribute() - XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryUnsignedValue( value ); - } - - /// See QueryIntAttribute() - XMLError QueryInt64Attribute(const char* name, int64_t* value) const { - const XMLAttribute* a = FindAttribute(name); - if (!a) { - return XML_NO_ATTRIBUTE; - } - return a->QueryInt64Value(value); - } - - /// See QueryIntAttribute() - XMLError QueryBoolAttribute( const char* name, bool* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryBoolValue( value ); - } - /// See QueryIntAttribute() - XMLError QueryDoubleAttribute( const char* name, double* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryDoubleValue( value ); - } - /// See QueryIntAttribute() - XMLError QueryFloatAttribute( const char* name, float* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryFloatValue( value ); - } - - - /** Given an attribute name, QueryAttribute() returns - XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion - can't be performed, or XML_NO_ATTRIBUTE if the attribute - doesn't exist. It is overloaded for the primitive types, - and is a generally more convenient replacement of - QueryIntAttribute() and related functions. - - If successful, the result of the conversion - will be written to 'value'. If not successful, nothing will - be written to 'value'. This allows you to provide default - value: - - @verbatim - int value = 10; - QueryAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 - @endverbatim - */ - int QueryAttribute( const char* name, int* value ) const { - return QueryIntAttribute( name, value ); - } - - int QueryAttribute( const char* name, unsigned int* value ) const { - return QueryUnsignedAttribute( name, value ); - } - - int QueryAttribute(const char* name, int64_t* value) const { - return QueryInt64Attribute(name, value); - } - - int QueryAttribute( const char* name, bool* value ) const { - return QueryBoolAttribute( name, value ); - } - - int QueryAttribute( const char* name, double* value ) const { - return QueryDoubleAttribute( name, value ); - } - - int QueryAttribute( const char* name, float* value ) const { - return QueryFloatAttribute( name, value ); - } - - /// Sets the named attribute to value. - void SetAttribute( const char* name, const char* value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - /// Sets the named attribute to value. - void SetAttribute( const char* name, int value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - /// Sets the named attribute to value. - void SetAttribute( const char* name, unsigned value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - - /// Sets the named attribute to value. - void SetAttribute(const char* name, int64_t value) { - XMLAttribute* a = FindOrCreateAttribute(name); - a->SetAttribute(value); - } - - /// Sets the named attribute to value. - void SetAttribute( const char* name, bool value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - /// Sets the named attribute to value. - void SetAttribute( const char* name, double value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - /// Sets the named attribute to value. - void SetAttribute( const char* name, float value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - - /** - Delete an attribute. - */ - void DeleteAttribute( const char* name ); - - /// Return the first attribute in the list. - const XMLAttribute* FirstAttribute() const { - return _rootAttribute; - } - /// Query a specific attribute in the list. - const XMLAttribute* FindAttribute( const char* name ) const; - - /** Convenience function for easy access to the text inside an element. Although easy - and concise, GetText() is limited compared to getting the XMLText child - and accessing it directly. - - If the first child of 'this' is a XMLText, the GetText() - returns the character string of the Text node, else null is returned. - - This is a convenient method for getting the text of simple contained text: - @verbatim - <foo>This is text</foo> - const char* str = fooElement->GetText(); - @endverbatim - - 'str' will be a pointer to "This is text". - - Note that this function can be misleading. If the element foo was created from - this XML: - @verbatim - <foo><b>This is text</b></foo> - @endverbatim - - then the value of str would be null. The first child node isn't a text node, it is - another element. From this XML: - @verbatim - <foo>This is <b>text</b></foo> - @endverbatim - GetText() will return "This is ". - */ - const char* GetText() const; - - /** Convenience function for easy access to the text inside an element. Although easy - and concise, SetText() is limited compared to creating an XMLText child - and mutating it directly. - - If the first child of 'this' is a XMLText, SetText() sets its value to - the given string, otherwise it will create a first child that is an XMLText. - - This is a convenient method for setting the text of simple contained text: - @verbatim - <foo>This is text</foo> - fooElement->SetText( "Hullaballoo!" ); - <foo>Hullaballoo!</foo> - @endverbatim - - Note that this function can be misleading. If the element foo was created from - this XML: - @verbatim - <foo><b>This is text</b></foo> - @endverbatim - - then it will not change "This is text", but rather prefix it with a text element: - @verbatim - <foo>Hullaballoo!<b>This is text</b></foo> - @endverbatim - - For this XML: - @verbatim - <foo /> - @endverbatim - SetText() will generate - @verbatim - <foo>Hullaballoo!</foo> - @endverbatim - */ - void SetText( const char* inText ); - /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText( int value ); - /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText( unsigned value ); - /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText(int64_t value); - /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText( bool value ); - /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText( double value ); - /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText( float value ); - - /** - Convenience method to query the value of a child text node. This is probably best - shown by example. Given you have a document is this form: - @verbatim - <point> - <x>1</x> - <y>1.4</y> - </point> - @endverbatim - - The QueryIntText() and similar functions provide a safe and easier way to get to the - "value" of x and y. - - @verbatim - int x = 0; - float y = 0; // types of x and y are contrived for example - const XMLElement* xElement = pointElement->FirstChildElement( "x" ); - const XMLElement* yElement = pointElement->FirstChildElement( "y" ); - xElement->QueryIntText( &x ); - yElement->QueryFloatText( &y ); - @endverbatim - - @returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted - to the requested type, and XML_NO_TEXT_NODE if there is no child text to query. - - */ - XMLError QueryIntText( int* ival ) const; - /// See QueryIntText() - XMLError QueryUnsignedText( unsigned* uval ) const; - /// See QueryIntText() - XMLError QueryInt64Text(int64_t* uval) const; - /// See QueryIntText() - XMLError QueryBoolText( bool* bval ) const; - /// See QueryIntText() - XMLError QueryDoubleText( double* dval ) const; - /// See QueryIntText() - XMLError QueryFloatText( float* fval ) const; - - int IntText(int defaultValue = 0) const; - - /// See QueryIntText() - unsigned UnsignedText(unsigned defaultValue = 0) const; - /// See QueryIntText() - int64_t Int64Text(int64_t defaultValue = 0) const; - /// See QueryIntText() - bool BoolText(bool defaultValue = false) const; - /// See QueryIntText() - double DoubleText(double defaultValue = 0) const; - /// See QueryIntText() - float FloatText(float defaultValue = 0) const; - - // internal: - enum ElementClosingType { - OPEN, // <foo> - CLOSED, // <foo/> - CLOSING // </foo> - }; - ElementClosingType ClosingType() const { - return _closingType; - } - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; - -protected: - char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); - -private: - XMLElement( XMLDocument* doc ); - virtual ~XMLElement(); - XMLElement( const XMLElement& ); // not supported - void operator=( const XMLElement& ); // not supported - - XMLAttribute* FindAttribute( const char* name ) { - return const_cast<XMLAttribute*>(const_cast<const XMLElement*>(this)->FindAttribute( name )); - } - XMLAttribute* FindOrCreateAttribute( const char* name ); - //void LinkAttribute( XMLAttribute* attrib ); - char* ParseAttributes( char* p, int* curLineNumPtr ); - static void DeleteAttribute( XMLAttribute* attribute ); - XMLAttribute* CreateAttribute(); - - enum { BUF_SIZE = 200 }; - ElementClosingType _closingType; - // The attribute list is ordered; there is no 'lastAttribute' - // because the list needs to be scanned for dupes before adding - // a new attribute. - XMLAttribute* _rootAttribute; -}; - - -enum Whitespace { - PRESERVE_WHITESPACE, - COLLAPSE_WHITESPACE -}; - - -/** A Document binds together all the functionality. - It can be saved, loaded, and printed to the screen. - All Nodes are connected and allocated to a Document. - If the Document is deleted, all its Nodes are also deleted. -*/ -class TINYXML2_LIB XMLDocument : public XMLNode -{ - friend class XMLElement; -public: - /// constructor - XMLDocument( bool processEntities = true, Whitespace whitespaceMode = PRESERVE_WHITESPACE ); - ~XMLDocument(); - - virtual XMLDocument* ToDocument() { - TIXMLASSERT( this == _document ); - return this; - } - virtual const XMLDocument* ToDocument() const { - TIXMLASSERT( this == _document ); - return this; - } - - /** - Parse an XML file from a character string. - Returns XML_SUCCESS (0) on success, or - an errorID. - - You may optionally pass in the 'nBytes', which is - the number of bytes which will be parsed. If not - specified, TinyXML-2 will assume 'xml' points to a - null terminated string. - */ - XMLError Parse( const char* xml, size_t nBytes=(size_t)(-1) ); - - /** - Load an XML file from disk. - Returns XML_SUCCESS (0) on success, or - an errorID. - */ - XMLError LoadFile( const char* filename ); - - /** - Load an XML file from disk. You are responsible - for providing and closing the FILE*. - - NOTE: The file should be opened as binary ("rb") - not text in order for TinyXML-2 to correctly - do newline normalization. - - Returns XML_SUCCESS (0) on success, or - an errorID. - */ - XMLError LoadFile( FILE* ); - - /** - Save the XML file to disk. - Returns XML_SUCCESS (0) on success, or - an errorID. - */ - XMLError SaveFile( const char* filename, bool compact = false ); - - /** - Save the XML file to disk. You are responsible - for providing and closing the FILE*. - - Returns XML_SUCCESS (0) on success, or - an errorID. - */ - XMLError SaveFile( FILE* fp, bool compact = false ); - - bool ProcessEntities() const { - return _processEntities; - } - Whitespace WhitespaceMode() const { - return _whitespaceMode; - } - - /** - Returns true if this document has a leading Byte Order Mark of UTF8. - */ - bool HasBOM() const { - return _writeBOM; - } - /** Sets whether to write the BOM when writing the file. - */ - void SetBOM( bool useBOM ) { - _writeBOM = useBOM; - } - - /** Return the root element of DOM. Equivalent to FirstChildElement(). - To get the first node, use FirstChild(). - */ - XMLElement* RootElement() { - return FirstChildElement(); - } - const XMLElement* RootElement() const { - return FirstChildElement(); - } - - /** Print the Document. If the Printer is not provided, it will - print to stdout. If you provide Printer, this can print to a file: - @verbatim - XMLPrinter printer( fp ); - doc.Print( &printer ); - @endverbatim - - Or you can use a printer to print to memory: - @verbatim - XMLPrinter printer; - doc.Print( &printer ); - // printer.CStr() has a const char* to the XML - @endverbatim - */ - void Print( XMLPrinter* streamer=0 ) const; - virtual bool Accept( XMLVisitor* visitor ) const; - - /** - Create a new Element associated with - this Document. The memory for the Element - is managed by the Document. - */ - XMLElement* NewElement( const char* name ); - /** - Create a new Comment associated with - this Document. The memory for the Comment - is managed by the Document. - */ - XMLComment* NewComment( const char* comment ); - /** - Create a new Text associated with - this Document. The memory for the Text - is managed by the Document. - */ - XMLText* NewText( const char* text ); - /** - Create a new Declaration associated with - this Document. The memory for the object - is managed by the Document. - - If the 'text' param is null, the standard - declaration is used.: - @verbatim - <?xml version="1.0" encoding="UTF-8"?> - @endverbatim - */ - XMLDeclaration* NewDeclaration( const char* text=0 ); - /** - Create a new Unknown associated with - this Document. The memory for the object - is managed by the Document. - */ - XMLUnknown* NewUnknown( const char* text ); - - /** - Delete a node associated with this document. - It will be unlinked from the DOM. - */ - void DeleteNode( XMLNode* node ); - - void SetError( XMLError error, const char* str1, const char* str2, int lineNum ); - - void ClearError() { - SetError(XML_SUCCESS, 0, 0, 0); - } - - /// Return true if there was an error parsing the document. - bool Error() const { - return _errorID != XML_SUCCESS; - } - /// Return the errorID. - XMLError ErrorID() const { - return _errorID; - } - const char* ErrorName() const; - static const char* ErrorIDToName(XMLError errorID); - - /// Return a possibly helpful diagnostic location or string. - const char* GetErrorStr1() const; - - /// Return a possibly helpful secondary diagnostic location or string. - const char* GetErrorStr2() const; - - /// Return the line where the error occured, or zero if unknown. - int GetErrorLineNum() const - { - return _errorLineNum; - } - /// If there is an error, print it to stdout. - void PrintError() const; - - /// Clear the document, resetting it to the initial state. - void Clear(); - - /** - Copies this document to a target document. - The target will be completely cleared before the copy. - If you want to copy a sub-tree, see XMLNode::DeepClone(). - - NOTE: that the 'target' must be non-null. - */ - void DeepCopy(XMLDocument* target); - - // internal - char* Identify( char* p, XMLNode** node ); - - // internal - void MarkInUse(XMLNode*); - - virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const { - return 0; - } - virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const { - return false; - } - -private: - XMLDocument( const XMLDocument& ); // not supported - void operator=( const XMLDocument& ); // not supported - - bool _writeBOM; - bool _processEntities; - XMLError _errorID; - Whitespace _whitespaceMode; - mutable StrPair _errorStr1; - mutable StrPair _errorStr2; - int _errorLineNum; - char* _charBuffer; - int _parseCurLineNum; - // Memory tracking does add some overhead. - // However, the code assumes that you don't - // have a bunch of unlinked nodes around. - // Therefore it takes less memory to track - // in the document vs. a linked list in the XMLNode, - // and the performance is the same. - DynArray<XMLNode*, 10> _unlinked; - - MemPoolT< sizeof(XMLElement) > _elementPool; - MemPoolT< sizeof(XMLAttribute) > _attributePool; - MemPoolT< sizeof(XMLText) > _textPool; - MemPoolT< sizeof(XMLComment) > _commentPool; - - static const char* _errorNames[XML_ERROR_COUNT]; - - void Parse(); - - template<class NodeType, int PoolElementSize> - NodeType* CreateUnlinkedNode( MemPoolT<PoolElementSize>& pool ); -}; - -template<class NodeType, int PoolElementSize> -inline NodeType* XMLDocument::CreateUnlinkedNode( MemPoolT<PoolElementSize>& pool ) -{ - TIXMLASSERT( sizeof( NodeType ) == PoolElementSize ); - TIXMLASSERT( sizeof( NodeType ) == pool.ItemSize() ); - NodeType* returnNode = new (pool.Alloc()) NodeType( this ); - TIXMLASSERT( returnNode ); - returnNode->_memPool = &pool; - - _unlinked.Push(returnNode); - return returnNode; -} - -/** - A XMLHandle is a class that wraps a node pointer with null checks; this is - an incredibly useful thing. Note that XMLHandle is not part of the TinyXML-2 - DOM structure. It is a separate utility class. - - Take an example: - @verbatim - <Document> - <Element attributeA = "valueA"> - <Child attributeB = "value1" /> - <Child attributeB = "value2" /> - </Element> - </Document> - @endverbatim - - Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very - easy to write a *lot* of code that looks like: - - @verbatim - XMLElement* root = document.FirstChildElement( "Document" ); - if ( root ) - { - XMLElement* element = root->FirstChildElement( "Element" ); - if ( element ) - { - XMLElement* child = element->FirstChildElement( "Child" ); - if ( child ) - { - XMLElement* child2 = child->NextSiblingElement( "Child" ); - if ( child2 ) - { - // Finally do something useful. - @endverbatim - - And that doesn't even cover "else" cases. XMLHandle addresses the verbosity - of such code. A XMLHandle checks for null pointers so it is perfectly safe - and correct to use: - - @verbatim - XMLHandle docHandle( &document ); - XMLElement* child2 = docHandle.FirstChildElement( "Document" ).FirstChildElement( "Element" ).FirstChildElement().NextSiblingElement(); - if ( child2 ) - { - // do something useful - @endverbatim - - Which is MUCH more concise and useful. - - It is also safe to copy handles - internally they are nothing more than node pointers. - @verbatim - XMLHandle handleCopy = handle; - @endverbatim - - See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects. -*/ -class TINYXML2_LIB XMLHandle -{ -public: - /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. - XMLHandle( XMLNode* node ) { - _node = node; - } - /// Create a handle from a node. - XMLHandle( XMLNode& node ) { - _node = &node; - } - /// Copy constructor - XMLHandle( const XMLHandle& ref ) { - _node = ref._node; - } - /// Assignment - XMLHandle& operator=( const XMLHandle& ref ) { - _node = ref._node; - return *this; - } - - /// Get the first child of this handle. - XMLHandle FirstChild() { - return XMLHandle( _node ? _node->FirstChild() : 0 ); - } - /// Get the first child element of this handle. - XMLHandle FirstChildElement( const char* name = 0 ) { - return XMLHandle( _node ? _node->FirstChildElement( name ) : 0 ); - } - /// Get the last child of this handle. - XMLHandle LastChild() { - return XMLHandle( _node ? _node->LastChild() : 0 ); - } - /// Get the last child element of this handle. - XMLHandle LastChildElement( const char* name = 0 ) { - return XMLHandle( _node ? _node->LastChildElement( name ) : 0 ); - } - /// Get the previous sibling of this handle. - XMLHandle PreviousSibling() { - return XMLHandle( _node ? _node->PreviousSibling() : 0 ); - } - /// Get the previous sibling element of this handle. - XMLHandle PreviousSiblingElement( const char* name = 0 ) { - return XMLHandle( _node ? _node->PreviousSiblingElement( name ) : 0 ); - } - /// Get the next sibling of this handle. - XMLHandle NextSibling() { - return XMLHandle( _node ? _node->NextSibling() : 0 ); - } - /// Get the next sibling element of this handle. - XMLHandle NextSiblingElement( const char* name = 0 ) { - return XMLHandle( _node ? _node->NextSiblingElement( name ) : 0 ); - } - - /// Safe cast to XMLNode. This can return null. - XMLNode* ToNode() { - return _node; - } - /// Safe cast to XMLElement. This can return null. - XMLElement* ToElement() { - return ( _node ? _node->ToElement() : 0 ); - } - /// Safe cast to XMLText. This can return null. - XMLText* ToText() { - return ( _node ? _node->ToText() : 0 ); - } - /// Safe cast to XMLUnknown. This can return null. - XMLUnknown* ToUnknown() { - return ( _node ? _node->ToUnknown() : 0 ); - } - /// Safe cast to XMLDeclaration. This can return null. - XMLDeclaration* ToDeclaration() { - return ( _node ? _node->ToDeclaration() : 0 ); - } - -private: - XMLNode* _node; -}; - - -/** - A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the - same in all regards, except for the 'const' qualifiers. See XMLHandle for API. -*/ -class TINYXML2_LIB XMLConstHandle -{ -public: - XMLConstHandle( const XMLNode* node ) { - _node = node; - } - XMLConstHandle( const XMLNode& node ) { - _node = &node; - } - XMLConstHandle( const XMLConstHandle& ref ) { - _node = ref._node; - } - - XMLConstHandle& operator=( const XMLConstHandle& ref ) { - _node = ref._node; - return *this; - } - - const XMLConstHandle FirstChild() const { - return XMLConstHandle( _node ? _node->FirstChild() : 0 ); - } - const XMLConstHandle FirstChildElement( const char* name = 0 ) const { - return XMLConstHandle( _node ? _node->FirstChildElement( name ) : 0 ); - } - const XMLConstHandle LastChild() const { - return XMLConstHandle( _node ? _node->LastChild() : 0 ); - } - const XMLConstHandle LastChildElement( const char* name = 0 ) const { - return XMLConstHandle( _node ? _node->LastChildElement( name ) : 0 ); - } - const XMLConstHandle PreviousSibling() const { - return XMLConstHandle( _node ? _node->PreviousSibling() : 0 ); - } - const XMLConstHandle PreviousSiblingElement( const char* name = 0 ) const { - return XMLConstHandle( _node ? _node->PreviousSiblingElement( name ) : 0 ); - } - const XMLConstHandle NextSibling() const { - return XMLConstHandle( _node ? _node->NextSibling() : 0 ); - } - const XMLConstHandle NextSiblingElement( const char* name = 0 ) const { - return XMLConstHandle( _node ? _node->NextSiblingElement( name ) : 0 ); - } - - - const XMLNode* ToNode() const { - return _node; - } - const XMLElement* ToElement() const { - return ( _node ? _node->ToElement() : 0 ); - } - const XMLText* ToText() const { - return ( _node ? _node->ToText() : 0 ); - } - const XMLUnknown* ToUnknown() const { - return ( _node ? _node->ToUnknown() : 0 ); - } - const XMLDeclaration* ToDeclaration() const { - return ( _node ? _node->ToDeclaration() : 0 ); - } - -private: - const XMLNode* _node; -}; - - -/** - Printing functionality. The XMLPrinter gives you more - options than the XMLDocument::Print() method. - - It can: - -# Print to memory. - -# Print to a file you provide. - -# Print XML without a XMLDocument. - - Print to Memory - - @verbatim - XMLPrinter printer; - doc.Print( &printer ); - SomeFunction( printer.CStr() ); - @endverbatim - - Print to a File - - You provide the file pointer. - @verbatim - XMLPrinter printer( fp ); - doc.Print( &printer ); - @endverbatim - - Print without a XMLDocument - - When loading, an XML parser is very useful. However, sometimes - when saving, it just gets in the way. The code is often set up - for streaming, and constructing the DOM is just overhead. - - The Printer supports the streaming case. The following code - prints out a trivially simple XML file without ever creating - an XML document. - - @verbatim - XMLPrinter printer( fp ); - printer.OpenElement( "foo" ); - printer.PushAttribute( "foo", "bar" ); - printer.CloseElement(); - @endverbatim -*/ -class TINYXML2_LIB XMLPrinter : public XMLVisitor -{ -public: - /** Construct the printer. If the FILE* is specified, - this will print to the FILE. Else it will print - to memory, and the result is available in CStr(). - If 'compact' is set to true, then output is created - with only required whitespace and newlines. - */ - XMLPrinter( FILE* file=0, bool compact = false, int depth = 0 ); - virtual ~XMLPrinter() {} - - /** If streaming, write the BOM and declaration. */ - void PushHeader( bool writeBOM, bool writeDeclaration ); - /** If streaming, start writing an element. - The element must be closed with CloseElement() - */ - void OpenElement( const char* name, bool compactMode=false ); - /// If streaming, add an attribute to an open element. - void PushAttribute( const char* name, const char* value ); - void PushAttribute( const char* name, int value ); - void PushAttribute( const char* name, unsigned value ); - void PushAttribute(const char* name, int64_t value); - void PushAttribute( const char* name, bool value ); - void PushAttribute( const char* name, double value ); - /// If streaming, close the Element. - virtual void CloseElement( bool compactMode=false ); - - /// Add a text node. - void PushText( const char* text, bool cdata=false ); - /// Add a text node from an integer. - void PushText( int value ); - /// Add a text node from an unsigned. - void PushText( unsigned value ); - /// Add a text node from an unsigned. - void PushText(int64_t value); - /// Add a text node from a bool. - void PushText( bool value ); - /// Add a text node from a float. - void PushText( float value ); - /// Add a text node from a double. - void PushText( double value ); - - /// Add a comment - void PushComment( const char* comment ); - - void PushDeclaration( const char* value ); - void PushUnknown( const char* value ); - - virtual bool VisitEnter( const XMLDocument& /*doc*/ ); - virtual bool VisitExit( const XMLDocument& /*doc*/ ) { - return true; - } - - virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute ); - virtual bool VisitExit( const XMLElement& element ); - - virtual bool Visit( const XMLText& text ); - virtual bool Visit( const XMLComment& comment ); - virtual bool Visit( const XMLDeclaration& declaration ); - virtual bool Visit( const XMLUnknown& unknown ); - - /** - If in print to memory mode, return a pointer to - the XML file in memory. - */ - const char* CStr() const { - return _buffer.Mem(); - } - /** - If in print to memory mode, return the size - of the XML file in memory. (Note the size returned - includes the terminating null.) - */ - int CStrSize() const { - return _buffer.Size(); - } - /** - If in print to memory mode, reset the buffer to the - beginning. - */ - void ClearBuffer() { - _buffer.Clear(); - _buffer.Push(0); - _firstElement = true; - } - -protected: - virtual bool CompactMode( const XMLElement& ) { return _compactMode; } - - /** Prints out the space before an element. You may override to change - the space and tabs used. A PrintSpace() override should call Print(). - */ - virtual void PrintSpace( int depth ); - void Print( const char* format, ... ); - - void SealElementIfJustOpened(); - bool _elementJustOpened; - DynArray< const char*, 10 > _stack; - -private: - void PrintString( const char*, bool restrictedEntitySet ); // prints out, after detecting entities. - - bool _firstElement; - FILE* _fp; - int _depth; - int _textDepth; - bool _processEntities; - bool _compactMode; - - enum { - ENTITY_RANGE = 64, - BUF_SIZE = 200 - }; - bool _entityFlag[ENTITY_RANGE]; - bool _restrictedEntityFlag[ENTITY_RANGE]; - - DynArray< char, 20 > _buffer; -}; - - -} // tinyxml2 - -#if defined(_MSC_VER) -# pragma warning(pop) -#endif - -#endif // TINYXML2_INCLUDED diff --git a/deps/yaracpp/CMakeLists.txt b/deps/yaracpp/CMakeLists.txt index 6e4631e3..5ef146d4 100644 --- a/deps/yaracpp/CMakeLists.txt +++ b/deps/yaracpp/CMakeLists.txt @@ -1,11 +1,24 @@ include(ExternalProject) +if(CMAKE_C_COMPILER) + set(CMAKE_C_COMPILER_OPTION "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}") +endif() +if(CMAKE_CXX_COMPILER) + set(CMAKE_CXX_COMPILER_OPTION "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}") +endif() + ExternalProject_Add(yaracpp-project - URL https://github.com/avast-tl/yaracpp/archive/a284ab17ab04d91f1725f579a8670a12ef1afd76.zip - URL_HASH SHA256=ff5e5996e90a56f2752c69ac7a12009075f7f355da5bff37e562239ae9749e44 + URL https://github.com/avast-tl/yaracpp/archive/v1.0.1.zip + URL_HASH SHA256=2252ba7cfd9b190d3e260a741bbd2abc0fbd7c0422eb13d878278a89016c4fda + DOWNLOAD_NAME yaracpp.zip CMAKE_ARGS # This does not work on MSVC, but may be useful on Linux. -DCMAKE_BUILD_TYPE=Release + # Force the use of the same compiler as used to build the top-level + # project. Otherwise, the external project may pick up a different + # compiler, which may result in link errors. + "${CMAKE_C_COMPILER_OPTION}" + "${CMAKE_CXX_COMPILER_OPTION}" # Disable the update step. UPDATE_COMMAND "" # Disable the install step. @@ -25,16 +38,32 @@ set(YARA_LIBRARY_NAME "libyara") if(MSVC) if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(YARA_LIBRARY_NAME "${YARA_LIBRARY_NAME}64") + set(YARAC_NAME "yarac64.exe" CACHE STRING "Yarac binary name" FORCE) else() set(YARA_LIBRARY_NAME "${YARA_LIBRARY_NAME}32") + set(YARAC_NAME "yarac32.exe" CACHE STRING "Yarac binary name" FORCE) endif() set(YARA_WIN_DIR ${YARA_DIR}/windows/vs2015) set(YARA_LIBRARY_DIR ${YARA_WIN_DIR}/libyara/$<CONFIGURATION>) + set(YARAC_PATH ${YARA_WIN_DIR}/\${CMAKE_INSTALL_CONFIG_NAME}/${YARAC_NAME}) else() set(YARA_LIBRARY_DIR ${YARA_DIR}/libyara/.libs) + set(YARAC_PATH ${YARA_DIR}/yarac) endif() +# Compile YARA rules for tools detection. +set(YARA_COMPILE_SH ${CMAKE_SOURCE_DIR}/support/yara_patterns/tools/compile-yara.sh) +install(CODE " + execute_process( + COMMAND sh \"${YARA_COMPILE_SH}\" \"${YARAC_PATH}\" \"${CMAKE_SOURCE_DIR}\" \"${CMAKE_INSTALL_PREFIX}\" + RESULT_VARIABLE COMPILE_YARA_RES + ) + if(COMPILE_YARA_RES) + message(FATAL_ERROR \"YARA tool signatures compilation FAILED\") + endif() +") + # Add libraries. add_library(yaracpp INTERFACE) add_dependencies(yaracpp yaracpp-project) diff --git a/deps/yaramod/CMakeLists.txt b/deps/yaramod/CMakeLists.txt index d4783b8a..cfbb58e0 100644 --- a/deps/yaramod/CMakeLists.txt +++ b/deps/yaramod/CMakeLists.txt @@ -4,12 +4,25 @@ find_package(BISON REQUIRED) include(ExternalProject) +if(CMAKE_C_COMPILER) + set(CMAKE_C_COMPILER_OPTION "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}") +endif() +if(CMAKE_CXX_COMPILER) + set(CMAKE_CXX_COMPILER_OPTION "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}") +endif() + ExternalProject_Add(yaramod-project - URL https://github.com/avast-tl/yaramod/archive/122610928203f4606276e1cb2552f04f6dd33c32.zip - URL_HASH SHA256=7fac768ccc8699fc44c1bac1e9ebea8ab57654f34a53ff772b025e50fa66c760 + URL https://github.com/avast-tl/yaramod/archive/v1.0.1.zip + URL_HASH SHA256=20793f99bd1a22e9b743400653d54790a48457ef0ae1cf8f089a1bf3bf38f339 + DOWNLOAD_NAME yaramod.zip CMAKE_ARGS # This does not work on MSVC, but may be useful on Linux. -DCMAKE_BUILD_TYPE=Release + # Force the use of the same compiler as used to build the top-level + # project. Otherwise, the external project may pick up a different + # compiler, which may result in link errors. + "${CMAKE_C_COMPILER_OPTION}" + "${CMAKE_CXX_COMPILER_OPTION}" # Disable the update step. UPDATE_COMMAND "" # Disable the install step. diff --git a/include/retdec/utils/os.h b/include/retdec/utils/os.h index 78a5b4eb..9c5f73cb 100644 --- a/include/retdec/utils/os.h +++ b/include/retdec/utils/os.h @@ -8,11 +8,11 @@ #define RETDEC_UTILS_OS_H // Obtain the used operating system. Currently, we only distinguish between -// Windows and Linux. +// Windows and UNIX. #if defined(__WIN) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) #define OS_WINDOWS #else - #define OS_LINUX + #define OS_UNIX #endif #endif diff --git a/scripts/retdec-archive-decompiler.sh b/scripts/retdec-archive-decompiler.sh index 66124d1b..c0fd9f31 100644 --- a/scripts/retdec-archive-decompiler.sh +++ b/scripts/retdec-archive-decompiler.sh @@ -8,10 +8,10 @@ # - the `timeout` command # -SCRIPTPATH="$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )" +SCRIPT_DIR="$(dirname "$(readlink -e "$0")")" if [ -z "$DECOMPILER_UTILS" ]; then - DECOMPILER_UTILS="$SCRIPTPATH/retdec-utils.sh" + DECOMPILER_UTILS="$SCRIPT_DIR/retdec-utils.sh" fi . "$DECOMPILER_UTILS" diff --git a/scripts/retdec-config.sh b/scripts/retdec-config.sh index e77c217d..a8a9f841 100644 --- a/scripts/retdec-config.sh +++ b/scripts/retdec-config.sh @@ -3,14 +3,14 @@ # Decompiler's configuration. This file should be included in every decompilation script. # -SCRIPTPATH="$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )" +SCRIPT_DIR="$(dirname "$(readlink -e "$0")")" ## ## Paths (everything has to be without the ending slash '/'). ## # Paths relative from script path. -INSTALL_BIN_DIR="$SCRIPTPATH" +INSTALL_BIN_DIR="$SCRIPT_DIR" UNIT_TESTS_DIR="$INSTALL_BIN_DIR" INSTALL_SHARE_DIR="$INSTALL_BIN_DIR/../share/retdec" INSTALL_SUPPORT_DIR="$INSTALL_SHARE_DIR/support" diff --git a/scripts/retdec-decompiler.sh b/scripts/retdec-decompiler.sh index 4ac8a3c8..1dc7fcb0 100644 --- a/scripts/retdec-decompiler.sh +++ b/scripts/retdec-decompiler.sh @@ -3,10 +3,10 @@ # The script decompiles the given file into the selected target high-level language. # -SCRIPTPATH="$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )" +SCRIPT_DIR="$(dirname "$(readlink -e "$0")")" if [ -z "$DECOMPILER_UTILS" ]; then - DECOMPILER_UTILS="$SCRIPTPATH/retdec-utils.sh" + DECOMPILER_UTILS="$SCRIPT_DIR/retdec-utils.sh" fi . "$DECOMPILER_UTILS" @@ -162,10 +162,10 @@ check_arguments() fi # Convert to absolute paths. - IN="$(readlink -f "$IN")" - OUT="$(readlink -f "$OUT")" + IN="$(get_realpath "$IN")" + OUT="$(get_realpath "$OUT")" if [ -e "$PDB_FILE" ]; then - PDB_FILE="$(readlink -f "$PDB_FILE")" + PDB_FILE="$(get_realpath "$PDB_FILE")" fi # Check that selected ranges are valid. @@ -245,8 +245,7 @@ PARSED_OPTIONS=$(getopt -o "$GETOPT_SHORTOPT" -l "$GETOPT_LONGOPT" -n "$SCRIPT_N eval set -- "$PARSED_OPTIONS" -while true ; -do +while true; do case "$1" in -a|--arch) # Target architecture. [ "$ARCH" ] && print_error_and_die "Duplicate option: -a|--arch" @@ -497,10 +496,7 @@ done check_arguments if [ "$MODE" = "raw" ]; then - - ## - ## Default values and initialization. - ## + # Default values and initialization. OUT_RAW_EXECUTABLE="$IN" # Entry point for THUMB must be odd. @@ -513,7 +509,6 @@ fi # Check for archives. if [ "$MODE" = "bin" ]; then - # Check for archives packed in Mach-O Universal Binaries. echo "##### Checking if file is a Mach-O Universal static library..." echo "RUN: $EXTRACT --list $IN" @@ -937,9 +932,7 @@ if [ "$BACKEND_EMIT_CFG" ]; then [ "$BACKEND_CFG_TEST" ] && LLVMIR2HLL_PARAMS+=(--backend-cfg-test) fi -## -## Decompile the optimized IR code. -## +# Decompile the optimized IR code. echo "" echo "##### Decompiling $OUT_BACKEND_BC into $OUT..." echo "RUN: $LLVMIR2HLL ${LLVMIR2HLL_PARAMS[@]}" @@ -957,9 +950,8 @@ check_whether_decompilation_should_be_forcefully_stopped "llvmir2hll" GRAPH_FORMAT=${GRAPH_FORMAT:=png} BACKEND_CG_CONVERSION=${BACKEND_CG_CONVERSION:=auto} BACKEND_CFG_CONVERSION=${BACKEND_CFG_CONVERSION:=auto} -## -## Convert .dot graphs to desired format. -## + +# Convert .dot graphs to desired format. if [[ ( "$BACKEND_EMIT_CG" && "$BACKEND_CG_CONVERSION" = "auto" ) || ( "$BACKEND_EMIT_CFG" && "$BACKEND_CFG_CONVERSION" = "auto" ) ]]; then echo "" @@ -982,9 +974,7 @@ fi sed -i 's/[ \t]*$//' "$OUT" sed -i '$ { /^$/ d}' "$OUT" -## -## Colorize output file. -## +# Colorize output file. if [ "$COLOR_IDA" ]; then "$IDA_COLORIZER" "$OUT" "$CONFIG" fi diff --git a/scripts/retdec-fileinfo.sh b/scripts/retdec-fileinfo.sh index b5de6226..7330d51d 100644 --- a/scripts/retdec-fileinfo.sh +++ b/scripts/retdec-fileinfo.sh @@ -6,10 +6,10 @@ # - is able to analyze archives (.a/.lib files). # -SCRIPTPATH="$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )" +SCRIPT_DIR="$(dirname "$(readlink -e "$0")")" if [ -z "$DECOMPILER_UTILS" ]; then - DECOMPILER_UTILS="$SCRIPTPATH/retdec-utils.sh" + DECOMPILER_UTILS="$SCRIPT_DIR/retdec-utils.sh" fi . "$DECOMPILER_UTILS" diff --git a/scripts/retdec-signature-from-library-creator.sh b/scripts/retdec-signature-from-library-creator.sh index fa2a1ee5..82d3a279 100644 --- a/scripts/retdec-signature-from-library-creator.sh +++ b/scripts/retdec-signature-from-library-creator.sh @@ -1,9 +1,9 @@ #!/bin/bash -SCRIPTPATH="$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )" +SCRIPT_DIR="$(dirname "$(readlink -e "$0")")" if [ -z "$DECOMPILER_UTILS" ]; then - DECOMPILER_UTILS="$SCRIPTPATH/retdec-utils.sh" + DECOMPILER_UTILS="$SCRIPT_DIR/retdec-utils.sh" fi . "$DECOMPILER_UTILS" @@ -97,7 +97,7 @@ if [ -z "$OUT_PATH" ]; then die_with_error_and_cleanup "option -o|--output is compulsory" else FILE_PATH="$OUT_PATH" - DIR="$(dirname "$(readlink -f "$FILE_PATH")")" + DIR="$(dirname "$(get_realpath "$FILE_PATH")")" DIR_PATH=$(mktemp -d "$DIR/XXXXXXXXX") fi diff --git a/scripts/retdec-tests-runner.sh b/scripts/retdec-tests-runner.sh index 7e643c85..eecc40b2 100644 --- a/scripts/retdec-tests-runner.sh +++ b/scripts/retdec-tests-runner.sh @@ -3,13 +3,20 @@ # Runs all the installed unit tests. # -SCRIPTPATH="$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )" +SCRIPT_DIR="$(dirname "$(readlink -e "$0")")" if [ -z "$DECOMPILER_CONFIG" ]; then - DECOMPILER_CONFIG="$SCRIPTPATH/retdec-config.sh" + DECOMPILER_CONFIG="$SCRIPT_DIR/retdec-config.sh" fi . "$DECOMPILER_CONFIG" +# +# First argument can be verbose. +# +if [ "$1" = "-v" ] || [ "$1" = "--verbose" ]; then + VERBOSE=1 +fi + # # Emits a colored version of the given message to the standard output (without # a new line). @@ -58,8 +65,12 @@ run_unit_tests_in_dir() { unit_test_name="$(sed 's/^.*\/bin\///' <<< "$unit_test")" echo_colored "$unit_test_name" "yellow" echo "" - $unit_test --gtest_color=yes | grep -v "RUN\|OK\|----------\|==========" |\ - grep -v "^$" | grep -v "Running main() from gmock_main.cc" + if [ "$VERBOSE" ]; then + $unit_test --gtest_color=yes + else + $unit_test --gtest_color=yes | grep -v "RUN\|OK\|----------\|==========" |\ + grep -v "^$" | grep -v "Running main() from gmock_main.cc" + fi RC=${PIPESTATUS[0]} if [ "$RC" != "0" ]; then TESTS_FAILED="1" diff --git a/scripts/retdec-unpacker.sh b/scripts/retdec-unpacker.sh index d7d3da48..3abd0e0d 100644 --- a/scripts/retdec-unpacker.sh +++ b/scripts/retdec-unpacker.sh @@ -26,10 +26,10 @@ RET_UNPACKER_FAILED=4 # 10 other errors #RET_OTHER_ERRORS=10 -SCRIPTPATH="$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )" +SCRIPT_DIR="$(dirname "$(readlink -e "$0")")" if [ -z "$DECOMPILER_UTILS" ]; then - DECOMPILER_UTILS="$SCRIPTPATH/retdec-utils.sh" + DECOMPILER_UTILS="$SCRIPT_DIR/retdec-utils.sh" fi . "$DECOMPILER_UTILS" @@ -64,8 +64,8 @@ check_arguments() OUT=${OUT:="$IN"-unpacked} # Convert to absolute paths. - IN="$(readlink -f "$IN")" - OUT="$(readlink -f "$OUT")" + IN="$(get_realpath "$IN")" + OUT="$(get_realpath "$OUT")" } # diff --git a/scripts/retdec-utils.sh b/scripts/retdec-utils.sh index a354ad89..e86054e4 100644 --- a/scripts/retdec-utils.sh +++ b/scripts/retdec-utils.sh @@ -3,14 +3,34 @@ # Compilation and decompilation utility functions. # -SCRIPTPATH="$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )" +SCRIPT_DIR="$(dirname "$(readlink -e "$0")")" if [ -z "$DECOMPILER_CONFIG" ]; then - DECOMPILER_CONFIG="$SCRIPTPATH/retdec-config.sh" + DECOMPILER_CONFIG="$SCRIPT_DIR/retdec-config.sh" fi . "$DECOMPILER_CONFIG" +# +# Prints the real, physical location of a directory or file, relative or +# absolute. +# 1 argument is needed +# +get_realpath() +{ + local input_path="$1" + # Use cygpath.exe on cygwin, due to cygwins virtual folder mountpoints + # (i.e., "/cygdrive/c/foo/bar" becomes "c:/foo/bar"). + # cygpath args: + # -m -- mixed mode, that is, forward slashes, instead of backward slashes + # -a -- absolute path (regardless if input is relative or not) + if [[ "$(uname -s)" == *CYGWIN* ]]; then + cygpath -ma "$input_path" + else + readlink -f "$input_path" + fi +} + # # Print error message to stderr and die. # 1 argument is needed diff --git a/scripts/type_extractor/gen_cstdlib_and_linux_jsons.sh b/scripts/type_extractor/gen_cstdlib_and_linux_jsons.sh index 53c0807d..e68925e0 100755 --- a/scripts/type_extractor/gen_cstdlib_and_linux_jsons.sh +++ b/scripts/type_extractor/gen_cstdlib_and_linux_jsons.sh @@ -70,7 +70,7 @@ FILES_FILTER=${FILES_FILTER:${#SEP}} # # Paths. # -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")"; pwd -P)" +SCRIPT_DIR="$(dirname "$(readlink -e "$0")")" SCRIPT_NAME="$(basename "$SCRIPT_NAME")" EXTRACTOR="$SCRIPT_DIR/extract_types.py" MERGER="$SCRIPT_DIR/merge_jsons.py" diff --git a/scripts/type_extractor/gen_windows_and_windrivers_jsons.sh b/scripts/type_extractor/gen_windows_and_windrivers_jsons.sh index 136c3682..efcffdbb 100755 --- a/scripts/type_extractor/gen_windows_and_windrivers_jsons.sh +++ b/scripts/type_extractor/gen_windows_and_windrivers_jsons.sh @@ -6,7 +6,7 @@ # # Paths. # -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")"; pwd -P)" +SCRIPT_DIR="$(dirname "$(readlink -e "$0")")" SCRIPT_NAME="$(basename "$SCRIPT_NAME")" EXTRACTOR="$SCRIPT_DIR/extract_types.py" MERGER="$SCRIPT_DIR/merge_jsons.py" diff --git a/src/ar-extractor/archive_wrapper.cpp b/src/ar-extractor/archive_wrapper.cpp index e69bb2a4..93487bae 100644 --- a/src/ar-extractor/archive_wrapper.cpp +++ b/src/ar-extractor/archive_wrapper.cpp @@ -228,7 +228,7 @@ bool ArchiveWrapper::getJsonList( Value object(kObjectType); object.AddMember("name", Value(outName.c_str(), allocator).Move(), allocator); if (numbers) { - object.AddMember("index", (uint64_t) counter++, allocator); + object.AddMember("index", static_cast<uint64_t>(counter++), allocator); } objects.PushBack(object, allocator); } diff --git a/src/bin2llvmirtool/bin2llvmir.cpp b/src/bin2llvmirtool/bin2llvmir.cpp index c5625dbb..45bbb50c 100644 --- a/src/bin2llvmirtool/bin2llvmir.cpp +++ b/src/bin2llvmirtool/bin2llvmir.cpp @@ -35,7 +35,6 @@ #include <llvm/IRReader/IRReader.h> #include <llvm/InitializePasses.h> #include <llvm/LinkAllIR.h> -#include <llvm/LinkAllPasses.h> #include <llvm/MC/SubtargetFeature.h> #include <llvm/Support/Debug.h> #include <llvm/Support/FileSystem.h> @@ -272,34 +271,15 @@ static inline void addPassWithoutVerification( */ void initializeLlvmPasses() { - InitializeAllTargets(); - InitializeAllTargetMCs(); - InitializeAllAsmPrinters(); // Initialize passes PassRegistry &Registry = *PassRegistry::getPassRegistry(); initializeCore(Registry); initializeScalarOpts(Registry); - initializeObjCARCOpts(Registry); - initializeVectorization(Registry); initializeIPO(Registry); initializeAnalysis(Registry); initializeTransformUtils(Registry); initializeInstCombine(Registry); - initializeInstrumentation(Registry); initializeTarget(Registry); - // For codegen passes, only passes that do IR to IR transformation are - // supported. - initializeCodeGenPreparePass(Registry); - initializeAtomicExpandPass(Registry); - initializeRewriteSymbolsPass(Registry); - initializeWinEHPreparePass(Registry); - initializeDwarfEHPreparePass(Registry); - initializeSafeStackPass(Registry); - initializeSjLjEHPreparePass(Registry); - initializePreISelIntrinsicLoweringLegacyPassPass(Registry); - initializeGlobalMergePass(Registry); - initializeInterleavedAccessPass(Registry); - initializeUnreachableBlockElimLegacyPassPass(Registry); } /** diff --git a/src/llvmir-emul/llvmir_emul.cpp b/src/llvmir-emul/llvmir_emul.cpp index b6bd1914..78934376 100644 --- a/src/llvmir-emul/llvmir_emul.cpp +++ b/src/llvmir-emul/llvmir_emul.cpp @@ -6,18 +6,6 @@ #include <iostream> -#include <llvm/ADT/StringExtras.h> -#include <llvm/ADT/Triple.h> -#include <llvm/Bitcode/ReaderWriter.h> -#include <llvm/CodeGen/LinkAllCodegenComponents.h> -#include <llvm/ExecutionEngine/GenericValue.h> -#include <llvm/ExecutionEngine/Interpreter.h> -#include <llvm/ExecutionEngine/JITEventListener.h> -#include <llvm/ExecutionEngine/MCJIT.h> -#include <llvm/ExecutionEngine/ObjectCache.h> -#include <llvm/ExecutionEngine/Orc/OrcRemoteTargetClient.h> -#include <llvm/ExecutionEngine/OrcMCJITReplacement.h> -#include <llvm/ExecutionEngine/SectionMemoryManager.h> #include <llvm/IR/CallSite.h> #include <llvm/IR/GetElementPtrTypeIterator.h> #include <llvm/IR/IRBuilder.h> @@ -26,9 +14,6 @@ #include <llvm/IR/Module.h> #include <llvm/IR/Type.h> #include <llvm/IR/TypeBuilder.h> -#include <llvm/IRReader/IRReader.h> -#include <llvm/Object/Archive.h> -#include <llvm/Object/ObjectFile.h> #include <llvm/Support/CommandLine.h> #include <llvm/Support/Debug.h> #include <llvm/Support/DynamicLibrary.h> @@ -46,7 +31,6 @@ #include <llvm/Support/SourceMgr.h> #include <llvm/Support/TargetSelect.h> #include <llvm/Support/raw_ostream.h> -#include <llvm/Transforms/Instrumentation.h> #include "retdec/llvmir-emul/llvmir_emul.h" @@ -3071,10 +3055,9 @@ void LlvmIrEmulator::visitCallInst(llvm::CallInst& I) if (cf && cf->isDeclaration() && cf->isIntrinsic() && cf->getIntrinsicID() != Intrinsic::fabs) // can not lower fabs { - auto iId = cf->getIntrinsicID(); - assert(iId != Intrinsic::vastart - && iId != Intrinsic::vaend - && iId != Intrinsic::vacopy); + assert(cf->getIntrinsicID() != Intrinsic::vastart + && cf->getIntrinsicID() != Intrinsic::vaend + && cf->getIntrinsicID() != Intrinsic::vacopy); BasicBlock::iterator me(&I); BasicBlock *Parent = I.getParent(); diff --git a/src/unpacker/dynamic_buffer.cpp b/src/unpacker/dynamic_buffer.cpp index 4bc96d75..ff54be27 100644 --- a/src/unpacker/dynamic_buffer.cpp +++ b/src/unpacker/dynamic_buffer.cpp @@ -82,7 +82,7 @@ DynamicBuffer::~DynamicBuffer() * * @param rhs Right hand side of the operator. * - * @return The new DynamicBuffer opbject. + * @return The new DynamicBuffer object. */ DynamicBuffer& DynamicBuffer::operator =(DynamicBuffer rhs) { diff --git a/src/utils/filesystem_path.cpp b/src/utils/filesystem_path.cpp index f4988f8e..937f61fe 100644 --- a/src/utils/filesystem_path.cpp +++ b/src/utils/filesystem_path.cpp @@ -5,9 +5,11 @@ */ #include <string> +#include <iterator> #include "retdec/utils/filesystem_path.h" #include "retdec/utils/os.h" +#include "retdec/utils/scope_exit.h" #include "retdec/utils/string.h" #include "retdec/utils/value.h" @@ -27,27 +29,14 @@ namespace utils { class FilesystemPathImpl { public: - FilesystemPathImpl(const std::string& path) - : _path(), _parentPath(), _subpaths(), _exists(), _isFile(), _isDirectory(), _isAbsolute() + FilesystemPathImpl(const std::string& path) : _path() { changePath(path); } - FilesystemPathImpl(const FilesystemPathImpl& rhs) - : _path(rhs._path), _parentPath(rhs._parentPath), _subpaths(rhs._subpaths), _exists(rhs._exists), - _isFile(rhs._isFile), _isDirectory(rhs._isDirectory), _isAbsolute(rhs._isAbsolute) {} + FilesystemPathImpl(const FilesystemPathImpl& rhs) : _path(rhs._path) {} virtual ~FilesystemPathImpl() = default; - void reset() - { - _parentPath = {}; - _subpaths = {}; - _exists = {}; - _isFile = {}; - _isDirectory = {}; - _isAbsolute = {}; - } - /** * Returns the path. * @@ -67,7 +56,6 @@ public: */ void changePath(std::string path) { - reset(); std::replace(path.begin(), path.end(), '/', pathSeparator); _path = endsWith(path, pathSeparator) ? path.substr(0, path.length() - 1) : path; } @@ -84,9 +72,6 @@ public: protected: std::string _path; - Maybe<std::string> _absolutePath, _parentPath; - Maybe<std::vector<std::string>> _subpaths; - Maybe<bool> _exists, _isFile, _isDirectory, _isAbsolute; }; #ifdef OS_WINDOWS @@ -100,42 +85,27 @@ public: virtual std::string getAbsolutePath() override { - if (_absolutePath.isDefined()) - return _absolutePath; - char absolutePath[MAX_PATH] = { '\0' }; if (GetFullPathName(_path.c_str(), MAX_PATH, absolutePath, nullptr) == 0) return {}; - _absolutePath = std::string(absolutePath); - return _absolutePath; + return absolutePath; } virtual std::string getParentPath() override { - if (_parentPath.isDefined()) - return _parentPath; - // PathRemoveFileSpec() supports only MAX_PATH long paths and modify its parameter char parentPathStr[MAX_PATH] = { '\0' }; strncpy(parentPathStr, _path.c_str(), MAX_PATH - 1); PathRemoveFileSpec(parentPathStr); - - _parentPath = std::string(parentPathStr); - return _parentPath; + return parentPathStr; } virtual bool subpathsInDirectory(std::vector<std::string>& subpaths) override { using namespace std::string_literals; - if (_subpaths.isDefined()) - { - subpaths = _subpaths.getValue(); - return true; - } - WIN32_FIND_DATA ffd; // We need to add wildcard to examine the content of the directory @@ -145,10 +115,7 @@ public: subpaths.clear(); HANDLE hFnd = FindFirstFile(examineDir.c_str(), &ffd); if (hFnd == reinterpret_cast<HANDLE>(-1)) - { - _subpaths = std::vector<std::string>{}; return false; - } do { @@ -164,55 +131,31 @@ public: subpaths.emplace_back(newPath); } while (FindNextFile(hFnd, &ffd)); - _subpaths = subpaths; return true; } virtual bool exists() override { - if (_exists.isDefined()) - return _exists; - - _exists = PathFileExists(_path.c_str()); - return _exists; + return PathFileExists(_path.c_str()); } virtual bool isFile() override { - if (_isFile.isDefined()) - return _isFile; - - isDirectory(); - return _isFile; + return !isDirectory(); } virtual bool isDirectory() override { - if (_isDirectory.isDefined()) - return _isDirectory; - WIN32_FIND_DATA ffd; if (FindFirstFile(_path.c_str(), &ffd) == reinterpret_cast<HANDLE>(-1)) - { - _exists = false; - _isDirectory = false; - _isFile = false; return false; - } - _exists = true; - _isDirectory = ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; - _isFile = !_isDirectory; - return _isDirectory; + return ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; } virtual bool isAbsolute() override { - if (_isAbsolute.isDefined()) - return _isAbsolute; - - _isAbsolute = !PathIsRelative(_path.c_str()); - return _isAbsolute; + return !PathIsRelative(_path.c_str()); } }; #else @@ -226,43 +169,36 @@ public: virtual std::string getAbsolutePath() override { - if (_absolutePath.isDefined()) - return _absolutePath; - +#ifdef PATH_MAX char absolutePath[PATH_MAX] = { '\0' }; if (realpath(_path.c_str(), absolutePath) == nullptr) return {}; +#else + char* absolutePathStr = realpath(_path.c_str(), nullptr); + SCOPE_EXIT { + free(absolutePathStr); + }; + std::string absolutePath = absolutePathStr; +#endif - _absolutePath = std::string(absolutePath); - return _absolutePath; + return absolutePath; } virtual std::string getParentPath() override { - if (_parentPath.isDefined()) - return _parentPath; - // dirname() can modify the path provided in parameter, so we need to make copy char* copyPathStr = new char[_path.length() + 1]; + SCOPE_EXIT { + delete[] copyPathStr; + }; strcpy(copyPathStr, _path.c_str()); // get the parent directory by calling dirname() - char* parentPathStr = dirname(copyPathStr); - - // copy the parent path into the string, so we can free the memory - _parentPath = std::string(parentPathStr); - delete[] copyPathStr; - return _parentPath; + return dirname(copyPathStr); } virtual bool subpathsInDirectory(std::vector<std::string>& subpaths) override { - if (_subpaths.isDefined()) - { - subpaths = _subpaths.getValue(); - return true; - } - subpaths.clear(); DIR* dir = opendir(_path.c_str()); if (dir == nullptr) @@ -284,46 +220,27 @@ public: } closedir(dir); - _subpaths = subpaths; return true; } virtual bool exists() override { - if (_exists.isDefined()) - return _exists; - - isDirectory(); - return _exists; + struct stat st; + return stat(_path.c_str(), &st) != 0; } virtual bool isFile() override { - if (_isFile.isDefined()) - return _isFile; - - isDirectory(); - return _isFile; + return !isDirectory(); } virtual bool isDirectory() override { - if (_isDirectory.isDefined()) - return _isDirectory; - struct stat st; if (stat(_path.c_str(), &st) != 0) - { - _exists = false; - _isDirectory = false; - _isFile = false; return false; - } - _exists = true; - _isDirectory = S_ISDIR(st.st_mode); - _isFile = S_ISREG(st.st_mode); - return _isDirectory; + return S_ISDIR(st.st_mode); } virtual bool isAbsolute() override @@ -528,11 +445,10 @@ void FilesystemPath::loadSubpaths() const if (!_impl->subpathsInDirectory(subpaths)) return; - for (const auto& subpath : subpaths) - { - auto fsSubpath = std::make_unique<FilesystemPath>(subpath); - _subpaths.push_back(std::move(fsSubpath)); - } + std::transform(subpaths.begin(), subpaths.end(), std::back_inserter(_subpaths), + [](const auto& subpath) { + return std::make_unique<FilesystemPath>(subpath); + }); _subpathsLoaded = true; } diff --git a/src/utils/time.cpp b/src/utils/time.cpp index 7a4ad61a..6cc280f3 100644 --- a/src/utils/time.cpp +++ b/src/utils/time.cpp @@ -85,20 +85,17 @@ std::string getCurrentYear() { * @brief Returns date in human readable form. * @param tm Timestamp for conversion. */ -std::string timestampToDate(std::tm *tm) -{ - if(!tm) { +std::string timestampToDate(std::tm *tm) { + if (!tm) { return ""; } const auto conDate = getDate(tm); const auto conTime = getTime(tm); if (conDate.empty() && conTime.empty()) { return ""; - } - else if (conDate.empty()) { + } else if (conDate.empty()) { return conTime; - } - else if (conTime.empty()) { + } else if (conTime.empty()) { return conDate; } diff --git a/support/README b/support/README new file mode 100644 index 00000000..5a3cf7d1 --- /dev/null +++ b/support/README @@ -0,0 +1 @@ +Compilation and installation of YARA rules is done in deps/yaracpp. diff --git a/support/yara_patterns/tools/compile-yara.sh b/support/yara_patterns/tools/compile-yara.sh new file mode 100644 index 00000000..72ca0f27 --- /dev/null +++ b/support/yara_patterns/tools/compile-yara.sh @@ -0,0 +1,112 @@ +#!/bin/sh +# +# Compile and install tool signatures. +# Usage: compile-yara.sh yarac-path source-path install-path +# + +############################################################################### + +# Print error and exit with non-zero value. +# $1 - error message. +printErrorAndDie() +{ + if ! [ -z "$1" ]; then + echo "Error: $1." + fi + exit 1 +} + +# Compile yara signatures. +# $1 - input folder +# $2 - output file +compileFiles() +{ + ERR_OUT="$("$CC" -w "$1"/*.yara "$2" 2>&1)" + if [ $? -ne 0 ]; then + printErrorAndDie "yarac failed during compilation of file $1" + fi + + # Check for errors in output - yarac returns 0 when it should not. + case "$ERR_OUT" in + *error*) + printErrorAndDie "yarac failed during compilation of file $1" + ;; +esac +} + +############################################################################### + +CC="$1" +if [ -z "$CC" ]; then + printErrorAndDie "missing path to yarac" +fi + +SRC="$2" +if [ -z "$SRC" ]; then + printErrorAndDie "missing path to rules folder" +fi + +OUT="$3" +if [ -z "$OUT" ]; then + printErrorAndDie "missing path to install folder" +fi + +############################################################################### + +# Convert from Windows to Unix path on Windows. +case "$(uname -s)" in + *Windows*|*CYGWIN*|*MINGW*|*MSYS*) + CC="$(echo "/$CC" | sed -e 's/\\/\//g' -e 's/://')" + SRC="$(echo "/$SRC" | sed -e 's/\\/\//g' -e 's/://')" + OUT="$(echo "/$OUT" | sed -e 's/\\/\//g' -e 's/://')" + ;; +esac + +# Directory paths. +SRC="$SRC/support/yara_patterns/tools" +OUT="$OUT/share/retdec/support/generic/yara_patterns/tools" + +############################################################################### + +# Remove old files if present. +rm -rf "$OUT" + +# Prepare directory structure. +mkdir -p "$OUT/pe" +mkdir -p "$OUT/elf" +mkdir -p "$OUT/macho" + +############################################################################### + +echo "compiling yara signatures..." + +## Compile PE32 signatures. +compileFiles "$SRC/pe/x86" "$OUT/pe/x86.yarac" +compileFiles "$SRC/pe/arm" "$OUT/pe/arm.yarac" + +## Compile PE32+ signatures. +compileFiles "$SRC/pe/x64" "$OUT/pe/x64.yarac" + +## Compile ELF signatures. +compileFiles "$SRC/elf/x86" "$OUT/elf/x86.yarac" +compileFiles "$SRC/elf/arm" "$OUT/elf/arm.yarac" +compileFiles "$SRC/elf/ppc" "$OUT/elf/ppc.yarac" +compileFiles "$SRC/elf/mips" "$OUT/elf/mips.yarac" + +## Compile ELF64 signatures. +compileFiles "$SRC/elf/x64" "$OUT/elf/x64.yarac" +compileFiles "$SRC/elf/arm64" "$OUT/elf/arm64.yarac" +compileFiles "$SRC/elf/ppc64" "$OUT/elf/ppc64.yarac" +compileFiles "$SRC/elf/mips64" "$OUT/elf/mips64.yarac" + +## Compile Mach-O signatures. +compileFiles "$SRC/macho/x86" "$OUT/macho/x86.yarac" +compileFiles "$SRC/macho/arm" "$OUT/macho/arm.yarac" +compileFiles "$SRC/macho/ppc" "$OUT/macho/ppc.yarac" + +## Compile 64-bit Mach-O signatures. +compileFiles "$SRC/macho/x64" "$OUT/macho/x64.yarac" +compileFiles "$SRC/macho/ppc64" "$OUT/macho/ppc64.yarac" + +echo "signatures compiled successfully" +exit diff --git a/tests/utils/time_tests.cpp b/tests/utils/time_tests.cpp index 30a62d36..a2b17303 100644 --- a/tests/utils/time_tests.cpp +++ b/tests/utils/time_tests.cpp @@ -4,8 +4,13 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ +#include <cstdlib> +#include <ctime> +#include <string> + #include <gtest/gtest.h> +#include "retdec/utils/os.h" #include "retdec/utils/time.h" using namespace ::testing; @@ -17,7 +22,58 @@ namespace tests { /** * @brief Tests for the @c time module. */ -class TimeTests: public Test {}; +class TimeTests: public Test { +protected: + virtual void SetUp() override { + // We have to force a specific timezone to make the tests + // deterministic (#90). + changeTimezoneToUTC(); + } + + virtual void TearDown() override { + changeTimezoneBackToOriginalValue(); + } + + void changeTimezoneToUTC() { + // Store the original timezone so we can set it back in + // changeTimezoneBackToOriginalValue(). + // Both Windows and POSIX use the same variable. + const auto TZ = std::getenv("TZ"); + if (TZ) { + originalTZ = TZ; + } + + #ifdef OS_WINDOWS + _putenv("TZ=UTC"); + _tzset(); + #else + setenv("TZ", "UTC", /*overwrite=*/1); + tzset(); + #endif + } + + void changeTimezoneBackToOriginalValue() { + #ifdef OS_WINDOWS + // Calling _putenv() specifying "VAR=" as a parameter (i.e. without + // the value) deletes the environment variable, which is what we + // want if originalTZ is empty. + const auto TZ_ENV = "TZ=" + originalTZ; + _putenv(TZ_ENV.c_str()); + _tzset(); + #else + if (originalTZ.empty()) { + unsetenv("TZ"); + } else { + setenv("TZ", originalTZ.c_str(), /*overwrite=*/1); + } + tzset(); + #endif + } + +private: + // Originally set timezone. + std::string originalTZ; +}; // // timestampToDate() @@ -25,7 +81,7 @@ class TimeTests: public Test {}; TEST_F(TimeTests, CorrectTimestampToDateConversion) { - EXPECT_EQ("2015-08-05 16:25:19", timestampToDate(std::time_t(1438784719))); + EXPECT_EQ("2015-08-05 14:25:19", timestampToDate(std::time_t(1438784719))); } } // namespace tests