Imported Upstream version 2.6.2 upstream upstream/2.6.2
authorDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 3 Apr 2024 02:43:08 +0000 (11:43 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 3 Apr 2024 02:43:08 +0000 (11:43 +0900)
94 files changed:
CMake.README
CMakeLists.txt
Changes
ConfigureChecks.cmake
Makefile.am
Makefile.in
README.md
acinclude.m4
aclocal.m4
cmake/autotools/expat-config-version.cmake.in
cmake/autotools/expat.cmake
configure
configure.ac
conftools/ax-cxx-compile-stdcxx-11.m4 [new file with mode: 0644]
conftools/ax-cxx-compile-stdcxx.m4 [new file with mode: 0644]
doc/Makefile.am
doc/Makefile.in
doc/ok.min.css
doc/reference.html
doc/xmlwf.1
doc/xmlwf.xml
examples/Makefile.am
examples/Makefile.in
examples/element_declarations.c [new file with mode: 0644]
expat.pc.cmake
expat.pc.in
expat_config.h
expat_config.h.cmake
expat_config.h.in
fuzz/xml_parse_fuzzer.c
fuzz/xml_parsebuffer_fuzzer.c
lib/Makefile.am
lib/Makefile.in
lib/expat.h
lib/internal.h
lib/libexpat.def.cmake
lib/siphash.h
lib/winconfig.h
lib/xmlparse.c
lib/xmlrole.c
lib/xmlrole.h
lib/xmltok.c
lib/xmltok.h
lib/xmltok_impl.c
tests/Makefile.am
tests/Makefile.in
tests/acc_tests.c [new file with mode: 0644]
tests/acc_tests.h [new file with mode: 0644]
tests/acc_tests_cxx.cpp [new file with mode: 0644]
tests/alloc_tests.c [new file with mode: 0644]
tests/alloc_tests.h [new file with mode: 0644]
tests/alloc_tests_cxx.cpp [new file with mode: 0644]
tests/basic_tests.c [new file with mode: 0644]
tests/basic_tests.h [new file with mode: 0644]
tests/basic_tests_cxx.cpp [new file with mode: 0644]
tests/benchmark/Makefile.in
tests/benchmark/benchmark.c
tests/chardata.c
tests/chardata_cxx.cpp [new file with mode: 0644]
tests/common.c [new file with mode: 0644]
tests/common.h [new file with mode: 0644]
tests/common_cxx.cpp [new file with mode: 0644]
tests/dummy.c [new file with mode: 0644]
tests/dummy.h [new file with mode: 0644]
tests/dummy_cxx.cpp [new file with mode: 0644]
tests/handlers.c [new file with mode: 0644]
tests/handlers.h [new file with mode: 0644]
tests/handlers_cxx.cpp [new file with mode: 0644]
tests/memcheck.c
tests/memcheck_cxx.cpp [new file with mode: 0644]
tests/minicheck.c
tests/minicheck.h
tests/minicheck_cxx.cpp [new file with mode: 0644]
tests/misc_tests.c [new file with mode: 0644]
tests/misc_tests.h [new file with mode: 0644]
tests/misc_tests_cxx.cpp [new file with mode: 0644]
tests/ns_tests.c [new file with mode: 0644]
tests/ns_tests.h [new file with mode: 0644]
tests/ns_tests_cxx.cpp [new file with mode: 0644]
tests/nsalloc_tests.c [new file with mode: 0644]
tests/nsalloc_tests.h [new file with mode: 0644]
tests/nsalloc_tests_cxx.cpp [new file with mode: 0644]
tests/runtests.c
tests/runtests_cxx.cpp [moved from tests/runtestspp.cpp with 91% similarity]
tests/structdata.c
tests/structdata_cxx.cpp [new file with mode: 0644]
win32/expat.iss
win32/version.rc.cmake [moved from win32/version.rc with 69% similarity]
xmlwf/Makefile.in
xmlwf/readfilemap.c
xmlwf/xmlfile.c
xmlwf/xmlfile.h
xmlwf/xmlwf.c
xmlwf/xmlwf_helpgen.py

index 2b94fff..5d5f43e 100644 (file)
@@ -3,25 +3,25 @@
 The cmake based buildsystem for expat works on Windows (cygwin, mingw, Visual
 Studio) and should work on all other platform cmake supports.
 
-Assuming ~/expat-2.5.0 is the source directory of expat, add a subdirectory
+Assuming ~/expat-2.6.2 is the source directory of expat, add a subdirectory
 build and change into that directory:
-~/expat-2.5.0$ mkdir build && cd build
-~/expat-2.5.0/build$
+~/expat-2.6.2$ mkdir build && cd build
+~/expat-2.6.2/build$
 
 From that directory, call cmake first, then call make, make test and
 make install in the usual way:
-~/expat-2.5.0/build$ cmake ..
+~/expat-2.6.2/build$ cmake ..
 -- The C compiler identification is GNU
 -- The CXX compiler identification is GNU
 ....
 -- Configuring done
 -- Generating done
--- Build files have been written to: /home/patrick/expat-2.5.0/build
+-- Build files have been written to: /home/patrick/expat-2.6.2/build
 
 If you want to specify the install location for your files, append
 -DCMAKE_INSTALL_PREFIX=/your/install/path to the cmake call.
 
-~/expat-2.5.0/build$ make && make test && make install
+~/expat-2.6.2/build$ make && make test && make install
 Scanning dependencies of target expat
 [  5%] Building C object CMakeFiles/expat.dir/lib/xmlparse.c.o
 [ 11%] Building C object CMakeFiles/expat.dir/lib/xmlrole.c.o
index 2b4c13c..ff08155 100644 (file)
@@ -7,12 +7,12 @@
 #
 # Copyright (c) 2010      Patrick Spendrin <ps_ml@gmx.de>
 # Copyright (c) 2012      Karl Waclawek <karl@waclawek.net>
-# Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
 # Copyright (c) 2016      Sergei Nikulov <sergey.nikulov@gmail.com>
 # Copyright (c) 2016      Björn Lindahl <bjorn.lindahl@foi.se>
 # Copyright (c) 2016      Tobias Taschner <github@tc84.de>
 # Copyright (c) 2016      Ben Boeckel <ben.boeckel@kitware.com>
-# Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+# Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
 # Copyright (c) 2017      Rolf Eike Beer <eike@sf-mail.de>
 # Copyright (c) 2017      Stephen Groat <stephen@groat.us>
 # Copyright (c) 2017      Franek Korta <fkorta@gmail.com>
 # Unlike most of Expat,
 # this file is copyrighted under the BSD-license for buildsystem files of KDE.
 
-cmake_minimum_required(VERSION 3.1.3)
+cmake_minimum_required(VERSION 3.5.0)
 
 project(expat
     VERSION
-        2.5.0
+        2.6.2
     LANGUAGES
         C
 )
+set(CMAKE_C_STANDARD 99)
+set(CMAKE_C_STANDARD_REQUIRED ON)
+set(CMAKE_C_EXTENSIONS OFF)  # i.e. -std=c99 rather than default -std=gnu99
 
-set(PACKAGE_BUGREPORT "expat-bugs@libexpat.org")
+set(PACKAGE_BUGREPORT "https://github.com/libexpat/libexpat/issues")
 set(PACKAGE_NAME "expat")
 set(PACKAGE_VERSION "${PROJECT_VERSION}")
 set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
@@ -136,10 +139,12 @@ if(UNIX OR _EXPAT_HELP)
     expat_shy_set(EXPAT_WITH_LIBBSD OFF CACHE BOOL "Utilize libbsd (for arc4random_buf)")
 endif()
 expat_shy_set(EXPAT_ENABLE_INSTALL ON CACHE BOOL "Install expat files in cmake install target")
-expat_shy_set(EXPAT_CONTEXT_BYTES 1024 CACHE STRING "Define to specify how much context to retain around the current parse point")
+expat_shy_set(EXPAT_CONTEXT_BYTES 1024 CACHE STRING "Define to specify how much context to retain around the current parse point, 0 to disable")
 mark_as_advanced(EXPAT_CONTEXT_BYTES)
 expat_shy_set(EXPAT_DTD ON CACHE BOOL "Define to make parameter entity parsing functionality available")
 mark_as_advanced(EXPAT_DTD)
+expat_shy_set(EXPAT_GE ON CACHE BOOL "Define to make general entity parsing functionality available")
+mark_as_advanced(EXPAT_GE)
 expat_shy_set(EXPAT_NS ON CACHE BOOL "Define to make XML Namespaces functionality available")
 mark_as_advanced(EXPAT_NS)
 expat_shy_set(EXPAT_WARNINGS_AS_ERRORS OFF CACHE BOOL "Treat all compiler warnings as errors")
@@ -167,11 +172,20 @@ endif()
 if(EXPAT_BUILD_TESTS)
     # We have to call enable_language() before modifying any CMAKE_CXX_* variables
     enable_language(CXX)
+
+    set(CMAKE_CXX_STANDARD 11)
+    set(CMAKE_CXX_STANDARD_REQUIRED ON)
+    set(CMAKE_CXX_EXTENSIONS OFF)  # i.e. -std=c++11 rather than default -std=gnu++11
 endif()
 
 #
 # Environment checks
 #
+if(EXPAT_DTD AND NOT EXPAT_GE)
+    message(SEND_ERROR "Option EXPAT_DTD requires that EXPAT_GE is also enabled.")
+    message(SEND_ERROR "Please either enable option EXPAT_GE (recommended) or disable EXPAT_DTD also.")
+endif()
+
 if(EXPAT_WITH_LIBBSD)
     find_library(LIB_BSD NAMES bsd)
     if(NOT LIB_BSD)
@@ -274,12 +288,16 @@ endif()
 
 _expat_copy_bool_int(EXPAT_ATTR_INFO        XML_ATTR_INFO)
 _expat_copy_bool_int(EXPAT_DTD              XML_DTD)
+_expat_copy_bool_int(EXPAT_GE               XML_GE)
 _expat_copy_bool_int(EXPAT_LARGE_SIZE       XML_LARGE_SIZE)
 _expat_copy_bool_int(EXPAT_MIN_SIZE         XML_MIN_SIZE)
 _expat_copy_bool_int(EXPAT_NS               XML_NS)
 if(NOT WIN32)
     _expat_copy_bool_int(EXPAT_DEV_URANDOM  XML_DEV_URANDOM)
 endif()
+if(NOT EXPAT_CONTEXT_BYTES GREATER 0)  # in particular with -DEXPAT_CONTEXT_BYTES=OFF
+    set(EXPAT_CONTEXT_BYTES 0)
+endif()
 set(XML_CONTEXT_BYTES ${EXPAT_CONTEXT_BYTES})
 
 macro(expat_install)
@@ -311,6 +329,10 @@ if (EXPAT_WARNINGS_AS_ERRORS)
         add_definitions(/WX)
     else()
         set(EXTRA_COMPILE_FLAGS "${EXTRA_COMPILE_FLAGS} -Werror")
+        if(MINGW)
+            # To avoid "error: unknown conversion type character ‘l’ in format [-Werror=format=]"
+            set(EXTRA_COMPILE_FLAGS "${EXTRA_COMPILE_FLAGS} -Wno-format")
+        endif()
     endif()
 endif()
 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_COMPILE_FLAGS}")
@@ -381,7 +403,13 @@ if(EXPAT_SHARED_LIBS)
             endif()
         endmacro()
 
-        _expat_def_file_toggle(EXPAT_DTD _EXPAT_COMMENT_DTD)
+        if(EXPAT_DTD OR EXPAT_GE)
+            set(_EXPAT_DTD_OR_GE TRUE)
+        else()
+            set(_EXPAT_DTD_OR_GE FALSE)
+        endif()
+
+        _expat_def_file_toggle(_EXPAT_DTD_OR_GE _EXPAT_COMMENT_DTD_OR_GE)
         _expat_def_file_toggle(EXPAT_ATTR_INFO _EXPAT_COMMENT_ATTR_INFO)
 
         configure_file("${CMAKE_CURRENT_SOURCE_DIR}/lib/libexpat.def.cmake" "${CMAKE_CURRENT_BINARY_DIR}/lib/libexpat.def")
@@ -389,7 +417,8 @@ if(EXPAT_SHARED_LIBS)
 
         # Add DLL version
         string(REPLACE "." "," _EXPAT_DLL_VERSION ${PROJECT_VERSION}.0)
-        set(_EXPAT_EXTRA_SOURCES ${_EXPAT_EXTRA_SOURCES} win32/version.rc)
+        configure_file("${CMAKE_CURRENT_SOURCE_DIR}/win32/version.rc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/win32/version.rc")
+        set(_EXPAT_EXTRA_SOURCES ${_EXPAT_EXTRA_SOURCES} "${CMAKE_CURRENT_BINARY_DIR}/win32/version.rc")
     endif()
 else()
     set(_SHARED STATIC)
@@ -436,9 +465,9 @@ foreach(build_type_upper
     set_property(TARGET expat PROPERTY ${build_type_upper}_POSTFIX ${EXPAT_${build_type_upper}_POSTFIX})
 endforeach()
 
-set(LIBCURRENT 9)    # sync
-set(LIBREVISION 10)  # with
-set(LIBAGE 8)        # configure.ac!
+set(LIBCURRENT 10)  # sync
+set(LIBREVISION 2)  # with
+set(LIBAGE 9)       # configure.ac!
 math(EXPR LIBCURRENT_MINUS_AGE "${LIBCURRENT} - ${LIBAGE}")
 
 if(NOT WIN32)
@@ -451,7 +480,7 @@ if(NOT WIN32)
             message(FATAL_ERROR "Expat requires CMake >=3.17 on platform \"APPLE\".")
         endif()
 
-        # NOTE: This intends to talk CMake into compatiblity with GNU Libtool
+        # NOTE: This intends to talk CMake into compatibility with GNU Libtool
         math(EXPR _EXPAT_MACHO_COMPATIBILITY_VERSION "${LIBCURRENT} + 1")
         set(_EXPAT_MACHO_CURRENT_VERSION "${_EXPAT_MACHO_COMPATIBILITY_VERSION}.${LIBREVISION}")
         set_property(TARGET expat PROPERTY MACHO_COMPATIBILITY_VERSION ${_EXPAT_MACHO_COMPATIBILITY_VERSION})
@@ -541,10 +570,10 @@ endif()
 #
 if(EXPAT_BUILD_TOOLS)
     set(xmlwf_SRCS
-        xmlwf/xmlwf.c
-        xmlwf/xmlfile.c
         xmlwf/codepage.c
         xmlwf/readfilemap.c
+        xmlwf/xmlfile.c
+        xmlwf/xmlwf.c
     )
 
     add_executable(xmlwf ${xmlwf_SRCS})
@@ -579,10 +608,8 @@ endif()
 # C code examples
 #
 if(EXPAT_BUILD_EXAMPLES)
-    add_executable(elements examples/elements.c)
-    add_executable(outline examples/outline.c)
-
-    foreach(_target elements outline)
+    foreach(_target element_declarations elements outline)
+        add_executable(${_target} examples/${_target}.c)
         set_property(TARGET ${_target} PROPERTY RUNTIME_OUTPUT_DIRECTORY examples)
         target_link_libraries(${_target} expat)
     endforeach()
@@ -595,14 +622,6 @@ if(EXPAT_BUILD_TESTS)
     ## these are unittests that can be run on any platform
     enable_testing()
 
-    set(test_SRCS
-        tests/chardata.c
-        tests/memcheck.c
-        tests/minicheck.c
-        tests/structdata.c
-        ${_EXPAT_C_SOURCES}
-    )
-
     if(NOT MSVC)
         if(MINGW)
             set(host whatever-mingw32)  # for nothing but run.sh
@@ -618,11 +637,47 @@ if(EXPAT_BUILD_TESTS)
         endif()
     endfunction()
 
-    set(_EXPAT_TEST_TARGETS runtests runtestspp)
-    add_executable(runtests   tests/runtests.c     ${test_SRCS})
-    add_executable(runtestspp tests/runtestspp.cpp ${test_SRCS})
+    set(_EXPAT_TEST_TARGETS runtests runtests_cxx)
+
+    add_executable(runtests
+        tests/acc_tests.c
+        tests/alloc_tests.c
+        tests/basic_tests.c
+        tests/chardata.c
+        tests/common.c
+        tests/dummy.c
+        tests/handlers.c
+        tests/memcheck.c
+        tests/minicheck.c
+        tests/misc_tests.c
+        tests/ns_tests.c
+        tests/nsalloc_tests.c
+        tests/runtests.c
+        tests/structdata.c
+        ${_EXPAT_C_SOURCES}
+    )
+
+    add_executable(runtests_cxx
+        tests/acc_tests_cxx.cpp
+        tests/alloc_tests_cxx.cpp
+        tests/basic_tests_cxx.cpp
+        tests/chardata_cxx.cpp
+        tests/common_cxx.cpp
+        tests/dummy_cxx.cpp
+        tests/handlers_cxx.cpp
+        tests/memcheck_cxx.cpp
+        tests/minicheck_cxx.cpp
+        tests/misc_tests_cxx.cpp
+        tests/ns_tests_cxx.cpp
+        tests/nsalloc_tests_cxx.cpp
+        tests/runtests_cxx.cpp
+        tests/structdata_cxx.cpp
+        ${_EXPAT_C_SOURCES}
+    )
 
     foreach(_target ${_EXPAT_TEST_TARGETS})
+        target_compile_definitions(${_target} PRIVATE -DXML_TESTING)
+
         set_property(TARGET ${_target} PROPERTY RUNTIME_OUTPUT_DIRECTORY tests)
         expat_add_test(${_target} $<TARGET_FILE:${_target}>)
 
@@ -634,6 +689,10 @@ if(EXPAT_BUILD_TESTS)
             target_link_libraries(${_target} ${LIB_BSD})
         endif()
     endforeach()
+
+    add_executable(benchmark tests/benchmark/benchmark.c)
+    set_property(TARGET benchmark PROPERTY RUNTIME_OUTPUT_DIRECTORY tests/benchmark)
+    target_link_libraries(benchmark expat)
 endif()
 
 #
@@ -893,6 +952,7 @@ message(STATUS "    // Advanced options, changes not advised")
 message(STATUS "    Attributes info .......... ${EXPAT_ATTR_INFO}")
 message(STATUS "    Context bytes ............ ${EXPAT_CONTEXT_BYTES}")
 message(STATUS "    DTD support .............. ${EXPAT_DTD}")
+message(STATUS "    General entities ......... ${EXPAT_GE}")
 message(STATUS "    Large size ............... ${EXPAT_LARGE_SIZE}")
 message(STATUS "    Minimum size ............. ${EXPAT_MIN_SIZE}")
 message(STATUS "    Namespace support ........ ${EXPAT_NS}")
diff --git a/Changes b/Changes
index e671710..52b366d 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,6 +1,196 @@
-NOTE: We are looking for help with a few things:
-      https://github.com/libexpat/libexpat/labels/help%20wanted
-      If you can help, please get in touch.  Thanks!
+                           __  __            _
+                        ___\ \/ /_ __   __ _| |_
+                       / _ \\  /| '_ \ / _` | __|
+                      |  __//  \| |_) | (_| | |_
+                       \___/_/\_\ .__/ \__,_|\__|
+                                |_| XML parser
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!! <blink>Expat is UNDERSTAFFED and WITHOUT FUNDING.</blink>                 !!
+!!                 ~~~~~~~~~~~~                                              !!
+!! The following topics need *additional skilled C developers* to progress   !!
+!! in a timely manner or at all (loosely ordered by descending priority):    !!
+!!                                                                           !!
+!! - <blink>fixing a complex non-public security issue</blink>,              !!
+!! - teaming up on researching and fixing future security reports and        !!
+!!   ClusterFuzz findings with few-days-max response times in communication  !!
+!!   in order to (1) have a sound fix ready before the end of a 90 days      !!
+!!   grace period and (2) in a sustainable manner,                           !!
+!! - implementing and auto-testing XML 1.0r5 support                         !!
+!!   (needs discussion before pull requests),                                !!
+!! - smart ideas on fixing the Autotools CMake files generation issue        !!
+!!   without breaking CI (needs discussion before pull requests),            !!
+!! - the Windows binaries topic (needs requirements engineering first),      !!
+!! - pushing migration from `int` to `size_t` further                        !!
+!!   including edge-cases test coverage (needs discussion before anything).  !!
+!!                                                                           !!
+!! For details, please reach out via e-mail to sebastian@pipping.org so we   !!
+!! can schedule a voice call on the topic, in English or German.             !!
+!!                                                                           !!
+!! THANK YOU!                        Sebastian Pipping -- Berlin, 2024-03-09 !!
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+Release 2.6.2 Wed March 13 2024
+        Security fixes:
+       #839 #842  CVE-2024-28757 -- Prevent billion laughs attacks with
+                    isolated use of external parsers.  Please see the commit
+                    message of commit 1d50b80cf31de87750103656f6eb693746854aa8
+                    for details.
+
+        Bug fixes:
+       #839 #841  Reject direct parameter entity recursion
+                    and avoid the related undefined behavior
+
+        Other changes:
+            #847  Autotools: Fix build for DOCBOOK_TO_MAN containing spaces
+            #837  Add missing #821 and #824 to 2.6.1 change log
+       #838 #843  Version info bumped from 10:1:9 (libexpat*.so.1.9.1)
+                    to 10:2:9 (libexpat*.so.1.9.2); see https://verbump.de/
+                    for what these numbers do
+
+        Special thanks to:
+            Philippe Antoine
+            Tomas Korbar
+                 and
+            Clang UndefinedBehaviorSanitizer
+            OSS-Fuzz / ClusterFuzz
+
+Release 2.6.1 Thu February 29 2024
+        Bug fixes:
+            #817  Make tests independent of CPU speed, and thus more robust
+       #828 #836  Expose billion laughs API with XML_DTD defined and
+                    XML_GE undefined, regression from 2.6.0
+
+        Other changes:
+            #829  Hide test-only code behind new internal macro
+            #833  Autotools: Reject expat_config.h.in defining SIZEOF_VOID_P
+       #821 #824  Autotools: Fix "make clean" for case:
+                    ./configure --without-docbook && make clean all
+            #819  Address compiler warnings
+       #832 #834  Version info bumped from 10:0:9 (libexpat*.so.1.9.0)
+                    to 10:1:9 (libexpat*.so.1.9.1); see https://verbump.de/
+                    for what these numbers do
+
+        Infrastructure:
+            #818  CI: Adapt to breaking changes in clang-format
+
+        Special thanks to:
+            David Hall
+            Snild Dolkow
+
+Release 2.6.0 Tue February 6 2024
+        Security fixes:
+      #789 #814  CVE-2023-52425 -- Fix quadratic runtime issues with big tokens
+                   that can cause denial of service, in partial where
+                   dealing with compressed XML input.  Applications
+                   that parsed a document in one go -- a single call to
+                   functions XML_Parse or XML_ParseBuffer -- were not affected.
+                   The smaller the chunks/buffers you use for parsing
+                   previously, the bigger the problem prior to the fix.
+                   Backporters should be careful to no omit parts of
+                   pull request #789 and to include earlier pull request #771,
+                   in order to not break the fix.
+           #777  CVE-2023-52426 -- Fix billion laughs attacks for users
+                   compiling *without* XML_DTD defined (which is not common).
+                   Users with XML_DTD defined have been protected since
+                   Expat >=2.4.0 (and that was CVE-2013-0340 back then).
+
+        Bug fixes:
+            #753  Fix parse-size-dependent "invalid token" error for
+                    external entities that start with a byte order mark
+            #780  Fix NULL pointer dereference in setContext via
+                    XML_ExternalEntityParserCreate for compilation with
+                    XML_DTD undefined
+       #812 #813  Protect against closing entities out of order
+
+        Other changes:
+            #723  Improve support for arc4random/arc4random_buf
+       #771 #788  Improve buffer growth in XML_GetBuffer and XML_Parse
+       #761 #770  xmlwf: Support --help and --version
+       #759 #770  xmlwf: Support custom buffer size for XML_GetBuffer and read
+            #744  xmlwf: Improve language and URL clickability in help output
+            #673  examples: Add new example "element_declarations.c"
+            #764  Be stricter about macro XML_CONTEXT_BYTES at build time
+            #765  Make inclusion to expat_config.h consistent
+       #726 #727  Autotools: configure.ac: Support --disable-maintainer-mode
+    #678 #705 ..
+  #706 #733 #792  Autotools: Sync CMake templates with CMake 3.26
+            #795  Autotools: Make installation of shipped man page doc/xmlwf.1
+                    independent of docbook2man availability
+            #815  Autotools|CMake: Add missing -DXML_STATIC to pkg-config file
+                    section "Cflags.private" in order to fix compilation
+                    against static libexpat using pkg-config on Windows
+       #724 #751  Autotools|CMake: Require a C99 compiler
+                    (a de-facto requirement already since Expat 2.2.2 of 2017)
+            #793  Autotools|CMake: Fix PACKAGE_BUGREPORT variable
+       #750 #786  Autotools|CMake: Make test suite require a C++11 compiler
+            #749  CMake: Require CMake >=3.5.0
+            #672  CMake: Lowercase off_t and size_t to help a bug in Meson
+            #746  CMake: Sort xmlwf sources alphabetically
+            #785  CMake|Windows: Fix generation of DLL file version info
+            #790  CMake: Build tests/benchmark/benchmark.c as well for
+                    a build with -DEXPAT_BUILD_TESTS=ON
+       #745 #757  docs: Document the importance of isFinal + adjust tests
+                    accordingly
+            #736  docs: Improve use of "NULL" and "null"
+            #713  docs: Be specific about version of XML (XML 1.0r4)
+                    and version of C (C99); (XML 1.0r5 will need a sponsor.)
+            #762  docs: reference.html: Promote function XML_ParseBuffer more
+            #779  docs: reference.html: Add HTML anchors to XML_* macros
+            #760  docs: reference.html: Upgrade to OK.css 1.2.0
+       #763 #739  docs: Fix typos
+            #696  docs|CI: Use HTTPS URLs instead of HTTP at various places
+    #669 #670 ..
+    #692 #703 ..
+       #733 #772  Address compiler warnings
+       #798 #800  Address clang-tidy warnings
+       #775 #776  Version info bumped from 9:10:8 (libexpat*.so.1.8.10)
+                    to 10:0:9 (libexpat*.so.1.9.0); see https://verbump.de/
+                    for what these numbers do
+
+        Infrastructure:
+       #700 #701  docs: Document security policy in file SECURITY.md
+            #766  docs: Improve parse buffer variables in-code documentation
+    #674 #738 ..
+    #740 #747 ..
+  #748 #781 #782  Refactor coverage and conformance tests
+       #714 #716  Refactor debug level variables to unsigned long
+            #671  Improve handling of empty environment variable value
+                    in function getDebugLevel (without visible user effect)
+    #755 #774 ..
+    #758 #783 ..
+       #784 #787  tests: Improve test coverage with regard to parse chunk size
+  #660 #797 #801  Fuzzing: Improve fuzzing coverage
+       #367 #799  Fuzzing|CI: Start running OSS-Fuzz fuzzing regression tests
+       #698 #721  CI: Resolve some Travis CI leftovers
+            #669  CI: Be robust towards absence of Git tags
+       #693 #694  CI: Set permissions to "contents: read" for security
+            #709  CI: Pin all GitHub Actions to specific commits for security
+            #739  CI: Reject spelling errors using codespell
+            #798  CI: Enforce clang-tidy clean code
+    #773 #808 ..
+       #809 #810  CI: Upgrade Clang from 15 to 18
+            #796  CI: Start using Clang's Control Flow Integrity sanitizer
+  #675 #720 #722  CI: Adapt to breaking changes in GitHub Actions Ubuntu images
+            #689  CI: Adapt to breaking changes in Clang/LLVM Debian packaging
+            #763  CI: Adapt to breaking changes in codespell
+            #803  CI: Adapt to breaking changes in Cppcheck
+
+        Special thanks to:
+            Ivan Galkin
+            Joyce Brum
+            Philippe Antoine
+            Rhodri James
+            Snild Dolkow
+            spookyahell
+            Steven Garske
+                 and
+            Clang AddressSanitizer
+            Clang UndefinedBehaviorSanitizer
+            codespell
+            GCC Farm Project
+            OSS-Fuzz
+            Sony Mobile
 
 Release 2.5.0 Tue October 25 2022
         Security fixes:
@@ -11,7 +201,7 @@ Release 2.5.0 Tue October 25 2022
                     arbitrary code execution.
 
         Bug fixes:
-       #612 #645  Fix curruption from undefined entities
+       #612 #645  Fix corruption from undefined entities
        #613 #654  Fix case when parsing was suspended while processing nested
                     entities
   #616 #652 #653  Stop leaking opening tag bindings after a closing tag
@@ -318,7 +508,7 @@ Release 2.4.2 Sun December 19 2021
                     see https://verbump.de/ for what these numbers do
 
         Special thanks to:
-            Dong-hee Na
+            Donghee Na
             Joergen Ibsen
             Kai Pastor
 
index 638f0aa..3fc732f 100644 (file)
@@ -46,11 +46,11 @@ else(WORDS_BIGENDIAN)
 endif(WORDS_BIGENDIAN)
 
 if(HAVE_SYS_TYPES_H)
-    check_symbol_exists("off_t" "sys/types.h" OFF_T)
-    check_symbol_exists("size_t" "sys/types.h" SIZE_T)
+    check_symbol_exists("off_t" "sys/types.h" off_t)
+    check_symbol_exists("size_t" "sys/types.h" size_t)
 else(HAVE_SYS_TYPES_H)
-    set(OFF_T "long")
-    set(SIZE_T "unsigned")
+    set(off_t "long")
+    set(size_t "unsigned")
 endif(HAVE_SYS_TYPES_H)
 
 check_c_source_compiles("
index 37ae373..9c2259d 100644 (file)
@@ -6,9 +6,10 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2017-2023 Sebastian Pipping <sebastian@pipping.org>
 # Copyright (c) 2018      KangLin <kl222@126.com>
 # Copyright (c) 2022      Johnny Jazeix <jazeix@gmail.com>
+# Copyright (c) 2023      Sony Corporation / Snild Dolkow <snild@sony.com>
 # Licensed under the MIT license:
 #
 # Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -84,7 +85,7 @@ _EXTRA_DIST_WINDOWS = \
     win32/expat.iss \
     win32/MANIFEST.txt \
     win32/README.txt \
-    win32/version.rc
+    win32/version.rc.cmake
 
 EXTRA_DIST = \
     $(_EXTRA_DIST_CMAKE) \
@@ -131,6 +132,11 @@ buildlib:
 run-benchmark:
        $(MAKE) -C tests/benchmark
        ./run.sh tests/benchmark/benchmark@EXEEXT@ -n $(top_srcdir)/../testdata/largefiles/recset.xml 65535 3
+       ./run.sh tests/benchmark/benchmark@EXEEXT@ -n $(top_srcdir)/../testdata/largefiles/aaaaaa_attr.xml 4096 3
+       ./run.sh tests/benchmark/benchmark@EXEEXT@ -n $(top_srcdir)/../testdata/largefiles/aaaaaa_cdata.xml 4096 3
+       ./run.sh tests/benchmark/benchmark@EXEEXT@ -n $(top_srcdir)/../testdata/largefiles/aaaaaa_comment.xml 4096 3
+       ./run.sh tests/benchmark/benchmark@EXEEXT@ -n $(top_srcdir)/../testdata/largefiles/aaaaaa_tag.xml 4096 3
+       ./run.sh tests/benchmark/benchmark@EXEEXT@ -n $(top_srcdir)/../testdata/largefiles/aaaaaa_text.xml 4096 3
 
 .PHONY: download-xmlts-zip
 download-xmlts-zip:
index 008c410..f505224 100644 (file)
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2017-2023 Sebastian Pipping <sebastian@pipping.org>
 # Copyright (c) 2018      KangLin <kl222@126.com>
 # Copyright (c) 2022      Johnny Jazeix <jazeix@gmail.com>
+# Copyright (c) 2023      Sony Corporation / Snild Dolkow <snild@sony.com>
 # Licensed under the MIT license:
 #
 # Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -136,6 +137,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
        $(top_srcdir)/conftools/ax-append-compile-flags.m4 \
        $(top_srcdir)/conftools/ax-append-link-flags.m4 \
        $(top_srcdir)/conftools/expatcfg-compiler-supports-visibility.m4 \
+       $(top_srcdir)/conftools/ax-cxx-compile-stdcxx.m4 \
+       $(top_srcdir)/conftools/ax-cxx-compile-stdcxx-11.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
@@ -339,6 +342,7 @@ FGREP = @FGREP@
 FILECMD = @FILECMD@
 FILEMAP = @FILEMAP@
 GREP = @GREP@
+HAVE_CXX11 = @HAVE_CXX11@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
@@ -358,6 +362,7 @@ LIPO = @LIPO@
 LN_S = @LN_S@
 LTLIBOBJS = @LTLIBOBJS@
 LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
 MAKEINFO = @MAKEINFO@
 MANIFEST_TOOL = @MANIFEST_TOOL@
 MKDIR_P = @MKDIR_P@
@@ -478,7 +483,7 @@ _EXTRA_DIST_WINDOWS = \
     win32/expat.iss \
     win32/MANIFEST.txt \
     win32/README.txt \
-    win32/version.rc
+    win32/version.rc.cmake
 
 EXTRA_DIST = \
     $(_EXTRA_DIST_CMAKE) \
@@ -506,7 +511,7 @@ all: expat_config.h
 .SUFFIXES:
 am--refresh: Makefile
        @:
-$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
        @for dep in $?; do \
          case '$(am__configure_deps)' in \
            *$$dep*) \
@@ -532,9 +537,9 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
        $(SHELL) ./config.status --recheck
 
-$(top_srcdir)/configure:  $(am__configure_deps)
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
        $(am__cd) $(srcdir) && $(AUTOCONF)
-$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
        $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
 $(am__aclocal_m4_deps):
 
@@ -545,7 +550,7 @@ expat_config.h: stamp-h1
 stamp-h1: $(srcdir)/expat_config.h.in $(top_builddir)/config.status
        @rm -f stamp-h1
        cd $(top_builddir) && $(SHELL) ./config.status expat_config.h
-$(srcdir)/expat_config.h.in:  $(am__configure_deps) 
+$(srcdir)/expat_config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) 
        ($(am__cd) $(top_srcdir) && $(AUTOHEADER))
        rm -f stamp-h1
        touch $@
@@ -1092,6 +1097,11 @@ buildlib:
 run-benchmark:
        $(MAKE) -C tests/benchmark
        ./run.sh tests/benchmark/benchmark@EXEEXT@ -n $(top_srcdir)/../testdata/largefiles/recset.xml 65535 3
+       ./run.sh tests/benchmark/benchmark@EXEEXT@ -n $(top_srcdir)/../testdata/largefiles/aaaaaa_attr.xml 4096 3
+       ./run.sh tests/benchmark/benchmark@EXEEXT@ -n $(top_srcdir)/../testdata/largefiles/aaaaaa_cdata.xml 4096 3
+       ./run.sh tests/benchmark/benchmark@EXEEXT@ -n $(top_srcdir)/../testdata/largefiles/aaaaaa_comment.xml 4096 3
+       ./run.sh tests/benchmark/benchmark@EXEEXT@ -n $(top_srcdir)/../testdata/largefiles/aaaaaa_tag.xml 4096 3
+       ./run.sh tests/benchmark/benchmark@EXEEXT@ -n $(top_srcdir)/../testdata/largefiles/aaaaaa_text.xml 4096 3
 
 .PHONY: download-xmlts-zip
 download-xmlts-zip:
index e5e237f..3c20adb 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,13 +1,14 @@
-[![Run Linux Travis CI tasks](https://github.com/libexpat/libexpat/actions/workflows/linux.yml/badge.svg)](https://github.com/libexpat/libexpat/actions/workflows/linux.yml)
+[![Run Linux CI tasks](https://github.com/libexpat/libexpat/actions/workflows/linux.yml/badge.svg)](https://github.com/libexpat/libexpat/actions/workflows/linux.yml)
 [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/libexpat/libexpat?svg=true)](https://ci.appveyor.com/project/libexpat/libexpat)
 [![Packaging status](https://repology.org/badge/tiny-repos/expat.svg)](https://repology.org/metapackage/expat/versions)
 [![Downloads SourceForge](https://img.shields.io/sourceforge/dt/expat?label=Downloads%20SourceForge)](https://sourceforge.net/projects/expat/files/)
 [![Downloads GitHub](https://img.shields.io/github/downloads/libexpat/libexpat/total?label=Downloads%20GitHub)](https://github.com/libexpat/libexpat/releases)
 
 
-# Expat, Release 2.5.0
+# Expat, Release 2.6.2
 
-This is Expat, a C library for parsing XML, started by
+This is Expat, a C99 library for parsing
+[XML 1.0 Fourth Edition](https://www.w3.org/TR/2006/REC-xml-20060816/), started by
 [James Clark](https://en.wikipedia.org/wiki/James_Clark_%28programmer%29) in 1997.
 Expat is a stream-oriented XML parser.  This means that you register
 handlers with the parser before starting the parse.  These handlers
index 7277ab2..498f709 100644 (file)
@@ -8,5 +8,7 @@ m4_include(conftools/ax-append-flag.m4)
 m4_include(conftools/ax-append-compile-flags.m4)
 m4_include(conftools/ax-append-link-flags.m4)
 m4_include(conftools/expatcfg-compiler-supports-visibility.m4)
+m4_include(conftools/ax-cxx-compile-stdcxx.m4)
+m4_include(conftools/ax-cxx-compile-stdcxx-11.m4)
 
 ### end of file
index 8f7e74f..8a0016b 100644 (file)
@@ -715,6 +715,42 @@ fi
 rmdir .tst 2>/dev/null
 AC_SUBST([am__leading_dot])])
 
+# Add --enable-maintainer-mode option to configure.         -*- Autoconf -*-
+# From Jim Meyering
+
+# Copyright (C) 1996-2021 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_MAINTAINER_MODE([DEFAULT-MODE])
+# ----------------------------------
+# Control maintainer-specific portions of Makefiles.
+# Default is to disable them, unless 'enable' is passed literally.
+# For symmetry, 'disable' may be passed as well.  Anyway, the user
+# can override the default with the --enable/--disable switch.
+AC_DEFUN([AM_MAINTAINER_MODE],
+[m4_case(m4_default([$1], [disable]),
+       [enable], [m4_define([am_maintainer_other], [disable])],
+       [disable], [m4_define([am_maintainer_other], [enable])],
+       [m4_define([am_maintainer_other], [enable])
+        m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])])
+AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles])
+  dnl maintainer-mode's default is 'disable' unless 'enable' is passed
+  AC_ARG_ENABLE([maintainer-mode],
+    [AS_HELP_STRING([--]am_maintainer_other[-maintainer-mode],
+      am_maintainer_other[ make rules and dependencies not useful
+      (and sometimes confusing) to the casual installer])],
+    [USE_MAINTAINER_MODE=$enableval],
+    [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes]))
+  AC_MSG_RESULT([$USE_MAINTAINER_MODE])
+  AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes])
+  MAINT=$MAINTAINER_MODE_TRUE
+  AC_SUBST([MAINT])dnl
+]
+)
+
 # Check to see how 'make' treats includes.                 -*- Autoconf -*-
 
 # Copyright (C) 2001-2021 Free Software Foundation, Inc.
index abdda6e..f880e63 100644 (file)
@@ -52,11 +52,6 @@ else()
 endif()
 
 
-# if the installed project requested no architecture check, don't perform the check
-if("FALSE")
-  return()
-endif()
-
 # if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it:
 if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "@ac_cv_sizeof_void_p@" STREQUAL "")
   return()
index 5eb47b9..b984c79 100644 (file)
@@ -7,7 +7,7 @@ if(CMAKE_VERSION VERSION_LESS "2.8.3")
    message(FATAL_ERROR "CMake >= 2.8.3 required")
 endif()
 cmake_policy(PUSH)
-cmake_policy(VERSION 2.8.3...3.22)
+cmake_policy(VERSION 2.8.3...3.26)
 #----------------------------------------------------------------
 # Generated CMake target import file.
 #----------------------------------------------------------------
index 1cf1cb1..8c1a613 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,8 +1,8 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.71 for expat 2.5.0.
+# Generated by GNU Autoconf 2.71 for expat 2.6.2.
 #
-# Report bugs to <expat-bugs@libexpat.org>.
+# Report bugs to <https://github.com/libexpat/libexpat/issues>.
 #
 #
 # Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
@@ -276,10 +276,10 @@ then :
     printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later."
   else
     printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and
-$0: expat-bugs@libexpat.org about your system, including
-$0: any error possibly output before this message. Then
-$0: install a modern shell, or manually run the script
-$0: under such a shell if you do have one."
+$0: https://github.com/libexpat/libexpat/issues about your
+$0: system, including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
   fi
   exit 1
 fi
@@ -621,9 +621,9 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='expat'
 PACKAGE_TARNAME='expat'
-PACKAGE_VERSION='2.5.0'
-PACKAGE_STRING='expat 2.5.0'
-PACKAGE_BUGREPORT='expat-bugs@libexpat.org'
+PACKAGE_VERSION='2.6.2'
+PACKAGE_STRING='expat 2.6.2'
+PACKAGE_BUGREPORT='https://github.com/libexpat/libexpat/issues'
 PACKAGE_URL=''
 
 ac_unique_file="Makefile.in"
@@ -681,8 +681,12 @@ EXPAT_MIN_SIZE
 EXPAT_LARGE_SIZE
 EXPAT_DTD
 EXPAT_ATTR_INFO
-WITH_DOCBOOK_FALSE
-WITH_DOCBOOK_TRUE
+WITH_DISTRIBUTABLE_MANPAGE_FALSE
+WITH_DISTRIBUTABLE_MANPAGE_TRUE
+WITH_PREBUILT_MANPAGE_FALSE
+WITH_PREBUILT_MANPAGE_TRUE
+WITH_MANPAGE_FALSE
+WITH_MANPAGE_TRUE
 DOCBOOK_TO_MAN
 FILEMAP
 LIBM
@@ -696,6 +700,7 @@ UNICODE_FALSE
 UNICODE_TRUE
 MINGW_FALSE
 MINGW_TRUE
+HAVE_CXX11
 WITH_TESTS_FALSE
 WITH_TESTS_TRUE
 WITH_EXAMPLES_FALSE
@@ -752,6 +757,9 @@ CFLAGS
 CC
 ac_ct_AR
 AR
+MAINT
+MAINTAINER_MODE_FALSE
+MAINTAINER_MODE_TRUE
 AM_BACKSLASH
 AM_DEFAULT_VERBOSITY
 AM_DEFAULT_V
@@ -834,6 +842,7 @@ ac_subst_files='PACKAGE_INIT'
 ac_user_opts='
 enable_option_checking
 enable_silent_rules
+enable_maintainer_mode
 enable_dependency_tracking
 enable_shared
 enable_static
@@ -1415,7 +1424,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures expat 2.5.0 to adapt to many kinds of systems.
+\`configure' configures expat 2.6.2 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1486,7 +1495,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of expat 2.5.0:";;
+     short | recursive ) echo "Configuration of expat 2.6.2:";;
    esac
   cat <<\_ACEOF
 
@@ -1496,6 +1505,9 @@ Optional Features:
   --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
   --enable-silent-rules   less verbose build output (undo: "make V=1")
   --disable-silent-rules  verbose build output (undo: "make V=0")
+  --disable-maintainer-mode
+                          disable make rules and dependencies not useful (and
+                          sometimes confusing) to the casual installer
   --enable-dependency-tracking
                           do not reject slow dependency extractors
   --disable-dependency-tracking
@@ -1556,7 +1568,7 @@ Some influential environment variables:
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
 
-Report bugs to <expat-bugs@libexpat.org>.
+Report bugs to <https://github.com/libexpat/libexpat/issues>.
 _ACEOF
 ac_status=$?
 fi
@@ -1620,7 +1632,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-expat configure 2.5.0
+expat configure 2.6.2
 generated by GNU Autoconf 2.71
 
 Copyright (C) 2021 Free Software Foundation, Inc.
@@ -2251,7 +2263,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by expat $as_me 2.5.0, which was
+It was created by expat $as_me 2.6.2, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -2537,9 +2549,7 @@ struct stat;
 /* Most of the following tests are stolen from RCS 5.7 src/conf.sh.  */
 struct buf { int x; };
 struct buf * (*rcsopen) (struct buf *, struct stat *, int);
-static char *e (p, i)
-     char **p;
-     int i;
+static char *e (char **p, int i)
 {
   return p[i];
 }
@@ -2590,6 +2600,7 @@ extern int puts (const char *);
 extern int printf (const char *, ...);
 extern int dprintf (int, const char *, ...);
 extern void *malloc (size_t);
+extern void free (void *);
 
 // Check varargs macros.  These examples are taken from C99 6.10.3.5.
 // dprintf is used instead of fprintf to avoid needing to declare
@@ -3818,7 +3829,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='expat'
- VERSION='2.5.0'
+ VERSION='2.6.2'
 
 
 printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -3922,11 +3933,35 @@ END
 fi
 
 
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5
+printf %s "checking whether to enable maintainer-specific portions of Makefiles... " >&6; }
+    # Check whether --enable-maintainer-mode was given.
+if test ${enable_maintainer_mode+y}
+then :
+  enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval
+else $as_nop
+  USE_MAINTAINER_MODE=yes
+fi
+
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5
+printf "%s\n" "$USE_MAINTAINER_MODE" >&6; }
+   if test $USE_MAINTAINER_MODE = yes; then
+  MAINTAINER_MODE_TRUE=
+  MAINTAINER_MODE_FALSE='#'
+else
+  MAINTAINER_MODE_TRUE='#'
+  MAINTAINER_MODE_FALSE=
+fi
+
+  MAINT=$MAINTAINER_MODE_TRUE
+
+  # to allow argument --disable-maintainer-mode
 
 
-LIBCURRENT=9    # sync
-LIBREVISION=10  # with
-LIBAGE=8        # CMakeLists.txt!
+
+LIBCURRENT=10  # sync
+LIBREVISION=2  # with
+LIBAGE=9       # CMakeLists.txt!
 
 ac_config_headers="$ac_config_headers expat_config.h"
 
@@ -13874,6 +13909,11 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
 
 
+if test "${ac_cv_prog_cc_c99}" = no
+then :
+  as_fn_error $? "Expat requires a C99 compiler." "$LINENO" 5
+fi
+
 if test "$GCC" = yes
 then :
 
@@ -14596,11 +14636,11 @@ if test x$ac_prog_cxx_stdcxx = xno
 then :
   { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++11 features" >&5
 printf %s "checking for $CXX option to enable C++11 features... " >&6; }
-if test ${ac_cv_prog_cxx_11+y}
+if test ${ac_cv_prog_cxx_cxx11+y}
 then :
   printf %s "(cached) " >&6
 else $as_nop
-  ac_cv_prog_cxx_11=no
+  ac_cv_prog_cxx_cxx11=no
 ac_save_CXX=$CXX
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
@@ -14642,11 +14682,11 @@ if test x$ac_prog_cxx_stdcxx = xno
 then :
   { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++98 features" >&5
 printf %s "checking for $CXX option to enable C++98 features... " >&6; }
-if test ${ac_cv_prog_cxx_98+y}
+if test ${ac_cv_prog_cxx_cxx98+y}
 then :
   printf %s "(cached) " >&6
 else $as_nop
-  ac_cv_prog_cxx_98=no
+  ac_cv_prog_cxx_cxx98=no
 ac_save_CXX=$CXX
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
@@ -18971,6 +19011,382 @@ else
 fi
 
 
+if test x${with_tests} = xyes
+then :
+    ax_cxx_compile_alternatives="11 0x"    ax_cxx_compile_cxx11_required=true
+  ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+  ac_success=no
+
+
+
+
+
+    if test x$ac_success = xno; then
+                    for alternative in ${ax_cxx_compile_alternatives}; do
+      for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}" MSVC; do
+        if test x"$switch" = xMSVC; then
+                                        switch=-std:c++${alternative}
+          cachevar=`printf "%s\n" "ax_cv_cxx_compile_cxx11_${switch}_MSVC" | $as_tr_sh`
+        else
+          cachevar=`printf "%s\n" "ax_cv_cxx_compile_cxx11_$switch" | $as_tr_sh`
+        fi
+        { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features with $switch" >&5
+printf %s "checking whether $CXX supports C++11 features with $switch... " >&6; }
+if eval test \${$cachevar+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_save_CXX="$CXX"
+           CXX="$CXX $switch"
+           cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+
+// If the compiler admits that it is not ready for C++11, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+// MSVC always sets __cplusplus to 199711L in older versions; newer versions
+// only set it correctly if /Zc:__cplusplus is specified as well as a
+// /std:c++NN switch:
+// https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/
+#elif __cplusplus < 201103L && !defined _MSC_VER
+
+#error "This is not a C++11 compiler"
+
+#else
+
+namespace cxx11
+{
+
+  namespace test_static_assert
+  {
+
+    template <typename T>
+    struct check
+    {
+      static_assert(sizeof(int) <= sizeof(T), "not big enough");
+    };
+
+  }
+
+  namespace test_final_override
+  {
+
+    struct Base
+    {
+      virtual ~Base() {}
+      virtual void f() {}
+    };
+
+    struct Derived : public Base
+    {
+      virtual ~Derived() override {}
+      virtual void f() override {}
+    };
+
+  }
+
+  namespace test_double_right_angle_brackets
+  {
+
+    template < typename T >
+    struct check {};
+
+    typedef check<void> single_type;
+    typedef check<check<void>> double_type;
+    typedef check<check<check<void>>> triple_type;
+    typedef check<check<check<check<void>>>> quadruple_type;
+
+  }
+
+  namespace test_decltype
+  {
+
+    int
+    f()
+    {
+      int a = 1;
+      decltype(a) b = 2;
+      return a + b;
+    }
+
+  }
+
+  namespace test_type_deduction
+  {
+
+    template < typename T1, typename T2 >
+    struct is_same
+    {
+      static const bool value = false;
+    };
+
+    template < typename T >
+    struct is_same<T, T>
+    {
+      static const bool value = true;
+    };
+
+    template < typename T1, typename T2 >
+    auto
+    add(T1 a1, T2 a2) -> decltype(a1 + a2)
+    {
+      return a1 + a2;
+    }
+
+    int
+    test(const int c, volatile int v)
+    {
+      static_assert(is_same<int, decltype(0)>::value == true, "");
+      static_assert(is_same<int, decltype(c)>::value == false, "");
+      static_assert(is_same<int, decltype(v)>::value == false, "");
+      auto ac = c;
+      auto av = v;
+      auto sumi = ac + av + 'x';
+      auto sumf = ac + av + 1.0;
+      static_assert(is_same<int, decltype(ac)>::value == true, "");
+      static_assert(is_same<int, decltype(av)>::value == true, "");
+      static_assert(is_same<int, decltype(sumi)>::value == true, "");
+      static_assert(is_same<int, decltype(sumf)>::value == false, "");
+      static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
+      return (sumf > 0.0) ? sumi : add(c, v);
+    }
+
+  }
+
+  namespace test_noexcept
+  {
+
+    int f() { return 0; }
+    int g() noexcept { return 0; }
+
+    static_assert(noexcept(f()) == false, "");
+    static_assert(noexcept(g()) == true, "");
+
+  }
+
+  namespace test_constexpr
+  {
+
+    template < typename CharT >
+    unsigned long constexpr
+    strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
+    {
+      return *s ? strlen_c_r(s + 1, acc + 1) : acc;
+    }
+
+    template < typename CharT >
+    unsigned long constexpr
+    strlen_c(const CharT *const s) noexcept
+    {
+      return strlen_c_r(s, 0UL);
+    }
+
+    static_assert(strlen_c("") == 0UL, "");
+    static_assert(strlen_c("1") == 1UL, "");
+    static_assert(strlen_c("example") == 7UL, "");
+    static_assert(strlen_c("another\0example") == 7UL, "");
+
+  }
+
+  namespace test_rvalue_references
+  {
+
+    template < int N >
+    struct answer
+    {
+      static constexpr int value = N;
+    };
+
+    answer<1> f(int&)       { return answer<1>(); }
+    answer<2> f(const int&) { return answer<2>(); }
+    answer<3> f(int&&)      { return answer<3>(); }
+
+    void
+    test()
+    {
+      int i = 0;
+      const int c = 0;
+      static_assert(decltype(f(i))::value == 1, "");
+      static_assert(decltype(f(c))::value == 2, "");
+      static_assert(decltype(f(0))::value == 3, "");
+    }
+
+  }
+
+  namespace test_uniform_initialization
+  {
+
+    struct test
+    {
+      static const int zero {};
+      static const int one {1};
+    };
+
+    static_assert(test::zero == 0, "");
+    static_assert(test::one == 1, "");
+
+  }
+
+  namespace test_lambdas
+  {
+
+    void
+    test1()
+    {
+      auto lambda1 = [](){};
+      auto lambda2 = lambda1;
+      lambda1();
+      lambda2();
+    }
+
+    int
+    test2()
+    {
+      auto a = [](int i, int j){ return i + j; }(1, 2);
+      auto b = []() -> int { return '0'; }();
+      auto c = [=](){ return a + b; }();
+      auto d = [&](){ return c; }();
+      auto e = [a, &b](int x) mutable {
+        const auto identity = [](int y){ return y; };
+        for (auto i = 0; i < a; ++i)
+          a += b--;
+        return x + identity(a + b);
+      }(0);
+      return a + b + c + d + e;
+    }
+
+    int
+    test3()
+    {
+      const auto nullary = [](){ return 0; };
+      const auto unary = [](int x){ return x; };
+      using nullary_t = decltype(nullary);
+      using unary_t = decltype(unary);
+      const auto higher1st = [](nullary_t f){ return f(); };
+      const auto higher2nd = [unary](nullary_t f1){
+        return [unary, f1](unary_t f2){ return f2(unary(f1())); };
+      };
+      return higher1st(nullary) + higher2nd(nullary)(unary);
+    }
+
+  }
+
+  namespace test_variadic_templates
+  {
+
+    template <int...>
+    struct sum;
+
+    template <int N0, int... N1toN>
+    struct sum<N0, N1toN...>
+    {
+      static constexpr auto value = N0 + sum<N1toN...>::value;
+    };
+
+    template <>
+    struct sum<>
+    {
+      static constexpr auto value = 0;
+    };
+
+    static_assert(sum<>::value == 0, "");
+    static_assert(sum<1>::value == 1, "");
+    static_assert(sum<23>::value == 23, "");
+    static_assert(sum<1, 2>::value == 3, "");
+    static_assert(sum<5, 5, 11>::value == 21, "");
+    static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
+
+  }
+
+  // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
+  // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
+  // because of this.
+  namespace test_template_alias_sfinae
+  {
+
+    struct foo {};
+
+    template<typename T>
+    using member = typename T::member_type;
+
+    template<typename T>
+    void func(...) {}
+
+    template<typename T>
+    void func(member<T>*) {}
+
+    void test();
+
+    void test() { func<foo>(0); }
+
+  }
+
+}  // namespace cxx11
+
+#endif  // __cplusplus >= 201103L
+
+
+
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"
+then :
+  eval $cachevar=yes
+else $as_nop
+  eval $cachevar=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+           CXX="$ac_save_CXX"
+fi
+eval ac_res=\$$cachevar
+              { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+        if eval test x\$$cachevar = xyes; then
+          CXX="$CXX $switch"
+          if test -n "$CXXCPP" ; then
+            CXXCPP="$CXXCPP $switch"
+          fi
+          ac_success=yes
+          break
+        fi
+      done
+      if test x$ac_success = xyes; then
+        break
+      fi
+    done
+  fi
+  ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+  if test x$ax_cxx_compile_cxx11_required = xtrue; then
+    if test x$ac_success = xno; then
+      as_fn_error $? "*** A compiler with support for C++11 language features is required." "$LINENO" 5
+    fi
+  fi
+  if test x$ac_success = xno; then
+    HAVE_CXX11=0
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: No compiler with C++11 support was found" >&5
+printf "%s\n" "$as_me: No compiler with C++11 support was found" >&6;}
+  else
+    HAVE_CXX11=1
+
+printf "%s\n" "#define HAVE_CXX11 1" >>confdefs.h
+
+  fi
+
+
+fi
 
 EXPATCFG_ON_MINGW=no
 case "${host_os}" in #(
 fi
 
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for arc4random_buf (BSD or libbsd)" >&5
-printf %s "checking for arc4random_buf (BSD or libbsd)... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for arc4random_buf (BSD, libbsd or glibc 2.36+)" >&5
+printf %s "checking for arc4random_buf (BSD, libbsd or glibc 2.36+)... " >&6; }
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-    #include <stdlib.h>  /* for arc4random_buf on BSD, for NULL */
     #if defined(HAVE_LIBBSD)
     # include <bsd/stdlib.h>
+    #else
+    # include <stdlib.h>  /* for arc4random_buf on BSD */
     #endif
     int main() {
-      arc4random_buf(NULL, 0U);
+      char dummy[123];  // double brackets for m4
+      arc4random_buf(dummy, 0U);
       return 0;
     }
 
@@ -19266,8 +19684,8 @@ else $as_nop
   { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
 printf "%s\n" "no" >&6; }
 
-   { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for arc4random (BSD, macOS or libbsd)" >&5
-printf %s "checking for arc4random (BSD, macOS or libbsd)... " >&6; }
+   { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for arc4random (BSD, macOS, libbsd or glibc 2.36+)" >&5
+printf %s "checking for arc4random (BSD, macOS, libbsd or glibc 2.36+)... " >&6; }
    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -19612,6 +20030,9 @@ fi
 printf "%s\n" "#define XML_NS 1" >>confdefs.h
 
 
+printf "%s\n" "#define XML_GE 1" >>confdefs.h
+
+
 printf "%s\n" "#define XML_DTD 1" >>confdefs.h
 
 
@@ -19646,10 +20067,12 @@ then :
 then :
   enable_xml_context=1024
 fi
+else $as_nop
+  enable_xml_context=0
+fi
 
 printf "%s\n" "#define XML_CONTEXT_BYTES ${enable_xml_context}" >>confdefs.h
 
-fi
 
 
 # Check whether --with-docbook was given.
@@ -19729,15 +20152,99 @@ then :
 fi
 fi
 
- if test "x${DOCBOOK_TO_MAN}" != x; then
-  WITH_DOCBOOK_TRUE=
-  WITH_DOCBOOK_FALSE='#'
+if test -f "${srcdir}"/doc/xmlwf.1
+then :
+   if true; then
+  WITH_MANPAGE_TRUE=
+  WITH_MANPAGE_FALSE='#'
+else
+  WITH_MANPAGE_TRUE='#'
+  WITH_MANPAGE_FALSE=
+fi
+
+   if test "x$with_docbook" = xno -o "x${DOCBOOK_TO_MAN}" = x
+then :
+   if true; then
+  WITH_PREBUILT_MANPAGE_TRUE=
+  WITH_PREBUILT_MANPAGE_FALSE='#'
+else
+  WITH_PREBUILT_MANPAGE_TRUE='#'
+  WITH_PREBUILT_MANPAGE_FALSE=
+fi
+
+       if false; then
+  WITH_DISTRIBUTABLE_MANPAGE_TRUE=
+  WITH_DISTRIBUTABLE_MANPAGE_FALSE='#'
+else
+  WITH_DISTRIBUTABLE_MANPAGE_TRUE='#'
+  WITH_DISTRIBUTABLE_MANPAGE_FALSE=
+fi
+
+else $as_nop
+   if false; then
+  WITH_PREBUILT_MANPAGE_TRUE=
+  WITH_PREBUILT_MANPAGE_FALSE='#'
+else
+  WITH_PREBUILT_MANPAGE_TRUE='#'
+  WITH_PREBUILT_MANPAGE_FALSE=
+fi
+
+       if true; then
+  WITH_DISTRIBUTABLE_MANPAGE_TRUE=
+  WITH_DISTRIBUTABLE_MANPAGE_FALSE='#'
+else
+  WITH_DISTRIBUTABLE_MANPAGE_TRUE='#'
+  WITH_DISTRIBUTABLE_MANPAGE_FALSE=
+fi
+
+fi
+
+else $as_nop
+  if test "x$with_docbook" != xno -a "x${DOCBOOK_TO_MAN}" != x
+then :
+   if true; then
+  WITH_MANPAGE_TRUE=
+  WITH_MANPAGE_FALSE='#'
+else
+  WITH_MANPAGE_TRUE='#'
+  WITH_MANPAGE_FALSE=
+fi
+
+       if true; then
+  WITH_DISTRIBUTABLE_MANPAGE_TRUE=
+  WITH_DISTRIBUTABLE_MANPAGE_FALSE='#'
+else
+  WITH_DISTRIBUTABLE_MANPAGE_TRUE='#'
+  WITH_DISTRIBUTABLE_MANPAGE_FALSE=
+fi
+
+else $as_nop
+   if false; then
+  WITH_MANPAGE_TRUE=
+  WITH_MANPAGE_FALSE='#'
+else
+  WITH_MANPAGE_TRUE='#'
+  WITH_MANPAGE_FALSE=
+fi
+
+       if false; then
+  WITH_DISTRIBUTABLE_MANPAGE_TRUE=
+  WITH_DISTRIBUTABLE_MANPAGE_FALSE='#'
 else
-  WITH_DOCBOOK_TRUE='#'
-  WITH_DOCBOOK_FALSE=
+  WITH_DISTRIBUTABLE_MANPAGE_TRUE='#'
+  WITH_DISTRIBUTABLE_MANPAGE_FALSE=
 fi
 
+fi
+    if false; then
+  WITH_PREBUILT_MANPAGE_TRUE=
+  WITH_PREBUILT_MANPAGE_FALSE='#'
+else
+  WITH_PREBUILT_MANPAGE_TRUE='#'
+  WITH_PREBUILT_MANPAGE_FALSE=
+fi
 
+fi
 
 if test "x${enable_xml_attr_info}" = xyes
 then :
@@ -19828,6 +20335,14 @@ printf "%s\n" "#define SIZEOF_VOID_P $ac_cv_sizeof_void_p" >>confdefs.h
 
 
 
+if grep -F -q SIZEOF_VOID_P "${srcdir}"/expat_config.h.in
+then :
+  as_fn_error $? "Plain autoreconf/autoheader does not cut it,
+                  please use ./buildconf.sh or imitate its effect
+                  through other means, so that file expat_config.h.in
+                  no longer defines macro SIZEOF_VOID_P, as that would
+                  break multilib support.  Thank you." "$LINENO" 5
+fi
 
 
 
@@ -19983,6 +20498,10 @@ else
   am__EXEEXT_FALSE=
 fi
 
+if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then
+  as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
   as_fn_error $? "conditional \"AMDEP\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -20028,8 +20547,44 @@ if test -z "${_INTERNAL_LARGE_SIZE_TRUE}" && test -z "${_INTERNAL_LARGE_SIZE_FAL
   as_fn_error $? "conditional \"_INTERNAL_LARGE_SIZE\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
-if test -z "${WITH_DOCBOOK_TRUE}" && test -z "${WITH_DOCBOOK_FALSE}"; then
-  as_fn_error $? "conditional \"WITH_DOCBOOK\" was never defined.
+if test -z "${WITH_MANPAGE_TRUE}" && test -z "${WITH_MANPAGE_FALSE}"; then
+  as_fn_error $? "conditional \"WITH_MANPAGE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${WITH_PREBUILT_MANPAGE_TRUE}" && test -z "${WITH_PREBUILT_MANPAGE_FALSE}"; then
+  as_fn_error $? "conditional \"WITH_PREBUILT_MANPAGE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${WITH_DISTRIBUTABLE_MANPAGE_TRUE}" && test -z "${WITH_DISTRIBUTABLE_MANPAGE_FALSE}"; then
+  as_fn_error $? "conditional \"WITH_DISTRIBUTABLE_MANPAGE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${WITH_PREBUILT_MANPAGE_TRUE}" && test -z "${WITH_PREBUILT_MANPAGE_FALSE}"; then
+  as_fn_error $? "conditional \"WITH_PREBUILT_MANPAGE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${WITH_DISTRIBUTABLE_MANPAGE_TRUE}" && test -z "${WITH_DISTRIBUTABLE_MANPAGE_FALSE}"; then
+  as_fn_error $? "conditional \"WITH_DISTRIBUTABLE_MANPAGE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${WITH_MANPAGE_TRUE}" && test -z "${WITH_MANPAGE_FALSE}"; then
+  as_fn_error $? "conditional \"WITH_MANPAGE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${WITH_DISTRIBUTABLE_MANPAGE_TRUE}" && test -z "${WITH_DISTRIBUTABLE_MANPAGE_FALSE}"; then
+  as_fn_error $? "conditional \"WITH_DISTRIBUTABLE_MANPAGE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${WITH_MANPAGE_TRUE}" && test -z "${WITH_MANPAGE_FALSE}"; then
+  as_fn_error $? "conditional \"WITH_MANPAGE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${WITH_DISTRIBUTABLE_MANPAGE_TRUE}" && test -z "${WITH_DISTRIBUTABLE_MANPAGE_FALSE}"; then
+  as_fn_error $? "conditional \"WITH_DISTRIBUTABLE_MANPAGE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${WITH_PREBUILT_MANPAGE_TRUE}" && test -z "${WITH_PREBUILT_MANPAGE_FALSE}"; then
+  as_fn_error $? "conditional \"WITH_PREBUILT_MANPAGE\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
 
@@ -20422,7 +20977,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by expat $as_me 2.5.0, which was
+This file was extended by expat $as_me 2.6.2, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -20482,7 +21037,7 @@ $config_headers
 Configuration commands:
 $config_commands
 
-Report bugs to <expat-bugs@libexpat.org>."
+Report bugs to <https://github.com/libexpat/libexpat/issues>."
 
 _ACEOF
 ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"`
@@ -20490,7 +21045,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config='$ac_cs_config_escaped'
 ac_cs_version="\\
-expat config.status 2.5.0
+expat config.status 2.6.2
 configured by $0, generated by GNU Autoconf 2.71,
   with options \\"\$ac_cs_config\\"
 
index d3642de..04415e3 100644 (file)
@@ -11,7 +11,7 @@ dnl   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
 dnl   Copyright (c) 2000-2005 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
 dnl   Copyright (c) 2001-2003 Greg Stein <gstein@users.sourceforge.net>
 dnl   Copyright (c) 2006-2012 Karl Waclawek <karl@waclawek.net>
-dnl   Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+dnl   Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
 dnl   Copyright (c) 2017      S. P. Zeidler <spz@netbsd.org>
 dnl   Copyright (c) 2017      Stephen Groat <stephen@groat.us>
 dnl   Copyright (c) 2017-2020 Joe Orton <jorton@redhat.com>
@@ -60,7 +60,7 @@ m4_define([expat_version],
   m4_ifdef([__gnu__],
            [esyscmd(conftools/get-version.sh lib/expat.h)],
            [2.2.x]))
-AC_INIT([expat], expat_version, [expat-bugs@libexpat.org])
+AC_INIT([expat], expat_version, [https://github.com/libexpat/libexpat/issues])
 m4_undefine([expat_version])
 
 AC_CONFIG_SRCDIR([Makefile.in])
@@ -68,6 +68,7 @@ AC_CONFIG_AUX_DIR([conftools])
 AC_CONFIG_MACRO_DIR([m4])
 AC_CANONICAL_HOST
 AM_INIT_AUTOMAKE
+AM_MAINTAINER_MODE([enable])  # to allow argument --disable-maintainer-mode
 
 
 dnl
@@ -81,9 +82,9 @@ dnl
 dnl If the API changes incompatibly set LIBAGE back to 0
 dnl
 
-LIBCURRENT=9    # sync
-LIBREVISION=10  # with
-LIBAGE=       # CMakeLists.txt!
+LIBCURRENT=10  # sync
+LIBREVISION=2  # with
+LIBAGE=9       # CMakeLists.txt!
 
 AC_CONFIG_HEADERS([expat_config.h])
 AH_TOP([#ifndef EXPAT_CONFIG_H
@@ -105,6 +106,9 @@ AC_SUBST(LIBAGE)
 AC_LANG([C])
 AC_PROG_CC_C99
 
+AS_IF([test "${ac_cv_prog_cc_c99}" = no],
+  [AC_MSG_ERROR([Expat requires a C99 compiler.])])
+
 AS_IF([test "$GCC" = yes],
   [AX_APPEND_COMPILE_FLAGS([-Wall -Wextra], [AM_CFLAGS])
    dnl Be careful about adding the -fexceptions option; some versions of
@@ -176,6 +180,8 @@ AC_ARG_WITH([tests],
   [with_tests=yes])
 AM_CONDITIONAL([WITH_TESTS], [test x${with_tests} = xyes])
 
+AS_IF([test x${with_tests} = xyes],
+      [AX_CXX_COMPILE_STDCXX_11([noext], [mandatory])])
 
 AS_VAR_SET([EXPATCFG_ON_MINGW],[no])
 AS_CASE("${host_os}",
@@ -202,14 +208,16 @@ AS_IF([test "x${with_libbsd}" != xno],
      [],
      [AS_IF([test "x${with_libbsd}" = xyes],
         [AC_MSG_ERROR([Enforced use of libbsd cannot be satisfied.])])])])
-AC_MSG_CHECKING([for arc4random_buf (BSD or libbsd)])
+AC_MSG_CHECKING([for arc4random_buf (BSD, libbsd or glibc 2.36+)])
 AC_LINK_IFELSE([AC_LANG_SOURCE([
-    #include <stdlib.h>  /* for arc4random_buf on BSD, for NULL */
     #if defined(HAVE_LIBBSD)
     # include <bsd/stdlib.h>
+    #else
+    # include <stdlib.h>  /* for arc4random_buf on BSD */
     #endif
     int main() {
-      arc4random_buf(NULL, 0U);
+      char dummy[[123]];  // double brackets for m4
+      arc4random_buf(dummy, 0U);
       return 0;
     }
   ])],
@@ -217,7 +225,7 @@ AC_LINK_IFELSE([AC_LANG_SOURCE([
    AC_MSG_RESULT([yes])],
   [AC_MSG_RESULT([no])
 
-   AC_MSG_CHECKING([for arc4random (BSD, macOS or libbsd)])
+   AC_MSG_CHECKING([for arc4random (BSD, macOS, libbsd or glibc 2.36+)])
    AC_LINK_IFELSE([AC_LANG_SOURCE([
        #if defined(HAVE_LIBBSD)
        # include <bsd/stdlib.h>
@@ -295,6 +303,8 @@ AC_SUBST(FILEMAP)
 dnl Some basic configuration:
 AC_DEFINE([XML_NS], 1,
           [Define to make XML Namespaces functionality available.])
+AC_DEFINE([XML_GE], 1,
+          [Define as 1/0 to enable/disable support for general entities.])
 AC_DEFINE([XML_DTD], 1,
           [Define to make parameter entity parsing functionality available.])
 AC_DEFINE([XML_DEV_URANDOM], 1,
@@ -319,9 +329,10 @@ AS_HELP_STRING([--disable-xml-context],
 AS_IF([test "x${enable_xml_context}" != "xno"],
   [AS_IF([test "x${enable_xml_context}" = "xyes" \
             -o "x${enable_xml_context}" = "x"],
-     [AS_VAR_SET(enable_xml_context,1024)])
-   AC_DEFINE_UNQUOTED([XML_CONTEXT_BYTES], [${enable_xml_context}],
-     [Define to specify how much context to retain around the current parse point.])])
+     [AS_VAR_SET(enable_xml_context,1024)])],
+  [AS_VAR_SET(enable_xml_context,0)])
+AC_DEFINE_UNQUOTED([XML_CONTEXT_BYTES], [${enable_xml_context}],
+  [Define to specify how much context to retain around the current parse point, 0 to disable.])
 
 AC_ARG_WITH([docbook],
   [AS_HELP_STRING([--with-docbook],
@@ -345,8 +356,23 @@ AS_IF([test "x${DOCBOOK_TO_MAN}" != x -a "x$with_docbook" != xno],
   You can also configure using --without-docbook if you can do without a man
   page for xmlwf.])])])
 
-AM_CONDITIONAL(WITH_DOCBOOK, [test "x${DOCBOOK_TO_MAN}" != x])
-
+dnl This will make sure that a release tarball shipping a pre-rendered xmlwf man page will
+dnl get it installed, when no working flavor of docbook2man is available (or wanted).
+dnl This relies on file xmlwf.1 being at least as recent as its source file xmlwf.xml.
+AS_IF([test -f "${srcdir}"/doc/xmlwf.1],
+  [AM_CONDITIONAL(WITH_MANPAGE, [true])
+   AS_IF([test "x$with_docbook" = xno -o "x${DOCBOOK_TO_MAN}" = x],
+     [AM_CONDITIONAL(WITH_PREBUILT_MANPAGE, [true])
+      AM_CONDITIONAL(WITH_DISTRIBUTABLE_MANPAGE, [false])],
+     [AM_CONDITIONAL(WITH_PREBUILT_MANPAGE, [false])
+      AM_CONDITIONAL(WITH_DISTRIBUTABLE_MANPAGE, [true])])
+   ],
+  [AS_IF([test "x$with_docbook" != xno -a "x${DOCBOOK_TO_MAN}" != x],
+     [AM_CONDITIONAL(WITH_MANPAGE, [true])
+      AM_CONDITIONAL(WITH_DISTRIBUTABLE_MANPAGE, [true])],
+     [AM_CONDITIONAL(WITH_MANPAGE, [false])
+      AM_CONDITIONAL(WITH_DISTRIBUTABLE_MANPAGE, [false])])
+   AM_CONDITIONAL(WITH_PREBUILT_MANPAGE, [false])])
 
 dnl Configure CMake file templates
 dnl NOTE: The *_TRUE variables read here are Automake conditionals
@@ -392,6 +418,14 @@ AC_SUBST([SO_MINOR])
 AC_SUBST([SO_PATCH])
 AC_SUBST([ac_cv_sizeof_void_p])
 
+dnl Protect against generating an expat_config.h that would break multilib
+AS_IF([grep -F -q SIZEOF_VOID_P "${srcdir}"/expat_config.h.in],
+  [AC_MSG_ERROR(
+    [Plain autoreconf/autoheader does not cut it,
+                  please use ./buildconf.sh or imitate its effect
+                  through other means, so that file expat_config.h.in
+                  no longer defines macro SIZEOF_VOID_P, as that would
+                  break multilib support.  Thank you.])])
 
 dnl write the Automake flags we set
 AC_SUBST([AM_CPPFLAGS])
diff --git a/conftools/ax-cxx-compile-stdcxx-11.m4 b/conftools/ax-cxx-compile-stdcxx-11.m4
new file mode 100644 (file)
index 0000000..1733fd8
--- /dev/null
@@ -0,0 +1,39 @@
+# =============================================================================
+#  https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html
+# =============================================================================
+#
+# SYNOPSIS
+#
+#   AX_CXX_COMPILE_STDCXX_11([ext|noext], [mandatory|optional])
+#
+# DESCRIPTION
+#
+#   Check for baseline language coverage in the compiler for the C++11
+#   standard; if necessary, add switches to CXX and CXXCPP to enable
+#   support.
+#
+#   This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX
+#   macro with the version set to C++11.  The two optional arguments are
+#   forwarded literally as the second and third argument respectively.
+#   Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for
+#   more information.  If you want to use this macro, you also need to
+#   download the ax_cxx_compile_stdcxx.m4 file.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
+#   Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
+#   Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
+#   Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
+#   Copyright (c) 2015 Paul Norman <penorman@mac.com>
+#   Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 18
+
+AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX])
+AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [AX_CXX_COMPILE_STDCXX([11], [$1], [$2])])
diff --git a/conftools/ax-cxx-compile-stdcxx.m4 b/conftools/ax-cxx-compile-stdcxx.m4
new file mode 100644 (file)
index 0000000..8edf515
--- /dev/null
@@ -0,0 +1,1018 @@
+# ===========================================================================
+#  https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
+#
+# DESCRIPTION
+#
+#   Check for baseline language coverage in the compiler for the specified
+#   version of the C++ standard.  If necessary, add switches to CXX and
+#   CXXCPP to enable support.  VERSION may be '11', '14', '17', or '20' for
+#   the respective C++ standard version.
+#
+#   The second argument, if specified, indicates whether you insist on an
+#   extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
+#   -std=c++11).  If neither is specified, you get whatever works, with
+#   preference for no added switch, and then for an extended mode.
+#
+#   The third argument, if specified 'mandatory' or if left unspecified,
+#   indicates that baseline support for the specified C++ standard is
+#   required and that the macro should error out if no mode with that
+#   support is found.  If specified 'optional', then configuration proceeds
+#   regardless, after defining HAVE_CXX${VERSION} if and only if a
+#   supporting mode is found.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
+#   Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
+#   Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
+#   Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
+#   Copyright (c) 2015 Paul Norman <penorman@mac.com>
+#   Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
+#   Copyright (c) 2016, 2018 Krzesimir Nowak <qdlacz@gmail.com>
+#   Copyright (c) 2019 Enji Cooper <yaneurabeya@gmail.com>
+#   Copyright (c) 2020 Jason Merrill <jason@redhat.com>
+#   Copyright (c) 2021 Jörn Heusipp <osmanx@problemloesungsmaschine.de>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved.  This file is offered as-is, without any
+#   warranty.
+
+#serial 18
+
+dnl  This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
+dnl  (serial version number 13).
+
+AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
+  m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
+        [$1], [14], [ax_cxx_compile_alternatives="14 1y"],
+        [$1], [17], [ax_cxx_compile_alternatives="17 1z"],
+        [$1], [20], [ax_cxx_compile_alternatives="20"],
+        [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
+  m4_if([$2], [], [],
+        [$2], [ext], [],
+        [$2], [noext], [],
+        [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
+  m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
+        [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
+        [$3], [optional], [ax_cxx_compile_cxx$1_required=false],
+        [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
+  AC_LANG_PUSH([C++])dnl
+  ac_success=no
+
+  m4_if([$2], [], [dnl
+    AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
+                  ax_cv_cxx_compile_cxx$1,
+      [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+        [ax_cv_cxx_compile_cxx$1=yes],
+        [ax_cv_cxx_compile_cxx$1=no])])
+    if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
+      ac_success=yes
+    fi])
+
+  m4_if([$2], [noext], [], [dnl
+  if test x$ac_success = xno; then
+    for alternative in ${ax_cxx_compile_alternatives}; do
+      switch="-std=gnu++${alternative}"
+      cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
+      AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
+                     $cachevar,
+        [ac_save_CXX="$CXX"
+         CXX="$CXX $switch"
+         AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+          [eval $cachevar=yes],
+          [eval $cachevar=no])
+         CXX="$ac_save_CXX"])
+      if eval test x\$$cachevar = xyes; then
+        CXX="$CXX $switch"
+        if test -n "$CXXCPP" ; then
+          CXXCPP="$CXXCPP $switch"
+        fi
+        ac_success=yes
+        break
+      fi
+    done
+  fi])
+
+  m4_if([$2], [ext], [], [dnl
+  if test x$ac_success = xno; then
+    dnl HP's aCC needs +std=c++11 according to:
+    dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
+    dnl Cray's crayCC needs "-h std=c++11"
+    dnl MSVC needs -std:c++NN for C++17 and later (default is C++14)
+    for alternative in ${ax_cxx_compile_alternatives}; do
+      for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}" MSVC; do
+        if test x"$switch" = xMSVC; then
+          dnl AS_TR_SH maps both `:` and `=` to `_` so -std:c++17 would collide
+          dnl with -std=c++17.  We suffix the cache variable name with _MSVC to
+          dnl avoid this.
+          switch=-std:c++${alternative}
+          cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_${switch}_MSVC])
+        else
+          cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
+        fi
+        AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
+                       $cachevar,
+          [ac_save_CXX="$CXX"
+           CXX="$CXX $switch"
+           AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+            [eval $cachevar=yes],
+            [eval $cachevar=no])
+           CXX="$ac_save_CXX"])
+        if eval test x\$$cachevar = xyes; then
+          CXX="$CXX $switch"
+          if test -n "$CXXCPP" ; then
+            CXXCPP="$CXXCPP $switch"
+          fi
+          ac_success=yes
+          break
+        fi
+      done
+      if test x$ac_success = xyes; then
+        break
+      fi
+    done
+  fi])
+  AC_LANG_POP([C++])
+  if test x$ax_cxx_compile_cxx$1_required = xtrue; then
+    if test x$ac_success = xno; then
+      AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
+    fi
+  fi
+  if test x$ac_success = xno; then
+    HAVE_CXX$1=0
+    AC_MSG_NOTICE([No compiler with C++$1 support was found])
+  else
+    HAVE_CXX$1=1
+    AC_DEFINE(HAVE_CXX$1,1,
+              [define if the compiler supports basic C++$1 syntax])
+  fi
+  AC_SUBST(HAVE_CXX$1)
+])
+
+
+dnl  Test body for checking C++11 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+)
+
+dnl  Test body for checking C++14 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
+)
+
+dnl  Test body for checking C++17 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_17
+)
+
+dnl  Test body for checking C++20 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_20],
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_17
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_20
+)
+
+
+dnl  Tests for new features in C++11
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
+
+// If the compiler admits that it is not ready for C++11, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+// MSVC always sets __cplusplus to 199711L in older versions; newer versions
+// only set it correctly if /Zc:__cplusplus is specified as well as a
+// /std:c++NN switch:
+// https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/
+#elif __cplusplus < 201103L && !defined _MSC_VER
+
+#error "This is not a C++11 compiler"
+
+#else
+
+namespace cxx11
+{
+
+  namespace test_static_assert
+  {
+
+    template <typename T>
+    struct check
+    {
+      static_assert(sizeof(int) <= sizeof(T), "not big enough");
+    };
+
+  }
+
+  namespace test_final_override
+  {
+
+    struct Base
+    {
+      virtual ~Base() {}
+      virtual void f() {}
+    };
+
+    struct Derived : public Base
+    {
+      virtual ~Derived() override {}
+      virtual void f() override {}
+    };
+
+  }
+
+  namespace test_double_right_angle_brackets
+  {
+
+    template < typename T >
+    struct check {};
+
+    typedef check<void> single_type;
+    typedef check<check<void>> double_type;
+    typedef check<check<check<void>>> triple_type;
+    typedef check<check<check<check<void>>>> quadruple_type;
+
+  }
+
+  namespace test_decltype
+  {
+
+    int
+    f()
+    {
+      int a = 1;
+      decltype(a) b = 2;
+      return a + b;
+    }
+
+  }
+
+  namespace test_type_deduction
+  {
+
+    template < typename T1, typename T2 >
+    struct is_same
+    {
+      static const bool value = false;
+    };
+
+    template < typename T >
+    struct is_same<T, T>
+    {
+      static const bool value = true;
+    };
+
+    template < typename T1, typename T2 >
+    auto
+    add(T1 a1, T2 a2) -> decltype(a1 + a2)
+    {
+      return a1 + a2;
+    }
+
+    int
+    test(const int c, volatile int v)
+    {
+      static_assert(is_same<int, decltype(0)>::value == true, "");
+      static_assert(is_same<int, decltype(c)>::value == false, "");
+      static_assert(is_same<int, decltype(v)>::value == false, "");
+      auto ac = c;
+      auto av = v;
+      auto sumi = ac + av + 'x';
+      auto sumf = ac + av + 1.0;
+      static_assert(is_same<int, decltype(ac)>::value == true, "");
+      static_assert(is_same<int, decltype(av)>::value == true, "");
+      static_assert(is_same<int, decltype(sumi)>::value == true, "");
+      static_assert(is_same<int, decltype(sumf)>::value == false, "");
+      static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
+      return (sumf > 0.0) ? sumi : add(c, v);
+    }
+
+  }
+
+  namespace test_noexcept
+  {
+
+    int f() { return 0; }
+    int g() noexcept { return 0; }
+
+    static_assert(noexcept(f()) == false, "");
+    static_assert(noexcept(g()) == true, "");
+
+  }
+
+  namespace test_constexpr
+  {
+
+    template < typename CharT >
+    unsigned long constexpr
+    strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
+    {
+      return *s ? strlen_c_r(s + 1, acc + 1) : acc;
+    }
+
+    template < typename CharT >
+    unsigned long constexpr
+    strlen_c(const CharT *const s) noexcept
+    {
+      return strlen_c_r(s, 0UL);
+    }
+
+    static_assert(strlen_c("") == 0UL, "");
+    static_assert(strlen_c("1") == 1UL, "");
+    static_assert(strlen_c("example") == 7UL, "");
+    static_assert(strlen_c("another\0example") == 7UL, "");
+
+  }
+
+  namespace test_rvalue_references
+  {
+
+    template < int N >
+    struct answer
+    {
+      static constexpr int value = N;
+    };
+
+    answer<1> f(int&)       { return answer<1>(); }
+    answer<2> f(const int&) { return answer<2>(); }
+    answer<3> f(int&&)      { return answer<3>(); }
+
+    void
+    test()
+    {
+      int i = 0;
+      const int c = 0;
+      static_assert(decltype(f(i))::value == 1, "");
+      static_assert(decltype(f(c))::value == 2, "");
+      static_assert(decltype(f(0))::value == 3, "");
+    }
+
+  }
+
+  namespace test_uniform_initialization
+  {
+
+    struct test
+    {
+      static const int zero {};
+      static const int one {1};
+    };
+
+    static_assert(test::zero == 0, "");
+    static_assert(test::one == 1, "");
+
+  }
+
+  namespace test_lambdas
+  {
+
+    void
+    test1()
+    {
+      auto lambda1 = [](){};
+      auto lambda2 = lambda1;
+      lambda1();
+      lambda2();
+    }
+
+    int
+    test2()
+    {
+      auto a = [](int i, int j){ return i + j; }(1, 2);
+      auto b = []() -> int { return '0'; }();
+      auto c = [=](){ return a + b; }();
+      auto d = [&](){ return c; }();
+      auto e = [a, &b](int x) mutable {
+        const auto identity = [](int y){ return y; };
+        for (auto i = 0; i < a; ++i)
+          a += b--;
+        return x + identity(a + b);
+      }(0);
+      return a + b + c + d + e;
+    }
+
+    int
+    test3()
+    {
+      const auto nullary = [](){ return 0; };
+      const auto unary = [](int x){ return x; };
+      using nullary_t = decltype(nullary);
+      using unary_t = decltype(unary);
+      const auto higher1st = [](nullary_t f){ return f(); };
+      const auto higher2nd = [unary](nullary_t f1){
+        return [unary, f1](unary_t f2){ return f2(unary(f1())); };
+      };
+      return higher1st(nullary) + higher2nd(nullary)(unary);
+    }
+
+  }
+
+  namespace test_variadic_templates
+  {
+
+    template <int...>
+    struct sum;
+
+    template <int N0, int... N1toN>
+    struct sum<N0, N1toN...>
+    {
+      static constexpr auto value = N0 + sum<N1toN...>::value;
+    };
+
+    template <>
+    struct sum<>
+    {
+      static constexpr auto value = 0;
+    };
+
+    static_assert(sum<>::value == 0, "");
+    static_assert(sum<1>::value == 1, "");
+    static_assert(sum<23>::value == 23, "");
+    static_assert(sum<1, 2>::value == 3, "");
+    static_assert(sum<5, 5, 11>::value == 21, "");
+    static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
+
+  }
+
+  // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
+  // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
+  // because of this.
+  namespace test_template_alias_sfinae
+  {
+
+    struct foo {};
+
+    template<typename T>
+    using member = typename T::member_type;
+
+    template<typename T>
+    void func(...) {}
+
+    template<typename T>
+    void func(member<T>*) {}
+
+    void test();
+
+    void test() { func<foo>(0); }
+
+  }
+
+}  // namespace cxx11
+
+#endif  // __cplusplus >= 201103L
+
+]])
+
+
+dnl  Tests for new features in C++14
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
+
+// If the compiler admits that it is not ready for C++14, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201402L && !defined _MSC_VER
+
+#error "This is not a C++14 compiler"
+
+#else
+
+namespace cxx14
+{
+
+  namespace test_polymorphic_lambdas
+  {
+
+    int
+    test()
+    {
+      const auto lambda = [](auto&&... args){
+        const auto istiny = [](auto x){
+          return (sizeof(x) == 1UL) ? 1 : 0;
+        };
+        const int aretiny[] = { istiny(args)... };
+        return aretiny[0];
+      };
+      return lambda(1, 1L, 1.0f, '1');
+    }
+
+  }
+
+  namespace test_binary_literals
+  {
+
+    constexpr auto ivii = 0b0000000000101010;
+    static_assert(ivii == 42, "wrong value");
+
+  }
+
+  namespace test_generalized_constexpr
+  {
+
+    template < typename CharT >
+    constexpr unsigned long
+    strlen_c(const CharT *const s) noexcept
+    {
+      auto length = 0UL;
+      for (auto p = s; *p; ++p)
+        ++length;
+      return length;
+    }
+
+    static_assert(strlen_c("") == 0UL, "");
+    static_assert(strlen_c("x") == 1UL, "");
+    static_assert(strlen_c("test") == 4UL, "");
+    static_assert(strlen_c("another\0test") == 7UL, "");
+
+  }
+
+  namespace test_lambda_init_capture
+  {
+
+    int
+    test()
+    {
+      auto x = 0;
+      const auto lambda1 = [a = x](int b){ return a + b; };
+      const auto lambda2 = [a = lambda1(x)](){ return a; };
+      return lambda2();
+    }
+
+  }
+
+  namespace test_digit_separators
+  {
+
+    constexpr auto ten_million = 100'000'000;
+    static_assert(ten_million == 100000000, "");
+
+  }
+
+  namespace test_return_type_deduction
+  {
+
+    auto f(int& x) { return x; }
+    decltype(auto) g(int& x) { return x; }
+
+    template < typename T1, typename T2 >
+    struct is_same
+    {
+      static constexpr auto value = false;
+    };
+
+    template < typename T >
+    struct is_same<T, T>
+    {
+      static constexpr auto value = true;
+    };
+
+    int
+    test()
+    {
+      auto x = 0;
+      static_assert(is_same<int, decltype(f(x))>::value, "");
+      static_assert(is_same<int&, decltype(g(x))>::value, "");
+      return x;
+    }
+
+  }
+
+}  // namespace cxx14
+
+#endif  // __cplusplus >= 201402L
+
+]])
+
+
+dnl  Tests for new features in C++17
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
+
+// If the compiler admits that it is not ready for C++17, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201703L && !defined _MSC_VER
+
+#error "This is not a C++17 compiler"
+
+#else
+
+#include <initializer_list>
+#include <utility>
+#include <type_traits>
+
+namespace cxx17
+{
+
+  namespace test_constexpr_lambdas
+  {
+
+    constexpr int foo = [](){return 42;}();
+
+  }
+
+  namespace test::nested_namespace::definitions
+  {
+
+  }
+
+  namespace test_fold_expression
+  {
+
+    template<typename... Args>
+    int multiply(Args... args)
+    {
+      return (args * ... * 1);
+    }
+
+    template<typename... Args>
+    bool all(Args... args)
+    {
+      return (args && ...);
+    }
+
+  }
+
+  namespace test_extended_static_assert
+  {
+
+    static_assert (true);
+
+  }
+
+  namespace test_auto_brace_init_list
+  {
+
+    auto foo = {5};
+    auto bar {5};
+
+    static_assert(std::is_same<std::initializer_list<int>, decltype(foo)>::value);
+    static_assert(std::is_same<int, decltype(bar)>::value);
+  }
+
+  namespace test_typename_in_template_template_parameter
+  {
+
+    template<template<typename> typename X> struct D;
+
+  }
+
+  namespace test_fallthrough_nodiscard_maybe_unused_attributes
+  {
+
+    int f1()
+    {
+      return 42;
+    }
+
+    [[nodiscard]] int f2()
+    {
+      [[maybe_unused]] auto unused = f1();
+
+      switch (f1())
+      {
+      case 17:
+        f1();
+        [[fallthrough]];
+      case 42:
+        f1();
+      }
+      return f1();
+    }
+
+  }
+
+  namespace test_extended_aggregate_initialization
+  {
+
+    struct base1
+    {
+      int b1, b2 = 42;
+    };
+
+    struct base2
+    {
+      base2() {
+        b3 = 42;
+      }
+      int b3;
+    };
+
+    struct derived : base1, base2
+    {
+        int d;
+    };
+
+    derived d1 {{1, 2}, {}, 4};  // full initialization
+    derived d2 {{}, {}, 4};      // value-initialized bases
+
+  }
+
+  namespace test_general_range_based_for_loop
+  {
+
+    struct iter
+    {
+      int i;
+
+      int& operator* ()
+      {
+        return i;
+      }
+
+      const int& operator* () const
+      {
+        return i;
+      }
+
+      iter& operator++()
+      {
+        ++i;
+        return *this;
+      }
+    };
+
+    struct sentinel
+    {
+      int i;
+    };
+
+    bool operator== (const iter& i, const sentinel& s)
+    {
+      return i.i == s.i;
+    }
+
+    bool operator!= (const iter& i, const sentinel& s)
+    {
+      return !(i == s);
+    }
+
+    struct range
+    {
+      iter begin() const
+      {
+        return {0};
+      }
+
+      sentinel end() const
+      {
+        return {5};
+      }
+    };
+
+    void f()
+    {
+      range r {};
+
+      for (auto i : r)
+      {
+        [[maybe_unused]] auto v = i;
+      }
+    }
+
+  }
+
+  namespace test_lambda_capture_asterisk_this_by_value
+  {
+
+    struct t
+    {
+      int i;
+      int foo()
+      {
+        return [*this]()
+        {
+          return i;
+        }();
+      }
+    };
+
+  }
+
+  namespace test_enum_class_construction
+  {
+
+    enum class byte : unsigned char
+    {};
+
+    byte foo {42};
+
+  }
+
+  namespace test_constexpr_if
+  {
+
+    template <bool cond>
+    int f ()
+    {
+      if constexpr(cond)
+      {
+        return 13;
+      }
+      else
+      {
+        return 42;
+      }
+    }
+
+  }
+
+  namespace test_selection_statement_with_initializer
+  {
+
+    int f()
+    {
+      return 13;
+    }
+
+    int f2()
+    {
+      if (auto i = f(); i > 0)
+      {
+        return 3;
+      }
+
+      switch (auto i = f(); i + 4)
+      {
+      case 17:
+        return 2;
+
+      default:
+        return 1;
+      }
+    }
+
+  }
+
+  namespace test_template_argument_deduction_for_class_templates
+  {
+
+    template <typename T1, typename T2>
+    struct pair
+    {
+      pair (T1 p1, T2 p2)
+        : m1 {p1},
+          m2 {p2}
+      {}
+
+      T1 m1;
+      T2 m2;
+    };
+
+    void f()
+    {
+      [[maybe_unused]] auto p = pair{13, 42u};
+    }
+
+  }
+
+  namespace test_non_type_auto_template_parameters
+  {
+
+    template <auto n>
+    struct B
+    {};
+
+    B<5> b1;
+    B<'a'> b2;
+
+  }
+
+  namespace test_structured_bindings
+  {
+
+    int arr[2] = { 1, 2 };
+    std::pair<int, int> pr = { 1, 2 };
+
+    auto f1() -> int(&)[2]
+    {
+      return arr;
+    }
+
+    auto f2() -> std::pair<int, int>&
+    {
+      return pr;
+    }
+
+    struct S
+    {
+      int x1 : 2;
+      volatile double y1;
+    };
+
+    S f3()
+    {
+      return {};
+    }
+
+    auto [ x1, y1 ] = f1();
+    auto& [ xr1, yr1 ] = f1();
+    auto [ x2, y2 ] = f2();
+    auto& [ xr2, yr2 ] = f2();
+    const auto [ x3, y3 ] = f3();
+
+  }
+
+  namespace test_exception_spec_type_system
+  {
+
+    struct Good {};
+    struct Bad {};
+
+    void g1() noexcept;
+    void g2();
+
+    template<typename T>
+    Bad
+    f(T*, T*);
+
+    template<typename T1, typename T2>
+    Good
+    f(T1*, T2*);
+
+    static_assert (std::is_same_v<Good, decltype(f(g1, g2))>);
+
+  }
+
+  namespace test_inline_variables
+  {
+
+    template<class T> void f(T)
+    {}
+
+    template<class T> inline T g(T)
+    {
+      return T{};
+    }
+
+    template<> inline void f<>(int)
+    {}
+
+    template<> int g<>(int)
+    {
+      return 5;
+    }
+
+  }
+
+}  // namespace cxx17
+
+#endif  // __cplusplus < 201703L && !defined _MSC_VER
+
+]])
+
+
+dnl  Tests for new features in C++20
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_20], [[
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 202002L && !defined _MSC_VER
+
+#error "This is not a C++20 compiler"
+
+#else
+
+#include <version>
+
+namespace cxx20
+{
+
+// As C++20 supports feature test macros in the standard, there is no
+// immediate need to actually test for feature availability on the
+// Autoconf side.
+
+}  // namespace cxx20
+
+#endif  // __cplusplus < 202002L && !defined _MSC_VER
+
+]])
index c3a3ce5..9d12923 100644 (file)
@@ -6,7 +6,7 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017-2022 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2017-2024 Sebastian Pipping <sebastian@pipping.org>
 # Copyright (c) 2017      Stephen Groat <stephen@groat.us>
 # Copyright (c) 2017      Joe Orton <jorton@redhat.com>
 # Licensed under the MIT license:
 
 .PHONY: dist-hook  # not inside conditional to avoid automake warning
 
-if WITH_DOCBOOK
+if WITH_MANPAGE
 dist_man_MANS = xmlwf.1
 
 xmlwf.1: xmlwf.xml
        -rm -f $@
-       $(DOCBOOK_TO_MAN) $<
+       test "x$(DOCBOOK_TO_MAN)" != x && $(DOCBOOK_TO_MAN) $<
        test -f $@ || mv XMLWF.1 $@
-else
+endif
+
+if !WITH_DISTRIBUTABLE_MANPAGE
 dist-hook:
        @echo 'ERROR: Configure with --with-docbook for "make dist".' 1>&2
        @false
 endif
 
-# https://www.gnu.org/software/automake/manual/automake.html#What-Gets-Cleaned
-.PHONY: clean-local
-clean-local: clean-local-check
-
-.PHONY: clean-local-check
-clean-local-check:
-       $(RM) xmlwf.1
+if !WITH_PREBUILT_MANPAGE
+CLEANFILES = xmlwf.1
+endif
 
 EXTRA_DIST = \
     ok.min.css \
index 7fef3ed..8235022 100644 (file)
@@ -22,7 +22,7 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017-2022 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2017-2024 Sebastian Pipping <sebastian@pipping.org>
 # Copyright (c) 2017      Stephen Groat <stephen@groat.us>
 # Copyright (c) 2017      Joe Orton <jorton@redhat.com>
 # Licensed under the MIT license:
@@ -132,6 +132,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
        $(top_srcdir)/conftools/ax-append-compile-flags.m4 \
        $(top_srcdir)/conftools/ax-append-link-flags.m4 \
        $(top_srcdir)/conftools/expatcfg-compiler-supports-visibility.m4 \
+       $(top_srcdir)/conftools/ax-cxx-compile-stdcxx.m4 \
+       $(top_srcdir)/conftools/ax-cxx-compile-stdcxx-11.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
@@ -241,6 +243,7 @@ FGREP = @FGREP@
 FILECMD = @FILECMD@
 FILEMAP = @FILEMAP@
 GREP = @GREP@
+HAVE_CXX11 = @HAVE_CXX11@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
@@ -260,6 +263,7 @@ LIPO = @LIPO@
 LN_S = @LN_S@
 LTLIBOBJS = @LTLIBOBJS@
 LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
 MAKEINFO = @MAKEINFO@
 MANIFEST_TOOL = @MANIFEST_TOOL@
 MKDIR_P = @MKDIR_P@
@@ -341,7 +345,8 @@ target_alias = @target_alias@
 top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
-@WITH_DOCBOOK_TRUE@dist_man_MANS = xmlwf.1
+@WITH_MANPAGE_TRUE@dist_man_MANS = xmlwf.1
+@WITH_PREBUILT_MANPAGE_FALSE@CLEANFILES = xmlwf.1
 EXTRA_DIST = \
     ok.min.css \
     reference.html \
@@ -351,7 +356,7 @@ EXTRA_DIST = \
 all: all-am
 
 .SUFFIXES:
-$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
        @for dep in $?; do \
          case '$(am__configure_deps)' in \
            *$$dep*) \
@@ -375,9 +380,9 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
        cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
 
-$(top_srcdir)/configure:  $(am__configure_deps)
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
        cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
        cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
 $(am__aclocal_m4_deps):
 
@@ -435,7 +440,7 @@ ctags CTAGS:
 
 cscope cscopelist:
 
-@WITH_DOCBOOK_TRUE@dist-hook:
+@WITH_DISTRIBUTABLE_MANPAGE_TRUE@dist-hook:
 distdir: $(BUILT_SOURCES)
        $(MAKE) $(AM_MAKEFLAGS) distdir-am
 
@@ -501,6 +506,7 @@ install-strip:
 mostlyclean-generic:
 
 clean-generic:
+       -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
 
 distclean-generic:
        -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
@@ -511,7 +517,7 @@ maintainer-clean-generic:
        @echo "it deletes files that may require special tools to rebuild."
 clean: clean-am
 
-clean-am: clean-generic clean-libtool clean-local mostlyclean-am
+clean-am: clean-generic clean-libtool mostlyclean-am
 
 distclean: distclean-am
        -rm -f Makefile
@@ -580,38 +586,31 @@ uninstall-man: uninstall-man1
 .MAKE: install-am install-strip
 
 .PHONY: all all-am check check-am clean clean-generic clean-libtool \
-       clean-local cscopelist-am ctags-am dist-hook distclean \
-       distclean-generic distclean-libtool distdir dvi dvi-am html \
-       html-am info info-am install install-am install-data \
-       install-data-am install-dvi install-dvi-am install-exec \
-       install-exec-am install-html install-html-am install-info \
-       install-info-am install-man install-man1 install-pdf \
-       install-pdf-am install-ps install-ps-am install-strip \
-       installcheck installcheck-am installdirs maintainer-clean \
-       maintainer-clean-generic mostlyclean mostlyclean-generic \
-       mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \
-       uninstall-am uninstall-man uninstall-man1
+       cscopelist-am ctags-am dist-hook distclean distclean-generic \
+       distclean-libtool distdir dvi dvi-am html html-am info info-am \
+       install install-am install-data install-data-am install-dvi \
+       install-dvi-am install-exec install-exec-am install-html \
+       install-html-am install-info install-info-am install-man \
+       install-man1 install-pdf install-pdf-am install-ps \
+       install-ps-am install-strip installcheck installcheck-am \
+       installdirs maintainer-clean maintainer-clean-generic \
+       mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
+       ps ps-am tags-am uninstall uninstall-am uninstall-man \
+       uninstall-man1
 
 .PRECIOUS: Makefile
 
 
 .PHONY: dist-hook  # not inside conditional to avoid automake warning
 
-@WITH_DOCBOOK_TRUE@xmlwf.1: xmlwf.xml
-@WITH_DOCBOOK_TRUE@    -rm -f $@
-@WITH_DOCBOOK_TRUE@    $(DOCBOOK_TO_MAN) $<
-@WITH_DOCBOOK_TRUE@    test -f $@ || mv XMLWF.1 $@
-@WITH_DOCBOOK_FALSE@dist-hook:
-@WITH_DOCBOOK_FALSE@   @echo 'ERROR: Configure with --with-docbook for "make dist".' 1>&2
-@WITH_DOCBOOK_FALSE@   @false
-
-# https://www.gnu.org/software/automake/manual/automake.html#What-Gets-Cleaned
-.PHONY: clean-local
-clean-local: clean-local-check
-
-.PHONY: clean-local-check
-clean-local-check:
-       $(RM) xmlwf.1
+@WITH_MANPAGE_TRUE@xmlwf.1: xmlwf.xml
+@WITH_MANPAGE_TRUE@    -rm -f $@
+@WITH_MANPAGE_TRUE@    test "x$(DOCBOOK_TO_MAN)" != x && $(DOCBOOK_TO_MAN) $<
+@WITH_MANPAGE_TRUE@    test -f $@ || mv XMLWF.1 $@
+
+@WITH_DISTRIBUTABLE_MANPAGE_FALSE@dist-hook:
+@WITH_DISTRIBUTABLE_MANPAGE_FALSE@     @echo 'ERROR: Configure with --with-docbook for "make dist".' 1>&2
+@WITH_DISTRIBUTABLE_MANPAGE_FALSE@     @false
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
index 8b5f86e..d324fab 100644 (file)
@@ -1,2 +1,2 @@
-/*! OK.css v1.0.3 | MIT License | github.com/andrewh0/okcss */@import url("https://rsms.me/inter/inter.css");
-/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none}:root{--ok-sans:"Inter",system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Open Sans","Helvetica Neue","Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--ok-mono:SFMono-Regular,Menlo,Monaco,Consolas,"Ubuntu Mono","Liberation Mono","Courier New",Courier,monospace;--ok-fw-0:400;--ok-fw-1:600;--ok-fw-2:700;--ok-fw-3:700;--ok-fs-0:2.5rem;--ok-fs-1:2rem;--ok-fs-2:1.5rem;--ok-fs-3:1.25rem;--ok-fs-4:1rem;--ok-fs-5:0.75rem;--ok-br:0.25rem;--ok-s-0:0;--ok-s-1:0.25rem;--ok-s-2:0.5rem;--ok-s-3:1rem;--ok-s-4:1.5rem;--ok-s-5:2rem;--ok-s-6:2.5rem;--ok-lh-body:1.5;--ok-lh-heading:1.25;--ok-t-hl:#ffcf30;--ok-accent-0:#3e67fa;--ok-accent-1:#4788ff;--ok-tc-accent:#3173de;--ok-tc-code:#c23a30;--ok-tc-0:#000;--ok-tc-1:#747474;--ok-tc-2:#848484;--ok-bg-0:#fff;--ok-bg-1:#f0f0f0;--ok-bg-2:#ccc;--ok-b-0:1px solid #cbcbcb;--ok-b-1:1px solid #848484;--ok-down-0:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='8' viewBox='0 0 12 8' width='24' fill='none'%3E%3Cpath d='M6 6l4-4h1v1-1L6 7 1 2h1l4 4z' fill='%23747474'/%3E%3C/svg%3E");--ok-down-1:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='8' viewBox='0 0 12 8' width='24' fill='none'%3E%3Cpath d='M6 6l4-4h1v1-1L6 7 1 2h1l4 4z' fill='%23848484'/%3E%3C/svg%3E")}@media (prefers-color-scheme:dark){:root{--ok-tc-accent:#5c9aff;--ok-tc-code:#ed5853;--ok-tc-0:#fff;--ok-tc-1:#ababab;--ok-tc-2:#929292;--ok-bg-0:#000;--ok-bg-1:#212121;--ok-bg-2:#3e3e3e;--ok-b-0:1px solid #747474;--ok-b-1:1px solid #929292;--ok-down-0:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='8' viewBox='0 0 12 8' width='24' fill='none'%3E%3Cpath d='M6 6l4-4h1v1-1L6 7 1 2h1l4 4z' fill='%23ababab'/%3E%3C/svg%3E");--ok-down-1:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='8' viewBox='0 0 12 8' width='24' fill='none'%3E%3Cpath d='M6 6l4-4h1v1-1L6 7 1 2h1l4 4z' fill='%23929292'/%3E%3C/svg%3E")}}*,:after,:before{box-sizing:border-box}*{margin:0;padding:0}html{font-family:var(--ok-sans);line-sizing:normal;line-height:var(--ok-lh-body);font-weight:var(--ok-fw-0);color:var(--ok-tc-0);background-color:var(--ok-bg-0);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%;text-rendering:optimizeLegibility}@supports (font-variation-settings:normal){html{font-family:Inter var,var(--ok-sans)}}button,input,optgroup,select,textarea{font-family:inherit;line-height:var(--ok-lh-body)}[role=button],button{cursor:pointer}audio,canvas,embed,iframe,img,input,object,select,svg,textarea,video{display:block}img,video{max-width:100%;height:auto}body{padding:var(--ok-s-3);max-width:80ch;margin:auto}h1{font-size:var(--ok-fs-0);margin:0}h2{font-size:var(--ok-fs-1)}h3{font-size:var(--ok-fs-2)}h4{font-size:var(--ok-fs-3)}h5{font-size:var(--ok-fs-4)}h6{font-size:var(--ok-fs-5);text-transform:uppercase;font-weight:var(--ok-fw-1)}h1,h2,h3,h4,h5,h6{line-height:var(--ok-lh-heading);font-weight:var(--ok-fw-3)}p{font-size:var(--ok-fs-4)}small{font-size:var(--ok-fs-5)}b,dt,strong{font-weight:var(--ok-fw-2)}address,article,aside,audio,blockquote,button,canvas,dd,details,dialog,div,dl,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,img,input,main,nav,object,ol,p,pre,section,select,summary,svg,table,textarea,ul,video{margin-bottom:var(--ok-s-4)}address:last-child,article:last-child,aside:last-child,blockquote:last-child,dd:last-child,details:last-child,dialog:last-child,div:last-child,dl:last-child,dt:last-child,fieldset:last-child,figcaption:last-child,figure:last-child,footer:last-child,form:last-child,h1:last-child,h2:last-child,h3:last-child,h4:last-child,h5:last-child,h6:last-child,header:last-child,hgroup:last-child,hr:last-child,input[type=checkbox],input[type=radio],main:last-child,nav:last-child,ol:last-child,p:last-child,pre:last-child,section:last-child,table:last-child,ul:last-child{margin-bottom:0}fieldset{padding:var(--ok-s-3);border:var(--ok-b-0);border-radius:var(--ok-br)}legend{font-weight:var(--ok-fw-1);text-transform:uppercase;font-size:var(--ok-fs-5)}input,select{padding:var(--ok-s-2);background:var(--ok-bg-0);border:var(--ok-b-0);border-radius:var(--ok-br);color:var(--ok-tc-0);min-width:25ch}input:disabled,select:disabled,textarea:disabled{color:var(--ok-tc-2)}input::placeholder,textarea::placeholder{color:var(--ok-tc-2)}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:var(--ok-tc-2)}input::-moz-placeholder,textarea::-moz-placeholder{color:var(--ok-tc-2)}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:var(--ok-tc-2)}input::-ms-input-placeholder,textarea::-ms-input-placeholder{color:var(--ok-tc-2)}input::-webkit-clear-button,input::-webkit-inner-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-results-button{display:none}input::-ms-clear,input::-ms-reveal{display:none}input:disabled,textarea:disabled{background-color:var(--ok-bg-1)}input[type=search]{-webkit-appearance:none;-moz-appearance:none;appearance:none}input[type=file]{max-width:300px}input[type=number]{-moz-appearance:textfield}input[type=checkbox],input[type=radio]{display:inline;min-width:auto}input[type=color]{height:2.5rem}input[type=date],input[type=datetime-local],input[type=month],input[type=time],input[type=week]{height:2.5rem;-webkit-appearance:none;-moz-appearance:none;appearance:none;position:relative}input[type=date]:before,input[type=datetime-local]:before,input[type=month]:before,input[type=time]:before,input[type=week]:before{position:absolute;right:0;top:0;background-image:var(--ok-down-0);background-repeat:no-repeat;background-position:right .2em top 50%;background-size:2em auto;width:2.5rem;height:calc(2.5rem - 2px);content:""}input[type=date]:disabled:before,input[type=datetime-local]:disabled:before,input[type=month]:disabled:before,input[type=time]:disabled:before,input[type=week]:disabled:before{background-image:var(--ok-down-1)}input::-webkit-calendar-picker-indicator{position:absolute;top:0;right:0;background-color:transparent;cursor:pointer;padding:0;width:2.5rem;height:calc(2.5rem - 2px);content:"";opacity:0}input[type=range]{overflow:visible;line-height:inherit;font-family:inherit;-webkit-appearance:none;-moz-appearance:none;appearance:none;margin:0;outline:none;cursor:pointer;padding:0;vertical-align:middle;border:none;min-height:2rem;background-color:transparent}input[type=range]::-webkit-slider-runnable-track{cursor:pointer;-webkit-appearance:none;border-radius:var(--ok-br);border:var(--ok-b-0);background-color:var(--ok-bg-1);background-image:linear-gradient(var(--ok-bg-1),var(--ok-bg-1));width:100%;height:.5rem;color:transparent;box-sizing:border-box;position:relative}input[type=range]::-moz-range-track{cursor:pointer;-moz-appearance:none;border-radius:var(--ok-br);border:var(--ok-b-0);background-color:var(--ok-bg-1);background-image:linear-gradient(var(--ok-bg-1),var(--ok-bg-1));width:100%;height:.5rem;color:transparent;box-sizing:border-box}input[type=range]::-ms-track{cursor:pointer;-ms-appearance:none;border-radius:var(--ok-br);border:var(--ok-b-0);background-color:var(--ok-bg-1);background-image:linear-gradient(var(--ok-bg-1),var(--ok-bg-1));width:100%;height:.5rem;color:transparent;box-sizing:border-box}input[type=range]::-ms-fill-lower{background:transparent}input[type=range]::-webkit-slider-thumb{cursor:pointer;-webkit-appearance:none;border-radius:50%;background-color:var(--ok-accent-0);width:1.5rem;height:1.5rem;margin-top:calc(-.5rem - 1px)}input[type=range]::-moz-range-thumb{cursor:pointer;-moz-appearance:none;border:none;border-radius:50%;background-color:var(--ok-accent-0);width:1.5rem;height:1.5rem}input[type=range]::-ms-thumb{cursor:pointer;-ms-appearance:none;border-radius:50%;background-color:var(--ok-accent-0);width:1.5rem;height:1.5rem;transform:translateY(.25rem)}input[type=range]:focus:not(:disabled)::-webkit-slider-thumb{background-color:var(--ok-accent-1)}input[type=range]:focus:not(:disabled)::-moz-range-thumb{background-color:var(--ok-accent-1)}input[type=range]:focus:not(:disabled)::-ms-thumb{background-color:var(--ok-accent-1)}input[type=range]:disabled{cursor:default}input[type=range]:disabled::-webkit-slider-runnable-track{cursor:default}input[type=range]:disabled::-moz-range-track{cursor:default}input[type=range]:disabled::-ms-track{cursor:default}input[type=range]:disabled::-webkit-slider-thumb{background-color:var(--ok-bg-2);cursor:default}input[type=range]:disabled::-moz-range-thumb{background-color:var(--ok-bg-2);cursor:default}input[type=range]:disabled::-ms-thumb{background-color:var(--ok-bg-2);cursor:default}select{-webkit-appearance:none;-moz-appearance:none;appearance:none;border:var(--ok-b-0);border-radius:var(--ok-br);background-color:var(--ok-bg-0);min-height:2.5rem;color:var(--ok-tc-0)}select:not([multiple]){background-image:var(--ok-down-0);background-repeat:no-repeat,repeat;background-position:right .2em top 50%;background-size:2em auto;padding-right:2.5em;height:2.5rem}select::-ms-expand{display:none}select:not([multiple]):disabled{background-image:var(--ok-down-1);background-color:var(--ok-bg-1);cursor:default}select[multiple]{border-radius:var(--ok-br)}select[multiple]:disabled{background-color:var(--ok-bg-1)}select[multiple] option{color:var(--ok-t-)}select[multiple]:disabled option{color:var(--ok-tc-2)}textarea{padding:var(--ok-s-2);resize:vertical;background:var(--ok-bg-0);border:var(--ok-b-0);border-radius:var(--ok-br);color:var(--ok-tc-0);min-height:calc(2.5rem - 2px);min-width:25ch}button,input[type=button],input[type=reset],input[type=submit]{display:inline-block;background-color:var(--ok-accent-0);border-radius:var(--ok-br);color:#fff;font-weight:var(--ok-fw-1);height:2.5rem;border:none;padding:var(--ok-s-2) var(--ok-s-3);white-space:nowrap;min-width:auto}input::-webkit-file-upload-button{display:inline-block;background-color:var(--ok-accent-0);border-radius:var(--ok-br);color:#fff;font-weight:var(--ok-fw-1);height:2.5rem;border:none;padding:var(--ok-s-2) var(--ok-s-3);white-space:nowrap}input:disabled::-webkit-file-upload-button{cursor:default;opacity:.5}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer}input:not(:disabled)::-webkit-file-upload-button{cursor:pointer}button:disabled,input[type=button]:disabled,input[type=reset]:disabled,input[type=submit]:disabled{cursor:default;opacity:.5;background-color:var(--ok-accent-0)}button:focus:not(:disabled),input[type=button]:focus:not(:disabled),input[type=reset]:focus:not(:disabled),input[type=submit]:focus:not(:disabled){background-color:var(--ok-accent-1)}input:not(:disabled):focus::-webkit-file-upload-button{background-color:var(--ok-accent-1)}table{border-collapse:collapse;border-radius:var(--ok-br);display:block;max-width:-webkit-fit-content;max-width:-moz-fit-content;max-width:fit-content;margin-left:auto;margin-right:auto;overflow-x:auto;white-space:nowrap}tfoot,thead{border:var(--ok-b-0)}tfoot tr,thead tr{background-color:var(--ok-bg-1);font-size:var(--ok-fs-5);text-transform:uppercase;color:var(--ok-tc-0)}td,th{border:var(--ok-b-0) 0;text-align:left;padding:.5rem}td{white-space:normal;max-width:20ch}tr{border:var(--ok-b-0)}table caption{font-size:var(--ok-fs-4);font-weight:var(--ok-fw-3);padding:.5rem}code,samp{padding:.2em .4em;color:var(--ok-tc-code)}code,pre,samp{font-family:var(--ok-mono);line-height:var(--ok-lh-body);background-color:var(--ok-bg-1);border-radius:var(--ok-br);text-transform:none}pre{padding:var(--ok-s-3);white-space:pre;overflow-x:auto}pre,var{color:var(--ok-tc-0)}var{font-family:var(--ok-mono);font-style:normal}code pre,pre code{background:inherit;font-size:inherit;color:inherit;border:0;padding:0;margin:0}code pre{display:inline}kbd{background-color:var(--ok-bg-1);border:var(--ok-b-0);border-radius:var(--ok-br);border-bottom:2px solid var(--ok-bg-2);padding:var(--ok-s-1);font-family:var(--ok-sans);color:var(--ok-tc-0)}a{text-decoration:none;font-weight:var(--ok-fw-1)}a,a:visited{color:var(--ok-tc-accent)}ol,ul{padding-left:var(--ok-s-5)}nav ul{text-decoration:none;padding-left:0}nav ul li{display:inline;margin-right:1em}audio,img,video{margin-left:auto;margin-right:auto}img{border-radius:var(--ok-br)}figure>img:not(:last-child){margin-bottom:var(--ok-s-1)}figure>figcaption{text-align:center}figcaption,time{font-size:var(--ok-fs-5);color:var(--ok-tc-1)}mark{padding:.2em .4em;background:var(--ok-t-hl);color:#000;border-radius:var(--ok-br)}iframe{border:var(--ok-b-0);border-radius:var(--ok-br);width:100%}hr{border:none;border-bottom:var(--ok-b-0)}footer{font-size:var(--ok-fs-5)}blockquote,footer{color:var(--ok-tc-1)}blockquote{position:relative;margin-left:0;margin-right:0;padding-left:var(--ok-s-5)}blockquote:before{position:absolute;height:100%;content:"";width:4px;left:0;border-radius:var(--ok-br);background-color:var(--ok-bg-1)}dd{padding-left:var(--ok-s-5)}abbr{cursor:help}@media (hover:hover){a:hover{text-decoration:underline}input:hover:not(:disabled):not(:focus):not([type=submit]):not([type=button]):not([type=reset]):not([type=range]),select:hover:not(:disabled):not(:focus),textarea:hover:not(:disabled):not(:focus){border:var(--ok-b-1)}input[type=range]:hover:not(:disabled)::-webkit-slider-runnable-track{border:var(--ok-b-1)}input[type=range]:hover:not(:disabled)::-moz-range-track{border:var(--ok-b-1)}input[type=range]:hover:not(:disabled)::-ms-track{border:var(--ok-b-1)}select:not([multiple]):not(:disabled):hover{cursor:pointer}button:hover:not(:disabled),input[type=button]:hover:not(:disabled),input[type=reset]:hover:not(:disabled),input[type=submit]:hover:not(:disabled){background-color:var(--ok-accent-1)}input:not(:disabled):hover::-webkit-file-upload-button{background-color:var(--ok-accent-1)}}
\ No newline at end of file
+/*! OK.css v1.2.0 | MIT License | github.com/andrewh0/okcss */@import url("https://rsms.me/inter/inter.css");
+/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none}:root{--ok-sans:"Inter",system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Open Sans","Helvetica Neue","Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--ok-mono:SFMono-Regular,Menlo,Monaco,Consolas,"Ubuntu Mono","Liberation Mono","Courier New",Courier,monospace;--ok-fw-0:400;--ok-fw-1:600;--ok-fw-2:700;--ok-fw-3:700;--ok-fs-0:2.5rem;--ok-fs-1:2rem;--ok-fs-2:1.5rem;--ok-fs-3:1.25rem;--ok-fs-4:1rem;--ok-fs-5:0.75rem;--ok-br:0.25rem;--ok-s-0:0;--ok-s-1:0.25rem;--ok-s-2:0.5rem;--ok-s-3:1rem;--ok-s-4:1.5rem;--ok-s-5:2rem;--ok-s-6:2.5rem;--ok-lh-body:1.5;--ok-lh-heading:1.25;--ok-t-hl:#ffcf30;--ok-accent-0:#3e67fa;--ok-accent-1:#4788ff;--ok-tc-accent:#3173de;--ok-tc-code:#c23a30;--ok-tc-0:#000;--ok-tc-1:#747474;--ok-tc-2:#848484;--ok-bg-0:#fff;--ok-bg-1:#f0f0f0;--ok-bg-2:#ccc;--ok-b-0:1px solid #cbcbcb;--ok-b-1:1px solid #848484;--ok-down-0:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='8' viewBox='0 0 12 8' width='24' fill='none'%3E%3Cpath d='M6 6l4-4h1v1-1L6 7 1 2h1l4 4z' fill='%23747474'/%3E%3C/svg%3E");--ok-down-1:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='8' viewBox='0 0 12 8' width='24' fill='none'%3E%3Cpath d='M6 6l4-4h1v1-1L6 7 1 2h1l4 4z' fill='%23848484'/%3E%3C/svg%3E")}@media (prefers-color-scheme:dark){:root{--ok-tc-accent:#5c9aff;--ok-tc-code:#ed5853;--ok-tc-0:#fff;--ok-tc-1:#ababab;--ok-tc-2:#929292;--ok-bg-0:#000;--ok-bg-1:#212121;--ok-bg-2:#3e3e3e;--ok-b-0:1px solid #747474;--ok-b-1:1px solid #929292;--ok-down-0:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='8' viewBox='0 0 12 8' width='24' fill='none'%3E%3Cpath d='M6 6l4-4h1v1-1L6 7 1 2h1l4 4z' fill='%23ababab'/%3E%3C/svg%3E");--ok-down-1:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='8' viewBox='0 0 12 8' width='24' fill='none'%3E%3Cpath d='M6 6l4-4h1v1-1L6 7 1 2h1l4 4z' fill='%23929292'/%3E%3C/svg%3E")}}*,:after,:before{box-sizing:border-box}*{margin:0;padding:0}html{font-family:var(--ok-sans);line-sizing:normal;line-height:var(--ok-lh-body);font-weight:var(--ok-fw-0);color:var(--ok-tc-0);background-color:var(--ok-bg-0);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%;text-rendering:optimizeLegibility;scroll-behavior:smooth}@supports (font-variation-settings:normal){html{font-family:Inter var,var(--ok-sans)}}button,input,optgroup,select,textarea{font-family:inherit;line-height:var(--ok-lh-body)}[role=button],button{cursor:pointer}audio,canvas,embed,iframe,img,input,object,select,svg,textarea,video{display:block}img,video{max-width:100%;height:auto}body{padding:var(--ok-s-3);max-width:80ch;margin:auto}article{padding-bottom:var(--ok-s-6)}h1{font-size:var(--ok-fs-0);margin:0}h2{font-size:var(--ok-fs-1)}h3{font-size:var(--ok-fs-2)}h4{font-size:var(--ok-fs-3)}h5{font-size:var(--ok-fs-4)}h6{font-size:var(--ok-fs-5);text-transform:uppercase;font-weight:var(--ok-fw-1)}h1,h2,h3,h4,h5,h6{line-height:var(--ok-lh-heading);font-weight:var(--ok-fw-3);margin-top:var(--ok-s-6);margin-bottom:var(--ok-s-3)}h1+*,h2+*,h3+*,h4+*,h5+*,h6+*,hr+*{margin-top:0}p{font-size:var(--ok-fs-4)}small{font-size:var(--ok-fs-5)}b,dt,strong{font-weight:var(--ok-fw-2)}address,article,aside,audio,blockquote,button,canvas,dd,details,dialog,div,dl,embed,fieldset,figcaption,figure,footer,form,header,hgroup,hr,iframe,img,input,main,nav,object,ol,p,pre,section,select,summary,svg,table,textarea,ul,video{margin-bottom:var(--ok-s-4)}address:last-child,article:last-child,aside:last-child,blockquote:last-child,dd:last-child,details:last-child,dialog:last-child,div:last-child,dl:last-child,dt:last-child,fieldset:last-child,figcaption:last-child,figure:last-child,footer:last-child,form:last-child,h1:last-child,h2:last-child,h3:last-child,h4:last-child,h5:last-child,h6:last-child,header:last-child,hgroup:last-child,hr:last-child,input[type=checkbox],input[type=radio],main:last-child,nav:last-child,ol:last-child,p:last-child,pre:last-child,section:last-child,table:last-child,ul:last-child{margin-bottom:0}fieldset{padding:var(--ok-s-3);border:var(--ok-b-0);border-radius:var(--ok-br)}legend{font-weight:var(--ok-fw-1);text-transform:uppercase;font-size:var(--ok-fs-5)}input,select{padding:var(--ok-s-2);background:var(--ok-bg-0);border:var(--ok-b-0);border-radius:var(--ok-br);color:var(--ok-tc-0);min-width:25ch}input:disabled,select:disabled,textarea:disabled{color:var(--ok-tc-2)}input::placeholder,textarea::placeholder{color:var(--ok-tc-2)}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:var(--ok-tc-2)}input::-moz-placeholder,textarea::-moz-placeholder{color:var(--ok-tc-2)}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:var(--ok-tc-2)}input::-ms-input-placeholder,textarea::-ms-input-placeholder{color:var(--ok-tc-2)}input::-webkit-clear-button,input::-webkit-inner-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-results-button{display:none}input::-ms-clear,input::-ms-reveal{display:none}input:disabled,textarea:disabled{background-color:var(--ok-bg-1)}input[type=search]{-webkit-appearance:none;-moz-appearance:none;appearance:none}input[type=file]{max-width:300px}input[type=number]{-moz-appearance:textfield}input[type=checkbox],input[type=radio]{display:inline;min-width:auto}input[type=color]{height:2.5rem}input[type=date],input[type=datetime-local],input[type=month],input[type=time],input[type=week]{height:2.5rem;-webkit-appearance:none;-moz-appearance:none;appearance:none;position:relative}input[type=date]:before,input[type=datetime-local]:before,input[type=month]:before,input[type=time]:before,input[type=week]:before{position:absolute;right:0;top:0;background-image:var(--ok-down-0);background-repeat:no-repeat;background-position:right .2em top 50%;background-size:2em auto;width:2.5rem;height:calc(2.5rem - 2px);content:""}input[type=date]:disabled:before,input[type=datetime-local]:disabled:before,input[type=month]:disabled:before,input[type=time]:disabled:before,input[type=week]:disabled:before{background-image:var(--ok-down-1)}input::-webkit-calendar-picker-indicator{position:absolute;top:0;right:0;background-color:transparent;cursor:pointer;padding:0;width:2.5rem;height:calc(2.5rem - 2px);content:"";opacity:0}input[type=range]{overflow:visible;line-height:inherit;font-family:inherit;-webkit-appearance:none;-moz-appearance:none;appearance:none;margin:0;outline:none;cursor:pointer;padding:0;vertical-align:middle;border:none;min-height:2rem;background-color:transparent}input[type=range]::-webkit-slider-runnable-track{cursor:pointer;-webkit-appearance:none;border-radius:var(--ok-br);border:var(--ok-b-0);background-color:var(--ok-bg-1);background-image:linear-gradient(var(--ok-bg-1),var(--ok-bg-1));width:100%;height:.5rem;color:transparent;box-sizing:border-box;position:relative}input[type=range]::-moz-range-track{cursor:pointer;-moz-appearance:none;border-radius:var(--ok-br);border:var(--ok-b-0);background-color:var(--ok-bg-1);background-image:linear-gradient(var(--ok-bg-1),var(--ok-bg-1));width:100%;height:.5rem;color:transparent;box-sizing:border-box}input[type=range]::-ms-track{cursor:pointer;-ms-appearance:none;border-radius:var(--ok-br);border:var(--ok-b-0);background-color:var(--ok-bg-1);background-image:linear-gradient(var(--ok-bg-1),var(--ok-bg-1));width:100%;height:.5rem;color:transparent;box-sizing:border-box}input[type=range]::-ms-fill-lower{background:transparent}input[type=range]::-webkit-slider-thumb{cursor:pointer;-webkit-appearance:none;border-radius:50%;background-color:var(--ok-accent-0);width:1.5rem;height:1.5rem;margin-top:calc(-.5rem - 1px)}input[type=range]::-moz-range-thumb{cursor:pointer;-moz-appearance:none;border:none;border-radius:50%;background-color:var(--ok-accent-0);width:1.5rem;height:1.5rem}input[type=range]::-ms-thumb{cursor:pointer;-ms-appearance:none;border-radius:50%;background-color:var(--ok-accent-0);width:1.5rem;height:1.5rem;transform:translateY(.25rem)}input[type=range]:focus:not(:disabled)::-webkit-slider-thumb{background-color:var(--ok-accent-1)}input[type=range]:focus:not(:disabled)::-moz-range-thumb{background-color:var(--ok-accent-1)}input[type=range]:focus:not(:disabled)::-ms-thumb{background-color:var(--ok-accent-1)}input[type=range]:disabled{cursor:default}input[type=range]:disabled::-webkit-slider-runnable-track{cursor:default}input[type=range]:disabled::-moz-range-track{cursor:default}input[type=range]:disabled::-ms-track{cursor:default}input[type=range]:disabled::-webkit-slider-thumb{background-color:var(--ok-bg-2);cursor:default}input[type=range]:disabled::-moz-range-thumb{background-color:var(--ok-bg-2);cursor:default}input[type=range]:disabled::-ms-thumb{background-color:var(--ok-bg-2);cursor:default}select{-webkit-appearance:none;-moz-appearance:none;appearance:none;border:var(--ok-b-0);border-radius:var(--ok-br);background-color:var(--ok-bg-0);min-height:2.5rem;color:var(--ok-tc-0)}select:not([multiple]){background-image:var(--ok-down-0);background-repeat:no-repeat,repeat;background-position:right .2em top 50%;background-size:2em auto;padding-right:2.5em;height:2.5rem}select::-ms-expand{display:none}select:not([multiple]):disabled{background-image:var(--ok-down-1);background-color:var(--ok-bg-1);cursor:default}select[multiple]{border-radius:var(--ok-br)}select[multiple]:disabled{background-color:var(--ok-bg-1)}select[multiple] option{color:var(--ok-t-)}select[multiple]:disabled option{color:var(--ok-tc-2)}textarea{padding:var(--ok-s-2);resize:vertical;background:var(--ok-bg-0);border:var(--ok-b-0);border-radius:var(--ok-br);color:var(--ok-tc-0);min-height:calc(2.5rem - 2px);min-width:25ch}button,input[type=button],input[type=reset],input[type=submit]{display:inline-block;background-color:var(--ok-accent-0);border-radius:var(--ok-br);color:#fff;font-weight:var(--ok-fw-1);height:2.5rem;border:none;padding:var(--ok-s-2) var(--ok-s-3);white-space:nowrap;min-width:auto}input::-webkit-file-upload-button{display:inline-block;background-color:var(--ok-accent-0);border-radius:var(--ok-br);color:#fff;font-weight:var(--ok-fw-1);height:2.5rem;border:none;padding:var(--ok-s-2) var(--ok-s-3);white-space:nowrap}input:disabled::-webkit-file-upload-button{cursor:default;opacity:.5}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer}input:not(:disabled)::-webkit-file-upload-button{cursor:pointer}button:disabled,input[type=button]:disabled,input[type=reset]:disabled,input[type=submit]:disabled{cursor:default;opacity:.5;background-color:var(--ok-accent-0)}button:focus:not(:disabled),input[type=button]:focus:not(:disabled),input[type=reset]:focus:not(:disabled),input[type=submit]:focus:not(:disabled){background-color:var(--ok-accent-1)}input:not(:disabled):focus::-webkit-file-upload-button{background-color:var(--ok-accent-1)}table{border-collapse:collapse;display:table;margin-left:auto;margin-right:auto;white-space:nowrap}tfoot,thead{border:var(--ok-b-0)}thead{position:-webkit-sticky;position:sticky;top:0}tfoot tr,thead tr{background-color:var(--ok-bg-1);font-size:var(--ok-fs-5);text-transform:uppercase;color:var(--ok-tc-0)}td,th{border:var(--ok-b-0) 0;text-align:left;padding:.5rem}td{white-space:normal;max-width:20ch}tr{border:var(--ok-b-0)}table caption{font-size:var(--ok-fs-4);font-weight:var(--ok-fw-3);padding:.5rem}code,samp{padding:.2em .4em;color:var(--ok-tc-code)}code,pre,samp{font-family:var(--ok-mono);font-size:87.5%;line-height:var(--ok-lh-body);background-color:var(--ok-bg-1);border-radius:var(--ok-br);text-transform:none}pre{padding:var(--ok-s-3);white-space:pre;overflow-x:auto}pre,var{color:var(--ok-tc-0)}var{font-family:var(--ok-mono);font-style:normal}code pre,pre code{background:inherit;font-size:inherit;color:inherit;border:0;padding:0;margin:0}code pre{display:inline}kbd{background-color:var(--ok-bg-1);border:var(--ok-b-0);border-radius:var(--ok-br);border-bottom:2px solid var(--ok-bg-2);padding:var(--ok-s-1);font-family:var(--ok-sans);color:var(--ok-tc-0)}a{text-decoration:none;font-weight:var(--ok-fw-1)}a,a:visited,a code,a mark,a samp{color:var(--ok-tc-accent)}ol,ul{padding-left:var(--ok-s-5)}nav ul{text-decoration:none;padding-left:0}nav ul li{display:inline;margin-right:1em}li p{margin-bottom:0}li,li p+p{margin-top:.5em}audio,img,video{margin-left:auto;margin-right:auto}img{border-radius:var(--ok-br)}figure>img:not(:last-child){margin-bottom:var(--ok-s-1)}figure>figcaption{text-align:center}figcaption,time{font-size:var(--ok-fs-5);color:var(--ok-tc-1)}mark{padding:.2em .4em;background:var(--ok-t-hl);color:#000;border-radius:var(--ok-br)}iframe{border:var(--ok-b-0);border-radius:var(--ok-br);width:100%}hr{border:none;border-bottom:var(--ok-b-0)}footer{font-size:var(--ok-fs-5)}blockquote,footer{color:var(--ok-tc-1)}blockquote{position:relative;margin-left:0;margin-right:0;padding-left:var(--ok-s-5)}blockquote:before{position:absolute;height:100%;content:"";width:4px;left:0;border-radius:var(--ok-br);background-color:var(--ok-bg-1)}dd{padding-left:var(--ok-s-5)}abbr{cursor:help}@media (hover:hover){a:hover{text-decoration:underline}input:hover:not(:disabled):not(:focus):not([type=submit]):not([type=button]):not([type=reset]):not([type=range]),select:hover:not(:disabled):not(:focus),textarea:hover:not(:disabled):not(:focus){border:var(--ok-b-1)}input[type=range]:hover:not(:disabled)::-webkit-slider-runnable-track{border:var(--ok-b-1)}input[type=range]:hover:not(:disabled)::-moz-range-track{border:var(--ok-b-1)}input[type=range]:hover:not(:disabled)::-ms-track{border:var(--ok-b-1)}select:not([multiple]):not(:disabled):hover{cursor:pointer}button:hover:not(:disabled),input[type=button]:hover:not(:disabled),input[type=reset]:hover:not(:disabled),input[type=submit]:hover:not(:disabled){background-color:var(--ok-accent-1)}input:not(:disabled):hover::-webkit-file-upload-button{background-color:var(--ok-accent-1)}}
\ No newline at end of file
index 8b0d47d..5614dc3 100644 (file)
    Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
    Copyright (c) 2000-2004 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
    Copyright (c) 2002-2012 Karl Waclawek <karl@waclawek.net>
-   Copyright (c) 2017-2022 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2024 Sebastian Pipping <sebastian@pipping.org>
    Copyright (c) 2017      Jakub Wilk <jwilk@jwilk.net>
    Copyright (c) 2021      Tomas Korbar <tkorbar@redhat.com>
    Copyright (c) 2021      Nicolas Cavallari <nicolas.cavallari@green-communications.fr>
    Copyright (c) 2022      Thijs Schreijer <thijs@thijsschreijer.nl>
+   Copyright (c) 2023      Hanno Böck <hanno@gentoo.org>
+   Copyright (c) 2023      Sony Corporation / Snild Dolkow <snild@sony.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -50,7 +52,7 @@
   <div>
     <h1>
       The Expat XML Parser
-      <small>Release 2.5.0</small>
+      <small>Release 2.6.2</small>
     </h1>
   </div>
 <div class="content">
@@ -68,11 +70,11 @@ Working Group at W3C that produced the XML specification.</p>
 
 <p>This is free software, licensed under the <a
 href="../COPYING">MIT/X Consortium license</a>. You may download it
-from <a href="http://www.libexpat.org/">the Expat home page</a>.
+from <a href="https://libexpat.github.io/">the Expat home page</a>.
 </p>
 
 <p>The bulk of this document was originally commissioned as an article
-by <a href="http://www.xml.com/">XML.com</a>. They graciously allowed
+by <a href="https://www.xml.com/">XML.com</a>. They graciously allowed
 Clark Cooper to retain copyright and to distribute it with Expat.
 This version has been substantially extended to include documentation
 on features which have been added since the original article was
@@ -151,10 +153,11 @@ interface.</p>
     </ul>
     </li>
     <li>
-      <a href="#billion-laughs">Billion Laughs Attack Protection</a>
+      <a href="#attack-protection">Attack Protection</a>
       <ul>
         <li><a href="#XML_SetBillionLaughsAttackProtectionMaximumAmplification">XML_SetBillionLaughsAttackProtectionMaximumAmplification</a></li>
         <li><a href="#XML_SetBillionLaughsAttackProtectionActivationThreshold">XML_SetBillionLaughsAttackProtectionActivationThreshold</a></li>
+        <li><a href="#XML_SetReparseDeferralEnabled">XML_SetReparseDeferralEnabled</a></li>
       </ul>
     </li>
     <li><a href="#miscellaneous">Miscellaneous Functions</a>
@@ -305,7 +308,7 @@ shoveling the document to the parser so that it can do its work.</p>
 
 <p>The Expat distribution comes as a compressed (with GNU gzip) tar
 file.  You may download the latest version from <a href=
-"http://sourceforge.net/projects/expat/" >Source Forge</a>.  After
+"https://sourceforge.net/projects/expat/" >Source Forge</a>.  After
 unpacking this, cd into the directory. Then follow either the Win32
 directions or Unix directions below.</p>
 
@@ -353,43 +356,67 @@ library and header would get installed in
 <h3>Configuring Expat Using the Pre-Processor</h3>
 
 <p>Expat's feature set can be configured using a small number of
-pre-processor definitions.  The definition of this symbols does not
-affect the set of entry points for Expat, only the behavior of the API
-and the definition of character types in the case of
-<code>XML_UNICODE_WCHAR_T</code>.  The symbols are:</p>
+pre-processor definitions.  The symbols are:</p>
 
 <dl class="cpp-symbols">
-<dt>XML_DTD</dt>
+<dt><a name="XML_GE">XML_GE</a></dt>
+<dd>
+Added in Expat 2.6.0.
+Include support for
+<a href="https://www.w3.org/TR/2006/REC-xml-20060816/#sec-physical-struct">general entities</a>
+(syntax <code>&amp;e1;</code> to reference and
+syntax <code>&lt;!ENTITY e1 'value1'&gt;</code> (an internal general entity) or
+<code>&lt;!ENTITY e2 SYSTEM 'file2'&gt;</code> (an external general entity) to declare).
+With <code>XML_GE</code> enabled, general entities will be replaced by their declared replacement text;
+for this to work for <em>external</em> general entities, in addition an
+<code><a href="#XML_SetExternalEntityRefHandler">XML_ExternalEntityRefHandler</a></code> must be set using
+<code><a href="#XML_SetExternalEntityRefHandler">XML_SetExternalEntityRefHandler</a></code>.
+Also, enabling <code>XML_GE</code> makes
+the functions <code><a href="#XML_SetBillionLaughsAttackProtectionMaximumAmplification">
+XML_SetBillionLaughsAttackProtectionMaximumAmplification</a></code> and <code>
+<a href="#XML_SetBillionLaughsAttackProtectionActivationThreshold">
+XML_SetBillionLaughsAttackProtectionActivationThreshold</a></code> available.
+<br/>
+With <code>XML_GE</code> disabled, Expat has a smaller memory footprint and can be faster, but will
+not load external general entities and will replace all general entities
+(except the <a href="https://www.w3.org/TR/2006/REC-xml-20060816/#sec-predefined-ent">predefined five</a>:
+<code>amp</code>, <code>apos</code>, <code>gt</code>, <code>lt</code>, <code>quot</code>)
+with a self-reference:
+for example, referencing an entity <code>e1</code> via <code>&amp;e1;</code> will be replaced
+by text <code>&amp;e1;</code>.
+</dd>
+
+<dt><a name="XML_DTD">XML_DTD</a></dt>
 <dd>Include support for using and reporting DTD-based content.  If
 this is defined, default attribute values from an external DTD subset
 are reported and attribute value normalization occurs based on the
 type of attributes defined in the external subset.  Without
 this, Expat has a smaller memory footprint and can be faster, but will
-not load external entities or process conditional sections. If defined, makes
+not load external parameter entities or process conditional sections. If defined, makes
 the functions <code><a 
 href="#XML_SetBillionLaughsAttackProtectionMaximumAmplification">
 XML_SetBillionLaughsAttackProtectionMaximumAmplification</a></code> and <code>
 <a href="#XML_SetBillionLaughsAttackProtectionActivationThreshold">
 XML_SetBillionLaughsAttackProtectionActivationThreshold</a></code> available.</dd>
 
-<dt>XML_NS</dt>
+<dt><a name="XML_NS">XML_NS</a></dt>
 <dd>When defined, support for the <cite><a href=
-"http://www.w3.org/TR/REC-xml-names/" >Namespaces in XML</a></cite>
+"https://www.w3.org/TR/REC-xml-names/" >Namespaces in XML</a></cite>
 specification is included.</dd>
 
-<dt>XML_UNICODE</dt>
+<dt><a name="XML_UNICODE">XML_UNICODE</a></dt>
 <dd>When defined, character data reported to the application is
 encoded in UTF-16 using wide characters of the type
 <code>XML_Char</code>.  This is implied if
 <code>XML_UNICODE_WCHAR_T</code> is defined.</dd>
 
-<dt>XML_UNICODE_WCHAR_T</dt>
+<dt><a name="XML_UNICODE_WCHAR_T">XML_UNICODE_WCHAR_T</a></dt>
 <dd>If defined, causes the <code>XML_Char</code> character type to be
 defined using the <code>wchar_t</code> type; otherwise, <code>unsigned
 short</code> is used.  Defining this implies
 <code>XML_UNICODE</code>.</dd>
 
-<dt>XML_LARGE_SIZE</dt>
+<dt><a name="XML_LARGE_SIZE">XML_LARGE_SIZE</a></dt>
 <dd>If defined, causes the <code>XML_Size</code> and <code>XML_Index</code>
 integer types to be at least 64 bits in size. This is intended to support
 processing of very large input streams, where the return values of
@@ -399,23 +426,23 @@ processing of very large input streams, where the return values of
 could overflow. It may not be supported by all compilers, and is turned
 off by default.</dd>
 
-<dt>XML_CONTEXT_BYTES</dt>
+<dt><a name="XML_CONTEXT_BYTES">XML_CONTEXT_BYTES</a></dt>
 <dd>The number of input bytes of markup context which the parser will
 ensure are available for reporting via <code><a href=
 "#XML_GetInputContext" >XML_GetInputContext</a></code>.  This is
-normally set to 1024, and must be set to a positive integer.  If this
-is not defined, the input context will not be available and <code><a
+normally set to 1024, and must be set to a positive integer to enable.
+If this is set to zero, the input context will not be available and <code><a
 href= "#XML_GetInputContext" >XML_GetInputContext</a></code> will
-always report NULL.  Without this, Expat has a smaller memory
+always report <code>NULL</code>.  Without this, Expat has a smaller memory
 footprint and can be faster.</dd>
 
-<dt>XML_STATIC</dt>
+<dt><a name="XML_STATIC">XML_STATIC</a></dt>
 <dd>On Windows, this should be set if Expat is going to be linked
 statically with the code that calls it; this is required to get all
 the right MSVC magic annotations correct.  This is ignored on other
 platforms.</dd>
 
-<dt>XML_ATTR_INFO</dt>
+<dt><a name="XML_ATTR_INFO">XML_ATTR_INFO</a></dt>
 <dd>If defined, makes the additional function <code><a href=
 "#XML_GetAttributeInfo" >XML_GetAttributeInfo</a></code> available
 for reporting attribute byte offsets.</dd>
@@ -669,8 +696,9 @@ function.  The StartNamespaceDeclHandler is called prior to the start
 tag handler and the EndNamespaceDeclHandler is called after the
 corresponding end tag that ends the namespace's scope.  The namespace
 start handler gets passed the prefix and URI for the namespace.  For a
-default namespace declaration (xmlns='...'), the prefix will be null.
-The URI will be null for the case where the default namespace is being
+default namespace declaration (xmlns='...'), the prefix will be
+<code>NULL</code>.
+The URI will be <code>NULL</code> for the case where the default namespace is being
 unset.  The namespace end handler just gets the prefix for the closing
 scope.</p>
 
@@ -799,7 +827,7 @@ has already been passed into the parser.  Applications for this
 include</p>
 
 <ul>
-  <li>Supporting the <a href= "http://www.w3.org/TR/xinclude/"
+  <li>Supporting the <a href= "https://www.w3.org/TR/xinclude/"
   >XInclude</a> specification.</li>
 
   <li>Delaying further processing until additional information is
@@ -947,16 +975,20 @@ XML_Parser XMLCALL
 XML_ParserCreate(const XML_Char *encoding);
 </pre>
 <div class="fcndef">
-Construct a new parser. If encoding is non-null, it specifies a
+<p>
+Construct a new parser. If encoding is non-<code>NULL</code>, it specifies a
 character encoding to use for the document. This overrides the document
 encoding declaration. There are four built-in encodings:
+</p>
 <ul>
 <li>US-ASCII</li>
 <li>UTF-8</li>
 <li>UTF-16</li>
 <li>ISO-8859-1</li>
 </ul>
+<p>
 Any other value will invoke a call to the UnknownEncodingHandler.
+</p>
 </div>
 
 <h4 id="XML_ParserCreateNS">XML_ParserCreateNS</h4>
@@ -1003,9 +1035,9 @@ typedef struct {
 </pre>
 <div class="fcndef">
 <p>Construct a new parser using the suite of memory handling functions
-specified in <code>ms</code>. If <code>ms</code> is NULL, then use the
+specified in <code>ms</code>. If <code>ms</code> is <code>NULL</code>, then use the
 standard set of memory management functions. If <code>sep</code> is
-non NULL, then namespace processing is enabled in the created parser
+non-<code>NULL</code>, then namespace processing is enabled in the created parser
 and the character pointed at by sep is used as the separator between
 the namespace URI and the local part of the name.</p>
 </div>
@@ -1077,6 +1109,11 @@ exceed the maximum integer value. Input data at the end of a buffer
 will remain unprocessed if it is part of an XML token for which the
 end is not part of that buffer.</p>
 
+<p><a name="isFinal"></a>The application <em>must</em> make a concluding
+<code><a href="#XML_Parse">XML_Parse</a></code> or
+<code><a href="#XML_ParseBuffer">XML_ParseBuffer</a></code> call
+with <code>isFinal</code> set to <code>XML_TRUE</code>.</p>
+
 <h4 id="XML_Parse">XML_Parse</h4>
 <pre class="fcndec">
 enum XML_Status XMLCALL
@@ -1092,17 +1129,50 @@ enum XML_Status {
 };
 </pre>
 <div class="fcndef">
+<p>
 Parse some more of the document. The string <code>s</code> is a buffer
 containing part (or perhaps all) of the document. The number of bytes of s
 that are part of the document is indicated by <code>len</code>. This means
-that <code>s</code> doesn't have to be null terminated. It also means that
+that <code>s</code> doesn't have to be null-terminated. It also means that
 if <code>len</code> is larger than the number of bytes in the block of
 memory that <code>s</code> points at, then a memory fault is likely. The
 <code>isFinal</code> parameter informs the parser that this is the last
 piece of the document. Frequently, the last piece is empty (i.e.
 <code>len</code> is zero.)
+</p>
+
+<p>
 If a parse error occurred, it returns <code>XML_STATUS_ERROR</code>.
 Otherwise it returns <code>XML_STATUS_OK</code> value.
+Note that regardless of the return value, there is no guarantee that all
+provided input has been parsed; only after <a href="#isFinal">the
+concluding call</a> will all handler callbacks and parsing errors have
+happened.
+</p>
+
+<p>
+Simplified, <code>XML_Parse</code> can be considered a convenience wrapper
+that is pairing calls
+to <code><a href="#XML_GetBuffer">XML_GetBuffer</a></code>
+and <code><a href="#XML_ParseBuffer">XML_ParseBuffer</a></code>
+(when Expat is built with macro <code>XML_CONTEXT_BYTES</code>
+defined to a positive value, which is both common and default).
+<code>XML_Parse</code> is then functionally equivalent to calling
+<code><a href="#XML_GetBuffer">XML_GetBuffer</a></code>,
+<code>memcpy</code>, and
+<code><a href="#XML_ParseBuffer">XML_ParseBuffer</a></code>.
+</p>
+
+<p>
+To avoid double copying of the input, direct use of functions
+<code><a href="#XML_GetBuffer">XML_GetBuffer</a></code> and
+<code><a href="#XML_ParseBuffer">XML_ParseBuffer</a></code> is advised
+for most production use, e.g.
+if you're using <code>read</code> or similar functionality to fill your
+buffers, fill directly into the buffer from
+<code><a href="#XML_GetBuffer">XML_GetBuffer</a></code>,
+then parse with <code><a href="#XML_ParseBuffer">XML_ParseBuffer</a></code>.
+</p>
 </div>
 
 <h4 id="XML_ParseBuffer">XML_ParseBuffer</h4>
@@ -1128,8 +1198,8 @@ XML_GetBuffer(XML_Parser p,
 </pre>
 <div class="fcndef">
 Obtain a buffer of size <code>len</code> to read a piece of the document
-into. A NULL value is returned if Expat can't allocate enough memory for
-this buffer. A NULL value may also be returned if <code>len</code> is zero.
+into. A <code>NULL</code> value is returned if Expat can't allocate enough memory for
+this buffer. A <code>NULL</code> value may also be returned if <code>len</code> is zero.
 This has to be called prior to every call to
 <code><a href= "#XML_ParseBuffer" >XML_ParseBuffer</a></code>. A
 typical use would look like this:
@@ -1275,7 +1345,7 @@ typedef struct {
 <p>Returns status of parser with respect to being initialized,
 parsing, finished, or suspended, and whether the final buffer is being
 processed.  The <code>status</code> parameter <em>must not</em> be
-NULL.</p>
+<code>NULL</code>.</p>
 
 <p>New in Expat 1.95.8.</p>
 </div>
@@ -1290,7 +1360,7 @@ to ignore all text not descended from a <code>para</code> element. One
 way it could do this is to set the character handler when a para start tag
 is seen, and unset it for the corresponding end tag.</p>
 
-<p>A handler may be <em>unset</em> by providing a NULL pointer to the
+<p>A handler may be <em>unset</em> by providing a <code>NULL</code> pointer to the
 appropriate handler setter. None of the handler setting functions have
 a return value.</p>
 
@@ -1318,7 +1388,7 @@ typedef void
 handler as a pointer to a vector of char pointers. Each attribute seen in
 a start (or empty) tag occupies 2 consecutive places in this vector: the
 attribute name followed by the attribute value. These pairs are terminated
-by a null pointer.</p>
+by a <code>NULL</code> pointer.</p>
 <p>Note that an empty tag generates a call to both start and end handlers
 (in that order).</p>
 </div>
@@ -1368,7 +1438,7 @@ is <em>NOT null-terminated</em>. You have to use the length argument
 to deal with the end of the string. A single block of contiguous text
 free of markup may still result in a sequence of calls to this handler.
 In other words, if you're searching for a pattern in the text, it may
-be split across calls to this handler. Note: Setting this handler to NULL
+be split across calls to this handler. Note: Setting this handler to <code>NULL</code>
 may <em>NOT immediately</em> terminate call-backs if the parser is currently
 processing such a single block of contiguous markup-free text, as the parser
 will continue calling back until the end of the block is reached.</p>
@@ -1526,16 +1596,16 @@ the format expected by the <code>context</code> argument to <code><a
 href="#XML_ExternalEntityParserCreate"
 >XML_ExternalEntityParserCreate</a></code>.  <code>code</code> is
 valid only until the handler returns, so if the referenced entity is
-to be parsed later, it must be copied.  <code>context</code> is NULL
+to be parsed later, it must be copied.  <code>context</code> is <code>NULL</code>
 only when the entity is a parameter entity, which is how one can
 differentiate between general and parameter entities.</p>
 
 <p>The <code>base</code> parameter is the base to use for relative
 system identifiers.  It is set by <code><a
-href="#XML_SetBase">XML_SetBase</a></code> and may be NULL. The
+href="#XML_SetBase">XML_SetBase</a></code> and may be <code>NULL</code>. The
 <code>publicId</code> parameter is the public id given in the entity
-declaration and may be NULL.  <code>systemId</code> is the system
-identifier specified in the entity declaration and is never NULL.</p>
+declaration and may be <code>NULL</code>.  <code>systemId</code> is the system
+identifier specified in the entity declaration and is never <code>NULL</code>.</p>
 
 <p>There are a couple of ways in which this handler differs from
 others.  First, this handler returns a status indicator (an
@@ -1564,10 +1634,10 @@ XML_SetExternalEntityRefHandlerArg(XML_Parser p,
 </pre>
 <div class="fcndef">
 <p>Set the argument passed to the ExternalEntityRefHandler.  If
-<code>arg</code> is not NULL, it is the new value passed to the
+<code>arg</code> is not <code>NULL</code>, it is the new value passed to the
 handler set using <code><a href="#XML_SetExternalEntityRefHandler"
 >XML_SetExternalEntityRefHandler</a></code>; if <code>arg</code> is
-NULL, the argument passed to the handler function will be the parser
+<code>NULL</code>, the argument passed to the handler function will be the parser
 object itself.</p>
 
 <p><strong>Note:</strong>
@@ -1650,14 +1720,14 @@ value is -1, then that byte is invalid as the initial byte in a sequence.
 If the value is -n, where n is an integer &gt; 1, then n is the number of
 bytes in the sequence and the actual conversion is accomplished by a
 call to the function pointed at by convert. This function may return -1
-if the sequence itself is invalid. The convert pointer may be null if
+if the sequence itself is invalid. The convert pointer may be <code>NULL</code> if
 there are only single byte codes. The data parameter passed to the convert
 function is the data pointer from <code>XML_Encoding</code>. The
 string s is <em>NOT</em> null-terminated and points at the sequence of
 bytes to be converted.</p>
 
 <p>The function pointed at by <code>release</code> is called by the
-parser when it is finished with the encoding. It may be NULL.</p>
+parser when it is finished with the encoding. It may be <code>NULL</code>.</p>
 </div>
 
 <div class="handler">
@@ -1724,8 +1794,8 @@ typedef void
 </pre>
 <p>Sets a handler that is called for XML declarations and also for
 text declarations discovered in external entities. The way to
-distinguish is that the <code>version</code> parameter will be NULL
-for text declarations. The <code>encoding</code> parameter may be NULL
+distinguish is that the <code>version</code> parameter will be <code>NULL</code>
+for text declarations. The <code>encoding</code> parameter may be <code>NULL</code>
 for an XML declaration. The <code>standalone</code> argument will
 contain -1, 0, or 1 indicating respectively that there was no
 standalone parameter in the declaration, that it was given as no, or
@@ -1749,7 +1819,7 @@ typedef void
 </pre>
 <p>Set a handler that is called at the start of a DOCTYPE declaration,
 before any external or internal subset is parsed. Both <code>sysid</code>
-and <code>pubid</code> may be NULL. The <code>has_internal_subset</code>
+and <code>pubid</code> may be <code>NULL</code>. The <code>has_internal_subset</code>
 will be non-zero if the DOCTYPE declaration has an internal subset.</p>
 </div>
 
@@ -1831,7 +1901,7 @@ around and freed at a later stage.</p>
 <code>XML_Content</code> nodes. If <code>type</code> equals
 <code>XML_CTYPE_EMPTY</code> or <code>XML_CTYPE_ANY</code>, then
 <code>quant</code> will be <code>XML_CQUANT_NONE</code>, and the other
-fields will be zero or NULL.  If <code>type</code> is
+fields will be zero or <code>NULL</code>.  If <code>type</code> is
 <code>XML_CTYPE_MIXED</code>, then <code>quant</code> will be
 <code>XML_CQUANT_NONE</code> or <code>XML_CQUANT_REP</code> and
 <code>numchildren</code> will contain the number of elements that are
@@ -1843,7 +1913,7 @@ XML_CTYPE_NAME with no quantification.  Only the root node can be type
 
 <p>For type <code>XML_CTYPE_NAME</code>, the <code>name</code> field
 points to the name and the <code>numchildren</code> and
-<code>children</code> fields will be zero and NULL. The
+<code>children</code> fields will be zero and <code>NULL</code>. The
 <code>quant</code> field will indicate any quantifiers placed on the
 name.</p>
 
@@ -1879,11 +1949,11 @@ is in the <code>attname</code> parameter. The attribute type is in the
 type in the declaration with whitespace removed.</p>
 
 <p>The <code>dflt</code> parameter holds the default value. It will be
-NULL in the case of "#IMPLIED" or "#REQUIRED" attributes. You can
+<code>NULL</code> in the case of "#IMPLIED" or "#REQUIRED" attributes. You can
 distinguish these two cases by checking the <code>isrequired</code>
 parameter, which will be true in the case of "#REQUIRED" attributes.
 Attributes which are "#FIXED" will have also have a true
-<code>isrequired</code>, but they will have the non-NULL fixed value
+<code>isrequired</code>, but they will have the non-<code>NULL</code> fixed value
 in the <code>dflt</code> parameter.</p>
 </div>
 
@@ -1911,14 +1981,14 @@ The <code>is_parameter_entity</code> argument will be non-zero in the
 case of parameter entities and zero otherwise.</p>
 
 <p>For internal entities (<code>&lt;!ENTITY foo "bar"&gt;</code>),
-<code>value</code> will be non-NULL and <code>systemId</code>,
-<code>publicId</code>, and <code>notationName</code> will all be NULL.
-The value string is <em>not</em> NULL terminated; the length is
+<code>value</code> will be non-<code>NULL</code> and <code>systemId</code>,
+<code>publicId</code>, and <code>notationName</code> will all be <code>NULL</code>.
+The value string is <em>not</em> null-terminated; the length is
 provided in the <code>value_length</code> parameter. Do not use
 <code>value_length</code> to test for internal entities, since it is
 legal to have zero-length values. Instead check for whether or not
-<code>value</code> is NULL.</p> <p>The <code>notationName</code>
-argument will have a non-NULL value only for unparsed entity
+<code>value</code> is <code>NULL</code>.</p> <p>The <code>notationName</code>
+argument will have a non-<code>NULL</code> value only for unparsed entity
 declarations.</p>
 </div>
 
@@ -2092,15 +2162,11 @@ untranslated bytes of the input.</p>
 triggering a call spans over a very large amount of input, the actual
 parse position may be before the beginning of the buffer.</p>
 
-<p>If <code>XML_CONTEXT_BYTES</code> is not defined, this will always
-return NULL.</p>
+<p>If <code>XML_CONTEXT_BYTES</code> is zero, this will always
+return <code>NULL</code>.</p>
 </div>
 
-<h3><a name="billion-laughs">Billion Laughs Attack Protection</a></h3>
-
-<p>The functions in this section configure the built-in
-  protection against various forms of
-  <a href="https://en.wikipedia.org/wiki/Billion_laughs_attack">billion laughs attacks</a>.</p>
+<h3><a name="attack-protection">Attack Protection</a><a name="billion-laughs"></a></h3>
 
 <h4 id="XML_SetBillionLaughsAttackProtectionMaximumAmplification">XML_SetBillionLaughsAttackProtectionMaximumAmplification</h4>
 <pre class="fcndec">
@@ -2188,6 +2254,27 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(XML_Parser p,
   </p>
 </div>
 
+<h4 id="XML_SetReparseDeferralEnabled">XML_SetReparseDeferralEnabled</h4>
+<pre class="fcndec">
+/* Added in Expat 2.6.0. */
+XML_Bool XMLCALL
+XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled);
+</pre>
+<div class="fcndef">
+  <p>
+    Large tokens may require many parse calls before enough data is available for Expat to parse it in full.
+    If Expat retried parsing the token on every parse call, parsing could take quadratic time.
+    To avoid this, Expat only retries once a significant amount of new data is available.
+    This function allows disabling this behavior.
+  </p>
+  <p>
+    The <code>enabled</code> argument should be <code>XML_TRUE</code> or <code>XML_FALSE</code>.
+  </p>
+  <p>
+    Returns <code>XML_TRUE</code> on success, and <code>XML_FALSE</code> on error.
+  </p>
+</div>
+
 <h3><a name="miscellaneous">Miscellaneous functions</a></h3>
 
 <p>The functions in this section either obtain state information from
@@ -2313,7 +2400,7 @@ XML_SetEncoding(XML_Parser p,
 </pre>
 <div class="fcndef">
 Set the encoding to be used by the parser. It is equivalent to
-passing a non-null encoding argument to the parser creation functions.
+passing a non-<code>NULL</code> encoding argument to the parser creation functions.
 It must not be called after <code><a href= "#XML_Parse"
 >XML_Parse</a></code> or <code><a href= "#XML_ParseBuffer"
 >XML_ParseBuffer</a></code> have been called on the given parser.
@@ -2385,7 +2472,7 @@ called.  The setting of parameter entity parsing, controlled using
 external entity reference handler set via <code><a href=
 "#XML_SetExternalEntityRefHandler"
 >XML_SetExternalEntityRefHandler</a></code> with both
-<code>publicId</code> and <code>systemId</code> set to NULL.</p>
+<code>publicId</code> and <code>systemId</code> set to <code>NULL</code>.</p>
 
 <p>If this function is called after parsing has begun, it returns
 <code>XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING</code> and ignores
@@ -2506,7 +2593,7 @@ check these features to do so at runtime.</p>
 
 <p>The return value is an array of <code>XML_Feature</code>,
 terminated by a record with a <code>feature</code> of
-<code>XML_FEATURE_END</code> and <code>name</code> of NULL,
+<code>XML_FEATURE_END</code> and <code>name</code> of <code>NULL</code>,
 identifying the feature-test macros Expat was compiled with.  Since an
 application that requires this kind of information needs to determine
 the type of character the <code>name</code> points to, records for the
@@ -2562,7 +2649,7 @@ XML_MemMalloc(XML_Parser parser, size_t size);
 <div class="fcndef">
 Allocate <code>size</code> bytes of memory using the allocator the
 <code>parser</code> object has been configured to use.  Returns a
-pointer to the memory or NULL on failure.  Memory allocated in this
+pointer to the memory or <code>NULL</code> on failure.  Memory allocated in this
 way must be freed using <code><a href="#XML_MemFree"
 >XML_MemFree</a></code>.
 </div>
@@ -2577,9 +2664,9 @@ Allocate <code>size</code> bytes of memory using the allocator the
 <code>parser</code> object has been configured to use.
 <code>ptr</code> must point to a block of memory allocated by <code><a
 href="#XML_MemMalloc" >XML_MemMalloc</a></code> or
-<code>XML_MemRealloc</code>, or be NULL.  This function tries to
+<code>XML_MemRealloc</code>, or be <code>NULL</code>.  This function tries to
 expand the block pointed to by <code>ptr</code> if possible.  Returns
-a pointer to the memory or NULL on failure.  On success, the original
+a pointer to the memory or <code>NULL</code> on failure.  On success, the original
 block has either been expanded or freed.  On failure, the original
 block has not been freed; the caller is responsible for freeing the
 original block.  Memory allocated in this way must be freed using
@@ -2595,7 +2682,7 @@ XML_MemFree(XML_Parser parser, void *ptr);
 <div class="fcndef">
 Free a block of memory pointed to by <code>ptr</code>.  The block must
 have been allocated by <code><a href="#XML_MemMalloc"
->XML_MemMalloc</a></code> or <code>XML_MemRealloc</code>, or be NULL.
+>XML_MemMalloc</a></code> or <code>XML_MemRealloc</code>, or be <code>NULL</code>.
 </div>
 
 <hr />
index 6d88adc..1904ddd 100644 (file)
@@ -5,7 +5,7 @@
 \\$2 \(la\\$1\(ra\\$3
 ..
 .if \n(.g .mso www.tmac
-.TH XMLWF 1 "October 25, 2022" "" ""
+.TH XMLWF 1 "March 13, 2024" "" ""
 .SH NAME
 xmlwf \- Determines if an XML document is well-formed
 .SH SYNOPSIS
@@ -25,7 +25,7 @@ xmlwf \- Determines if an XML document is well-formed
 \fBxmlwf\fR \kx
 .if (\nx>(\n(.l/2)) .nr x (\n(.l/5)
 'in \n(.iu+\nxu
-\fB-h\fR 
+\fB-h\fR | \fB--help\fR 
 'in \n(.iu-\nxu
 .ad b
 'hy
@@ -35,7 +35,7 @@ xmlwf \- Determines if an XML document is well-formed
 \fBxmlwf\fR \kx
 .if (\nx>(\n(.l/2)) .nr x (\n(.l/5)
 'in \n(.iu+\nxu
-\fB-v\fR 
+\fB-v\fR | \fB--version\fR 
 'in \n(.iu-\nxu
 .ad b
 'hy
@@ -158,6 +158,16 @@ supports four built-in encodings:
 \*(T<ISO\-8859\-1\*(T>.
 Also see the \*(T<\fB\-w\fR\*(T> option.
 .TP 
+\*(T<\fB\-g\fR\*(T> \fIbytes\fR
+Sets the buffer size to request per call pair to
+\*(T<\fBXML_GetBuffer\fR\*(T> and \*(T<\fBread\fR\*(T>
+(default: 8 KiB).
+.TP 
+\*(T<\fB\-h\fR\*(T>, \*(T<\fB\-\-help\fR\*(T>
+Prints short usage information on command \fBxmlwf\fR,
+and then exits.
+Similar to this man page but more concise.
+.TP 
 \*(T<\fB\-k\fR\*(T>
 When processing multiple files, \fBxmlwf\fR
 by default halts after the the first file with an error.
@@ -189,6 +199,10 @@ Normally \fBxmlwf\fR never parses parameter
 entities. \*(T<\fB\-p\fR\*(T> tells it to always parse them.
 \*(T<\fB\-p\fR\*(T> implies \*(T<\fB\-x\fR\*(T>.
 .TP 
+\*(T<\fB\-q\fR\*(T>
+Disable reparse deferral, and allow quadratic parse runtime
+on large tokens (default: reparse deferral enabled).
+.TP 
 \*(T<\fB\-r\fR\*(T>
 Normally \fBxmlwf\fR memory-maps the XML file
 before parsing; this can result in faster parsing on many
@@ -217,7 +231,7 @@ without client overhead.
 \*(T<\fB\-t\fR\*(T> turns off most of the output options
 (\*(T<\fB\-d\fR\*(T>, \*(T<\fB\-m\fR\*(T>, \*(T<\fB\-c\fR\*(T>, ...).
 .TP 
-\*(T<\fB\-v\fR\*(T>
+\*(T<\fB\-v\fR\*(T>, \*(T<\fB\-\-version\fR\*(T>
 Prints the version of the Expat library being used, including some
 information on the compile-time configuration of the library, and
 then exits.
@@ -281,7 +295,7 @@ halts upon encountering a well-formedness or output-file error.
 If \*(T<\fB\-k\fR\*(T> is provided, \fBxmlwf\fR continues
 processing the remaining input files, describing problems found with any of them.
 .SH "EXIT STATUS"
-For option \*(T<\fB\-v\fR\*(T> or \*(T<\fB\-h\fR\*(T>, \fBxmlwf\fR always exits with status code 0. For other cases, the following exit status codes are returned:
+For options \*(T<\fB\-v\fR\*(T>|\*(T<\fB\-\-version\fR\*(T> or \*(T<\fB\-h\fR\*(T>|\*(T<\fB\-\-help\fR\*(T>, \fBxmlwf\fR always exits with status code 0. For other cases, the following exit status codes are returned:
 .TP 
 \*(T<\fB0\fR\*(T>
 The input files are well-formed and the output (if requested) was written successfully.
index 9603abf..fd77f84 100644 (file)
@@ -9,7 +9,7 @@
    Copyright (c) 2001      Scott Bronson <bronson@rinspin.com>
    Copyright (c) 2002-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
    Copyright (c) 2009      Karl Waclawek <karl@waclawek.net>
-   Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
    Copyright (c) 2016      Ardo van Rangelrooij <ardo@debian.org>
    Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Copyright (c) 2020      Joe Orton <jorton@redhat.com>
@@ -21,7 +21,7 @@
           "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
   <!ENTITY dhfirstname "<firstname>Scott</firstname>">
   <!ENTITY dhsurname   "<surname>Bronson</surname>">
-  <!ENTITY dhdate      "<date>October 25, 2022</date>">
+  <!ENTITY dhdate      "<date>March 13, 2024</date>">
   <!-- Please adjust this^^ date whenever cutting a new release. -->
   <!ENTITY dhsection   "<manvolnum>1</manvolnum>">
   <!ENTITY dhemail     "<email>bronson@rinspin.com</email>">
     </cmdsynopsis>
     <cmdsynopsis>
       <command>&dhpackage;</command>
-      <arg choice="plain"><option>-h</option></arg>
+      <group choice="plain">
+        <arg><option>-h</option></arg>
+        <arg><option>--help</option></arg>
+      </group>
     </cmdsynopsis>
     <cmdsynopsis>
       <command>&dhpackage;</command>
-      <arg choice="plain"><option>-v</option></arg>
+      <group choice="plain">
+        <arg><option>-v</option></arg>
+        <arg><option>--version</option></arg>
+      </group>
     </cmdsynopsis>
   </refsynopsisdiv>
  
@@ -252,6 +258,29 @@ supports both.
       </varlistentry>
 
       <varlistentry>
+        <term><option>-g</option> <replaceable>bytes</replaceable></term>
+        <listitem>
+          <para>
+            Sets the buffer size to request per call pair to
+            <function>XML_GetBuffer</function> and <function>read</function>
+            (default: 8 KiB).
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-h</option></term>
+        <term><option>--help</option></term>
+        <listitem>
+          <para>
+            Prints short usage information on command <command>&dhpackage;</command>,
+            and then exits.
+            Similar to this man page but more concise.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><option>-k</option></term>
         <listitem>
           <para>
@@ -314,6 +343,16 @@ supports both.
       </varlistentry>
 
       <varlistentry>
+        <term><option>-q</option></term>
+        <listitem>
+          <para>
+            Disable reparse deferral, and allow quadratic parse runtime
+            on large tokens (default: reparse deferral enabled).
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><option>-r</option></term>
         <listitem>
                <para>
@@ -362,6 +401,7 @@ supports both.
       
       <varlistentry>
         <term><option>-v</option></term>
+        <term><option>--version</option></term>
         <listitem>
                <para>
   Prints the version of the Expat library being used, including some
@@ -461,7 +501,7 @@ supports both.
 
   <refsect1>
   <title>EXIT STATUS</title>
-    <para>For option <option>-v</option> or <option>-h</option>, <command>&dhpackage;</command> always exits with status code 0.  For other cases, the following exit status codes are returned:
+    <para>For options <option>-v</option>|<option>--version</option> or <option>-h</option>|<option>--help</option>, <command>&dhpackage;</command> always exits with status code 0.  For other cases, the following exit status codes are returned:
     <variablelist>
       <varlistentry>
         <term><option>0</option></term>
index d386b59..e2e22bc 100644 (file)
@@ -6,8 +6,8 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
-# Copyright (c) 2020 Jeffrey Walton <noloader@gmail.com>
+# Copyright (c) 2017-2022 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2020      Jeffrey Walton <noloader@gmail.com>
 # Licensed under the MIT license:
 #
 # Permission is  hereby granted,  free of charge,  to any  person obtaining
 
 AM_CPPFLAGS = @AM_CPPFLAGS@ -I$(srcdir)/../lib
 
-noinst_PROGRAMS = elements outline
+noinst_PROGRAMS = element_declarations elements outline
+
+element_declarations_SOURCES = element_declarations.c
+element_declarations_LDADD = ../lib/libexpat.la
 
 elements_SOURCES = elements.c
 elements_LDADD = ../lib/libexpat.la
index 05c2440..0ccc020 100644 (file)
@@ -22,8 +22,8 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
-# Copyright (c) 2020 Jeffrey Walton <noloader@gmail.com>
+# Copyright (c) 2017-2022 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2020      Jeffrey Walton <noloader@gmail.com>
 # Licensed under the MIT license:
 #
 # Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -119,7 +119,8 @@ PRE_UNINSTALL = :
 POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
-noinst_PROGRAMS = elements$(EXEEXT) outline$(EXEEXT)
+noinst_PROGRAMS = element_declarations$(EXEEXT) elements$(EXEEXT) \
+       outline$(EXEEXT)
 subdir = examples
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
@@ -133,6 +134,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
        $(top_srcdir)/conftools/ax-append-compile-flags.m4 \
        $(top_srcdir)/conftools/ax-append-link-flags.m4 \
        $(top_srcdir)/conftools/expatcfg-compiler-supports-visibility.m4 \
+       $(top_srcdir)/conftools/ax-cxx-compile-stdcxx.m4 \
+       $(top_srcdir)/conftools/ax-cxx-compile-stdcxx-11.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
@@ -142,13 +145,16 @@ CONFIG_HEADER = $(top_builddir)/expat_config.h
 CONFIG_CLEAN_FILES =
 CONFIG_CLEAN_VPATH_FILES =
 PROGRAMS = $(noinst_PROGRAMS)
-am_elements_OBJECTS = elements.$(OBJEXT)
-elements_OBJECTS = $(am_elements_OBJECTS)
-elements_DEPENDENCIES = ../lib/libexpat.la
+am_element_declarations_OBJECTS = element_declarations.$(OBJEXT)
+element_declarations_OBJECTS = $(am_element_declarations_OBJECTS)
+element_declarations_DEPENDENCIES = ../lib/libexpat.la
 AM_V_lt = $(am__v_lt_@AM_V@)
 am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
 am__v_lt_0 = --silent
 am__v_lt_1 = 
+am_elements_OBJECTS = elements.$(OBJEXT)
+elements_OBJECTS = $(am_elements_OBJECTS)
+elements_DEPENDENCIES = ../lib/libexpat.la
 am_outline_OBJECTS = outline.$(OBJEXT)
 outline_OBJECTS = $(am_outline_OBJECTS)
 outline_DEPENDENCIES = ../lib/libexpat.la
@@ -167,7 +173,8 @@ am__v_at_1 =
 DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/conftools/depcomp
 am__maybe_remake_depfiles = depfiles
-am__depfiles_remade = ./$(DEPDIR)/elements.Po ./$(DEPDIR)/outline.Po
+am__depfiles_remade = ./$(DEPDIR)/element_declarations.Po \
+       ./$(DEPDIR)/elements.Po ./$(DEPDIR)/outline.Po
 am__mv = mv -f
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
        $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -187,8 +194,10 @@ AM_V_CCLD = $(am__v_CCLD_@AM_V@)
 am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
 am__v_CCLD_0 = @echo "  CCLD    " $@;
 am__v_CCLD_1 = 
-SOURCES = $(elements_SOURCES) $(outline_SOURCES)
-DIST_SOURCES = $(elements_SOURCES) $(outline_SOURCES)
+SOURCES = $(element_declarations_SOURCES) $(elements_SOURCES) \
+       $(outline_SOURCES)
+DIST_SOURCES = $(element_declarations_SOURCES) $(elements_SOURCES) \
+       $(outline_SOURCES)
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
@@ -262,6 +271,7 @@ FGREP = @FGREP@
 FILECMD = @FILECMD@
 FILEMAP = @FILEMAP@
 GREP = @GREP@
+HAVE_CXX11 = @HAVE_CXX11@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
@@ -281,6 +291,7 @@ LIPO = @LIPO@
 LN_S = @LN_S@
 LTLIBOBJS = @LTLIBOBJS@
 LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
 MAKEINFO = @MAKEINFO@
 MANIFEST_TOOL = @MANIFEST_TOOL@
 MKDIR_P = @MKDIR_P@
@@ -362,6 +373,8 @@ target_alias = @target_alias@
 top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
+element_declarations_SOURCES = element_declarations.c
+element_declarations_LDADD = ../lib/libexpat.la
 elements_SOURCES = elements.c
 elements_LDADD = ../lib/libexpat.la
 outline_SOURCES = outline.c
@@ -370,7 +383,7 @@ all: all-am
 
 .SUFFIXES:
 .SUFFIXES: .c .lo .o .obj
-$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
        @for dep in $?; do \
          case '$(am__configure_deps)' in \
            *$$dep*) \
@@ -394,9 +407,9 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
        cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
 
-$(top_srcdir)/configure:  $(am__configure_deps)
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
        cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
        cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
 $(am__aclocal_m4_deps):
 
@@ -409,6 +422,10 @@ clean-noinstPROGRAMS:
        echo " rm -f" $$list; \
        rm -f $$list
 
+element_declarations$(EXEEXT): $(element_declarations_OBJECTS) $(element_declarations_DEPENDENCIES) $(EXTRA_element_declarations_DEPENDENCIES) 
+       @rm -f element_declarations$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(element_declarations_OBJECTS) $(element_declarations_LDADD) $(LIBS)
+
 elements$(EXEEXT): $(elements_OBJECTS) $(elements_DEPENDENCIES) $(EXTRA_elements_DEPENDENCIES) 
        @rm -f elements$(EXEEXT)
        $(AM_V_CCLD)$(LINK) $(elements_OBJECTS) $(elements_LDADD) $(LIBS)
@@ -423,6 +440,7 @@ mostlyclean-compile:
 distclean-compile:
        -rm -f *.tab.c
 
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/element_declarations.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elements.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/outline.Po@am__quote@ # am--include-marker
 
@@ -583,7 +601,8 @@ clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \
        mostlyclean-am
 
 distclean: distclean-am
-               -rm -f ./$(DEPDIR)/elements.Po
+               -rm -f ./$(DEPDIR)/element_declarations.Po
+       -rm -f ./$(DEPDIR)/elements.Po
        -rm -f ./$(DEPDIR)/outline.Po
        -rm -f Makefile
 distclean-am: clean-am distclean-compile distclean-generic \
@@ -630,7 +649,8 @@ install-ps-am:
 installcheck-am:
 
 maintainer-clean: maintainer-clean-am
-               -rm -f ./$(DEPDIR)/elements.Po
+               -rm -f ./$(DEPDIR)/element_declarations.Po
+       -rm -f ./$(DEPDIR)/elements.Po
        -rm -f ./$(DEPDIR)/outline.Po
        -rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic
diff --git a/examples/element_declarations.c b/examples/element_declarations.c
new file mode 100644 (file)
index 0000000..7ce8544
--- /dev/null
@@ -0,0 +1,234 @@
+/* Read an XML document from standard input and print
+   element declarations (if any) to standard output.
+   It must be used with Expat compiled for UTF-8 output.
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2001-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2004-2006 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
+   Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2019      Zhongyuan Zhou <zhouzhongyuan@huawei.com>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <expat.h>
+
+#ifdef XML_LARGE_SIZE
+#  define XML_FMT_INT_MOD "ll"
+#else
+#  define XML_FMT_INT_MOD "l"
+#endif
+
+#ifdef XML_UNICODE_WCHAR_T
+#  define XML_FMT_STR "ls"
+#else
+#  define XML_FMT_STR "s"
+#endif
+
+// While traversing the XML_Content tree, we avoid recursion
+// to not be vulnerable to a denial of service attack.
+typedef struct StackStruct {
+  const XML_Content *model;
+  unsigned level;
+  struct StackStruct *prev;
+} Stack;
+
+static Stack *
+stackPushMalloc(Stack *stackTop, const XML_Content *model, unsigned level) {
+  Stack *const newStackTop = malloc(sizeof(Stack));
+  if (! newStackTop) {
+    return NULL;
+  }
+  newStackTop->model = model;
+  newStackTop->level = level;
+  newStackTop->prev = stackTop;
+  return newStackTop;
+}
+
+static Stack *
+stackPopFree(Stack *stackTop) {
+  Stack *const newStackTop = stackTop->prev;
+  free(stackTop);
+  return newStackTop;
+}
+
+static char *
+contentTypeName(enum XML_Content_Type contentType) {
+  switch (contentType) {
+  case XML_CTYPE_EMPTY:
+    return "EMPTY";
+  case XML_CTYPE_ANY:
+    return "ANY";
+  case XML_CTYPE_MIXED:
+    return "MIXED";
+  case XML_CTYPE_NAME:
+    return "NAME";
+  case XML_CTYPE_CHOICE:
+    return "CHOICE";
+  case XML_CTYPE_SEQ:
+    return "SEQ";
+  default:
+    return "???";
+  }
+}
+
+static char *
+contentQuantName(enum XML_Content_Quant contentQuant) {
+  switch (contentQuant) {
+  case XML_CQUANT_NONE:
+    return "NONE";
+  case XML_CQUANT_OPT:
+    return "OPT";
+  case XML_CQUANT_REP:
+    return "REP";
+  case XML_CQUANT_PLUS:
+    return "PLUS";
+  default:
+    return "???";
+  }
+}
+
+static void
+dumpContentModelElement(const XML_Content *model, unsigned level,
+                        const XML_Content *root) {
+  // Indent
+  unsigned u = 0;
+  for (; u < level; u++) {
+    printf("  ");
+  }
+
+  // Node
+  printf("[%u] type=%s(%d), quant=%s(%d)", (unsigned)(model - root),
+         contentTypeName(model->type), model->type,
+         contentQuantName(model->quant), model->quant);
+  if (model->name) {
+    printf(", name=\"%" XML_FMT_STR "\"", model->name);
+  } else {
+    printf(", name=NULL");
+  }
+  printf(", numchildren=%d", model->numchildren);
+  printf("\n");
+}
+
+static bool
+dumpContentModel(const XML_Char *name, const XML_Content *root) {
+  printf("Element \"%" XML_FMT_STR "\":\n", name);
+  Stack *stackTop = stackPushMalloc(NULL, root, 1);
+  if (! stackTop) {
+    return false;
+  }
+
+  while (stackTop) {
+    const XML_Content *const model = stackTop->model;
+    const unsigned level = stackTop->level;
+
+    dumpContentModelElement(model, level, root);
+
+    stackTop = stackPopFree(stackTop);
+
+    for (size_t u = model->numchildren; u >= 1; u--) {
+      Stack *const newStackTop
+          = stackPushMalloc(stackTop, model->children + (u - 1), level + 1);
+      if (! newStackTop) {
+        // We ran out of memory, so let's free all memory allocated
+        // earlier in this function, to be leak-clean:
+        while (stackTop != NULL) {
+          stackTop = stackPopFree(stackTop);
+        }
+        return false;
+      }
+      stackTop = newStackTop;
+    }
+  }
+
+  printf("\n");
+  return true;
+}
+
+static void XMLCALL
+handleElementDeclaration(void *userData, const XML_Char *name,
+                         XML_Content *model) {
+  XML_Parser parser = (XML_Parser)userData;
+  const bool success = dumpContentModel(name, model);
+  XML_FreeContentModel(parser, model);
+  if (! success) {
+    XML_StopParser(parser, /* resumable= */ XML_FALSE);
+  }
+}
+
+int
+main(void) {
+  XML_Parser parser = XML_ParserCreate(NULL);
+  int done;
+
+  if (! parser) {
+    fprintf(stderr, "Couldn't allocate memory for parser\n");
+    return 1;
+  }
+
+  XML_SetUserData(parser, parser);
+  XML_SetElementDeclHandler(parser, handleElementDeclaration);
+
+  do {
+    void *const buf = XML_GetBuffer(parser, BUFSIZ);
+    if (! buf) {
+      fprintf(stderr, "Couldn't allocate memory for buffer\n");
+      XML_ParserFree(parser);
+      return 1;
+    }
+
+    const size_t len = fread(buf, 1, BUFSIZ, stdin);
+
+    if (ferror(stdin)) {
+      fprintf(stderr, "Read error\n");
+      XML_ParserFree(parser);
+      return 1;
+    }
+
+    done = feof(stdin);
+
+    if (XML_ParseBuffer(parser, (int)len, done) == XML_STATUS_ERROR) {
+      enum XML_Error errorCode = XML_GetErrorCode(parser);
+      if (errorCode == XML_ERROR_ABORTED) {
+        errorCode = XML_ERROR_NO_MEMORY;
+      }
+      fprintf(stderr,
+              "Parse error at line %" XML_FMT_INT_MOD "u:\n%" XML_FMT_STR "\n",
+              XML_GetCurrentLineNumber(parser), XML_ErrorString(errorCode));
+      XML_ParserFree(parser);
+      return 1;
+    }
+  } while (! done);
+
+  XML_ParserFree(parser);
+  return 0;
+}
index da7a054..56da413 100644 (file)
@@ -10,3 +10,4 @@ URL: https://libexpat.github.io/
 Libs: -L${libdir} -l$<TARGET_PROPERTY:expat,pkgconfig_$<LOWER_CASE:$<CONFIG>>_output_name>
 Libs.private: $<TARGET_PROPERTY:expat,pkgconfig_libm>
 Cflags: -I${includedir}
+Cflags.private: -DXML_STATIC
index db08065..a53ab11 100644 (file)
@@ -10,3 +10,4 @@ URL: https://libexpat.github.io/
 Libs: -L${libdir} -l@PACKAGE_NAME@
 Libs.private: @LIBM@
 Cflags: -I${includedir}
+Cflags.private: -DXML_STATIC
index c63eb3c..bc83d39 100644 (file)
@@ -16,6 +16,9 @@
 /* Define to 1 if you have the `arc4random_buf' function. */
 #define HAVE_ARC4RANDOM_BUF 1
 
+/* define if the compiler supports basic C++11 syntax */
+#define HAVE_CXX11 1
+
 /* Define to 1 if you have the <dlfcn.h> header file. */
 #define HAVE_DLFCN_H 1
 
 #define PACKAGE "expat"
 
 /* Define to the address where bug reports for this package should be sent. */
-#define PACKAGE_BUGREPORT "expat-bugs@libexpat.org"
+#define PACKAGE_BUGREPORT "https://github.com/libexpat/libexpat/issues"
 
 /* Define to the full name of this package. */
 #define PACKAGE_NAME "expat"
 
 /* Define to the full name and version of this package. */
-#define PACKAGE_STRING "expat 2.5.0"
+#define PACKAGE_STRING "expat 2.6.2"
 
 /* Define to the one symbol short name of this package. */
 #define PACKAGE_TARNAME "expat"
@@ -89,7 +92,7 @@
 #define PACKAGE_URL ""
 
 /* Define to the version of this package. */
-#define PACKAGE_VERSION "2.5.0"
+#define PACKAGE_VERSION "2.6.2"
 
 /* Define to 1 if all of the C90 standard headers exist (not just the ones
    required in a freestanding environment). This macro is provided for
 #define STDC_HEADERS 1
 
 /* Version number of package */
-#define VERSION "2.5.0"
+#define VERSION "2.6.2"
 
 /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
    significant byte first (like Motorola and SPARC, unlike Intel). */
 /* #undef XML_ATTR_INFO */
 
 /* Define to specify how much context to retain around the current parse
-   point. */
+   point, 0 to disable. */
 #define XML_CONTEXT_BYTES 1024
 
 /* Define to include code reading entropy from `/dev/urandom'. */
 /* Define to make parameter entity parsing functionality available. */
 #define XML_DTD 1
 
+/* Define as 1/0 to enable/disable support for general entities. */
+#define XML_GE 1
+
 /* Define to make XML Namespaces functionality available. */
 #define XML_NS 1
 
index 78fcb4c..ceb9b4e 100644 (file)
@@ -82,7 +82,9 @@
 #cmakedefine PACKAGE_VERSION "@PACKAGE_VERSION@"
 
 /* Define to 1 if you have the ANSI C header files. */
+#ifndef STDC_HEADERS
 #cmakedefine STDC_HEADERS
+#endif
 
 /* whether byteorder is bigendian */
 #cmakedefine WORDS_BIGENDIAN
@@ -92,8 +94,8 @@
 #cmakedefine XML_ATTR_INFO
 
 /* Define to specify how much context to retain around the current parse
-   point. */
-#cmakedefine XML_CONTEXT_BYTES @XML_CONTEXT_BYTES@
+   point, 0 to disable. */
+#define XML_CONTEXT_BYTES @XML_CONTEXT_BYTES@
 
 #if ! defined(_WIN32)
 /* Define to include code reading entropy from `/dev/urandom'. */
 /* Define to make parameter entity parsing functionality available. */
 #cmakedefine XML_DTD
 
+/* Define as 1/0 to enable/disable support for general entities. */
+#define XML_GE @XML_GE@
+
 /* Define to make XML Namespaces functionality available. */
 #cmakedefine XML_NS
 
 #endif
 
 /* Define to `long' if <sys/types.h> does not define. */
-#cmakedefine off_t @OFF_T@
+#cmakedefine off_t @off_t@
 
 /* Define to `unsigned' if <sys/types.h> does not define. */
-#cmakedefine size_t @SIZE_T@
+#cmakedefine size_t @size_t@
 
 #endif // ndef EXPAT_CONFIG_H
index 077569c..91c3234 100644 (file)
@@ -15,6 +15,9 @@
 /* Define to 1 if you have the `arc4random_buf' function. */
 #undef HAVE_ARC4RANDOM_BUF
 
+/* define if the compiler supports basic C++11 syntax */
+#undef HAVE_CXX11
+
 /* Define to 1 if you have the <dlfcn.h> header file. */
 #undef HAVE_DLFCN_H
 
 #undef XML_ATTR_INFO
 
 /* Define to specify how much context to retain around the current parse
-   point. */
+   point, 0 to disable. */
 #undef XML_CONTEXT_BYTES
 
 /* Define to include code reading entropy from `/dev/urandom'. */
 /* Define to make parameter entity parsing functionality available. */
 #undef XML_DTD
 
+/* Define as 1/0 to enable/disable support for general entities. */
+#undef XML_GE
+
 /* Define to make XML Namespaces functionality available. */
 #undef XML_NS
 
index 48b5021..a7e8414 100644 (file)
@@ -47,18 +47,59 @@ end(void *userData, const XML_Char *name) {
   (void)name;
 }
 
-int
-LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
-  XML_Parser p = XML_ParserCreate(xstr(ENCODING_FOR_FUZZING));
-  assert(p);
+static void XMLCALL
+may_stop_character_handler(void *userData, const XML_Char *s, int len) {
+  XML_Parser parser = (XML_Parser)userData;
+  if (len > 1 && s[0] == 's') {
+    XML_StopParser(parser, s[1] == 'r' ? XML_FALSE : XML_TRUE);
+  }
+}
 
+static void
+ParseOneInput(XML_Parser p, const uint8_t *data, size_t size) {
   // Set the hash salt using siphash to generate a deterministic hash.
   struct sipkey *key = sip_keyof(hash_key);
   XML_SetHashSalt(p, (unsigned long)siphash24(data, size, key));
+  (void)sip24_valid;
 
+  XML_SetUserData(p, p);
   XML_SetElementHandler(p, start, end);
+  XML_SetCharacterDataHandler(p, may_stop_character_handler);
   XML_Parse(p, (const XML_Char *)data, size, 0);
-  XML_Parse(p, (const XML_Char *)data, size, 1);
-  XML_ParserFree(p);
+  if (XML_Parse(p, (const XML_Char *)data, size, 1) == XML_STATUS_ERROR) {
+    XML_ErrorString(XML_GetErrorCode(p));
+  }
+  XML_GetCurrentLineNumber(p);
+  if (size % 2) {
+    XML_ParserReset(p, NULL);
+  }
+}
+
+int
+LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+  XML_Parser parentParser = XML_ParserCreate(xstr(ENCODING_FOR_FUZZING));
+  assert(parentParser);
+  ParseOneInput(parentParser, data, size);
+  // not freed yet, but used later and freed then
+
+  XML_Parser namespaceParser = XML_ParserCreateNS(NULL, '!');
+  assert(namespaceParser);
+  ParseOneInput(namespaceParser, data, size);
+  XML_ParserFree(namespaceParser);
+
+  XML_Parser externalEntityParser
+      = XML_ExternalEntityParserCreate(parentParser, "e1", NULL);
+  assert(externalEntityParser);
+  ParseOneInput(externalEntityParser, data, size);
+  XML_ParserFree(externalEntityParser);
+
+  XML_Parser externalDtdParser
+      = XML_ExternalEntityParserCreate(parentParser, NULL, NULL);
+  assert(externalDtdParser);
+  ParseOneInput(externalDtdParser, data, size);
+  XML_ParserFree(externalDtdParser);
+
+  // finally frees this parser which served as parent
+  XML_ParserFree(parentParser);
   return 0;
 }
index 0c7a8f2..0327aa9 100644 (file)
@@ -48,24 +48,70 @@ end(void *userData, const XML_Char *name) {
   (void)name;
 }
 
-int
-LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
-  if (size == 0)
-    return 0;
-
-  XML_Parser p = XML_ParserCreate(xstr(ENCODING_FOR_FUZZING));
-  assert(p);
-  XML_SetElementHandler(p, start, end);
+static void XMLCALL
+may_stop_character_handler(void *userData, const XML_Char *s, int len) {
+  XML_Parser parser = (XML_Parser)userData;
+  if (len > 1 && s[0] == 's') {
+    XML_StopParser(parser, s[1] == 'r' ? XML_FALSE : XML_TRUE);
+  }
+}
 
+static void
+ParseOneInput(XML_Parser p, const uint8_t *data, size_t size) {
   // Set the hash salt using siphash to generate a deterministic hash.
   struct sipkey *key = sip_keyof(hash_key);
   XML_SetHashSalt(p, (unsigned long)siphash24(data, size, key));
+  (void)sip24_valid;
 
+  XML_SetUserData(p, p);
+  XML_SetElementHandler(p, start, end);
+  XML_SetCharacterDataHandler(p, may_stop_character_handler);
   void *buf = XML_GetBuffer(p, size);
   assert(buf);
-
   memcpy(buf, data, size);
-  XML_ParseBuffer(p, size, size == 0);
-  XML_ParserFree(p);
+  XML_ParseBuffer(p, size, 0);
+  buf = XML_GetBuffer(p, size);
+  if (buf == NULL) {
+    return;
+  }
+  memcpy(buf, data, size);
+  if (XML_ParseBuffer(p, size, 1) == XML_STATUS_ERROR) {
+    XML_ErrorString(XML_GetErrorCode(p));
+  }
+  XML_GetCurrentLineNumber(p);
+  if (size % 2) {
+    XML_ParserReset(p, NULL);
+  }
+}
+
+int
+LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+  if (size == 0)
+    return 0;
+
+  XML_Parser parentParser = XML_ParserCreate(xstr(ENCODING_FOR_FUZZING));
+  assert(parentParser);
+  ParseOneInput(parentParser, data, size);
+  // not freed yet, but used later and freed then
+
+  XML_Parser namespaceParser = XML_ParserCreateNS(NULL, '!');
+  assert(namespaceParser);
+  ParseOneInput(namespaceParser, data, size);
+  XML_ParserFree(namespaceParser);
+
+  XML_Parser externalEntityParser
+      = XML_ExternalEntityParserCreate(parentParser, "e1", NULL);
+  assert(externalEntityParser);
+  ParseOneInput(externalEntityParser, data, size);
+  XML_ParserFree(externalEntityParser);
+
+  XML_Parser externalDtdParser
+      = XML_ExternalEntityParserCreate(parentParser, NULL, NULL);
+  assert(externalDtdParser);
+  ParseOneInput(externalDtdParser, data, size);
+  XML_ParserFree(externalDtdParser);
+
+  // finally frees this parser which served as parent
+  XML_ParserFree(parentParser);
   return 0;
 }
index 0e0185b..1958f32 100644 (file)
@@ -6,7 +6,7 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017-2022 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2017-2024 Sebastian Pipping <sebastian@pipping.org>
 # Copyright (c) 2017      Tomasz Kłoczko <kloczek@fedoraproject.org>
 # Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
 # Licensed under the MIT license:
@@ -36,7 +36,9 @@ include_HEADERS = \
     expat_external.h
 
 lib_LTLIBRARIES = libexpat.la
-noinst_LTLIBRARIES = libexpatinternal.la
+if WITH_TESTS
+noinst_LTLIBRARIES = libtestpat.la
+endif
 
 libexpat_la_LDFLAGS = \
     @AM_LDFLAGS@ \
@@ -44,17 +46,16 @@ libexpat_la_LDFLAGS = \
     -no-undefined \
     -version-info @LIBCURRENT@:@LIBREVISION@:@LIBAGE@
 
-libexpat_la_SOURCES =
-
-# This layer of indirection allows
-# the test suite to access internal symbols
-# despite compiling with -fvisibility=hidden
-libexpatinternal_la_SOURCES = \
+libexpat_la_SOURCES = \
     xmlparse.c \
     xmltok.c \
     xmlrole.c
 
-libexpat_la_LIBADD = libexpatinternal.la
+if WITH_TESTS
+libtestpat_la_CPPFLAGS = -DXML_TESTING
+
+libtestpat_la_SOURCES = $(libexpat_la_SOURCES)
+endif
 
 doc_DATA = \
     ../AUTHORS \
index 34bd215..7f2394a 100644 (file)
@@ -22,7 +22,7 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017-2022 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2017-2024 Sebastian Pipping <sebastian@pipping.org>
 # Copyright (c) 2017      Tomasz Kłoczko <kloczek@fedoraproject.org>
 # Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
 # Licensed under the MIT license:
@@ -135,6 +135,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
        $(top_srcdir)/conftools/ax-append-compile-flags.m4 \
        $(top_srcdir)/conftools/ax-append-link-flags.m4 \
        $(top_srcdir)/conftools/expatcfg-compiler-supports-visibility.m4 \
+       $(top_srcdir)/conftools/ax-cxx-compile-stdcxx.m4 \
+       $(top_srcdir)/conftools/ax-cxx-compile-stdcxx-11.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
@@ -174,8 +176,8 @@ am__uninstall_files_from_dir = { \
 am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(docdir)" \
        "$(DESTDIR)$(includedir)"
 LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES)
-libexpat_la_DEPENDENCIES = libexpatinternal.la
-am_libexpat_la_OBJECTS =
+libexpat_la_LIBADD =
+am_libexpat_la_OBJECTS = xmlparse.lo xmltok.lo xmlrole.lo
 libexpat_la_OBJECTS = $(am_libexpat_la_OBJECTS)
 AM_V_lt = $(am__v_lt_@AM_V@)
 am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
@@ -184,9 +186,13 @@ am__v_lt_1 =
 libexpat_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
        $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
        $(libexpat_la_LDFLAGS) $(LDFLAGS) -o $@
-libexpatinternal_la_LIBADD =
-am_libexpatinternal_la_OBJECTS = xmlparse.lo xmltok.lo xmlrole.lo
-libexpatinternal_la_OBJECTS = $(am_libexpatinternal_la_OBJECTS)
+libtestpat_la_LIBADD =
+am__libtestpat_la_SOURCES_DIST = xmlparse.c xmltok.c xmlrole.c
+am__objects_1 = libtestpat_la-xmlparse.lo libtestpat_la-xmltok.lo \
+       libtestpat_la-xmlrole.lo
+@WITH_TESTS_TRUE@am_libtestpat_la_OBJECTS = $(am__objects_1)
+libtestpat_la_OBJECTS = $(am_libtestpat_la_OBJECTS)
+@WITH_TESTS_TRUE@am_libtestpat_la_rpath =
 AM_V_P = $(am__v_P_@AM_V@)
 am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
 am__v_P_0 = false
@@ -202,8 +208,10 @@ am__v_at_1 =
 DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/conftools/depcomp
 am__maybe_remake_depfiles = depfiles
-am__depfiles_remade = ./$(DEPDIR)/xmlparse.Plo ./$(DEPDIR)/xmlrole.Plo \
-       ./$(DEPDIR)/xmltok.Plo
+am__depfiles_remade = ./$(DEPDIR)/libtestpat_la-xmlparse.Plo \
+       ./$(DEPDIR)/libtestpat_la-xmlrole.Plo \
+       ./$(DEPDIR)/libtestpat_la-xmltok.Plo ./$(DEPDIR)/xmlparse.Plo \
+       ./$(DEPDIR)/xmlrole.Plo ./$(DEPDIR)/xmltok.Plo
 am__mv = mv -f
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
        $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -223,8 +231,9 @@ AM_V_CCLD = $(am__v_CCLD_@AM_V@)
 am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
 am__v_CCLD_0 = @echo "  CCLD    " $@;
 am__v_CCLD_1 = 
-SOURCES = $(libexpat_la_SOURCES) $(libexpatinternal_la_SOURCES)
-DIST_SOURCES = $(libexpat_la_SOURCES) $(libexpatinternal_la_SOURCES)
+SOURCES = $(libexpat_la_SOURCES) $(libtestpat_la_SOURCES)
+DIST_SOURCES = $(libexpat_la_SOURCES) \
+       $(am__libtestpat_la_SOURCES_DIST)
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
@@ -300,6 +309,7 @@ FGREP = @FGREP@
 FILECMD = @FILECMD@
 FILEMAP = @FILEMAP@
 GREP = @GREP@
+HAVE_CXX11 = @HAVE_CXX11@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
@@ -319,6 +329,7 @@ LIPO = @LIPO@
 LN_S = @LN_S@
 LTLIBOBJS = @LTLIBOBJS@
 LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
 MAKEINFO = @MAKEINFO@
 MANIFEST_TOOL = @MANIFEST_TOOL@
 MKDIR_P = @MKDIR_P@
@@ -406,24 +417,20 @@ include_HEADERS = \
     expat_external.h
 
 lib_LTLIBRARIES = libexpat.la
-noinst_LTLIBRARIES = libexpatinternal.la
+@WITH_TESTS_TRUE@noinst_LTLIBRARIES = libtestpat.la
 libexpat_la_LDFLAGS = \
     @AM_LDFLAGS@ \
     @LIBM@ \
     -no-undefined \
     -version-info @LIBCURRENT@:@LIBREVISION@:@LIBAGE@
 
-libexpat_la_SOURCES = 
-
-# This layer of indirection allows
-# the test suite to access internal symbols
-# despite compiling with -fvisibility=hidden
-libexpatinternal_la_SOURCES = \
+libexpat_la_SOURCES = \
     xmlparse.c \
     xmltok.c \
     xmlrole.c
 
-libexpat_la_LIBADD = libexpatinternal.la
+@WITH_TESTS_TRUE@libtestpat_la_CPPFLAGS = -DXML_TESTING
+@WITH_TESTS_TRUE@libtestpat_la_SOURCES = $(libexpat_la_SOURCES)
 doc_DATA = \
     ../AUTHORS \
     ../Changes
@@ -451,7 +458,7 @@ all: all-am
 
 .SUFFIXES:
 .SUFFIXES: .c .lo .o .obj
-$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
        @for dep in $?; do \
          case '$(am__configure_deps)' in \
            *$$dep*) \
@@ -475,9 +482,9 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
        cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
 
-$(top_srcdir)/configure:  $(am__configure_deps)
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
        cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
        cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
 $(am__aclocal_m4_deps):
 
@@ -530,8 +537,8 @@ clean-noinstLTLIBRARIES:
 libexpat.la: $(libexpat_la_OBJECTS) $(libexpat_la_DEPENDENCIES) $(EXTRA_libexpat_la_DEPENDENCIES) 
        $(AM_V_CCLD)$(libexpat_la_LINK) -rpath $(libdir) $(libexpat_la_OBJECTS) $(libexpat_la_LIBADD) $(LIBS)
 
-libexpatinternal.la: $(libexpatinternal_la_OBJECTS) $(libexpatinternal_la_DEPENDENCIES) $(EXTRA_libexpatinternal_la_DEPENDENCIES) 
-       $(AM_V_CCLD)$(LINK)  $(libexpatinternal_la_OBJECTS) $(libexpatinternal_la_LIBADD) $(LIBS)
+libtestpat.la: $(libtestpat_la_OBJECTS) $(libtestpat_la_DEPENDENCIES) $(EXTRA_libtestpat_la_DEPENDENCIES) 
+       $(AM_V_CCLD)$(LINK) $(am_libtestpat_la_rpath) $(libtestpat_la_OBJECTS) $(libtestpat_la_LIBADD) $(LIBS)
 
 mostlyclean-compile:
        -rm -f *.$(OBJEXT)
@@ -539,6 +546,9 @@ mostlyclean-compile:
 distclean-compile:
        -rm -f *.tab.c
 
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtestpat_la-xmlparse.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtestpat_la-xmlrole.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtestpat_la-xmltok.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmlparse.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmlrole.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmltok.Plo@am__quote@ # am--include-marker
@@ -570,6 +580,27 @@ am--depfiles: $(am__depfiles_remade)
 @AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
 
+libtestpat_la-xmlparse.lo: xmlparse.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtestpat_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtestpat_la-xmlparse.lo -MD -MP -MF $(DEPDIR)/libtestpat_la-xmlparse.Tpo -c -o libtestpat_la-xmlparse.lo `test -f 'xmlparse.c' || echo '$(srcdir)/'`xmlparse.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) $(DEPDIR)/libtestpat_la-xmlparse.Tpo $(DEPDIR)/libtestpat_la-xmlparse.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='xmlparse.c' object='libtestpat_la-xmlparse.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtestpat_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtestpat_la-xmlparse.lo `test -f 'xmlparse.c' || echo '$(srcdir)/'`xmlparse.c
+
+libtestpat_la-xmltok.lo: xmltok.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtestpat_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtestpat_la-xmltok.lo -MD -MP -MF $(DEPDIR)/libtestpat_la-xmltok.Tpo -c -o libtestpat_la-xmltok.lo `test -f 'xmltok.c' || echo '$(srcdir)/'`xmltok.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) $(DEPDIR)/libtestpat_la-xmltok.Tpo $(DEPDIR)/libtestpat_la-xmltok.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='xmltok.c' object='libtestpat_la-xmltok.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtestpat_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtestpat_la-xmltok.lo `test -f 'xmltok.c' || echo '$(srcdir)/'`xmltok.c
+
+libtestpat_la-xmlrole.lo: xmlrole.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtestpat_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtestpat_la-xmlrole.lo -MD -MP -MF $(DEPDIR)/libtestpat_la-xmlrole.Tpo -c -o libtestpat_la-xmlrole.lo `test -f 'xmlrole.c' || echo '$(srcdir)/'`xmlrole.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) $(DEPDIR)/libtestpat_la-xmlrole.Tpo $(DEPDIR)/libtestpat_la-xmlrole.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='xmlrole.c' object='libtestpat_la-xmlrole.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtestpat_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtestpat_la-xmlrole.lo `test -f 'xmlrole.c' || echo '$(srcdir)/'`xmlrole.c
+
 mostlyclean-libtool:
        -rm -f *.lo
 
@@ -745,7 +776,10 @@ clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
        clean-noinstLTLIBRARIES mostlyclean-am
 
 distclean: distclean-am
-               -rm -f ./$(DEPDIR)/xmlparse.Plo
+               -rm -f ./$(DEPDIR)/libtestpat_la-xmlparse.Plo
+       -rm -f ./$(DEPDIR)/libtestpat_la-xmlrole.Plo
+       -rm -f ./$(DEPDIR)/libtestpat_la-xmltok.Plo
+       -rm -f ./$(DEPDIR)/xmlparse.Plo
        -rm -f ./$(DEPDIR)/xmlrole.Plo
        -rm -f ./$(DEPDIR)/xmltok.Plo
        -rm -f Makefile
@@ -794,7 +828,10 @@ install-ps-am:
 installcheck-am:
 
 maintainer-clean: maintainer-clean-am
-               -rm -f ./$(DEPDIR)/xmlparse.Plo
+               -rm -f ./$(DEPDIR)/libtestpat_la-xmlparse.Plo
+       -rm -f ./$(DEPDIR)/libtestpat_la-xmlrole.Plo
+       -rm -f ./$(DEPDIR)/libtestpat_la-xmltok.Plo
+       -rm -f ./$(DEPDIR)/xmlparse.Plo
        -rm -f ./$(DEPDIR)/xmlrole.Plo
        -rm -f ./$(DEPDIR)/xmltok.Plo
        -rm -f Makefile
index 1c83563..c2770be 100644 (file)
    Copyright (c) 2000-2005 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
    Copyright (c) 2001-2002 Greg Stein <gstein@users.sourceforge.net>
    Copyright (c) 2002-2016 Karl Waclawek <karl@waclawek.net>
-   Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
    Copyright (c) 2016      Cristian Rodríguez <crrodriguez@opensuse.org>
    Copyright (c) 2016      Thomas Beutlich <tc@tbeu.de>
    Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Copyright (c) 2022      Thijs Schreijer <thijs@thijsschreijer.nl>
+   Copyright (c) 2023      Hanno Böck <hanno@gentoo.org>
+   Copyright (c) 2023      Sony Corporation / Snild Dolkow <snild@sony.com>
+   Copyright (c) 2024      Taichi Haradaguchi <20001722@ymail.ne.jp>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -269,7 +272,7 @@ XML_ParserCreate_MM(const XML_Char *encoding,
                     const XML_Memory_Handling_Suite *memsuite,
                     const XML_Char *namespaceSeparator);
 
-/* Prepare a parser object to be re-used.  This is particularly
+/* Prepare a parser object to be reused.  This is particularly
    valuable when memory allocation overhead is disproportionately high,
    such as when a large number of small documnents need to be parsed.
    All handlers are cleared from the parser, except for the
@@ -951,7 +954,7 @@ XMLPARSEAPI(XML_Index) XML_GetCurrentByteIndex(XML_Parser parser);
 XMLPARSEAPI(int)
 XML_GetCurrentByteCount(XML_Parser parser);
 
-/* If XML_CONTEXT_BYTES is defined, returns the input buffer, sets
+/* If XML_CONTEXT_BYTES is >=1, returns the input buffer, sets
    the integer pointed to by offset to the offset within this buffer
    of the current parse position, and sets the integer pointed to by size
    to the size of this buffer (the number of input bytes). Otherwise
@@ -1025,7 +1028,9 @@ enum XML_FeatureEnum {
   XML_FEATURE_ATTR_INFO,
   /* Added in Expat 2.4.0. */
   XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT,
-  XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT
+  XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT,
+  /* Added in Expat 2.6.0. */
+  XML_FEATURE_GE
   /* Additional features must be added to the end of this enum. */
 };
 
@@ -1038,24 +1043,30 @@ typedef struct {
 XMLPARSEAPI(const XML_Feature *)
 XML_GetFeatureList(void);
 
-#ifdef XML_DTD
-/* Added in Expat 2.4.0. */
+#if defined(XML_DTD) || (defined(XML_GE) && XML_GE == 1)
+/* Added in Expat 2.4.0 for XML_DTD defined and
+ * added in Expat 2.6.0 for XML_GE == 1. */
 XMLPARSEAPI(XML_Bool)
 XML_SetBillionLaughsAttackProtectionMaximumAmplification(
     XML_Parser parser, float maximumAmplificationFactor);
 
-/* Added in Expat 2.4.0. */
+/* Added in Expat 2.4.0 for XML_DTD defined and
+ * added in Expat 2.6.0 for XML_GE == 1. */
 XMLPARSEAPI(XML_Bool)
 XML_SetBillionLaughsAttackProtectionActivationThreshold(
     XML_Parser parser, unsigned long long activationThresholdBytes);
 #endif
 
+/* Added in Expat 2.6.0. */
+XMLPARSEAPI(XML_Bool)
+XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled);
+
 /* Expat follows the semantic versioning convention.
-   See http://semver.org.
+   See https://semver.org
 */
 #define XML_MAJOR_VERSION 2
-#define XML_MINOR_VERSION 5
-#define XML_MICRO_VERSION 0
+#define XML_MINOR_VERSION 6
+#define XML_MICRO_VERSION 2
 
 #ifdef __cplusplus
 }
index e09f533..167ec36 100644 (file)
    Copyright (c) 2002-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
    Copyright (c) 2002-2006 Karl Waclawek <karl@waclawek.net>
    Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
-   Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
    Copyright (c) 2018      Yury Gribov <tetra2005@gmail.com>
    Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <snild@sony.com>
+   Copyright (c) 2024      Taichi Haradaguchi <20001722@ymail.ne.jp>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -154,12 +156,21 @@ extern "C" {
 void _INTERNAL_trim_to_complete_utf8_characters(const char *from,
                                                 const char **fromLimRef);
 
-#if defined(XML_DTD)
+#if defined(XML_GE) && XML_GE == 1
 unsigned long long testingAccountingGetCountBytesDirect(XML_Parser parser);
 unsigned long long testingAccountingGetCountBytesIndirect(XML_Parser parser);
 const char *unsignedCharToPrintable(unsigned char c);
 #endif
 
+extern
+#if ! defined(XML_TESTING)
+    const
+#endif
+    XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c
+#if defined(XML_TESTING)
+extern unsigned int g_bytesScanned; // used for testing only
+#endif
+
 #ifdef __cplusplus
 }
 #endif
index cf434a2..10ee9cd 100644 (file)
@@ -75,5 +75,7 @@ EXPORTS
   XML_SetHashSalt @67
 ; internal @68 removed with version 2.3.1
 ; added with version 2.4.0
-@_EXPAT_COMMENT_DTD@ XML_SetBillionLaughsAttackProtectionActivationThreshold @69
-@_EXPAT_COMMENT_DTD@ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70
+@_EXPAT_COMMENT_DTD_OR_GE@ XML_SetBillionLaughsAttackProtectionActivationThreshold @69
+@_EXPAT_COMMENT_DTD_OR_GE@ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70
+; added with version 2.6.0
+  XML_SetReparseDeferralEnabled @71
index 303283a..a1ed99e 100644 (file)
  * if this code is included and compiled as C++; related GCC warning is:
  * warning: use of C++11 long long integer constant [-Wlong-long]
  */
-#define _SIP_ULL(high, low) ((((uint64_t)high) << 32) | (low))
+#define SIP_ULL(high, low) ((((uint64_t)high) << 32) | (low))
 
 #define SIP_ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
 
@@ -190,10 +190,10 @@ sip_round(struct siphash *H, const int rounds) {
 
 static struct siphash *
 sip24_init(struct siphash *H, const struct sipkey *key) {
-  H->v0 = _SIP_ULL(0x736f6d65U, 0x70736575U) ^ key->k[0];
-  H->v1 = _SIP_ULL(0x646f7261U, 0x6e646f6dU) ^ key->k[1];
-  H->v2 = _SIP_ULL(0x6c796765U, 0x6e657261U) ^ key->k[0];
-  H->v3 = _SIP_ULL(0x74656462U, 0x79746573U) ^ key->k[1];
+  H->v0 = SIP_ULL(0x736f6d65U, 0x70736575U) ^ key->k[0];
+  H->v1 = SIP_ULL(0x646f7261U, 0x6e646f6dU) ^ key->k[1];
+  H->v2 = SIP_ULL(0x6c796765U, 0x6e657261U) ^ key->k[0];
+  H->v3 = SIP_ULL(0x74656462U, 0x79746573U) ^ key->k[1];
 
   H->p = H->buf;
   H->c = 0;
index 2ecd61b..0580551 100644 (file)
@@ -9,7 +9,8 @@
    Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
    Copyright (c) 2002      Greg Stein <gstein@users.sourceforge.net>
    Copyright (c) 2005      Karl Waclawek <karl@waclawek.net>
-   Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2023 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2023      Orgad Shaneh <orgad.shaneh@audiocodes.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -35,7 +36,9 @@
 #ifndef WINCONFIG_H
 #define WINCONFIG_H
 
-#define WIN32_LEAN_AND_MEAN
+#ifndef WIN32_LEAN_AND_MEAN
+#  define WIN32_LEAN_AND_MEAN
+#endif
 #include <windows.h>
 #undef WIN32_LEAN_AND_MEAN
 
index b6c2eca..2951fec 100644 (file)
@@ -1,4 +1,4 @@
-/* 5ab094ffadd6edfc94c3eee53af44a86951f9f1f0933ada3114bbce2bfb02c99 (2.5.0+)
+/* 2a14271ad4d35e82bde8ba210b4edb7998794bcbae54deab114046a300f9639a (2.6.2+)
                             __  __            _
                          ___\ \/ /_ __   __ _| |_
                         / _ \\  /| '_ \ / _` | __|
@@ -13,7 +13,7 @@
    Copyright (c) 2002-2016 Karl Waclawek <karl@waclawek.net>
    Copyright (c) 2005-2009 Steven Solie <steven@solie.ca>
    Copyright (c) 2016      Eric Rahm <erahm@mozilla.com>
-   Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
    Copyright (c) 2016      Gaurav <g.gupta@samsung.com>
    Copyright (c) 2016      Thomas Beutlich <tc@tbeu.de>
    Copyright (c) 2016      Gustavo Grieco <gustavo.grieco@imag.fr>
    Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
    Copyright (c) 2019-2020 Ben Wagner <bungeman@chromium.org>
    Copyright (c) 2019      Vadim Zeitlin <vadim@zeitlins.org>
-   Copyright (c) 2021      Dong-hee Na <donghee.na@python.org>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
    Copyright (c) 2022      Samanta Navarro <ferivoz@riseup.net>
    Copyright (c) 2022      Jeffrey Walton <noloader@gmail.com>
    Copyright (c) 2022      Jann Horn <jannh@google.com>
+   Copyright (c) 2022      Sean McBride <sean@rogue-research.com>
+   Copyright (c) 2023      Owain Davies <owaind@bath.edu>
+   Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <snild@sony.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
 
 #define XML_BUILDING_EXPAT 1
 
-#include <expat_config.h>
+#include "expat_config.h"
 
-#if ! defined(_GNU_SOURCE)
-#  define _GNU_SOURCE 1 /* syscall prototype */
+#if ! defined(XML_GE) || (1 - XML_GE - 1 == 2) || (XML_GE < 0) || (XML_GE > 1)
+#  error XML_GE (for general entities) must be defined, non-empty, either 1 or 0 (0 to disable, 1 to enable; 1 is a common default)
+#endif
+
+#if defined(XML_DTD) && XML_GE == 0
+#  error Either undefine XML_DTD or define XML_GE to 1.
+#endif
+
+#if ! defined(XML_CONTEXT_BYTES) || (1 - XML_CONTEXT_BYTES - 1 == 2)           \
+    || (XML_CONTEXT_BYTES + 0 < 0)
+#  error XML_CONTEXT_BYTES must be defined, non-empty and >=0 (0 to disable, >=1 to enable; 1024 is a common default)
+#endif
+
+#if defined(HAVE_SYSCALL_GETRANDOM)
+#  if ! defined(_GNU_SOURCE)
+#    define _GNU_SOURCE 1 /* syscall prototype */
+#  endif
 #endif
 
 #ifdef _WIN32
@@ -73,6 +91,7 @@
 #  endif
 #endif
 
+#include <stdbool.h>
 #include <stddef.h>
 #include <string.h> /* memset(), memcpy() */
 #include <assert.h>
     Your options include: \
       * Linux >=3.17 + glibc >=2.25 (getrandom): HAVE_GETRANDOM, \
       * Linux >=3.17 + glibc (including <2.25) (syscall SYS_getrandom): HAVE_SYSCALL_GETRANDOM, \
-      * BSD / macOS >=10.7 (arc4random_buf): HAVE_ARC4RANDOM_BUF, \
-      * BSD / macOS (including <10.7) (arc4random): HAVE_ARC4RANDOM, \
+      * BSD / macOS >=10.7 / glibc >=2.36 (arc4random_buf): HAVE_ARC4RANDOM_BUF, \
+      * BSD / macOS (including <10.7) / glibc >=2.36 (arc4random): HAVE_ARC4RANDOM, \
       * libbsd (arc4random_buf): HAVE_ARC4RANDOM_BUF + HAVE_LIBBSD, \
       * libbsd (arc4random): HAVE_ARC4RANDOM + HAVE_LIBBSD, \
       * Linux (including <3.17) / BSD / macOS (including <10.7) / Solaris >=8 (/dev/urandom): XML_DEV_URANDOM, \
@@ -191,11 +210,13 @@ typedef char ICHAR;
 #endif
 
 /* Round up n to be a multiple of sz, where sz is a power of 2. */
-#define ROUND_UP(n, sz) (((n) + ((sz)-1)) & ~((sz)-1))
+#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1))
 
 /* Do safe (NULL-aware) pointer arithmetic */
 #define EXPAT_SAFE_PTR_DIFF(p, q) (((p) && (q)) ? ((p) - (q)) : 0)
 
+#define EXPAT_MIN(a, b) (((a) < (b)) ? (a) : (b))
+
 #include "internal.h"
 #include "xmltok.h"
 #include "xmlrole.h"
@@ -227,7 +248,7 @@ static void copy_salt_to_sipkey(XML_Parser parser, struct sipkey *key);
    it odd, since odd numbers are always relative prime to a power of 2.
 */
 #define SECOND_HASH(hash, mask, power)                                         \
-  ((((hash) & ~(mask)) >> ((power)-1)) & ((mask) >> 2))
+  ((((hash) & ~(mask)) >> ((power) - 1)) & ((mask) >> 2))
 #define PROBE_STEP(hash, mask, power)                                          \
   ((unsigned char)((SECOND_HASH(hash, mask, power)) | 1))
 
@@ -279,7 +300,7 @@ typedef struct {
    XML_Parse()/XML_ParseBuffer(), the buffer is re-allocated to
    contain the 'raw' name as well.
 
-   A parser re-uses these structures, maintaining a list of allocated
+   A parser reuses these structures, maintaining a list of allocated
    TAG objects in a free list.
 */
 typedef struct tag {
@@ -408,12 +429,12 @@ enum XML_Account {
   XML_ACCOUNT_NONE              /* i.e. do not account, was accounted already */
 };
 
-#ifdef XML_DTD
+#if XML_GE == 1
 typedef unsigned long long XmlBigCount;
 typedef struct accounting {
   XmlBigCount countBytesDirect;
   XmlBigCount countBytesIndirect;
-  int debugLevel;
+  unsigned long debugLevel;
   float maximumAmplificationFactor; // >=1.0
   unsigned long long activationThresholdBytes;
 } ACCOUNTING;
@@ -422,9 +443,9 @@ typedef struct entity_stats {
   unsigned int countEverOpened;
   unsigned int currentDepth;
   unsigned int maximumDepthSeen;
-  int debugLevel;
+  unsigned long debugLevel;
 } ENTITY_STATS;
-#endif /* XML_DTD */
+#endif /* XML_GE == 1 */
 
 typedef enum XML_Error PTRCALL Processor(XML_Parser parser, const char *start,
                                          const char *end, const char **endPtr);
@@ -464,41 +485,47 @@ static enum XML_Error doContent(XML_Parser parser, int startTagLevel,
                                 const ENCODING *enc, const char *start,
                                 const char *end, const char **endPtr,
                                 XML_Bool haveMore, enum XML_Account account);
-static enum XML_Error doCdataSection(XML_Parser parser, const ENCODING *,
+static enum XML_Error doCdataSection(XML_Parser parser, const ENCODING *enc,
                                      const char **startPtr, const char *end,
                                      const char **nextPtr, XML_Bool haveMore,
                                      enum XML_Account account);
 #ifdef XML_DTD
-static enum XML_Error doIgnoreSection(XML_Parser parser, const ENCODING *,
+static enum XML_Error doIgnoreSection(XML_Parser parser, const ENCODING *enc,
                                       const char **startPtr, const char *end,
                                       const char **nextPtr, XML_Bool haveMore);
 #endif /* XML_DTD */
 
 static void freeBindings(XML_Parser parser, BINDING *bindings);
-static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *,
-                                const char *s, TAG_NAME *tagNamePtr,
+static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *enc,
+                                const char *attStr, TAG_NAME *tagNamePtr,
                                 BINDING **bindingsPtr,
                                 enum XML_Account account);
 static enum XML_Error addBinding(XML_Parser parser, PREFIX *prefix,
                                  const ATTRIBUTE_ID *attId, const XML_Char *uri,
                                  BINDING **bindingsPtr);
-static int defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, XML_Bool isCdata,
-                           XML_Bool isId, const XML_Char *dfltValue,
-                           XML_Parser parser);
-static enum XML_Error storeAttributeValue(XML_Parser parser, const ENCODING *,
-                                          XML_Bool isCdata, const char *,
-                                          const char *, STRING_POOL *,
+static int defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId,
+                           XML_Bool isCdata, XML_Bool isId,
+                           const XML_Char *value, XML_Parser parser);
+static enum XML_Error storeAttributeValue(XML_Parser parser,
+                                          const ENCODING *enc, XML_Bool isCdata,
+                                          const char *ptr, const char *end,
+                                          STRING_POOL *pool,
                                           enum XML_Account account);
-static enum XML_Error appendAttributeValue(XML_Parser parser, const ENCODING *,
-                                           XML_Bool isCdata, const char *,
-                                           const char *, STRING_POOL *,
+static enum XML_Error appendAttributeValue(XML_Parser parser,
+                                           const ENCODING *enc,
+                                           XML_Bool isCdata, const char *ptr,
+                                           const char *end, STRING_POOL *pool,
                                            enum XML_Account account);
 static ATTRIBUTE_ID *getAttributeId(XML_Parser parser, const ENCODING *enc,
                                     const char *start, const char *end);
-static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *);
+static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType);
+#if XML_GE == 1
 static enum XML_Error storeEntityValue(XML_Parser parser, const ENCODING *enc,
                                        const char *start, const char *end,
                                        enum XML_Account account);
+#else
+static enum XML_Error storeSelfEntityValue(XML_Parser parser, ENTITY *entity);
+#endif
 static int reportProcessingInstruction(XML_Parser parser, const ENCODING *enc,
                                        const char *start, const char *end);
 static int reportComment(XML_Parser parser, const ENCODING *enc,
@@ -518,21 +545,22 @@ static void dtdDestroy(DTD *p, XML_Bool isDocEntity,
                        const XML_Memory_Handling_Suite *ms);
 static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
                    const XML_Memory_Handling_Suite *ms);
-static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *, STRING_POOL *,
-                           const HASH_TABLE *);
+static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable,
+                           STRING_POOL *newPool, const HASH_TABLE *oldTable);
 static NAMED *lookup(XML_Parser parser, HASH_TABLE *table, KEY name,
                      size_t createSize);
-static void FASTCALL hashTableInit(HASH_TABLE *,
+static void FASTCALL hashTableInit(HASH_TABLE *table,
                                    const XML_Memory_Handling_Suite *ms);
-static void FASTCALL hashTableClear(HASH_TABLE *);
-static void FASTCALL hashTableDestroy(HASH_TABLE *);
-static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *);
-static NAMED *FASTCALL hashTableIterNext(HASH_TABLE_ITER *);
+static void FASTCALL hashTableClear(HASH_TABLE *table);
+static void FASTCALL hashTableDestroy(HASH_TABLE *table);
+static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *iter,
+                                       const HASH_TABLE *table);
+static NAMED *FASTCALL hashTableIterNext(HASH_TABLE_ITER *iter);
 
-static void FASTCALL poolInit(STRING_POOL *,
+static void FASTCALL poolInit(STRING_POOL *pool,
                               const XML_Memory_Handling_Suite *ms);
-static void FASTCALL poolClear(STRING_POOL *);
-static void FASTCALL poolDestroy(STRING_POOL *);
+static void FASTCALL poolClear(STRING_POOL *pool);
+static void FASTCALL poolDestroy(STRING_POOL *pool);
 static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc,
                             const char *ptr, const char *end);
 static XML_Char *poolStoreString(STRING_POOL *pool, const ENCODING *enc,
@@ -562,7 +590,7 @@ static XML_Parser parserCreate(const XML_Char *encodingName,
 
 static void parserInit(XML_Parser parser, const XML_Char *encodingName);
 
-#ifdef XML_DTD
+#if XML_GE == 1
 static float accountingGetCurrentAmplification(XML_Parser rootParser);
 static void accountingReportStats(XML_Parser originParser, const char *epilog);
 static void accountingOnAbort(XML_Parser originParser);
@@ -585,13 +613,12 @@ static void entityTrackingOnClose(XML_Parser parser, ENTITY *entity,
 
 static XML_Parser getRootParserOf(XML_Parser parser,
                                   unsigned int *outLevelDiff);
-#endif /* XML_DTD */
+#endif /* XML_GE == 1 */
 
 static unsigned long getDebugLevel(const char *variableName,
                                    unsigned long defaultDebugLevel);
 
 #define poolStart(pool) ((pool)->start)
-#define poolEnd(pool) ((pool)->ptr)
 #define poolLength(pool) ((pool)->ptr - (pool)->start)
 #define poolChop(pool) ((void)--(pool->ptr))
 #define poolLastChar(pool) (((pool)->ptr)[-1])
@@ -602,21 +629,41 @@ static unsigned long getDebugLevel(const char *variableName,
        ? 0                                                                     \
        : ((*((pool)->ptr)++ = c), 1))
 
+#if ! defined(XML_TESTING)
+const
+#endif
+    XML_Bool g_reparseDeferralEnabledDefault
+    = XML_TRUE; // write ONLY in runtests.c
+#if defined(XML_TESTING)
+unsigned int g_bytesScanned = 0; // used for testing only
+#endif
+
 struct XML_ParserStruct {
   /* The first member must be m_userData so that the XML_GetUserData
      macro works. */
   void *m_userData;
   void *m_handlerArg;
-  char *m_buffer;
+
+  // How the four parse buffer pointers below relate in time and space:
+  //
+  //   m_buffer <= m_bufferPtr <= m_bufferEnd  <= m_bufferLim
+  //   |           |              |               |
+  //   <--parsed-->|              |               |
+  //               <---parsing--->|               |
+  //                              <--unoccupied-->|
+  //   <---------total-malloced/realloced-------->|
+
+  char *m_buffer; // malloc/realloc base pointer of parse buffer
   const XML_Memory_Handling_Suite m_mem;
-  /* first character to be parsed */
-  const char *m_bufferPtr;
-  /* past last character to be parsed */
-  char *m_bufferEnd;
-  /* allocated end of m_buffer */
-  const char *m_bufferLim;
+  const char *m_bufferPtr; // first character to be parsed
+  char *m_bufferEnd;       // past last character to be parsed
+  const char *m_bufferLim; // allocated end of m_buffer
+
   XML_Index m_parseEndByteIndex;
   const char *m_parseEndPtr;
+  size_t m_partialTokenBytesBefore; /* used in heuristic to avoid O(n^2) */
+  XML_Bool m_reparseDeferralEnabled;
+  int m_lastBufferRequestSize;
   XML_Char *m_dataBuf;
   XML_Char *m_dataBufEnd;
   XML_StartElementHandler m_startElementHandler;
@@ -703,7 +750,7 @@ struct XML_ParserStruct {
   enum XML_ParamEntityParsing m_paramEntityParsing;
 #endif
   unsigned long m_hash_secret_salt;
-#ifdef XML_DTD
+#if XML_GE == 1
   ACCOUNTING m_accounting;
   ENTITY_STATS m_entity_stats;
 #endif
@@ -948,6 +995,49 @@ get_hash_secret_salt(XML_Parser parser) {
   return parser->m_hash_secret_salt;
 }
 
+static enum XML_Error
+callProcessor(XML_Parser parser, const char *start, const char *end,
+              const char **endPtr) {
+  const size_t have_now = EXPAT_SAFE_PTR_DIFF(end, start);
+
+  if (parser->m_reparseDeferralEnabled
+      && ! parser->m_parsingStatus.finalBuffer) {
+    // Heuristic: don't try to parse a partial token again until the amount of
+    // available data has increased significantly.
+    const size_t had_before = parser->m_partialTokenBytesBefore;
+    // ...but *do* try anyway if we're close to causing a reallocation.
+    size_t available_buffer
+        = EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer);
+#if XML_CONTEXT_BYTES > 0
+    available_buffer -= EXPAT_MIN(available_buffer, XML_CONTEXT_BYTES);
+#endif
+    available_buffer
+        += EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd);
+    // m_lastBufferRequestSize is never assigned a value < 0, so the cast is ok
+    const bool enough
+        = (have_now >= 2 * had_before)
+          || ((size_t)parser->m_lastBufferRequestSize > available_buffer);
+
+    if (! enough) {
+      *endPtr = start; // callers may expect this to be set
+      return XML_ERROR_NONE;
+    }
+  }
+#if defined(XML_TESTING)
+  g_bytesScanned += (unsigned)have_now;
+#endif
+  const enum XML_Error ret = parser->m_processor(parser, start, end, endPtr);
+  if (ret == XML_ERROR_NONE) {
+    // if we consumed nothing, remember what we had on this parse attempt.
+    if (*endPtr == start) {
+      parser->m_partialTokenBytesBefore = have_now;
+    } else {
+      parser->m_partialTokenBytesBefore = 0;
+    }
+  }
+  return ret;
+}
+
 static XML_Bool /* only valid for root parser */
 startParsing(XML_Parser parser) {
   /* hash functions must be initialized before setContext() is called */
@@ -1129,6 +1219,9 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
   parser->m_bufferEnd = parser->m_buffer;
   parser->m_parseEndByteIndex = 0;
   parser->m_parseEndPtr = NULL;
+  parser->m_partialTokenBytesBefore = 0;
+  parser->m_reparseDeferralEnabled = g_reparseDeferralEnabledDefault;
+  parser->m_lastBufferRequestSize = 0;
   parser->m_declElementType = NULL;
   parser->m_declAttributeId = NULL;
   parser->m_declEntity = NULL;
@@ -1163,7 +1256,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
 #endif
   parser->m_hash_secret_salt = 0;
 
-#ifdef XML_DTD
+#if XML_GE == 1
   memset(&parser->m_accounting, 0, sizeof(ACCOUNTING));
   parser->m_accounting.debugLevel = getDebugLevel("EXPAT_ACCOUNTING_DEBUG", 0u);
   parser->m_accounting.maximumAmplificationFactor
@@ -1298,6 +1391,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context,
      to worry which hash secrets each table has.
   */
   unsigned long oldhash_secret_salt;
+  XML_Bool oldReparseDeferralEnabled;
 
   /* Validate the oldParser parameter before we pull everything out of it */
   if (oldParser == NULL)
@@ -1342,6 +1436,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context,
      to worry which hash secrets each table has.
   */
   oldhash_secret_salt = parser->m_hash_secret_salt;
+  oldReparseDeferralEnabled = parser->m_reparseDeferralEnabled;
 
 #ifdef XML_DTD
   if (! context)
@@ -1394,6 +1489,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context,
   parser->m_defaultExpandInternalEntities = oldDefaultExpandInternalEntities;
   parser->m_ns_triplets = oldns_triplets;
   parser->m_hash_secret_salt = oldhash_secret_salt;
+  parser->m_reparseDeferralEnabled = oldReparseDeferralEnabled;
   parser->m_parentParser = oldParser;
 #ifdef XML_DTD
   parser->m_paramEntityParsing = oldParamEntityParsing;
@@ -1848,55 +1944,8 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) {
     parser->m_parsingStatus.parsing = XML_PARSING;
   }
 
-  if (len == 0) {
-    parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal;
-    if (! isFinal)
-      return XML_STATUS_OK;
-    parser->m_positionPtr = parser->m_bufferPtr;
-    parser->m_parseEndPtr = parser->m_bufferEnd;
-
-    /* If data are left over from last buffer, and we now know that these
-       data are the final chunk of input, then we have to check them again
-       to detect errors based on that fact.
-    */
-    parser->m_errorCode
-        = parser->m_processor(parser, parser->m_bufferPtr,
-                              parser->m_parseEndPtr, &parser->m_bufferPtr);
-
-    if (parser->m_errorCode == XML_ERROR_NONE) {
-      switch (parser->m_parsingStatus.parsing) {
-      case XML_SUSPENDED:
-        /* It is hard to be certain, but it seems that this case
-         * cannot occur.  This code is cleaning up a previous parse
-         * with no new data (since len == 0).  Changing the parsing
-         * state requires getting to execute a handler function, and
-         * there doesn't seem to be an opportunity for that while in
-         * this circumstance.
-         *
-         * Given the uncertainty, we retain the code but exclude it
-         * from coverage tests.
-         *
-         * LCOV_EXCL_START
-         */
-        XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr,
-                          parser->m_bufferPtr, &parser->m_position);
-        parser->m_positionPtr = parser->m_bufferPtr;
-        return XML_STATUS_SUSPENDED;
-        /* LCOV_EXCL_STOP */
-      case XML_INITIALIZED:
-      case XML_PARSING:
-        parser->m_parsingStatus.parsing = XML_FINISHED;
-        /* fall through */
-      default:
-        return XML_STATUS_OK;
-      }
-    }
-    parser->m_eventEndPtr = parser->m_eventPtr;
-    parser->m_processor = errorProcessor;
-    return XML_STATUS_ERROR;
-  }
-#ifndef XML_CONTEXT_BYTES
-  else if (parser->m_bufferPtr == parser->m_bufferEnd) {
+#if XML_CONTEXT_BYTES == 0
+  if (parser->m_bufferPtr == parser->m_bufferEnd) {
     const char *end;
     int nLeftOver;
     enum XML_Status result;
@@ -1907,12 +1956,15 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) {
       parser->m_processor = errorProcessor;
       return XML_STATUS_ERROR;
     }
+    // though this isn't a buffer request, we assume that `len` is the app's
+    // preferred buffer fill size, and therefore save it here.
+    parser->m_lastBufferRequestSize = len;
     parser->m_parseEndByteIndex += len;
     parser->m_positionPtr = s;
     parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal;
 
     parser->m_errorCode
-        = parser->m_processor(parser, s, parser->m_parseEndPtr = s + len, &end);
+        = callProcessor(parser, s, parser->m_parseEndPtr = s + len, &end);
 
     if (parser->m_errorCode != XML_ERROR_NONE) {
       parser->m_eventEndPtr = parser->m_eventPtr;
@@ -1939,23 +1991,25 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) {
                       &parser->m_position);
     nLeftOver = s + len - end;
     if (nLeftOver) {
-      if (parser->m_buffer == NULL
-          || nLeftOver > parser->m_bufferLim - parser->m_buffer) {
-        /* avoid _signed_ integer overflow */
-        char *temp = NULL;
-        const int bytesToAllocate = (int)((unsigned)len * 2U);
-        if (bytesToAllocate > 0) {
-          temp = (char *)REALLOC(parser, parser->m_buffer, bytesToAllocate);
-        }
-        if (temp == NULL) {
-          parser->m_errorCode = XML_ERROR_NO_MEMORY;
-          parser->m_eventPtr = parser->m_eventEndPtr = NULL;
-          parser->m_processor = errorProcessor;
-          return XML_STATUS_ERROR;
-        }
-        parser->m_buffer = temp;
-        parser->m_bufferLim = parser->m_buffer + bytesToAllocate;
+      // Back up and restore the parsing status to avoid XML_ERROR_SUSPENDED
+      // (and XML_ERROR_FINISHED) from XML_GetBuffer.
+      const enum XML_Parsing originalStatus = parser->m_parsingStatus.parsing;
+      parser->m_parsingStatus.parsing = XML_PARSING;
+      void *const temp = XML_GetBuffer(parser, nLeftOver);
+      parser->m_parsingStatus.parsing = originalStatus;
+      // GetBuffer may have overwritten this, but we want to remember what the
+      // app requested, not how many bytes were left over after parsing.
+      parser->m_lastBufferRequestSize = len;
+      if (temp == NULL) {
+        // NOTE: parser->m_errorCode has already been set by XML_GetBuffer().
+        parser->m_eventPtr = parser->m_eventEndPtr = NULL;
+        parser->m_processor = errorProcessor;
+        return XML_STATUS_ERROR;
       }
+      // Since we know that the buffer was empty and XML_CONTEXT_BYTES is 0, we
+      // don't have any data to preserve, and can copy straight into the start
+      // of the buffer rather than the GetBuffer return pointer (which may be
+      // pointing further into the allocated buffer).
       memcpy(parser->m_buffer, end, nLeftOver);
     }
     parser->m_bufferPtr = parser->m_buffer;
@@ -1966,16 +2020,15 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) {
     parser->m_eventEndPtr = parser->m_bufferPtr;
     return result;
   }
-#endif /* not defined XML_CONTEXT_BYTES */
-  else {
-    void *buff = XML_GetBuffer(parser, len);
-    if (buff == NULL)
-      return XML_STATUS_ERROR;
-    else {
-      memcpy(buff, s, len);
-      return XML_ParseBuffer(parser, len, isFinal);
-    }
+#endif /* XML_CONTEXT_BYTES == 0 */
+  void *buff = XML_GetBuffer(parser, len);
+  if (buff == NULL)
+    return XML_STATUS_ERROR;
+  if (len > 0) {
+    assert(s != NULL); // make sure s==NULL && len!=0 was rejected above
+    memcpy(buff, s, len);
   }
+  return XML_ParseBuffer(parser, len, isFinal);
 }
 
 enum XML_Status XMLCALL
@@ -2015,8 +2068,8 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) {
   parser->m_parseEndByteIndex += len;
   parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal;
 
-  parser->m_errorCode = parser->m_processor(
-      parser, start, parser->m_parseEndPtr, &parser->m_bufferPtr);
+  parser->m_errorCode = callProcessor(parser, start, parser->m_parseEndPtr,
+                                      &parser->m_bufferPtr);
 
   if (parser->m_errorCode != XML_ERROR_NONE) {
     parser->m_eventEndPtr = parser->m_eventPtr;
@@ -2061,10 +2114,14 @@ XML_GetBuffer(XML_Parser parser, int len) {
   default:;
   }
 
-  if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd)) {
-#ifdef XML_CONTEXT_BYTES
+  // whether or not the request succeeds, `len` seems to be the app's preferred
+  // buffer fill size; remember it.
+  parser->m_lastBufferRequestSize = len;
+  if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd)
+      || parser->m_buffer == NULL) {
+#if XML_CONTEXT_BYTES > 0
     int keep;
-#endif /* defined XML_CONTEXT_BYTES */
+#endif /* XML_CONTEXT_BYTES > 0 */
     /* Do not invoke signed arithmetic overflow: */
     int neededSize = (int)((unsigned)len
                            + (unsigned)EXPAT_SAFE_PTR_DIFF(
@@ -2073,7 +2130,7 @@ XML_GetBuffer(XML_Parser parser, int len) {
       parser->m_errorCode = XML_ERROR_NO_MEMORY;
       return NULL;
     }
-#ifdef XML_CONTEXT_BYTES
+#if XML_CONTEXT_BYTES > 0
     keep = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer);
     if (keep > XML_CONTEXT_BYTES)
       keep = XML_CONTEXT_BYTES;
@@ -2083,10 +2140,11 @@ XML_GetBuffer(XML_Parser parser, int len) {
       return NULL;
     }
     neededSize += keep;
-#endif /* defined XML_CONTEXT_BYTES */
-    if (neededSize
-        <= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) {
-#ifdef XML_CONTEXT_BYTES
+#endif /* XML_CONTEXT_BYTES > 0 */
+    if (parser->m_buffer && parser->m_bufferPtr
+        && neededSize
+               <= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) {
+#if XML_CONTEXT_BYTES > 0
       if (keep < EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer)) {
         int offset
             = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer)
@@ -2099,19 +2157,17 @@ XML_GetBuffer(XML_Parser parser, int len) {
         parser->m_bufferPtr -= offset;
       }
 #else
-      if (parser->m_buffer && parser->m_bufferPtr) {
-        memmove(parser->m_buffer, parser->m_bufferPtr,
-                EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr));
-        parser->m_bufferEnd
-            = parser->m_buffer
-              + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr);
-        parser->m_bufferPtr = parser->m_buffer;
-      }
-#endif /* not defined XML_CONTEXT_BYTES */
+      memmove(parser->m_buffer, parser->m_bufferPtr,
+              EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr));
+      parser->m_bufferEnd
+          = parser->m_buffer
+            + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr);
+      parser->m_bufferPtr = parser->m_buffer;
+#endif /* XML_CONTEXT_BYTES > 0 */
     } else {
       char *newBuf;
       int bufferSize
-          = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferPtr);
+          = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer);
       if (bufferSize == 0)
         bufferSize = INIT_BUFFER_SIZE;
       do {
@@ -2128,7 +2184,7 @@ XML_GetBuffer(XML_Parser parser, int len) {
         return NULL;
       }
       parser->m_bufferLim = newBuf + bufferSize;
-#ifdef XML_CONTEXT_BYTES
+#if XML_CONTEXT_BYTES > 0
       if (parser->m_bufferPtr) {
         memcpy(newBuf, &parser->m_bufferPtr[-keep],
                EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)
@@ -2158,7 +2214,7 @@ XML_GetBuffer(XML_Parser parser, int len) {
         parser->m_bufferEnd = newBuf;
       }
       parser->m_bufferPtr = parser->m_buffer = newBuf;
-#endif /* not defined XML_CONTEXT_BYTES */
+#endif /* XML_CONTEXT_BYTES > 0 */
     }
     parser->m_eventPtr = parser->m_eventEndPtr = NULL;
     parser->m_positionPtr = NULL;
@@ -2208,7 +2264,7 @@ XML_ResumeParser(XML_Parser parser) {
   }
   parser->m_parsingStatus.parsing = XML_PARSING;
 
-  parser->m_errorCode = parser->m_processor(
+  parser->m_errorCode = callProcessor(
       parser, parser->m_bufferPtr, parser->m_parseEndPtr, &parser->m_bufferPtr);
 
   if (parser->m_errorCode != XML_ERROR_NONE) {
@@ -2272,7 +2328,7 @@ XML_GetCurrentByteCount(XML_Parser parser) {
 
 const char *XMLCALL
 XML_GetInputContext(XML_Parser parser, int *offset, int *size) {
-#ifdef XML_CONTEXT_BYTES
+#if XML_CONTEXT_BYTES > 0
   if (parser == NULL)
     return NULL;
   if (parser->m_eventPtr && parser->m_buffer) {
@@ -2286,7 +2342,7 @@ XML_GetInputContext(XML_Parser parser, int *offset, int *size) {
   (void)parser;
   (void)offset;
   (void)size;
-#endif /* defined XML_CONTEXT_BYTES */
+#endif /* XML_CONTEXT_BYTES > 0 */
   return (const char *)0;
 }
 
@@ -2506,7 +2562,7 @@ XML_GetFeatureList(void) {
 #ifdef XML_DTD
       {XML_FEATURE_DTD, XML_L("XML_DTD"), 0},
 #endif
-#ifdef XML_CONTEXT_BYTES
+#if XML_CONTEXT_BYTES > 0
       {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"),
        XML_CONTEXT_BYTES},
 #endif
@@ -2522,8 +2578,9 @@ XML_GetFeatureList(void) {
 #ifdef XML_ATTR_INFO
       {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0},
 #endif
-#ifdef XML_DTD
-      /* Added in Expat 2.4.0. */
+#if XML_GE == 1
+      /* Added in Expat 2.4.0 for XML_DTD defined and
+       * added in Expat 2.6.0 for XML_GE == 1. */
       {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT,
        XML_L("XML_BLAP_MAX_AMP"),
        (long int)
@@ -2531,13 +2588,15 @@ XML_GetFeatureList(void) {
       {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT,
        XML_L("XML_BLAP_ACT_THRES"),
        EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT},
+      /* Added in Expat 2.6.0. */
+      {XML_FEATURE_GE, XML_L("XML_GE"), 0},
 #endif
       {XML_FEATURE_END, NULL, 0}};
 
   return features;
 }
 
-#ifdef XML_DTD
+#if XML_GE == 1
 XML_Bool XMLCALL
 XML_SetBillionLaughsAttackProtectionMaximumAmplification(
     XML_Parser parser, float maximumAmplificationFactor) {
@@ -2559,7 +2618,16 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(
   parser->m_accounting.activationThresholdBytes = activationThresholdBytes;
   return XML_TRUE;
 }
-#endif /* XML_DTD */
+#endif /* XML_GE == 1 */
+
+XML_Bool XMLCALL
+XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled) {
+  if (parser != NULL && (enabled == XML_TRUE || enabled == XML_FALSE)) {
+    parser->m_reparseDeferralEnabled = enabled;
+    return XML_TRUE;
+  }
+  return XML_FALSE;
+}
 
 /* Initially tag->rawName always points into the parse buffer;
    for those TAG instances opened while the current parse buffer was
@@ -2581,7 +2649,7 @@ storeRawNames(XML_Parser parser) {
     */
     if (tag->rawName == rawNameBuf)
       break;
-    /* For re-use purposes we need to ensure that the
+    /* For reuse purposes we need to ensure that the
        size of tag->buf is a multiple of sizeof(XML_Char).
     */
     rawNameLen = ROUND_UP(tag->rawNameLength, sizeof(XML_Char));
@@ -2645,13 +2713,13 @@ externalEntityInitProcessor2(XML_Parser parser, const char *start,
   int tok = XmlContentTok(parser->m_encoding, start, end, &next);
   switch (tok) {
   case XML_TOK_BOM:
-#ifdef XML_DTD
+#if XML_GE == 1
     if (! accountingDiffTolerated(parser, tok, start, next, __LINE__,
                                   XML_ACCOUNT_DIRECT)) {
       accountingOnAbort(parser);
       return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
     }
-#endif /* XML_DTD */
+#endif /* XML_GE == 1 */
 
     /* If we are at the end of the buffer, this would cause the next stage,
        i.e. externalEntityInitProcessor3, to pass control directly to
@@ -2765,7 +2833,7 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
   for (;;) {
     const char *next = s; /* XmlContentTok doesn't always set the last arg */
     int tok = XmlContentTok(enc, s, end, &next);
-#ifdef XML_DTD
+#if XML_GE == 1
     const char *accountAfter
         = ((tok == XML_TOK_TRAILING_RSQB) || (tok == XML_TOK_TRAILING_CR))
               ? (haveMore ? s /* i.e. 0 bytes */ : end)
@@ -2831,14 +2899,14 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
       XML_Char ch = (XML_Char)XmlPredefinedEntityName(
           enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar);
       if (ch) {
-#ifdef XML_DTD
+#if XML_GE == 1
         /* NOTE: We are replacing 4-6 characters original input for 1 character
          *       so there is no amplification and hence recording without
          *       protection. */
         accountingDiffTolerated(parser, tok, (char *)&ch,
                                 ((char *)&ch) + sizeof(XML_Char), __LINE__,
                                 XML_ACCOUNT_ENTITY_EXPANSION);
-#endif /* XML_DTD */
+#endif /* XML_GE == 1 */
         if (parser->m_characterDataHandler)
           parser->m_characterDataHandler(parser->m_handlerArg, &ch, 1);
         else if (parser->m_defaultHandler)
@@ -3039,13 +3107,13 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
           if (parser->m_ns && localPart) {
             /* localPart and prefix may have been overwritten in
                tag->name.str, since this points to the binding->uri
-               buffer which gets re-used; so we have to add them again
+               buffer which gets reused; so we have to add them again
             */
             uri = (XML_Char *)tag->name.str + tag->name.uriLen;
             /* don't need to check for space - already done in storeAtts() */
             while (*localPart)
               *uri++ = *localPart++;
-            prefix = (XML_Char *)tag->name.prefix;
+            prefix = tag->name.prefix;
             if (parser->m_ns_triplets && prefix) {
               *uri++ = parser->m_namespaceSeparator;
               while (*prefix)
@@ -3112,7 +3180,7 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
          However, now we have a start/endCdataSectionHandler, so it seems
          easier to let the user deal with this.
       */
-      else if (0 && parser->m_characterDataHandler)
+      else if ((0) && parser->m_characterDataHandler)
         parser->m_characterDataHandler(parser->m_handlerArg, parser->m_dataBuf,
                                        0);
       /* END disabled code */
@@ -3141,8 +3209,8 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
               (int)(dataPtr - (ICHAR *)parser->m_dataBuf));
         } else
           parser->m_characterDataHandler(
-              parser->m_handlerArg, (XML_Char *)s,
-              (int)((XML_Char *)end - (XML_Char *)s));
+              parser->m_handlerArg, (const XML_Char *)s,
+              (int)((const XML_Char *)end - (const XML_Char *)s));
       } else if (parser->m_defaultHandler)
         reportDefault(parser, enc, s, end);
       /* We are at the end of the final buffer, should we check for
@@ -3175,8 +3243,8 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
             *eventPP = s;
           }
         } else
-          charDataHandler(parser->m_handlerArg, (XML_Char *)s,
-                          (int)((XML_Char *)next - (XML_Char *)s));
+          charDataHandler(parser->m_handlerArg, (const XML_Char *)s,
+                          (int)((const XML_Char *)next - (const XML_Char *)s));
       } else if (parser->m_defaultHandler)
         reportDefault(parser, enc, s, next);
     } break;
@@ -4040,7 +4108,7 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
   for (;;) {
     const char *next = s; /* in case of XML_TOK_NONE or XML_TOK_PARTIAL */
     int tok = XmlCdataSectionTok(enc, s, end, &next);
-#ifdef XML_DTD
+#if XML_GE == 1
     if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) {
       accountingOnAbort(parser);
       return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
@@ -4055,7 +4123,7 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
         parser->m_endCdataSectionHandler(parser->m_handlerArg);
       /* BEGIN disabled code */
       /* see comment under XML_TOK_CDATA_SECT_OPEN */
-      else if (0 && parser->m_characterDataHandler)
+      else if ((0) && parser->m_characterDataHandler)
         parser->m_characterDataHandler(parser->m_handlerArg, parser->m_dataBuf,
                                        0);
       /* END disabled code */
@@ -4091,8 +4159,8 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
             *eventPP = s;
           }
         } else
-          charDataHandler(parser->m_handlerArg, (XML_Char *)s,
-                          (int)((XML_Char *)next - (XML_Char *)s));
+          charDataHandler(parser->m_handlerArg, (const XML_Char *)s,
+                          (int)((const XML_Char *)next - (const XML_Char *)s));
       } else if (parser->m_defaultHandler)
         reportDefault(parser, enc, s, next);
     } break;
@@ -4192,7 +4260,7 @@ doIgnoreSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
   *eventPP = s;
   *startPtr = NULL;
   tok = XmlIgnoreSectionTok(enc, s, end, &next);
-#  ifdef XML_DTD
+#  if XML_GE == 1
   if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
                                 XML_ACCOUNT_DIRECT)) {
     accountingOnAbort(parser);
@@ -4284,7 +4352,7 @@ processXmlDecl(XML_Parser parser, int isGeneralTextEntity, const char *s,
   const XML_Char *storedversion = NULL;
   int standalone = -1;
 
-#ifdef XML_DTD
+#if XML_GE == 1
   if (! accountingDiffTolerated(parser, XML_TOK_XML_DECL, s, next, __LINE__,
                                 XML_ACCOUNT_DIRECT)) {
     accountingOnAbort(parser);
@@ -4482,16 +4550,16 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end,
       parser->m_processor = entityValueProcessor;
       return entityValueProcessor(parser, next, end, nextPtr);
     }
-    /* If we are at the end of the buffer, this would cause XmlPrologTok to
-       return XML_TOK_NONE on the next call, which would then cause the
-       function to exit with *nextPtr set to s - that is what we want for other
-       tokens, but not for the BOM - we would rather like to skip it;
-       then, when this routine is entered the next time, XmlPrologTok will
-       return XML_TOK_INVALID, since the BOM is still in the buffer
+    /* XmlPrologTok has now set the encoding based on the BOM it found, and we
+       must move s and nextPtr forward to consume the BOM.
+
+       If we didn't, and got XML_TOK_NONE from the next XmlPrologTok call, we
+       would leave the BOM in the buffer and return. On the next call to this
+       function, our XmlPrologTok call would return XML_TOK_INVALID, since it
+       is not valid to have multiple BOMs.
     */
-    else if (tok == XML_TOK_BOM && next == end
-             && ! parser->m_parsingStatus.finalBuffer) {
-#  ifdef XML_DTD
+    else if (tok == XML_TOK_BOM) {
+#  if XML_GE == 1
       if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
                                     XML_ACCOUNT_DIRECT)) {
         accountingOnAbort(parser);
@@ -4500,7 +4568,7 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end,
 #  endif
 
       *nextPtr = next;
-      return XML_ERROR_NONE;
+      s = next;
     }
     /* If we get this token, we have the start of what might be a
        normal tag, but not a declaration (i.e. it doesn't begin with
@@ -4707,11 +4775,13 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
       }
     }
     role = XmlTokenRole(&parser->m_prologState, tok, s, next, enc);
-#ifdef XML_DTD
+#if XML_GE == 1
     switch (role) {
     case XML_ROLE_INSTANCE_START: // bytes accounted in contentProcessor
     case XML_ROLE_XML_DECL:       // bytes accounted in processXmlDecl
-    case XML_ROLE_TEXT_DECL:      // bytes accounted in processXmlDecl
+#  ifdef XML_DTD
+    case XML_ROLE_TEXT_DECL: // bytes accounted in processXmlDecl
+#  endif
       break;
     default:
       if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) {
@@ -5029,6 +5099,9 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
       break;
     case XML_ROLE_ENTITY_VALUE:
       if (dtd->keepProcessing) {
+#if XML_GE == 1
+        // This will store the given replacement text in
+        // parser->m_declEntity->textPtr.
         enum XML_Error result
             = storeEntityValue(parser, enc, s + enc->minBytesPerChar,
                                next - enc->minBytesPerChar, XML_ACCOUNT_NONE);
@@ -5049,6 +5122,25 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
           poolDiscard(&dtd->entityValuePool);
         if (result != XML_ERROR_NONE)
           return result;
+#else
+        // This will store "&amp;entity123;" in parser->m_declEntity->textPtr
+        // to end up as "&entity123;" in the handler.
+        if (parser->m_declEntity != NULL) {
+          const enum XML_Error result
+              = storeSelfEntityValue(parser, parser->m_declEntity);
+          if (result != XML_ERROR_NONE)
+            return result;
+
+          if (parser->m_entityDeclHandler) {
+            *eventEndPP = s;
+            parser->m_entityDeclHandler(
+                parser->m_handlerArg, parser->m_declEntity->name,
+                parser->m_declEntity->is_param, parser->m_declEntity->textPtr,
+                parser->m_declEntity->textLen, parser->m_curBase, 0, 0, 0);
+            handleDefault = XML_FALSE;
+          }
+        }
+#endif
       }
       break;
     case XML_ROLE_DOCTYPE_SYSTEM_ID:
@@ -5107,6 +5199,16 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
       }
       break;
     case XML_ROLE_ENTITY_COMPLETE:
+#if XML_GE == 0
+      // This will store "&amp;entity123;" in entity->textPtr
+      // to end up as "&entity123;" in the handler.
+      if (parser->m_declEntity != NULL) {
+        const enum XML_Error result
+            = storeSelfEntityValue(parser, parser->m_declEntity);
+        if (result != XML_ERROR_NONE)
+          return result;
+      }
+#endif
       if (dtd->keepProcessing && parser->m_declEntity
           && parser->m_entityDeclHandler) {
         *eventEndPP = s;
@@ -5648,7 +5750,7 @@ epilogProcessor(XML_Parser parser, const char *s, const char *end,
   for (;;) {
     const char *next = NULL;
     int tok = XmlPrologTok(parser->m_encoding, s, end, &next);
-#ifdef XML_DTD
+#if XML_GE == 1
     if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
                                   XML_ACCOUNT_DIRECT)) {
       accountingOnAbort(parser);
@@ -5728,7 +5830,7 @@ processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) {
       return XML_ERROR_NO_MEMORY;
   }
   entity->open = XML_TRUE;
-#ifdef XML_DTD
+#if XML_GE == 1
   entityTrackingOnOpen(parser, entity, __LINE__);
 #endif
   entity->processed = 0;
@@ -5761,10 +5863,10 @@ processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) {
     if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) {
       entity->processed = (int)(next - textStart);
       parser->m_processor = internalEntityProcessor;
-    } else {
-#ifdef XML_DTD
+    } else if (parser->m_openInternalEntities->entity == entity) {
+#if XML_GE == 1
       entityTrackingOnClose(parser, entity, __LINE__);
-#endif /* XML_DTD */
+#endif /* XML_GE == 1 */
       entity->open = XML_FALSE;
       parser->m_openInternalEntities = openEntity->next;
       /* put openEntity back in list of free instances */
@@ -5813,7 +5915,7 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
     return result;
   }
 
-#ifdef XML_DTD
+#if XML_GE == 1
   entityTrackingOnClose(parser, entity, __LINE__);
 #endif
   entity->open = XML_FALSE;
@@ -5892,7 +5994,7 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
     const char *next
         = ptr; /* XmlAttributeValueTok doesn't always set the last arg */
     int tok = XmlAttributeValueTok(enc, ptr, end, &next);
-#ifdef XML_DTD
+#if XML_GE == 1
     if (! accountingDiffTolerated(parser, tok, ptr, next, __LINE__, account)) {
       accountingOnAbort(parser);
       return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
@@ -5957,14 +6059,14 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
       XML_Char ch = (XML_Char)XmlPredefinedEntityName(
           enc, ptr + enc->minBytesPerChar, next - enc->minBytesPerChar);
       if (ch) {
-#ifdef XML_DTD
+#if XML_GE == 1
         /* NOTE: We are replacing 4-6 characters original input for 1 character
          *       so there is no amplification and hence recording without
          *       protection. */
         accountingDiffTolerated(parser, tok, (char *)&ch,
                                 ((char *)&ch) + sizeof(XML_Char), __LINE__,
                                 XML_ACCOUNT_ENTITY_EXPANSION);
-#endif /* XML_DTD */
+#endif /* XML_GE == 1 */
         if (! poolAppendChar(pool, ch))
           return XML_ERROR_NO_MEMORY;
         break;
@@ -6042,14 +6144,14 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
         enum XML_Error result;
         const XML_Char *textEnd = entity->textPtr + entity->textLen;
         entity->open = XML_TRUE;
-#ifdef XML_DTD
+#if XML_GE == 1
         entityTrackingOnOpen(parser, entity, __LINE__);
 #endif
         result = appendAttributeValue(parser, parser->m_internalEncoding,
                                       isCdata, (const char *)entity->textPtr,
                                       (const char *)textEnd, pool,
                                       XML_ACCOUNT_ENTITY_EXPANSION);
-#ifdef XML_DTD
+#if XML_GE == 1
         entityTrackingOnClose(parser, entity, __LINE__);
 #endif
         entity->open = XML_FALSE;
@@ -6079,6 +6181,7 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
   /* not reached */
 }
 
+#if XML_GE == 1
 static enum XML_Error
 storeEntityValue(XML_Parser parser, const ENCODING *enc,
                  const char *entityTextPtr, const char *entityTextEnd,
@@ -6086,12 +6189,12 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
   DTD *const dtd = parser->m_dtd; /* save one level of indirection */
   STRING_POOL *pool = &(dtd->entityValuePool);
   enum XML_Error result = XML_ERROR_NONE;
-#ifdef XML_DTD
+#  ifdef XML_DTD
   int oldInEntityValue = parser->m_prologState.inEntityValue;
   parser->m_prologState.inEntityValue = 1;
-#else
+#  else
   UNUSED_P(account);
-#endif /* XML_DTD */
+#  endif /* XML_DTD */
   /* never return Null for the value argument in EntityDeclHandler,
      since this would indicate an external entity; therefore we
      have to make sure that entityValuePool.start is not null */
@@ -6105,18 +6208,16 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
         = entityTextPtr; /* XmlEntityValueTok doesn't always set the last arg */
     int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next);
 
-#ifdef XML_DTD
     if (! accountingDiffTolerated(parser, tok, entityTextPtr, next, __LINE__,
                                   account)) {
       accountingOnAbort(parser);
       result = XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
       goto endEntityValue;
     }
-#endif
 
     switch (tok) {
     case XML_TOK_PARAM_ENTITY_REF:
-#ifdef XML_DTD
+#  ifdef XML_DTD
       if (parser->m_isParamEntity || enc != parser->m_encoding) {
         const XML_Char *name;
         ENTITY *entity;
@@ -6139,7 +6240,7 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
           dtd->keepProcessing = dtd->standalone;
           goto endEntityValue;
         }
-        if (entity->open) {
+        if (entity->open || (entity == parser->m_declEntity)) {
           if (enc == parser->m_encoding)
             parser->m_eventPtr = entityTextPtr;
           result = XML_ERROR_RECURSIVE_ENTITY_REF;
@@ -6178,7 +6279,7 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
         }
         break;
       }
-#endif /* XML_DTD */
+#  endif /* XML_DTD */
       /* In the internal subset, PE references are not legal
          within markup declarations, e.g entity values in this case. */
       parser->m_eventPtr = entityTextPtr;
@@ -6259,12 +6360,38 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
     entityTextPtr = next;
   }
 endEntityValue:
-#ifdef XML_DTD
+#  ifdef XML_DTD
   parser->m_prologState.inEntityValue = oldInEntityValue;
-#endif /* XML_DTD */
+#  endif /* XML_DTD */
   return result;
 }
 
+#else /* XML_GE == 0 */
+
+static enum XML_Error
+storeSelfEntityValue(XML_Parser parser, ENTITY *entity) {
+  // This will store "&amp;entity123;" in entity->textPtr
+  // to end up as "&entity123;" in the handler.
+  const char *const entity_start = "&amp;";
+  const char *const entity_end = ";";
+
+  STRING_POOL *const pool = &(parser->m_dtd->entityValuePool);
+  if (! poolAppendString(pool, entity_start)
+      || ! poolAppendString(pool, entity->name)
+      || ! poolAppendString(pool, entity_end)) {
+    poolDiscard(pool);
+    return XML_ERROR_NO_MEMORY;
+  }
+
+  entity->textPtr = poolStart(pool);
+  entity->textLen = (int)(poolLength(pool));
+  poolFinish(pool);
+
+  return XML_ERROR_NONE;
+}
+
+#endif /* XML_GE == 0 */
+
 static void FASTCALL
 normalizeLines(XML_Char *s) {
   XML_Char *p;
@@ -6375,8 +6502,9 @@ reportDefault(XML_Parser parser, const ENCODING *enc, const char *s,
     } while ((convert_res != XML_CONVERT_COMPLETED)
              && (convert_res != XML_CONVERT_INPUT_INCOMPLETE));
   } else
-    parser->m_defaultHandler(parser->m_handlerArg, (XML_Char *)s,
-                             (int)((XML_Char *)end - (XML_Char *)s));
+    parser->m_defaultHandler(
+        parser->m_handlerArg, (const XML_Char *)s,
+        (int)((const XML_Char *)end - (const XML_Char *)s));
 }
 
 static int
@@ -6480,7 +6608,7 @@ getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start,
   name = poolStoreString(&dtd->pool, enc, start, end);
   if (! name)
     return NULL;
-  /* skip quotation mark - its storage will be re-used (like in name[-1]) */
+  /* skip quotation mark - its storage will be reused (like in name[-1]) */
   ++name;
   id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, name,
                               sizeof(ATTRIBUTE_ID));
@@ -6630,6 +6758,10 @@ getContext(XML_Parser parser) {
 
 static XML_Bool
 setContext(XML_Parser parser, const XML_Char *context) {
+  if (context == NULL) {
+    return XML_FALSE;
+  }
+
   DTD *const dtd = parser->m_dtd; /* save one level of indirection */
   const XML_Char *s = context;
 
@@ -7220,7 +7352,7 @@ poolAppend(STRING_POOL *pool, const ENCODING *enc, const char *ptr,
     return NULL;
   for (;;) {
     const enum XML_Convert_Result convert_res = XmlConvert(
-        enc, &ptr, end, (ICHAR **)&(pool->ptr), (ICHAR *)pool->end);
+        enc, &ptr, end, (ICHAR **)&(pool->ptr), (const ICHAR *)pool->end);
     if ((convert_res == XML_CONVERT_COMPLETED)
         || (convert_res == XML_CONVERT_INPUT_INCOMPLETE))
       break;
@@ -7651,10 +7783,12 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
   return result;
 }
 
-#ifdef XML_DTD
+#if XML_GE == 1
 
 static float
 accountingGetCurrentAmplification(XML_Parser rootParser) {
+  //                                          1.........1.........12 => 22
+  const size_t lenOfShortestInclude = sizeof("<!ENTITY a SYSTEM 'b'>") - 1;
   const XmlBigCount countBytesOutput
       = rootParser->m_accounting.countBytesDirect
         + rootParser->m_accounting.countBytesIndirect;
@@ -7662,7 +7796,9 @@ accountingGetCurrentAmplification(XML_Parser rootParser) {
       = rootParser->m_accounting.countBytesDirect
             ? (countBytesOutput
                / (float)(rootParser->m_accounting.countBytesDirect))
-            : 1.0f;
+            : ((lenOfShortestInclude
+                + rootParser->m_accounting.countBytesIndirect)
+               / (float)lenOfShortestInclude);
   assert(! rootParser->m_parentParser);
   return amplificationFactor;
 }
@@ -7672,7 +7808,7 @@ accountingReportStats(XML_Parser originParser, const char *epilog) {
   const XML_Parser rootParser = getRootParserOf(originParser, NULL);
   assert(! rootParser->m_parentParser);
 
-  if (rootParser->m_accounting.debugLevel < 1) {
+  if (rootParser->m_accounting.debugLevel == 0u) {
     return;
   }
 
@@ -7709,7 +7845,7 @@ accountingReportDiff(XML_Parser rootParser,
 
   /* Note: Performance is of no concern here */
   const char *walker = before;
-  if ((rootParser->m_accounting.debugLevel >= 3)
+  if ((rootParser->m_accounting.debugLevel >= 3u)
       || (after - before)
              <= (ptrdiff_t)(contextLength + ellipsisLength + contextLength)) {
     for (; walker < after; walker++) {
@@ -7774,7 +7910,7 @@ accountingDiffTolerated(XML_Parser originParser, int tok, const char *before,
         || (amplificationFactor
             <= rootParser->m_accounting.maximumAmplificationFactor);
 
-  if (rootParser->m_accounting.debugLevel >= 2) {
+  if (rootParser->m_accounting.debugLevel >= 2u) {
     accountingReportStats(rootParser, "");
     accountingReportDiff(rootParser, levelsAwayFromRootParser, before, after,
                          bytesMore, source_line, account);
@@ -7801,7 +7937,7 @@ static void
 entityTrackingReportStats(XML_Parser rootParser, ENTITY *entity,
                           const char *action, int sourceLine) {
   assert(! rootParser->m_parentParser);
-  if (rootParser->m_entity_stats.debugLevel < 1)
+  if (rootParser->m_entity_stats.debugLevel == 0u)
     return;
 
 #  if defined(XML_UNICODE)
@@ -8382,7 +8518,7 @@ unsignedCharToPrintable(unsigned char c) {
   assert(0); /* never gets here */
 }
 
-#endif /* XML_DTD */
+#endif /* XML_GE == 1 */
 
 static unsigned long
 getDebugLevel(const char *variableName, unsigned long defaultDebugLevel) {
@@ -8393,9 +8529,9 @@ getDebugLevel(const char *variableName, unsigned long defaultDebugLevel) {
   const char *const value = valueOrNull;
 
   errno = 0;
-  char *afterValue = (char *)value;
+  char *afterValue = NULL;
   unsigned long debugLevel = strtoul(value, &afterValue, 10);
-  if ((errno != 0) || (afterValue[0] != '\0')) {
+  if ((errno != 0) || (afterValue == value) || (afterValue[0] != '\0')) {
     errno = 0;
     return defaultDebugLevel;
   }
index 3f0f5c1..2c48bf4 100644 (file)
    Copyright (c) 2002-2006 Karl Waclawek <karl@waclawek.net>
    Copyright (c) 2002-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
    Copyright (c) 2005-2009 Steven Solie <steven@solie.ca>
-   Copyright (c) 2016-2021 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016-2023 Sebastian Pipping <sebastian@pipping.org>
    Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
-   Copyright (c) 2021      Dong-hee Na <donghee.na@python.org>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -38,7 +38,7 @@
    USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
-#include <expat_config.h>
+#include "expat_config.h"
 
 #include <stddef.h>
 
index d6e1fa1..a790427 100644 (file)
@@ -10,7 +10,7 @@
    Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
    Copyright (c) 2002      Karl Waclawek <karl@waclawek.net>
    Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
-   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2024 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -127,9 +127,9 @@ typedef struct prolog_state {
 #endif /* XML_DTD */
 } PROLOG_STATE;
 
-void XmlPrologStateInit(PROLOG_STATE *);
+void XmlPrologStateInit(PROLOG_STATE *state);
 #ifdef XML_DTD
-void XmlPrologStateInitExternalEntity(PROLOG_STATE *);
+void XmlPrologStateInitExternalEntity(PROLOG_STATE *state);
 #endif /* XML_DTD */
 
 #define XmlTokenRole(state, tok, ptr, end, enc)                                \
index 2b7012a..29a66d7 100644 (file)
@@ -12,7 +12,7 @@
    Copyright (c) 2002      Greg Stein <gstein@users.sourceforge.net>
    Copyright (c) 2002-2016 Karl Waclawek <karl@waclawek.net>
    Copyright (c) 2005-2009 Steven Solie <steven@solie.ca>
-   Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
    Copyright (c) 2016      Pascal Cuoq <cuoq@trust-in-soft.com>
    Copyright (c) 2016      Don Lewis <truckman@apache.org>
    Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Copyright (c) 2017      Benbuck Nason <bnason@netflix.com>
    Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
    Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
-   Copyright (c) 2021      Dong-hee Na <donghee.na@python.org>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
    Copyright (c) 2022      Martin Ettl <ettl.martin78@googlemail.com>
+   Copyright (c) 2022      Sean McBride <sean@rogue-research.com>
+   Copyright (c) 2023      Hanno Böck <hanno@gentoo.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -44,7 +46,7 @@
    USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
-#include <expat_config.h>
+#include "expat_config.h"
 
 #include <stddef.h>
 #include <string.h> /* memcpy */
@@ -76,7 +78,7 @@
 #define VTABLE VTABLE1, PREFIX(toUtf8), PREFIX(toUtf16)
 
 #define UCS2_GET_NAMING(pages, hi, lo)                                         \
-  (namingBitmap[(pages[hi] << 3) + ((lo) >> 5)] & (1u << ((lo)&0x1F)))
+  (namingBitmap[(pages[hi] << 3) + ((lo) >> 5)] & (1u << ((lo) & 0x1F)))
 
 /* A 2 byte UTF-8 representation splits the characters 11 bits between
    the bottom 5 and 6 bits of the bytes.  We need 8 bits to index into
    & (1u << (((byte)[2]) & 0x1F)))
 
 /* Detection of invalid UTF-8 sequences is based on Table 3.1B
-   of Unicode 3.2: http://www.unicode.org/unicode/reports/tr28/
+   of Unicode 3.2: https://www.unicode.org/unicode/reports/tr28/
    with the additional restriction of not allowing the Unicode
    code points 0xFFFF and 0xFFFE (sequences EF,BF,BF and EF,BF,BE).
    Implementation details:
@@ -225,7 +227,7 @@ struct normal_encoding {
       /* isNmstrt2 */ NULL, /* isNmstrt3 */ NULL, /* isNmstrt4 */ NULL,        \
       /* isInvalid2 */ NULL, /* isInvalid3 */ NULL, /* isInvalid4 */ NULL
 
-static int FASTCALL checkCharRefNumber(int);
+static int FASTCALL checkCharRefNumber(int result);
 
 #include "xmltok_impl.h"
 #include "ascii.h"
@@ -243,7 +245,7 @@ static int FASTCALL checkCharRefNumber(int);
 #endif
 
 #define SB_BYTE_TYPE(enc, p)                                                   \
-  (((struct normal_encoding *)(enc))->type[(unsigned char)*(p)])
+  (((const struct normal_encoding *)(enc))->type[(unsigned char)*(p)])
 
 #ifdef XML_MIN_SIZE
 static int PTRFASTCALL
@@ -407,7 +409,7 @@ utf8_toUtf16(const ENCODING *enc, const char **fromP, const char *fromLim,
   unsigned short *to = *toP;
   const char *from = *fromP;
   while (from < fromLim && to < toLim) {
-    switch (((struct normal_encoding *)enc)->type[(unsigned char)*from]) {
+    switch (SB_BYTE_TYPE(enc, from)) {
     case BT_LEAD2:
       if (fromLim - from < 2) {
         res = XML_CONVERT_INPUT_INCOMPLETE;
@@ -715,31 +717,26 @@ unicode_byte_type(char hi, char lo) {
       return res;                                                              \
   }
 
-#define SET2(ptr, ch) (((ptr)[0] = ((ch)&0xff)), ((ptr)[1] = ((ch) >> 8)))
 #define GET_LO(ptr) ((unsigned char)(ptr)[0])
 #define GET_HI(ptr) ((unsigned char)(ptr)[1])
 
 DEFINE_UTF16_TO_UTF8(little2_)
 DEFINE_UTF16_TO_UTF16(little2_)
 
-#undef SET2
 #undef GET_LO
 #undef GET_HI
 
-#define SET2(ptr, ch) (((ptr)[0] = ((ch) >> 8)), ((ptr)[1] = ((ch)&0xFF)))
 #define GET_LO(ptr) ((unsigned char)(ptr)[1])
 #define GET_HI(ptr) ((unsigned char)(ptr)[0])
 
 DEFINE_UTF16_TO_UTF8(big2_)
 DEFINE_UTF16_TO_UTF16(big2_)
 
-#undef SET2
 #undef GET_LO
 #undef GET_HI
 
 #define LITTLE2_BYTE_TYPE(enc, p)                                              \
-  ((p)[1] == 0 ? ((struct normal_encoding *)(enc))->type[(unsigned char)*(p)]  \
-               : unicode_byte_type((p)[1], (p)[0]))
+  ((p)[1] == 0 ? SB_BYTE_TYPE(enc, p) : unicode_byte_type((p)[1], (p)[0]))
 #define LITTLE2_BYTE_TO_ASCII(p) ((p)[1] == 0 ? (p)[0] : -1)
 #define LITTLE2_CHAR_MATCHES(p, c) ((p)[1] == 0 && (p)[0] == (c))
 #define LITTLE2_IS_NAME_CHAR_MINBPC(p)                                         \
@@ -872,9 +869,7 @@ static const struct normal_encoding internal_little2_encoding
 #endif
 
 #define BIG2_BYTE_TYPE(enc, p)                                                 \
-  ((p)[0] == 0                                                                 \
-       ? ((struct normal_encoding *)(enc))->type[(unsigned char)(p)[1]]        \
-       : unicode_byte_type((p)[0], (p)[1]))
+  ((p)[0] == 0 ? SB_BYTE_TYPE(enc, p + 1) : unicode_byte_type((p)[0], (p)[1]))
 #define BIG2_BYTE_TO_ASCII(p) ((p)[0] == 0 ? (p)[1] : -1)
 #define BIG2_CHAR_MATCHES(p, c) ((p)[0] == 0 && (p)[1] == (c))
 #define BIG2_IS_NAME_CHAR_MINBPC(p)                                            \
index 6f630c2..c51fce1 100644 (file)
@@ -10,7 +10,7 @@
    Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
    Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
    Copyright (c) 2002-2005 Karl Waclawek <karl@waclawek.net>
-   Copyright (c) 2016-2017 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
    Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Licensed under the MIT license:
 
@@ -289,7 +289,8 @@ int XmlParseXmlDecl(int isGeneralTextEntity, const ENCODING *enc,
                     const char **encodingNamePtr,
                     const ENCODING **namedEncodingPtr, int *standalonePtr);
 
-int XmlInitEncoding(INIT_ENCODING *, const ENCODING **, const char *name);
+int XmlInitEncoding(INIT_ENCODING *p, const ENCODING **encPtr,
+                    const char *name);
 const ENCODING *XmlGetUtf8InternalEncoding(void);
 const ENCODING *XmlGetUtf16InternalEncoding(void);
 int FASTCALL XmlUtf8Encode(int charNumber, char *buf);
@@ -307,7 +308,8 @@ int XmlParseXmlDeclNS(int isGeneralTextEntity, const ENCODING *enc,
                       const char **encodingNamePtr,
                       const ENCODING **namedEncodingPtr, int *standalonePtr);
 
-int XmlInitEncodingNS(INIT_ENCODING *, const ENCODING **, const char *name);
+int XmlInitEncodingNS(INIT_ENCODING *p, const ENCODING **encPtr,
+                      const char *name);
 const ENCODING *XmlGetUtf8InternalEncodingNS(void);
 const ENCODING *XmlGetUtf16InternalEncodingNS(void);
 ENCODING *XmlInitUnknownEncodingNS(void *mem, int *table, CONVERTER convert,
index 1971d74..239a2d0 100644 (file)
 #  endif
 
 #  define HAS_CHARS(enc, ptr, end, count)                                      \
-    ((end) - (ptr) >= ((count)*MINBPC(enc)))
+    ((end) - (ptr) >= ((count) * MINBPC(enc)))
 
 #  define HAS_CHAR(enc, ptr, end) HAS_CHARS(enc, ptr, end, 1)
 
index cb68e11..c38c430 100644 (file)
@@ -6,8 +6,8 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org>
-# Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+# Copyright (c) 2017-2024 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
 # Copyright (c) 2020      Jeffrey Walton <noloader@gmail.com>
 # Licensed under the MIT license:
 #
 
 SUBDIRS = . benchmark
 
-AM_CPPFLAGS = @AM_CPPFLAGS@ -I$(srcdir)/../lib
+AM_CPPFLAGS = @AM_CPPFLAGS@ -I$(srcdir)/../lib -DXML_TESTING
 
-noinst_LIBRARIES = libruntests.a
-
-check_PROGRAMS = runtests runtestspp
-TESTS = runtests runtestspp
+check_PROGRAMS = runtests runtests_cxx
+TESTS = runtests runtests_cxx
 
 # To support MinGW and Non-MinGW at the same time:
 LOG_DRIVER = $(srcdir)/../test-driver-wrapper.sh
 
-libruntests_a_SOURCES = \
+runtests_SOURCES = \
+    acc_tests.c \
+    alloc_tests.c \
+    basic_tests.c \
     chardata.c \
-    structdata.c \
+    common.c \
+    dummy.c \
+    handlers.c \
     memcheck.c \
-    minicheck.c
-
-runtests_SOURCES = \
-    runtests.c
+    minicheck.c \
+    misc_tests.c \
+    ns_tests.c \
+    nsalloc_tests.c \
+    runtests.c \
+    structdata.c
 
-runtestspp_SOURCES = \
-    runtestspp.cpp
+runtests_cxx_SOURCES = \
+    acc_tests_cxx.cpp \
+    alloc_tests_cxx.cpp \
+    basic_tests_cxx.cpp \
+    chardata_cxx.cpp \
+    common_cxx.cpp \
+    dummy_cxx.cpp \
+    handlers_cxx.cpp \
+    memcheck_cxx.cpp \
+    minicheck_cxx.cpp \
+    misc_tests_cxx.cpp \
+    nsalloc_tests_cxx.cpp \
+    ns_tests_cxx.cpp \
+    runtests_cxx.cpp \
+    structdata_cxx.cpp
 
-runtests_LDADD = libruntests.a ../lib/libexpatinternal.la
-runtestspp_LDADD = libruntests.a ../lib/libexpatinternal.la
+runtests_LDADD = ../lib/libtestpat.la
+runtests_cxx_LDADD = ../lib/libtestpat.la
 
 runtests_LDFLAGS = @AM_LDFLAGS@ @LIBM@
-runtestspp_LDFLAGS = @AM_LDFLAGS@ @LIBM@
+runtests_cxx_LDFLAGS = @AM_LDFLAGS@ @LIBM@
 
 EXTRA_DIST = \
+    acc_tests.h \
+    alloc_tests.h \
+    basic_tests.h \
     chardata.h \
+    common.h \
+    dummy.h \
+    handlers.h \
+    misc_tests.h \
+    ns_tests.h \
+    nsalloc_tests.h \
     structdata.h \
     minicheck.h \
     memcheck.h \
index fb8ad54..00c7e8d 100644 (file)
@@ -22,8 +22,8 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org>
-# Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+# Copyright (c) 2017-2024 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
 # Copyright (c) 2020      Jeffrey Walton <noloader@gmail.com>
 # Licensed under the MIT license:
 #
@@ -45,7 +45,6 @@
 # DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
 # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 # USE OR OTHER DEALINGS IN THE SOFTWARE.
-
 VPATH = @srcdir@
 am__is_gnu_make = { \
   if test -z '$(MAKELEVEL)'; then \
@@ -120,8 +119,8 @@ PRE_UNINSTALL = :
 POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
-check_PROGRAMS = runtests$(EXEEXT) runtestspp$(EXEEXT)
-TESTS = runtests$(EXEEXT) runtestspp$(EXEEXT)
+check_PROGRAMS = runtests$(EXEEXT) runtests_cxx$(EXEEXT)
+TESTS = runtests$(EXEEXT) runtests_cxx$(EXEEXT)
 subdir = tests
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
@@ -135,6 +134,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
        $(top_srcdir)/conftools/ax-append-compile-flags.m4 \
        $(top_srcdir)/conftools/ax-append-link-flags.m4 \
        $(top_srcdir)/conftools/expatcfg-compiler-supports-visibility.m4 \
+       $(top_srcdir)/conftools/ax-cxx-compile-stdcxx.m4 \
+       $(top_srcdir)/conftools/ax-cxx-compile-stdcxx-11.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
@@ -143,20 +144,14 @@ mkinstalldirs = $(install_sh) -d
 CONFIG_HEADER = $(top_builddir)/expat_config.h
 CONFIG_CLEAN_FILES =
 CONFIG_CLEAN_VPATH_FILES =
-LIBRARIES = $(noinst_LIBRARIES)
-ARFLAGS = cru
-AM_V_AR = $(am__v_AR_@AM_V@)
-am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@)
-am__v_AR_0 = @echo "  AR      " $@;
-am__v_AR_1 = 
-libruntests_a_AR = $(AR) $(ARFLAGS)
-libruntests_a_LIBADD =
-am_libruntests_a_OBJECTS = chardata.$(OBJEXT) structdata.$(OBJEXT) \
-       memcheck.$(OBJEXT) minicheck.$(OBJEXT)
-libruntests_a_OBJECTS = $(am_libruntests_a_OBJECTS)
-am_runtests_OBJECTS = runtests.$(OBJEXT)
+am_runtests_OBJECTS = acc_tests.$(OBJEXT) alloc_tests.$(OBJEXT) \
+       basic_tests.$(OBJEXT) chardata.$(OBJEXT) common.$(OBJEXT) \
+       dummy.$(OBJEXT) handlers.$(OBJEXT) memcheck.$(OBJEXT) \
+       minicheck.$(OBJEXT) misc_tests.$(OBJEXT) ns_tests.$(OBJEXT) \
+       nsalloc_tests.$(OBJEXT) runtests.$(OBJEXT) \
+       structdata.$(OBJEXT)
 runtests_OBJECTS = $(am_runtests_OBJECTS)
-runtests_DEPENDENCIES = libruntests.a ../lib/libexpatinternal.la
+runtests_DEPENDENCIES = ../lib/libtestpat.la
 AM_V_lt = $(am__v_lt_@AM_V@)
 am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
 am__v_lt_0 = --silent
@@ -164,12 +159,19 @@ am__v_lt_1 =
 runtests_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
        $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
        $(runtests_LDFLAGS) $(LDFLAGS) -o $@
-am_runtestspp_OBJECTS = runtestspp.$(OBJEXT)
-runtestspp_OBJECTS = $(am_runtestspp_OBJECTS)
-runtestspp_DEPENDENCIES = libruntests.a ../lib/libexpatinternal.la
-runtestspp_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+am_runtests_cxx_OBJECTS = acc_tests_cxx.$(OBJEXT) \
+       alloc_tests_cxx.$(OBJEXT) basic_tests_cxx.$(OBJEXT) \
+       chardata_cxx.$(OBJEXT) common_cxx.$(OBJEXT) \
+       dummy_cxx.$(OBJEXT) handlers_cxx.$(OBJEXT) \
+       memcheck_cxx.$(OBJEXT) minicheck_cxx.$(OBJEXT) \
+       misc_tests_cxx.$(OBJEXT) nsalloc_tests_cxx.$(OBJEXT) \
+       ns_tests_cxx.$(OBJEXT) runtests_cxx.$(OBJEXT) \
+       structdata_cxx.$(OBJEXT)
+runtests_cxx_OBJECTS = $(am_runtests_cxx_OBJECTS)
+runtests_cxx_DEPENDENCIES = ../lib/libtestpat.la
+runtests_cxx_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
        $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
-       $(CXXFLAGS) $(runtestspp_LDFLAGS) $(LDFLAGS) -o $@
+       $(CXXFLAGS) $(runtests_cxx_LDFLAGS) $(LDFLAGS) -o $@
 AM_V_P = $(am__v_P_@AM_V@)
 am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
 am__v_P_0 = false
@@ -185,9 +187,21 @@ am__v_at_1 =
 DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/conftools/depcomp
 am__maybe_remake_depfiles = depfiles
-am__depfiles_remade = ./$(DEPDIR)/chardata.Po ./$(DEPDIR)/memcheck.Po \
-       ./$(DEPDIR)/minicheck.Po ./$(DEPDIR)/runtests.Po \
-       ./$(DEPDIR)/runtestspp.Po ./$(DEPDIR)/structdata.Po
+am__depfiles_remade = ./$(DEPDIR)/acc_tests.Po \
+       ./$(DEPDIR)/acc_tests_cxx.Po ./$(DEPDIR)/alloc_tests.Po \
+       ./$(DEPDIR)/alloc_tests_cxx.Po ./$(DEPDIR)/basic_tests.Po \
+       ./$(DEPDIR)/basic_tests_cxx.Po ./$(DEPDIR)/chardata.Po \
+       ./$(DEPDIR)/chardata_cxx.Po ./$(DEPDIR)/common.Po \
+       ./$(DEPDIR)/common_cxx.Po ./$(DEPDIR)/dummy.Po \
+       ./$(DEPDIR)/dummy_cxx.Po ./$(DEPDIR)/handlers.Po \
+       ./$(DEPDIR)/handlers_cxx.Po ./$(DEPDIR)/memcheck.Po \
+       ./$(DEPDIR)/memcheck_cxx.Po ./$(DEPDIR)/minicheck.Po \
+       ./$(DEPDIR)/minicheck_cxx.Po ./$(DEPDIR)/misc_tests.Po \
+       ./$(DEPDIR)/misc_tests_cxx.Po ./$(DEPDIR)/ns_tests.Po \
+       ./$(DEPDIR)/ns_tests_cxx.Po ./$(DEPDIR)/nsalloc_tests.Po \
+       ./$(DEPDIR)/nsalloc_tests_cxx.Po ./$(DEPDIR)/runtests.Po \
+       ./$(DEPDIR)/runtests_cxx.Po ./$(DEPDIR)/structdata.Po \
+       ./$(DEPDIR)/structdata_cxx.Po
 am__mv = mv -f
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
        $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -225,10 +239,8 @@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
 am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
 am__v_CXXLD_0 = @echo "  CXXLD   " $@;
 am__v_CXXLD_1 = 
-SOURCES = $(libruntests_a_SOURCES) $(runtests_SOURCES) \
-       $(runtestspp_SOURCES)
-DIST_SOURCES = $(libruntests_a_SOURCES) $(runtests_SOURCES) \
-       $(runtestspp_SOURCES)
+SOURCES = $(runtests_SOURCES) $(runtests_cxx_SOURCES)
+DIST_SOURCES = $(runtests_SOURCES) $(runtests_cxx_SOURCES)
 RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
        ctags-recursive dvi-recursive html-recursive info-recursive \
        install-data-recursive install-dvi-recursive \
@@ -503,7 +515,7 @@ am__relativize = \
 ACLOCAL = @ACLOCAL@
 AMTAR = @AMTAR@
 AM_CFLAGS = @AM_CFLAGS@
-AM_CPPFLAGS = @AM_CPPFLAGS@ -I$(srcdir)/../lib
+AM_CPPFLAGS = @AM_CPPFLAGS@ -I$(srcdir)/../lib -DXML_TESTING
 AM_CXXFLAGS = @AM_CXXFLAGS@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
 AM_LDFLAGS = @AM_LDFLAGS@
@@ -548,6 +560,7 @@ FGREP = @FGREP@
 FILECMD = @FILECMD@
 FILEMAP = @FILEMAP@
 GREP = @GREP@
+HAVE_CXX11 = @HAVE_CXX11@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
@@ -567,6 +580,7 @@ LIPO = @LIPO@
 LN_S = @LN_S@
 LTLIBOBJS = @LTLIBOBJS@
 LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
 MAKEINFO = @MAKEINFO@
 MANIFEST_TOOL = @MANIFEST_TOOL@
 MKDIR_P = @MKDIR_P@
@@ -649,28 +663,56 @@ top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 SUBDIRS = . benchmark
-noinst_LIBRARIES = libruntests.a
 
 # To support MinGW and Non-MinGW at the same time:
 LOG_DRIVER = $(srcdir)/../test-driver-wrapper.sh
-libruntests_a_SOURCES = \
+runtests_SOURCES = \
+    acc_tests.c \
+    alloc_tests.c \
+    basic_tests.c \
     chardata.c \
-    structdata.c \
+    common.c \
+    dummy.c \
+    handlers.c \
     memcheck.c \
-    minicheck.c
-
-runtests_SOURCES = \
-    runtests.c
-
-runtestspp_SOURCES = \
-    runtestspp.cpp
-
-runtests_LDADD = libruntests.a ../lib/libexpatinternal.la
-runtestspp_LDADD = libruntests.a ../lib/libexpatinternal.la
+    minicheck.c \
+    misc_tests.c \
+    ns_tests.c \
+    nsalloc_tests.c \
+    runtests.c \
+    structdata.c
+
+runtests_cxx_SOURCES = \
+    acc_tests_cxx.cpp \
+    alloc_tests_cxx.cpp \
+    basic_tests_cxx.cpp \
+    chardata_cxx.cpp \
+    common_cxx.cpp \
+    dummy_cxx.cpp \
+    handlers_cxx.cpp \
+    memcheck_cxx.cpp \
+    minicheck_cxx.cpp \
+    misc_tests_cxx.cpp \
+    nsalloc_tests_cxx.cpp \
+    ns_tests_cxx.cpp \
+    runtests_cxx.cpp \
+    structdata_cxx.cpp
+
+runtests_LDADD = ../lib/libtestpat.la
+runtests_cxx_LDADD = ../lib/libtestpat.la
 runtests_LDFLAGS = @AM_LDFLAGS@ @LIBM@
-runtestspp_LDFLAGS = @AM_LDFLAGS@ @LIBM@
+runtests_cxx_LDFLAGS = @AM_LDFLAGS@ @LIBM@
 EXTRA_DIST = \
+    acc_tests.h \
+    alloc_tests.h \
+    basic_tests.h \
     chardata.h \
+    common.h \
+    dummy.h \
+    handlers.h \
+    misc_tests.h \
+    ns_tests.h \
+    nsalloc_tests.h \
     structdata.h \
     minicheck.h \
     memcheck.h \
@@ -683,7 +725,7 @@ all: all-recursive
 
 .SUFFIXES:
 .SUFFIXES: .c .cpp .lo .log .o .obj .test .test$(EXEEXT) .trs
-$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
        @for dep in $?; do \
          case '$(am__configure_deps)' in \
            *$$dep*) \
@@ -707,9 +749,9 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
        cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
 
-$(top_srcdir)/configure:  $(am__configure_deps)
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
        cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
        cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
 $(am__aclocal_m4_deps):
 
@@ -722,21 +764,13 @@ clean-checkPROGRAMS:
        echo " rm -f" $$list; \
        rm -f $$list
 
-clean-noinstLIBRARIES:
-       -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
-
-libruntests.a: $(libruntests_a_OBJECTS) $(libruntests_a_DEPENDENCIES) $(EXTRA_libruntests_a_DEPENDENCIES) 
-       $(AM_V_at)-rm -f libruntests.a
-       $(AM_V_AR)$(libruntests_a_AR) libruntests.a $(libruntests_a_OBJECTS) $(libruntests_a_LIBADD)
-       $(AM_V_at)$(RANLIB) libruntests.a
-
 runtests$(EXEEXT): $(runtests_OBJECTS) $(runtests_DEPENDENCIES) $(EXTRA_runtests_DEPENDENCIES) 
        @rm -f runtests$(EXEEXT)
        $(AM_V_CCLD)$(runtests_LINK) $(runtests_OBJECTS) $(runtests_LDADD) $(LIBS)
 
-runtestspp$(EXEEXT): $(runtestspp_OBJECTS) $(runtestspp_DEPENDENCIES) $(EXTRA_runtestspp_DEPENDENCIES) 
-       @rm -f runtestspp$(EXEEXT)
-       $(AM_V_CXXLD)$(runtestspp_LINK) $(runtestspp_OBJECTS) $(runtestspp_LDADD) $(LIBS)
+runtests_cxx$(EXEEXT): $(runtests_cxx_OBJECTS) $(runtests_cxx_DEPENDENCIES) $(EXTRA_runtests_cxx_DEPENDENCIES) 
+       @rm -f runtests_cxx$(EXEEXT)
+       $(AM_V_CXXLD)$(runtests_cxx_LINK) $(runtests_cxx_OBJECTS) $(runtests_cxx_LDADD) $(LIBS)
 
 mostlyclean-compile:
        -rm -f *.$(OBJEXT)
@@ -744,12 +778,34 @@ mostlyclean-compile:
 distclean-compile:
        -rm -f *.tab.c
 
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acc_tests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acc_tests_cxx.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alloc_tests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alloc_tests_cxx.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/basic_tests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/basic_tests_cxx.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chardata.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chardata_cxx.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common_cxx.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dummy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dummy_cxx.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/handlers.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/handlers_cxx.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcheck.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memcheck_cxx.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/minicheck.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/minicheck_cxx.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc_tests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc_tests_cxx.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ns_tests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ns_tests_cxx.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nsalloc_tests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nsalloc_tests_cxx.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/runtests.Po@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/runtestspp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/runtests_cxx.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/structdata.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/structdata_cxx.Po@am__quote@ # am--include-marker
 
 $(am__depfiles_remade):
        @$(MKDIR_P) $(@D)
@@ -1052,9 +1108,9 @@ runtests.log: runtests$(EXEEXT)
        --log-file $$b.log --trs-file $$b.trs \
        $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
        "$$tst" $(AM_TESTS_FD_REDIRECT)
-runtestspp.log: runtestspp$(EXEEXT)
-       @p='runtestspp$(EXEEXT)'; \
-       b='runtestspp'; \
+runtests_cxx.log: runtests_cxx$(EXEEXT)
+       @p='runtests_cxx$(EXEEXT)'; \
+       b='runtests_cxx'; \
        $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
        --log-file $$b.log --trs-file $$b.trs \
        $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
@@ -1135,7 +1191,7 @@ check-am: all-am
        $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
        $(MAKE) $(AM_MAKEFLAGS) check-TESTS
 check: check-recursive
-all-am: Makefile $(LIBRARIES)
+all-am: Makefile
 installdirs: installdirs-recursive
 installdirs-am:
 install: install-recursive
@@ -1174,15 +1230,37 @@ maintainer-clean-generic:
 clean: clean-recursive
 
 clean-am: clean-checkPROGRAMS clean-generic clean-libtool \
-       clean-noinstLIBRARIES mostlyclean-am
+       mostlyclean-am
 
 distclean: distclean-recursive
-               -rm -f ./$(DEPDIR)/chardata.Po
+               -rm -f ./$(DEPDIR)/acc_tests.Po
+       -rm -f ./$(DEPDIR)/acc_tests_cxx.Po
+       -rm -f ./$(DEPDIR)/alloc_tests.Po
+       -rm -f ./$(DEPDIR)/alloc_tests_cxx.Po
+       -rm -f ./$(DEPDIR)/basic_tests.Po
+       -rm -f ./$(DEPDIR)/basic_tests_cxx.Po
+       -rm -f ./$(DEPDIR)/chardata.Po
+       -rm -f ./$(DEPDIR)/chardata_cxx.Po
+       -rm -f ./$(DEPDIR)/common.Po
+       -rm -f ./$(DEPDIR)/common_cxx.Po
+       -rm -f ./$(DEPDIR)/dummy.Po
+       -rm -f ./$(DEPDIR)/dummy_cxx.Po
+       -rm -f ./$(DEPDIR)/handlers.Po
+       -rm -f ./$(DEPDIR)/handlers_cxx.Po
        -rm -f ./$(DEPDIR)/memcheck.Po
+       -rm -f ./$(DEPDIR)/memcheck_cxx.Po
        -rm -f ./$(DEPDIR)/minicheck.Po
+       -rm -f ./$(DEPDIR)/minicheck_cxx.Po
+       -rm -f ./$(DEPDIR)/misc_tests.Po
+       -rm -f ./$(DEPDIR)/misc_tests_cxx.Po
+       -rm -f ./$(DEPDIR)/ns_tests.Po
+       -rm -f ./$(DEPDIR)/ns_tests_cxx.Po
+       -rm -f ./$(DEPDIR)/nsalloc_tests.Po
+       -rm -f ./$(DEPDIR)/nsalloc_tests_cxx.Po
        -rm -f ./$(DEPDIR)/runtests.Po
-       -rm -f ./$(DEPDIR)/runtestspp.Po
+       -rm -f ./$(DEPDIR)/runtests_cxx.Po
        -rm -f ./$(DEPDIR)/structdata.Po
+       -rm -f ./$(DEPDIR)/structdata_cxx.Po
        -rm -f Makefile
 distclean-am: clean-am distclean-compile distclean-generic \
        distclean-tags
@@ -1228,12 +1306,34 @@ install-ps-am:
 installcheck-am:
 
 maintainer-clean: maintainer-clean-recursive
-               -rm -f ./$(DEPDIR)/chardata.Po
+               -rm -f ./$(DEPDIR)/acc_tests.Po
+       -rm -f ./$(DEPDIR)/acc_tests_cxx.Po
+       -rm -f ./$(DEPDIR)/alloc_tests.Po
+       -rm -f ./$(DEPDIR)/alloc_tests_cxx.Po
+       -rm -f ./$(DEPDIR)/basic_tests.Po
+       -rm -f ./$(DEPDIR)/basic_tests_cxx.Po
+       -rm -f ./$(DEPDIR)/chardata.Po
+       -rm -f ./$(DEPDIR)/chardata_cxx.Po
+       -rm -f ./$(DEPDIR)/common.Po
+       -rm -f ./$(DEPDIR)/common_cxx.Po
+       -rm -f ./$(DEPDIR)/dummy.Po
+       -rm -f ./$(DEPDIR)/dummy_cxx.Po
+       -rm -f ./$(DEPDIR)/handlers.Po
+       -rm -f ./$(DEPDIR)/handlers_cxx.Po
        -rm -f ./$(DEPDIR)/memcheck.Po
+       -rm -f ./$(DEPDIR)/memcheck_cxx.Po
        -rm -f ./$(DEPDIR)/minicheck.Po
+       -rm -f ./$(DEPDIR)/minicheck_cxx.Po
+       -rm -f ./$(DEPDIR)/misc_tests.Po
+       -rm -f ./$(DEPDIR)/misc_tests_cxx.Po
+       -rm -f ./$(DEPDIR)/ns_tests.Po
+       -rm -f ./$(DEPDIR)/ns_tests_cxx.Po
+       -rm -f ./$(DEPDIR)/nsalloc_tests.Po
+       -rm -f ./$(DEPDIR)/nsalloc_tests_cxx.Po
        -rm -f ./$(DEPDIR)/runtests.Po
-       -rm -f ./$(DEPDIR)/runtestspp.Po
+       -rm -f ./$(DEPDIR)/runtests_cxx.Po
        -rm -f ./$(DEPDIR)/structdata.Po
+       -rm -f ./$(DEPDIR)/structdata_cxx.Po
        -rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic
 
@@ -1256,19 +1356,18 @@ uninstall-am:
 
 .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
        am--depfiles check check-TESTS check-am clean \
-       clean-checkPROGRAMS clean-generic clean-libtool \
-       clean-noinstLIBRARIES cscopelist-am ctags ctags-am distclean \
-       distclean-compile distclean-generic distclean-libtool \
-       distclean-tags distdir dvi dvi-am html html-am info info-am \
-       install install-am install-data install-data-am install-dvi \
-       install-dvi-am install-exec install-exec-am install-html \
-       install-html-am install-info install-info-am install-man \
-       install-pdf install-pdf-am install-ps install-ps-am \
-       install-strip installcheck installcheck-am installdirs \
-       installdirs-am maintainer-clean maintainer-clean-generic \
-       mostlyclean mostlyclean-compile mostlyclean-generic \
-       mostlyclean-libtool pdf pdf-am ps ps-am recheck tags tags-am \
-       uninstall uninstall-am
+       clean-checkPROGRAMS clean-generic clean-libtool cscopelist-am \
+       ctags ctags-am distclean distclean-compile distclean-generic \
+       distclean-libtool distclean-tags distdir dvi dvi-am html \
+       html-am info info-am install install-am install-data \
+       install-data-am install-dvi install-dvi-am install-exec \
+       install-exec-am install-html install-html-am install-info \
+       install-info-am install-man install-pdf install-pdf-am \
+       install-ps install-ps-am install-strip installcheck \
+       installcheck-am installdirs installdirs-am maintainer-clean \
+       maintainer-clean-generic mostlyclean mostlyclean-compile \
+       mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+       recheck tags tags-am uninstall uninstall-am
 
 .PRECIOUS: Makefile
 
diff --git a/tests/acc_tests.c b/tests/acc_tests.c
new file mode 100644 (file)
index 0000000..f193aa5
--- /dev/null
@@ -0,0 +1,455 @@
+/* Tests in the "accounting" test case for the Expat test suite
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
+   Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Joe Orton <jorton@redhat.com>
+   Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2020      Tim Gates <tim.gates@iress.com>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
+   Copyright (c) 2023      Sony Corporation / Snild Dolkow <snild@sony.com>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <math.h> /* NAN, INFINITY */
+#include <stdio.h>
+#include <string.h>
+
+#include "expat_config.h"
+
+#include "expat.h"
+#include "internal.h"
+#include "common.h"
+#include "minicheck.h"
+#include "chardata.h"
+#include "handlers.h"
+#include "acc_tests.h"
+
+#if XML_GE == 1
+START_TEST(test_accounting_precision) {
+  struct AccountingTestCase cases[] = {
+      {"<e/>", NULL, NULL, 0},
+      {"<e></e>", NULL, NULL, 0},
+
+      /* Attributes */
+      {"<e k1=\"v2\" k2=\"v2\"/>", NULL, NULL, 0},
+      {"<e k1=\"v2\" k2=\"v2\"></e>", NULL, NULL, 0},
+      {"<p:e xmlns:p=\"https://domain.invalid/\" />", NULL, NULL, 0},
+      {"<e k=\"&amp;&apos;&gt;&lt;&quot;\" />", NULL, NULL,
+       sizeof(XML_Char) * 5 /* number of predefined entities */},
+      {"<e1 xmlns='https://example.org/'>\n"
+       "  <e2 xmlns=''/>\n"
+       "</e1>",
+       NULL, NULL, 0},
+
+      /* Text */
+      {"<e>text</e>", NULL, NULL, 0},
+      {"<e1><e2>text1<e3/>text2</e2></e1>", NULL, NULL, 0},
+      {"<e>&amp;&apos;&gt;&lt;&quot;</e>", NULL, NULL,
+       sizeof(XML_Char) * 5 /* number of predefined entities */},
+      {"<e>&#65;&#41;</e>", NULL, NULL, 0},
+
+      /* Prolog */
+      {"<?xml version=\"1.0\"?><root/>", NULL, NULL, 0},
+
+      /* Whitespace */
+      {"  <e1>  <e2>  </e2>  </e1>  ", NULL, NULL, 0},
+      {"<e1  ><e2  /></e1  >", NULL, NULL, 0},
+      {"<e1><e2 k = \"v\"/><e3 k = 'v'/></e1>", NULL, NULL, 0},
+
+      /* Comments */
+      {"<!-- Comment --><e><!-- Comment --></e>", NULL, NULL, 0},
+
+      /* Processing instructions */
+      {"<?xml-stylesheet type=\"text/xsl\" href=\"https://domain.invalid/\" media=\"all\"?><e/>",
+       NULL, NULL, 0},
+      {"<?pi0?><?pi1 ?><?pi2  ?><r/><?pi4?>", NULL, NULL, 0},
+#  ifdef XML_DTD
+      {"<?pi0?><?pi1 ?><?pi2  ?><!DOCTYPE r SYSTEM 'first.ent'><r/>",
+       "<?pi3?><!ENTITY % e1 SYSTEM 'second.ent'><?pi4?>%e1;<?pi5?>", "<?pi6?>",
+       0},
+#  endif /* XML_DTD */
+
+      /* CDATA */
+      {"<e><![CDATA[one two three]]></e>", NULL, NULL, 0},
+      /* The following is the essence of this OSS-Fuzz finding:
+         https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=34302
+         https://oss-fuzz.com/testcase-detail/4860575394955264
+      */
+      {"<!DOCTYPE r [\n"
+       "<!ENTITY e \"111<![CDATA[2 <= 2]]>333\">\n"
+       "]>\n"
+       "<r>&e;</r>\n",
+       NULL, NULL, sizeof(XML_Char) * strlen("111<![CDATA[2 <= 2]]>333")},
+
+#  ifdef XML_DTD
+      /* Conditional sections */
+      {"<!DOCTYPE r [\n"
+       "<!ENTITY % draft 'INCLUDE'>\n"
+       "<!ENTITY % final 'IGNORE'>\n"
+       "<!ENTITY % import SYSTEM \"first.ent\">\n"
+       "%import;\n"
+       "]>\n"
+       "<r/>\n",
+       "<![%draft;[<!--1-->]]>\n"
+       "<![%final;[<!--22-->]]>",
+       NULL, sizeof(XML_Char) * (strlen("INCLUDE") + strlen("IGNORE"))},
+#  endif /* XML_DTD */
+
+      /* General entities */
+      {"<!DOCTYPE root [\n"
+       "<!ENTITY nine \"123456789\">\n"
+       "]>\n"
+       "<root>&nine;</root>",
+       NULL, NULL, sizeof(XML_Char) * strlen("123456789")},
+      {"<!DOCTYPE root [\n"
+       "<!ENTITY nine \"123456789\">\n"
+       "]>\n"
+       "<root k1=\"&nine;\"/>",
+       NULL, NULL, sizeof(XML_Char) * strlen("123456789")},
+      {"<!DOCTYPE root [\n"
+       "<!ENTITY nine \"123456789\">\n"
+       "<!ENTITY nine2 \"&nine;&nine;\">\n"
+       "]>\n"
+       "<root>&nine2;&nine2;&nine2;</root>",
+       NULL, NULL,
+       sizeof(XML_Char) * 3 /* calls to &nine2; */ * 2 /* calls to &nine; */
+           * (strlen("&nine;") + strlen("123456789"))},
+      {"<!DOCTYPE r [\n"
+       "  <!ENTITY five SYSTEM 'first.ent'>\n"
+       "]>\n"
+       "<r>&five;</r>",
+       "12345", NULL, 0},
+      {"<!DOCTYPE r [\n"
+       "  <!ENTITY five SYSTEM 'first.ent'>\n"
+       "]>\n"
+       "<r>&five;</r>",
+       "\xEF\xBB\xBF" /* UTF-8 BOM */, NULL, 0},
+
+#  ifdef XML_DTD
+      /* Parameter entities */
+      {"<!DOCTYPE r [\n"
+       "<!ENTITY % comment \"<!---->\">\n"
+       "%comment;\n"
+       "]>\n"
+       "<r/>",
+       NULL, NULL, sizeof(XML_Char) * strlen("<!---->")},
+      {"<!DOCTYPE r [\n"
+       "<!ENTITY % ninedef \"&#60;!ENTITY nine &#34;123456789&#34;&#62;\">\n"
+       "%ninedef;\n"
+       "]>\n"
+       "<r>&nine;</r>",
+       NULL, NULL,
+       sizeof(XML_Char)
+           * (strlen("<!ENTITY nine \"123456789\">") + strlen("123456789"))},
+      {"<!DOCTYPE r [\n"
+       "<!ENTITY % comment \"<!--1-->\">\n"
+       "<!ENTITY % comment2 \"&#37;comment;<!--22-->&#37;comment;\">\n"
+       "%comment2;\n"
+       "]>\n"
+       "<r/>\n",
+       NULL, NULL,
+       sizeof(XML_Char)
+           * (strlen("%comment;<!--22-->%comment;") + 2 * strlen("<!--1-->"))},
+      {"<!DOCTYPE r [\n"
+       "  <!ENTITY % five \"12345\">\n"
+       "  <!ENTITY % five2def \"&#60;!ENTITY five2 &#34;[&#37;five;][&#37;five;]]]]&#34;&#62;\">\n"
+       "  %five2def;\n"
+       "]>\n"
+       "<r>&five2;</r>",
+       NULL, NULL, /* from "%five2def;": */
+       sizeof(XML_Char)
+           * (strlen("<!ENTITY five2 \"[%five;][%five;]]]]\">")
+              + 2 /* calls to "%five;" */ * strlen("12345")
+              + /* from "&five2;": */ strlen("[12345][12345]]]]"))},
+      {"<!DOCTYPE r SYSTEM \"first.ent\">\n"
+       "<r/>",
+       "<!ENTITY % comment '<!--1-->'>\n"
+       "<!ENTITY % comment2 '<!--22-->%comment;<!--22-->%comment;<!--22-->'>\n"
+       "%comment2;",
+       NULL,
+       sizeof(XML_Char)
+           * (strlen("<!--22-->%comment;<!--22-->%comment;<!--22-->")
+              + 2 /* calls to "%comment;" */ * strlen("<!---->"))},
+      {"<!DOCTYPE r SYSTEM 'first.ent'>\n"
+       "<r/>",
+       "<!ENTITY % e1 PUBLIC 'foo' 'second.ent'>\n"
+       "<!ENTITY % e2 '<!--22-->%e1;<!--22-->'>\n"
+       "%e2;\n",
+       "<!--1-->", sizeof(XML_Char) * strlen("<!--22--><!--1--><!--22-->")},
+      {
+          "<!DOCTYPE r SYSTEM 'first.ent'>\n"
+          "<r/>",
+          "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
+          "<!ENTITY % e2 '%e1;'>",
+          "<?xml version='1.0' encoding='utf-8'?>\n"
+          "hello\n"
+          "xml" /* without trailing newline! */,
+          0,
+      },
+      {
+          "<!DOCTYPE r SYSTEM 'first.ent'>\n"
+          "<r/>",
+          "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
+          "<!ENTITY % e2 '%e1;'>",
+          "<?xml version='1.0' encoding='utf-8'?>\n"
+          "hello\n"
+          "xml\n" /* with trailing newline! */,
+          0,
+      },
+      {"<!DOCTYPE doc SYSTEM 'first.ent'>\n"
+       "<doc></doc>\n",
+       "<!ELEMENT doc EMPTY>\n"
+       "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
+       "<!ENTITY % e2 '%e1;'>\n"
+       "%e1;\n",
+       "\xEF\xBB\xBF<!ATTLIST doc a1 CDATA 'value'>" /* UTF-8 BOM */,
+       strlen("\xEF\xBB\xBF<!ATTLIST doc a1 CDATA 'value'>")},
+#  endif /* XML_DTD */
+  };
+
+  const size_t countCases = sizeof(cases) / sizeof(cases[0]);
+  size_t u = 0;
+  for (; u < countCases; u++) {
+    const unsigned long long expectedCountBytesDirect
+        = strlen(cases[u].primaryText);
+    const unsigned long long expectedCountBytesIndirect
+        = (cases[u].firstExternalText ? strlen(cases[u].firstExternalText) : 0)
+          + (cases[u].secondExternalText ? strlen(cases[u].secondExternalText)
+                                         : 0)
+          + cases[u].expectedCountBytesIndirectExtra;
+
+    XML_Parser parser = XML_ParserCreate(NULL);
+    XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    if (cases[u].firstExternalText) {
+      XML_SetExternalEntityRefHandler(parser,
+                                      accounting_external_entity_ref_handler);
+      XML_SetUserData(parser, (void *)&cases[u]);
+    }
+
+    enum XML_Status status
+        = _XML_Parse_SINGLE_BYTES(parser, cases[u].primaryText,
+                                  (int)strlen(cases[u].primaryText), XML_TRUE);
+    if (status != XML_STATUS_OK) {
+      _xml_failure(parser, __FILE__, __LINE__);
+    }
+
+    const unsigned long long actualCountBytesDirect
+        = testingAccountingGetCountBytesDirect(parser);
+    const unsigned long long actualCountBytesIndirect
+        = testingAccountingGetCountBytesIndirect(parser);
+
+    XML_ParserFree(parser);
+
+    if (actualCountBytesDirect != expectedCountBytesDirect) {
+      fprintf(
+          stderr,
+          "Document " EXPAT_FMT_SIZE_T("") " of " EXPAT_FMT_SIZE_T("") ": Expected " EXPAT_FMT_ULL(
+              "") " count direct bytes, got " EXPAT_FMT_ULL("") " instead.\n",
+          u + 1, countCases, expectedCountBytesDirect, actualCountBytesDirect);
+      fail("Count of direct bytes is off");
+    }
+
+    if (actualCountBytesIndirect != expectedCountBytesIndirect) {
+      fprintf(
+          stderr,
+          "Document " EXPAT_FMT_SIZE_T("") " of " EXPAT_FMT_SIZE_T("") ": Expected " EXPAT_FMT_ULL(
+              "") " count indirect bytes, got " EXPAT_FMT_ULL("") " instead.\n",
+          u + 1, countCases, expectedCountBytesIndirect,
+          actualCountBytesIndirect);
+      fail("Count of indirect bytes is off");
+    }
+  }
+}
+END_TEST
+
+START_TEST(test_billion_laughs_attack_protection_api) {
+  XML_Parser parserWithoutParent = XML_ParserCreate(NULL);
+  XML_Parser parserWithParent = XML_ExternalEntityParserCreate(
+      parserWithoutParent, XCS("entity123"), NULL);
+  if (parserWithoutParent == NULL)
+    fail("parserWithoutParent is NULL");
+  if (parserWithParent == NULL)
+    fail("parserWithParent is NULL");
+
+  // XML_SetBillionLaughsAttackProtectionMaximumAmplification, error cases
+  if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(NULL, 123.0f)
+      == XML_TRUE)
+    fail("Call with NULL parser is NOT supposed to succeed");
+  if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(parserWithParent,
+                                                               123.0f)
+      == XML_TRUE)
+    fail("Call with non-root parser is NOT supposed to succeed");
+  if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+          parserWithoutParent, NAN)
+      == XML_TRUE)
+    fail("Call with NaN limit is NOT supposed to succeed");
+  if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+          parserWithoutParent, -1.0f)
+      == XML_TRUE)
+    fail("Call with negative limit is NOT supposed to succeed");
+  if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+          parserWithoutParent, 0.9f)
+      == XML_TRUE)
+    fail("Call with positive limit <1.0 is NOT supposed to succeed");
+
+  // XML_SetBillionLaughsAttackProtectionMaximumAmplification, success cases
+  if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+          parserWithoutParent, 1.0f)
+      == XML_FALSE)
+    fail("Call with positive limit >=1.0 is supposed to succeed");
+  if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+          parserWithoutParent, 123456.789f)
+      == XML_FALSE)
+    fail("Call with positive limit >=1.0 is supposed to succeed");
+  if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+          parserWithoutParent, INFINITY)
+      == XML_FALSE)
+    fail("Call with positive limit >=1.0 is supposed to succeed");
+
+  // XML_SetBillionLaughsAttackProtectionActivationThreshold, error cases
+  if (XML_SetBillionLaughsAttackProtectionActivationThreshold(NULL, 123)
+      == XML_TRUE)
+    fail("Call with NULL parser is NOT supposed to succeed");
+  if (XML_SetBillionLaughsAttackProtectionActivationThreshold(parserWithParent,
+                                                              123)
+      == XML_TRUE)
+    fail("Call with non-root parser is NOT supposed to succeed");
+
+  // XML_SetBillionLaughsAttackProtectionActivationThreshold, success cases
+  if (XML_SetBillionLaughsAttackProtectionActivationThreshold(
+          parserWithoutParent, 123)
+      == XML_FALSE)
+    fail("Call with non-NULL parentless parser is supposed to succeed");
+
+  XML_ParserFree(parserWithParent);
+  XML_ParserFree(parserWithoutParent);
+}
+END_TEST
+
+START_TEST(test_helper_unsigned_char_to_printable) {
+  // Smoke test
+  unsigned char uc = 0;
+  for (; uc < (unsigned char)-1; uc++) {
+    set_subtest("char %u", (unsigned)uc);
+    const char *const printable = unsignedCharToPrintable(uc);
+    if (printable == NULL)
+      fail("unsignedCharToPrintable returned NULL");
+    else if (strlen(printable) < (size_t)1)
+      fail("unsignedCharToPrintable returned empty string");
+  }
+
+  // Two concrete samples
+  set_subtest("char 'A'");
+  if (strcmp(unsignedCharToPrintable('A'), "A") != 0)
+    fail("unsignedCharToPrintable result mistaken");
+  set_subtest("char '\\'");
+  if (strcmp(unsignedCharToPrintable('\\'), "\\\\") != 0)
+    fail("unsignedCharToPrintable result mistaken");
+}
+END_TEST
+
+START_TEST(test_amplification_isolated_external_parser) {
+  // NOTE: Length 44 is precisely twice the length of "<!ENTITY a SYSTEM 'b'>"
+  // (22) that is used in function accountingGetCurrentAmplification in
+  // xmlparse.c.
+  //                  1.........1.........1.........1.........1..4 => 44
+  const char doc[] = "<!ENTITY % p1 '123456789_123456789_1234567'>";
+  const int docLen = (int)sizeof(doc) - 1;
+  const float maximumToleratedAmplification = 2.0f;
+
+  struct TestCase {
+    int offsetOfThreshold;
+    enum XML_Status expectedStatus;
+  };
+
+  struct TestCase cases[] = {
+      {-2, XML_STATUS_ERROR}, {-1, XML_STATUS_ERROR}, {0, XML_STATUS_ERROR},
+      {+1, XML_STATUS_OK},    {+2, XML_STATUS_OK},
+  };
+
+  for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
+    const int offsetOfThreshold = cases[i].offsetOfThreshold;
+    const enum XML_Status expectedStatus = cases[i].expectedStatus;
+    const unsigned long long activationThresholdBytes
+        = docLen + offsetOfThreshold;
+
+    set_subtest("offsetOfThreshold=%d, expectedStatus=%d", offsetOfThreshold,
+                expectedStatus);
+
+    XML_Parser parser = XML_ParserCreate(NULL);
+    assert_true(parser != NULL);
+
+    assert_true(XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+                    parser, maximumToleratedAmplification)
+                == XML_TRUE);
+    assert_true(XML_SetBillionLaughsAttackProtectionActivationThreshold(
+                    parser, activationThresholdBytes)
+                == XML_TRUE);
+
+    XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL);
+    assert_true(ext_parser != NULL);
+
+    const enum XML_Status actualStatus
+        = _XML_Parse_SINGLE_BYTES(ext_parser, doc, docLen, XML_TRUE);
+
+    assert_true(actualStatus == expectedStatus);
+    if (actualStatus != XML_STATUS_OK) {
+      assert_true(XML_GetErrorCode(ext_parser)
+                  == XML_ERROR_AMPLIFICATION_LIMIT_BREACH);
+    }
+
+    XML_ParserFree(ext_parser);
+    XML_ParserFree(parser);
+  }
+}
+END_TEST
+
+#endif // XML_GE == 1
+
+void
+make_accounting_test_case(Suite *s) {
+#if XML_GE == 1
+  TCase *tc_accounting = tcase_create("accounting tests");
+
+  suite_add_tcase(s, tc_accounting);
+
+  tcase_add_test(tc_accounting, test_accounting_precision);
+  tcase_add_test(tc_accounting, test_billion_laughs_attack_protection_api);
+  tcase_add_test(tc_accounting, test_helper_unsigned_char_to_printable);
+  tcase_add_test__ifdef_xml_dtd(tc_accounting,
+                                test_amplification_isolated_external_parser);
+#else
+  UNUSED_P(s);
+#endif /* XML_GE == 1 */
+}
diff --git a/tests/acc_tests.h b/tests/acc_tests.h
new file mode 100644 (file)
index 0000000..bbb93f3
--- /dev/null
@@ -0,0 +1,56 @@
+/* Tests in the "accounting" test case for the Expat test suite
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
+   Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Joe Orton <jorton@redhat.com>
+   Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2020      Tim Gates <tim.gates@iress.com>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef XML_ACC_TESTS_H
+#  define XML_ACC_TESTS_H
+
+extern void make_accounting_test_case(Suite *s);
+
+#endif /* XML_ACC_TESTS_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/acc_tests_cxx.cpp b/tests/acc_tests_cxx.cpp
new file mode 100644 (file)
index 0000000..0164499
--- /dev/null
@@ -0,0 +1,32 @@
+/* C++ compilation harness for the test suite.
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2023 Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "acc_tests.c"
diff --git a/tests/alloc_tests.c b/tests/alloc_tests.c
new file mode 100644 (file)
index 0000000..e5d46eb
--- /dev/null
@@ -0,0 +1,2127 @@
+/* Tests in the "allocation" test case for the Expat test suite
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
+   Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2023 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Joe Orton <jorton@redhat.com>
+   Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2020      Tim Gates <tim.gates@iress.com>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
+   Copyright (c) 2023      Sony Corporation / Snild Dolkow <snild@sony.com>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#if defined(NDEBUG)
+#  undef NDEBUG /* because test suite relies on assert(...) at the moment */
+#endif
+
+#include <string.h>
+#include <assert.h>
+
+#include "expat.h"
+#include "common.h"
+#include "minicheck.h"
+#include "dummy.h"
+#include "handlers.h"
+#include "alloc_tests.h"
+
+static void
+alloc_setup(void) {
+  XML_Memory_Handling_Suite memsuite = {duff_allocator, duff_reallocator, free};
+
+  /* Ensure the parser creation will go through */
+  g_allocation_count = ALLOC_ALWAYS_SUCCEED;
+  g_reallocation_count = REALLOC_ALWAYS_SUCCEED;
+  g_parser = XML_ParserCreate_MM(NULL, &memsuite, NULL);
+  if (g_parser == NULL)
+    fail("Parser not created");
+}
+
+static void
+alloc_teardown(void) {
+  basic_teardown();
+}
+
+/* Test the effects of allocation failures on xml declaration processing */
+START_TEST(test_alloc_parse_xdecl) {
+  const char *text = "<?xml version='1.0' encoding='utf-8'?>\n"
+                     "<doc>Hello, world</doc>";
+  int i;
+  const int max_alloc_count = 15;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetXmlDeclHandler(g_parser, dummy_xdecl_handler);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* Resetting the parser is insufficient, because some memory
+     * allocations are cached within the parser.  Instead we use
+     * the teardown and setup routines to ensure that we have the
+     * right sort of parser back in our hands.
+     */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing allocator");
+  if (i == max_alloc_count)
+    fail("Parse failed with max allocations");
+}
+END_TEST
+
+/* As above, but with an encoding big enough to cause storing the
+ * version information to expand the string pool being used.
+ */
+START_TEST(test_alloc_parse_xdecl_2) {
+  const char *text
+      = "<?xml version='1.0' encoding='"
+        /* Each line is 64 characters */
+        "ThisIsAStupidlyLongEncodingNameIntendedToTriggerPoolGrowth123456"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMN"
+        "'?>"
+        "<doc>Hello, world</doc>";
+  int i;
+  const int max_alloc_count = 20;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetXmlDeclHandler(g_parser, dummy_xdecl_handler);
+    XML_SetUnknownEncodingHandler(g_parser, long_encoding_handler, NULL);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing allocator");
+  if (i == max_alloc_count)
+    fail("Parse failed with max allocations");
+}
+END_TEST
+
+/* Test the effects of allocation failures on a straightforward parse */
+START_TEST(test_alloc_parse_pi) {
+  const char *text = "<?xml version='1.0' encoding='utf-8'?>\n"
+                     "<?pi unknown?>\n"
+                     "<doc>"
+                     "Hello, world"
+                     "</doc>";
+  int i;
+  const int max_alloc_count = 15;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetProcessingInstructionHandler(g_parser, dummy_pi_handler);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing allocator");
+  if (i == max_alloc_count)
+    fail("Parse failed with max allocations");
+}
+END_TEST
+
+START_TEST(test_alloc_parse_pi_2) {
+  const char *text = "<?xml version='1.0' encoding='utf-8'?>\n"
+                     "<doc>"
+                     "Hello, world"
+                     "<?pi unknown?>\n"
+                     "</doc>";
+  int i;
+  const int max_alloc_count = 15;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetProcessingInstructionHandler(g_parser, dummy_pi_handler);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing allocator");
+  if (i == max_alloc_count)
+    fail("Parse failed with max allocations");
+}
+END_TEST
+
+START_TEST(test_alloc_parse_pi_3) {
+  const char *text
+      = "<?"
+        /* 64 characters per line */
+        "This processing instruction should be long enough to ensure that"
+        "it triggers the growth of an internal string pool when the      "
+        "allocator fails at a cruicial moment FGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "Q?><doc/>";
+  int i;
+  const int max_alloc_count = 20;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetProcessingInstructionHandler(g_parser, dummy_pi_handler);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing allocator");
+  if (i == max_alloc_count)
+    fail("Parse failed with max allocations");
+}
+END_TEST
+
+START_TEST(test_alloc_parse_comment) {
+  const char *text = "<?xml version='1.0' encoding='utf-8'?>\n"
+                     "<!-- Test parsing this comment -->"
+                     "<doc>Hi</doc>";
+  int i;
+  const int max_alloc_count = 15;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetCommentHandler(g_parser, dummy_comment_handler);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing allocator");
+  if (i == max_alloc_count)
+    fail("Parse failed with max allocations");
+}
+END_TEST
+
+START_TEST(test_alloc_parse_comment_2) {
+  const char *text = "<?xml version='1.0' encoding='utf-8'?>\n"
+                     "<doc>"
+                     "Hello, world"
+                     "<!-- Parse this comment too -->"
+                     "</doc>";
+  int i;
+  const int max_alloc_count = 15;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetCommentHandler(g_parser, dummy_comment_handler);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing allocator");
+  if (i == max_alloc_count)
+    fail("Parse failed with max allocations");
+}
+END_TEST
+
+/* Test that external parser creation running out of memory is
+ * correctly reported.  Based on the external entity test cases.
+ */
+START_TEST(test_alloc_create_external_parser) {
+  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
+                     "<!DOCTYPE doc SYSTEM 'foo'>\n"
+                     "<doc>&entity;</doc>";
+  char foo_text[] = "<!ELEMENT doc (#PCDATA)*>";
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetUserData(g_parser, foo_text);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_duff_loader);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_ERROR) {
+    fail("External parser allocator returned success incorrectly");
+  }
+}
+END_TEST
+
+/* More external parser memory allocation testing */
+START_TEST(test_alloc_run_external_parser) {
+  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
+                     "<!DOCTYPE doc SYSTEM 'foo'>\n"
+                     "<doc>&entity;</doc>";
+  char foo_text[] = "<!ELEMENT doc (#PCDATA)*>";
+  unsigned int i;
+  const unsigned int max_alloc_count = 15;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetUserData(g_parser, foo_text);
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_null_loader);
+    g_allocation_count = i;
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing ignored failing allocator");
+  else if (i == max_alloc_count)
+    fail("Parsing failed with allocation count 10");
+}
+END_TEST
+
+/* Test that running out of memory in dtdCopy is correctly reported.
+ * Based on test_default_ns_from_ext_subset_and_ext_ge()
+ */
+START_TEST(test_alloc_dtd_copy_default_atts) {
+  const char *text = "<?xml version='1.0'?>\n"
+                     "<!DOCTYPE doc SYSTEM 'http://example.org/doc.dtd' [\n"
+                     "  <!ENTITY en SYSTEM 'http://example.org/entity.ent'>\n"
+                     "]>\n"
+                     "<doc xmlns='http://example.org/ns1'>\n"
+                     "&en;\n"
+                     "</doc>";
+  int callno = 0;
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_dbl_handler);
+  XML_SetUserData(g_parser, &callno);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test more external entity allocation failure paths */
+START_TEST(test_alloc_external_entity) {
+  const char *text = "<?xml version='1.0'?>\n"
+                     "<!DOCTYPE doc SYSTEM 'http://example.org/doc.dtd' [\n"
+                     "  <!ENTITY en SYSTEM 'http://example.org/entity.ent'>\n"
+                     "]>\n"
+                     "<doc xmlns='http://example.org/ns1'>\n"
+                     "&en;\n"
+                     "</doc>";
+  int i;
+  const int alloc_test_max_repeats = 50;
+  int callno = 0;
+
+  for (i = 0; i < alloc_test_max_repeats; i++) {
+    g_allocation_count = -1;
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_dbl_handler_2);
+    callno = 0;
+    XML_SetUserData(g_parser, &callno);
+    g_allocation_count = i;
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        == XML_STATUS_OK)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  g_allocation_count = -1;
+  if (i == 0)
+    fail("External entity parsed despite duff allocator");
+  if (i == alloc_test_max_repeats)
+    fail("External entity not parsed at max allocation count");
+}
+END_TEST
+
+/* Test more allocation failure paths */
+START_TEST(test_alloc_ext_entity_set_encoding) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
+                     "]>\n"
+                     "<doc>&en;</doc>";
+  int i;
+  const int max_allocation_count = 30;
+
+  for (i = 0; i < max_allocation_count; i++) {
+    XML_SetExternalEntityRefHandler(g_parser,
+                                    external_entity_alloc_set_encoding);
+    g_allocation_count = i;
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        == XML_STATUS_OK)
+      break;
+    g_allocation_count = -1;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Encoding check succeeded despite failing allocator");
+  if (i == max_allocation_count)
+    fail("Encoding failed at max allocation count");
+}
+END_TEST
+
+/* Test the effects of allocation failure in internal entities.
+ * Based on test_unknown_encoding_internal_entity
+ */
+START_TEST(test_alloc_internal_entity) {
+  const char *text = "<?xml version='1.0' encoding='unsupported-encoding'?>\n"
+                     "<!DOCTYPE test [<!ENTITY foo 'bar'>]>\n"
+                     "<test a='&foo;'/>";
+  unsigned int i;
+  const unsigned int max_alloc_count = 20;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetUnknownEncodingHandler(g_parser, unknown_released_encoding_handler,
+                                  NULL);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Internal entity worked despite failing allocations");
+  else if (i == max_alloc_count)
+    fail("Internal entity failed at max allocation count");
+}
+END_TEST
+
+/* Test the robustness against allocation failure of element handling
+ * Based on test_dtd_default_handling().
+ */
+START_TEST(test_alloc_dtd_default_handling) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "<!ENTITY e SYSTEM 'http://example.org/e'>\n"
+                     "<!NOTATION n SYSTEM 'http://example.org/n'>\n"
+                     "<!ENTITY e1 SYSTEM 'http://example.org/e' NDATA n>\n"
+                     "<!ELEMENT doc (#PCDATA)>\n"
+                     "<!ATTLIST doc a CDATA #IMPLIED>\n"
+                     "<?pi in dtd?>\n"
+                     "<!--comment in dtd-->\n"
+                     "]>\n"
+                     "<doc><![CDATA[text in doc]]></doc>";
+  const XML_Char *expected = XCS("\n\n\n\n\n\n\n\n\n<doc>text in doc</doc>");
+  CharData storage;
+  int i;
+  const int max_alloc_count = 25;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    init_dummy_handlers();
+    XML_SetDefaultHandler(g_parser, accumulate_characters);
+    XML_SetDoctypeDeclHandler(g_parser, dummy_start_doctype_handler,
+                              dummy_end_doctype_handler);
+    XML_SetEntityDeclHandler(g_parser, dummy_entity_decl_handler);
+    XML_SetNotationDeclHandler(g_parser, dummy_notation_decl_handler);
+    XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
+    XML_SetAttlistDeclHandler(g_parser, dummy_attlist_decl_handler);
+    XML_SetProcessingInstructionHandler(g_parser, dummy_pi_handler);
+    XML_SetCommentHandler(g_parser, dummy_comment_handler);
+    XML_SetCdataSectionHandler(g_parser, dummy_start_cdata_handler,
+                               dummy_end_cdata_handler);
+    XML_SetUnparsedEntityDeclHandler(g_parser,
+                                     dummy_unparsed_entity_decl_handler);
+    CharData_Init(&storage);
+    XML_SetUserData(g_parser, &storage);
+    XML_SetCharacterDataHandler(g_parser, accumulate_characters);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Default DTD parsed despite allocation failures");
+  if (i == max_alloc_count)
+    fail("Default DTD not parsed with maximum alloc count");
+  CharData_CheckXMLChars(&storage, expected);
+  if (get_dummy_handler_flags()
+      != (DUMMY_START_DOCTYPE_HANDLER_FLAG | DUMMY_END_DOCTYPE_HANDLER_FLAG
+          | DUMMY_ENTITY_DECL_HANDLER_FLAG | DUMMY_NOTATION_DECL_HANDLER_FLAG
+          | DUMMY_ELEMENT_DECL_HANDLER_FLAG | DUMMY_ATTLIST_DECL_HANDLER_FLAG
+          | DUMMY_COMMENT_HANDLER_FLAG | DUMMY_PI_HANDLER_FLAG
+          | DUMMY_START_CDATA_HANDLER_FLAG | DUMMY_END_CDATA_HANDLER_FLAG
+          | DUMMY_UNPARSED_ENTITY_DECL_HANDLER_FLAG))
+    fail("Not all handlers were called");
+}
+END_TEST
+
+/* Test robustness of XML_SetEncoding() with a failing allocator */
+START_TEST(test_alloc_explicit_encoding) {
+  int i;
+  const int max_alloc_count = 5;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    if (XML_SetEncoding(g_parser, XCS("us-ascii")) == XML_STATUS_OK)
+      break;
+  }
+  if (i == 0)
+    fail("Encoding set despite failing allocator");
+  else if (i == max_alloc_count)
+    fail("Encoding not set at max allocation count");
+}
+END_TEST
+
+/* Test robustness of XML_SetBase against a failing allocator */
+START_TEST(test_alloc_set_base) {
+  const XML_Char *new_base = XCS("/local/file/name.xml");
+  int i;
+  const int max_alloc_count = 5;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    if (XML_SetBase(g_parser, new_base) == XML_STATUS_OK)
+      break;
+  }
+  if (i == 0)
+    fail("Base set despite failing allocator");
+  else if (i == max_alloc_count)
+    fail("Base not set with max allocation count");
+}
+END_TEST
+
+/* Test buffer extension in the face of a duff reallocator */
+START_TEST(test_alloc_realloc_buffer) {
+  const char *text = get_buffer_test_text;
+  void *buffer;
+  int i;
+  const int max_realloc_count = 10;
+
+  /* Get a smallish buffer */
+  for (i = 0; i < max_realloc_count; i++) {
+    g_reallocation_count = i;
+    buffer = XML_GetBuffer(g_parser, 1536);
+    if (buffer == NULL)
+      fail("1.5K buffer reallocation failed");
+    assert(buffer != NULL);
+    memcpy(buffer, text, strlen(text));
+    if (XML_ParseBuffer(g_parser, (int)strlen(text), XML_FALSE)
+        == XML_STATUS_OK)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  g_reallocation_count = -1;
+  if (i == 0)
+    fail("Parse succeeded with no reallocation");
+  else if (i == max_realloc_count)
+    fail("Parse failed with max reallocation count");
+}
+END_TEST
+
+/* Same test for external entity parsers */
+START_TEST(test_alloc_ext_entity_realloc_buffer) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
+                     "]>\n"
+                     "<doc>&en;</doc>";
+  int i;
+  const int max_realloc_count = 10;
+
+  for (i = 0; i < max_realloc_count; i++) {
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_reallocator);
+    XML_SetUserData(g_parser, &i);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        == XML_STATUS_OK)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Succeeded with no reallocations");
+  if (i == max_realloc_count)
+    fail("Failed with max reallocations");
+}
+END_TEST
+
+/* Test elements with many attributes are handled correctly */
+START_TEST(test_alloc_realloc_many_attributes) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "<!ATTLIST doc za CDATA 'default'>\n"
+                     "<!ATTLIST doc zb CDATA 'def2'>\n"
+                     "<!ATTLIST doc zc CDATA 'def3'>\n"
+                     "]>\n"
+                     "<doc a='1'"
+                     "     b='2'"
+                     "     c='3'"
+                     "     d='4'"
+                     "     e='5'"
+                     "     f='6'"
+                     "     g='7'"
+                     "     h='8'"
+                     "     i='9'"
+                     "     j='10'"
+                     "     k='11'"
+                     "     l='12'"
+                     "     m='13'"
+                     "     n='14'"
+                     "     p='15'"
+                     "     q='16'"
+                     "     r='17'"
+                     "     s='18'>"
+                     "</doc>";
+  int i;
+  const int max_realloc_count = 10;
+
+  for (i = 0; i < max_realloc_count; i++) {
+    g_reallocation_count = i;
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite no reallocations");
+  if (i == max_realloc_count)
+    fail("Parse failed at max reallocations");
+}
+END_TEST
+
+/* Test handling of a public entity with failing allocator */
+START_TEST(test_alloc_public_entity_value) {
+  const char *text = "<!DOCTYPE doc SYSTEM 'http://example.org/'>\n"
+                     "<doc></doc>\n";
+  char dtd_text[]
+      = "<!ELEMENT doc EMPTY>\n"
+        "<!ENTITY % e1 PUBLIC 'foo' 'bar.ent'>\n"
+        "<!ENTITY % "
+        /* Each line is 64 characters */
+        "ThisIsAStupidlyLongParameterNameIntendedToTriggerPoolGrowth12345"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        " '%e1;'>\n"
+        "%e1;\n";
+  int i;
+  const int max_alloc_count = 50;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    init_dummy_handlers();
+    XML_SetUserData(g_parser, dtd_text);
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_public);
+    /* Provoke a particular code path */
+    XML_SetEntityDeclHandler(g_parser, dummy_entity_decl_handler);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing allocation");
+  if (i == max_alloc_count)
+    fail("Parsing failed at max allocation count");
+  if (get_dummy_handler_flags() != DUMMY_ENTITY_DECL_HANDLER_FLAG)
+    fail("Entity declaration handler not called");
+}
+END_TEST
+
+START_TEST(test_alloc_realloc_subst_public_entity_value) {
+  const char *text = "<!DOCTYPE doc SYSTEM 'http://example.org/'>\n"
+                     "<doc></doc>\n";
+  char dtd_text[]
+      = "<!ELEMENT doc EMPTY>\n"
+        "<!ENTITY % "
+        /* Each line is 64 characters */
+        "ThisIsAStupidlyLongParameterNameIntendedToTriggerPoolGrowth12345"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        " PUBLIC 'foo' 'bar.ent'>\n"
+        "%ThisIsAStupidlyLongParameterNameIntendedToTriggerPoolGrowth12345"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP;";
+  int i;
+  const int max_realloc_count = 10;
+
+  for (i = 0; i < max_realloc_count; i++) {
+    g_reallocation_count = i;
+    XML_SetUserData(g_parser, dtd_text);
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_public);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing reallocation");
+  if (i == max_realloc_count)
+    fail("Parsing failed at max reallocation count");
+}
+END_TEST
+
+START_TEST(test_alloc_parse_public_doctype) {
+  const char *text
+      = "<?xml version='1.0' encoding='utf-8'?>\n"
+        "<!DOCTYPE doc PUBLIC '"
+        /* 64 characters per line */
+        "http://example.com/a/long/enough/name/to/trigger/pool/growth/zz/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "' 'test'>\n"
+        "<doc></doc>";
+  int i;
+  const int max_alloc_count = 25;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    init_dummy_handlers();
+    XML_SetDoctypeDeclHandler(g_parser, dummy_start_doctype_decl_handler,
+                              dummy_end_doctype_decl_handler);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing allocator");
+  if (i == max_alloc_count)
+    fail("Parse failed at maximum allocation count");
+  if (get_dummy_handler_flags()
+      != (DUMMY_START_DOCTYPE_DECL_HANDLER_FLAG
+          | DUMMY_END_DOCTYPE_DECL_HANDLER_FLAG))
+    fail("Doctype handler functions not called");
+}
+END_TEST
+
+START_TEST(test_alloc_parse_public_doctype_long_name) {
+  const char *text
+      = "<?xml version='1.0' encoding='utf-8'?>\n"
+        "<!DOCTYPE doc PUBLIC 'http://example.com/foo' '"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
+        "'>\n"
+        "<doc></doc>";
+  int i;
+  const int max_alloc_count = 25;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetDoctypeDeclHandler(g_parser, dummy_start_doctype_decl_handler,
+                              dummy_end_doctype_decl_handler);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing allocator");
+  if (i == max_alloc_count)
+    fail("Parse failed at maximum allocation count");
+}
+END_TEST
+
+/* Test foreign DTD handling */
+START_TEST(test_alloc_set_foreign_dtd) {
+  const char *text1 = "<?xml version='1.0' encoding='us-ascii'?>\n"
+                      "<doc>&entity;</doc>";
+  char text2[] = "<!ELEMENT doc (#PCDATA)*>";
+  int i;
+  const int max_alloc_count = 25;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetUserData(g_parser, &text2);
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_alloc);
+    if (XML_UseForeignDTD(g_parser, XML_TRUE) != XML_ERROR_NONE)
+      fail("Could not set foreign DTD");
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text1, (int)strlen(text1), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing allocator");
+  if (i == max_alloc_count)
+    fail("Parse failed at maximum allocation count");
+}
+END_TEST
+
+/* Test based on ibm/valid/P32/ibm32v04.xml */
+START_TEST(test_alloc_attribute_enum_value) {
+  const char *text = "<?xml version='1.0' standalone='no'?>\n"
+                     "<!DOCTYPE animal SYSTEM 'test.dtd'>\n"
+                     "<animal>This is a \n    <a/>  \n\nyellow tiger</animal>";
+  char dtd_text[] = "<!ELEMENT animal (#PCDATA|a)*>\n"
+                    "<!ELEMENT a EMPTY>\n"
+                    "<!ATTLIST animal xml:space (default|preserve) 'preserve'>";
+  int i;
+  const int max_alloc_count = 30;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_alloc);
+    XML_SetUserData(g_parser, dtd_text);
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    /* An attribute list handler provokes a different code path */
+    XML_SetAttlistDeclHandler(g_parser, dummy_attlist_decl_handler);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing allocator");
+  if (i == max_alloc_count)
+    fail("Parse failed at maximum allocation count");
+}
+END_TEST
+
+/* Test attribute enums sufficient to overflow the string pool */
+START_TEST(test_alloc_realloc_attribute_enum_value) {
+  const char *text = "<?xml version='1.0' standalone='no'?>\n"
+                     "<!DOCTYPE animal SYSTEM 'test.dtd'>\n"
+                     "<animal>This is a yellow tiger</animal>";
+  /* We wish to define a collection of attribute enums that will
+   * cause the string pool storing them to have to expand.  This
+   * means more than 1024 bytes, including the parentheses and
+   * separator bars.
+   */
+  char dtd_text[]
+      = "<!ELEMENT animal (#PCDATA)*>\n"
+        "<!ATTLIST animal thing "
+        "(default"
+        /* Each line is 64 characters */
+        "|ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|BBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|CBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|DBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|EBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|FBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|GBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|HBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|IBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|JBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|KBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|LBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|MBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|NBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|OBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|PBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO)"
+        " 'default'>";
+  int i;
+  const int max_realloc_count = 10;
+
+  for (i = 0; i < max_realloc_count; i++) {
+    g_reallocation_count = i;
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_alloc);
+    XML_SetUserData(g_parser, dtd_text);
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    /* An attribute list handler provokes a different code path */
+    XML_SetAttlistDeclHandler(g_parser, dummy_attlist_decl_handler);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing reallocator");
+  if (i == max_realloc_count)
+    fail("Parse failed at maximum reallocation count");
+}
+END_TEST
+
+/* Test attribute enums in a #IMPLIED attribute forcing pool growth */
+START_TEST(test_alloc_realloc_implied_attribute) {
+  /* Forcing this particular code path is a balancing act.  The
+   * addition of the closing parenthesis and terminal NUL must be
+   * what pushes the string of enums over the 1024-byte limit,
+   * otherwise a different code path will pick up the realloc.
+   */
+  const char *text
+      = "<!DOCTYPE doc [\n"
+        "<!ELEMENT doc EMPTY>\n"
+        "<!ATTLIST doc a "
+        /* Each line is 64 characters */
+        "(ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|BBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|CBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|DBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|EBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|FBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|GBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|HBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|IBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|JBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|KBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|LBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|MBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|NBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|OBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|PBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMN)"
+        " #IMPLIED>\n"
+        "]><doc/>";
+  int i;
+  const int max_realloc_count = 10;
+
+  for (i = 0; i < max_realloc_count; i++) {
+    g_reallocation_count = i;
+    XML_SetAttlistDeclHandler(g_parser, dummy_attlist_decl_handler);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing reallocator");
+  if (i == max_realloc_count)
+    fail("Parse failed at maximum reallocation count");
+}
+END_TEST
+
+/* Test attribute enums in a defaulted attribute forcing pool growth */
+START_TEST(test_alloc_realloc_default_attribute) {
+  /* Forcing this particular code path is a balancing act.  The
+   * addition of the closing parenthesis and terminal NUL must be
+   * what pushes the string of enums over the 1024-byte limit,
+   * otherwise a different code path will pick up the realloc.
+   */
+  const char *text
+      = "<!DOCTYPE doc [\n"
+        "<!ELEMENT doc EMPTY>\n"
+        "<!ATTLIST doc a "
+        /* Each line is 64 characters */
+        "(ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|BBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|CBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|DBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|EBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|FBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|GBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|HBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|IBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|JBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|KBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|LBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|MBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|NBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|OBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        "|PBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMN)"
+        " 'ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO'"
+        ">\n]><doc/>";
+  int i;
+  const int max_realloc_count = 10;
+
+  for (i = 0; i < max_realloc_count; i++) {
+    g_reallocation_count = i;
+    XML_SetAttlistDeclHandler(g_parser, dummy_attlist_decl_handler);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing reallocator");
+  if (i == max_realloc_count)
+    fail("Parse failed at maximum reallocation count");
+}
+END_TEST
+
+/* Test long notation name with dodgy allocator */
+START_TEST(test_alloc_notation) {
+  const char *text
+      = "<!DOCTYPE doc [\n"
+        "<!NOTATION "
+        /* Each line is 64 characters */
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        " SYSTEM 'http://example.org/n'>\n"
+        "<!ENTITY e SYSTEM 'http://example.org/e' NDATA "
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        ">\n"
+        "<!ELEMENT doc EMPTY>\n"
+        "]>\n<doc/>";
+  int i;
+  const int max_alloc_count = 20;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    init_dummy_handlers();
+    XML_SetNotationDeclHandler(g_parser, dummy_notation_decl_handler);
+    XML_SetEntityDeclHandler(g_parser, dummy_entity_decl_handler);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite allocation failures");
+  if (i == max_alloc_count)
+    fail("Parse failed at maximum allocation count");
+  if (get_dummy_handler_flags()
+      != (DUMMY_ENTITY_DECL_HANDLER_FLAG | DUMMY_NOTATION_DECL_HANDLER_FLAG))
+    fail("Entity declaration handler not called");
+}
+END_TEST
+
+/* Test public notation with dodgy allocator */
+START_TEST(test_alloc_public_notation) {
+  const char *text
+      = "<!DOCTYPE doc [\n"
+        "<!NOTATION note PUBLIC '"
+        /* 64 characters per line */
+        "http://example.com/a/long/enough/name/to/trigger/pool/growth/zz/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "' 'foo'>\n"
+        "<!ENTITY e SYSTEM 'http://example.com/e' NDATA note>\n"
+        "<!ELEMENT doc EMPTY>\n"
+        "]>\n<doc/>";
+  int i;
+  const int max_alloc_count = 20;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    init_dummy_handlers();
+    XML_SetNotationDeclHandler(g_parser, dummy_notation_decl_handler);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite allocation failures");
+  if (i == max_alloc_count)
+    fail("Parse failed at maximum allocation count");
+  if (get_dummy_handler_flags() != DUMMY_NOTATION_DECL_HANDLER_FLAG)
+    fail("Notation handler not called");
+}
+END_TEST
+
+/* Test public notation with dodgy allocator */
+START_TEST(test_alloc_system_notation) {
+  const char *text
+      = "<!DOCTYPE doc [\n"
+        "<!NOTATION note SYSTEM '"
+        /* 64 characters per line */
+        "http://example.com/a/long/enough/name/to/trigger/pool/growth/zz/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "'>\n"
+        "<!ENTITY e SYSTEM 'http://example.com/e' NDATA note>\n"
+        "<!ELEMENT doc EMPTY>\n"
+        "]>\n<doc/>";
+  int i;
+  const int max_alloc_count = 20;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    init_dummy_handlers();
+    XML_SetNotationDeclHandler(g_parser, dummy_notation_decl_handler);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite allocation failures");
+  if (i == max_alloc_count)
+    fail("Parse failed at maximum allocation count");
+  if (get_dummy_handler_flags() != DUMMY_NOTATION_DECL_HANDLER_FLAG)
+    fail("Notation handler not called");
+}
+END_TEST
+
+START_TEST(test_alloc_nested_groups) {
+  const char *text
+      = "<!DOCTYPE doc [\n"
+        "<!ELEMENT doc "
+        /* Sixteen elements per line */
+        "(e,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,"
+        "(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?"
+        "))))))))))))))))))))))))))))))))>\n"
+        "<!ELEMENT e EMPTY>"
+        "]>\n"
+        "<doc><e/></doc>";
+  CharData storage;
+  int i;
+  const int max_alloc_count = 20;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    CharData_Init(&storage);
+    XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
+    XML_SetStartElementHandler(g_parser, record_element_start_handler);
+    XML_SetUserData(g_parser, &storage);
+    init_dummy_handlers();
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+
+  if (i == 0)
+    fail("Parse succeeded despite failing reallocator");
+  if (i == max_alloc_count)
+    fail("Parse failed at maximum reallocation count");
+  CharData_CheckXMLChars(&storage, XCS("doce"));
+  if (get_dummy_handler_flags() != DUMMY_ELEMENT_DECL_HANDLER_FLAG)
+    fail("Element handler not fired");
+}
+END_TEST
+
+START_TEST(test_alloc_realloc_nested_groups) {
+  const char *text
+      = "<!DOCTYPE doc [\n"
+        "<!ELEMENT doc "
+        /* Sixteen elements per line */
+        "(e,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,"
+        "(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?"
+        "))))))))))))))))))))))))))))))))>\n"
+        "<!ELEMENT e EMPTY>"
+        "]>\n"
+        "<doc><e/></doc>";
+  CharData storage;
+  int i;
+  const int max_realloc_count = 10;
+
+  for (i = 0; i < max_realloc_count; i++) {
+    g_reallocation_count = i;
+    CharData_Init(&storage);
+    XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
+    XML_SetStartElementHandler(g_parser, record_element_start_handler);
+    XML_SetUserData(g_parser, &storage);
+    init_dummy_handlers();
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+
+  if (i == 0)
+    fail("Parse succeeded despite failing reallocator");
+  if (i == max_realloc_count)
+    fail("Parse failed at maximum reallocation count");
+  CharData_CheckXMLChars(&storage, XCS("doce"));
+  if (get_dummy_handler_flags() != DUMMY_ELEMENT_DECL_HANDLER_FLAG)
+    fail("Element handler not fired");
+}
+END_TEST
+
+START_TEST(test_alloc_large_group) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "<!ELEMENT doc ("
+                     "a1|a2|a3|a4|a5|a6|a7|a8|"
+                     "b1|b2|b3|b4|b5|b6|b7|b8|"
+                     "c1|c2|c3|c4|c5|c6|c7|c8|"
+                     "d1|d2|d3|d4|d5|d6|d7|d8|"
+                     "e1"
+                     ")+>\n"
+                     "]>\n"
+                     "<doc>\n"
+                     "<a1/>\n"
+                     "</doc>\n";
+  int i;
+  const int max_alloc_count = 50;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
+    init_dummy_handlers();
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing allocator");
+  if (i == max_alloc_count)
+    fail("Parse failed at maximum allocation count");
+  if (get_dummy_handler_flags() != DUMMY_ELEMENT_DECL_HANDLER_FLAG)
+    fail("Element handler flag not raised");
+}
+END_TEST
+
+START_TEST(test_alloc_realloc_group_choice) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "<!ELEMENT doc ("
+                     "a1|a2|a3|a4|a5|a6|a7|a8|"
+                     "b1|b2|b3|b4|b5|b6|b7|b8|"
+                     "c1|c2|c3|c4|c5|c6|c7|c8|"
+                     "d1|d2|d3|d4|d5|d6|d7|d8|"
+                     "e1"
+                     ")+>\n"
+                     "]>\n"
+                     "<doc>\n"
+                     "<a1/>\n"
+                     "<b2 attr='foo'>This is a foo</b2>\n"
+                     "<c3></c3>\n"
+                     "</doc>\n";
+  int i;
+  const int max_realloc_count = 10;
+
+  for (i = 0; i < max_realloc_count; i++) {
+    g_reallocation_count = i;
+    XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
+    init_dummy_handlers();
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing reallocator");
+  if (i == max_realloc_count)
+    fail("Parse failed at maximum reallocation count");
+  if (get_dummy_handler_flags() != DUMMY_ELEMENT_DECL_HANDLER_FLAG)
+    fail("Element handler flag not raised");
+}
+END_TEST
+
+START_TEST(test_alloc_pi_in_epilog) {
+  const char *text = "<doc></doc>\n"
+                     "<?pi in epilog?>";
+  int i;
+  const int max_alloc_count = 15;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetProcessingInstructionHandler(g_parser, dummy_pi_handler);
+    init_dummy_handlers();
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse completed despite failing allocator");
+  if (i == max_alloc_count)
+    fail("Parse failed at maximum allocation count");
+  if (get_dummy_handler_flags() != DUMMY_PI_HANDLER_FLAG)
+    fail("Processing instruction handler not invoked");
+}
+END_TEST
+
+START_TEST(test_alloc_comment_in_epilog) {
+  const char *text = "<doc></doc>\n"
+                     "<!-- comment in epilog -->";
+  int i;
+  const int max_alloc_count = 15;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetCommentHandler(g_parser, dummy_comment_handler);
+    init_dummy_handlers();
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse completed despite failing allocator");
+  if (i == max_alloc_count)
+    fail("Parse failed at maximum allocation count");
+  if (get_dummy_handler_flags() != DUMMY_COMMENT_HANDLER_FLAG)
+    fail("Processing instruction handler not invoked");
+}
+END_TEST
+
+START_TEST(test_alloc_realloc_long_attribute_value) {
+  const char *text
+      = "<!DOCTYPE doc [<!ENTITY foo '"
+        /* Each line is 64 characters */
+        "This entity will be substituted as an attribute value, and is   "
+        "calculated to be exactly long enough that the terminating NUL   "
+        "that the library adds internally will trigger the string pool to"
+        "grow. GHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "'>]>\n"
+        "<doc a='&foo;'></doc>";
+  int i;
+  const int max_realloc_count = 10;
+
+  for (i = 0; i < max_realloc_count; i++) {
+    g_reallocation_count = i;
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing reallocator");
+  if (i == max_realloc_count)
+    fail("Parse failed at maximum reallocation count");
+}
+END_TEST
+
+START_TEST(test_alloc_attribute_whitespace) {
+  const char *text = "<doc a=' '></doc>";
+  int i;
+  const int max_alloc_count = 15;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing allocator");
+  if (i == max_alloc_count)
+    fail("Parse failed at maximum allocation count");
+}
+END_TEST
+
+START_TEST(test_alloc_attribute_predefined_entity) {
+  const char *text = "<doc a='&amp;'></doc>";
+  int i;
+  const int max_alloc_count = 15;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing allocator");
+  if (i == max_alloc_count)
+    fail("Parse failed at maximum allocation count");
+}
+END_TEST
+
+/* Test that a character reference at the end of a suitably long
+ * default value for an attribute can trigger pool growth, and recovers
+ * if the allocator fails on it.
+ */
+START_TEST(test_alloc_long_attr_default_with_char_ref) {
+  const char *text
+      = "<!DOCTYPE doc [<!ATTLIST doc a CDATA '"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHI"
+        "&#x31;'>]>\n"
+        "<doc/>";
+  int i;
+  const int max_alloc_count = 20;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing allocator");
+  if (i == max_alloc_count)
+    fail("Parse failed at maximum allocation count");
+}
+END_TEST
+
+/* Test that a long character reference substitution triggers a pool
+ * expansion correctly for an attribute value.
+ */
+START_TEST(test_alloc_long_attr_value) {
+  const char *text
+      = "<!DOCTYPE test [<!ENTITY foo '\n"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "'>]>\n"
+        "<test a='&foo;'/>";
+  int i;
+  const int max_alloc_count = 25;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing allocator");
+  if (i == max_alloc_count)
+    fail("Parse failed at maximum allocation count");
+}
+END_TEST
+
+/* Test that an error in a nested parameter entity substitution is
+ * handled correctly.  It seems unlikely that the code path being
+ * exercised can be reached purely by carefully crafted XML, but an
+ * allocation error in the right place will definitely do it.
+ */
+START_TEST(test_alloc_nested_entities) {
+  const char *text = "<!DOCTYPE doc SYSTEM 'http://example.org/one.ent'>\n"
+                     "<doc />";
+  ExtFaults test_data
+      = {"<!ENTITY % pe1 '"
+         /* 64 characters per line */
+         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+         "'>\n"
+         "<!ENTITY % pe2 '%pe1;'>\n"
+         "<!ENTITY % pe3 '%pe2;'>",
+         "Memory Fail not faulted", NULL, XML_ERROR_NO_MEMORY};
+
+  /* Causes an allocation error in a nested storeEntityValue() */
+  g_allocation_count = 12;
+  XML_SetUserData(g_parser, &test_data);
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_faulter);
+  expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
+                 "Entity allocation failure not noted");
+}
+END_TEST
+
+START_TEST(test_alloc_realloc_param_entity_newline) {
+  const char *text = "<!DOCTYPE doc SYSTEM 'http://example.org/'>\n"
+                     "<doc/>";
+  char dtd_text[]
+      = "<!ENTITY % pe '<!ATTLIST doc att CDATA \""
+        /* 64 characters per line */
+        "This default value is carefully crafted so that the carriage    "
+        "return right at the end of the entity string causes an internal "
+        "string pool to have to grow.  This allows us to test the alloc  "
+        "failure path from that point. OPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDE"
+        "\">\n'>"
+        "%pe;\n";
+  int i;
+  const int max_realloc_count = 5;
+
+  for (i = 0; i < max_realloc_count; i++) {
+    g_reallocation_count = i;
+    XML_SetUserData(g_parser, dtd_text);
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_alloc);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing reallocator");
+  if (i == max_realloc_count)
+    fail("Parse failed at maximum reallocation count");
+}
+END_TEST
+
+START_TEST(test_alloc_realloc_ce_extends_pe) {
+  const char *text = "<!DOCTYPE doc SYSTEM 'http://example.org/'>\n"
+                     "<doc/>";
+  char dtd_text[]
+      = "<!ENTITY % pe '<!ATTLIST doc att CDATA \""
+        /* 64 characters per line */
+        "This default value is carefully crafted so that the character   "
+        "entity at the end causes an internal string pool to have to     "
+        "grow.  This allows us to test the allocation failure path from  "
+        "that point onwards. EFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFG&#x51;"
+        "\">\n'>"
+        "%pe;\n";
+  int i;
+  const int max_realloc_count = 5;
+
+  for (i = 0; i < max_realloc_count; i++) {
+    g_reallocation_count = i;
+    XML_SetUserData(g_parser, dtd_text);
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_alloc);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parse succeeded despite failing reallocator");
+  if (i == max_realloc_count)
+    fail("Parse failed at maximum reallocation count");
+}
+END_TEST
+
+START_TEST(test_alloc_realloc_attributes) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ATTLIST doc\n"
+                     "    a1  (a|b|c)   'a'\n"
+                     "    a2  (foo|bar) #IMPLIED\n"
+                     "    a3  NMTOKEN   #IMPLIED\n"
+                     "    a4  NMTOKENS  #IMPLIED\n"
+                     "    a5  ID        #IMPLIED\n"
+                     "    a6  IDREF     #IMPLIED\n"
+                     "    a7  IDREFS    #IMPLIED\n"
+                     "    a8  ENTITY    #IMPLIED\n"
+                     "    a9  ENTITIES  #IMPLIED\n"
+                     "    a10 CDATA     #IMPLIED\n"
+                     "  >]>\n"
+                     "<doc>wombat</doc>\n";
+  int i;
+  const int max_realloc_count = 5;
+
+  for (i = 0; i < max_realloc_count; i++) {
+    g_reallocation_count = i;
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+
+  if (i == 0)
+    fail("Parse succeeded despite failing reallocator");
+  if (i == max_realloc_count)
+    fail("Parse failed at maximum reallocation count");
+}
+END_TEST
+
+START_TEST(test_alloc_long_doc_name) {
+  const char *text =
+      /* 64 characters per line */
+      "<LongRootElementNameThatWillCauseTheNextAllocationToExpandTheStr"
+      "ingPoolForTheDTDQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+      " a='1'/>";
+  int i;
+  const int max_alloc_count = 20;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing reallocations");
+  else if (i == max_alloc_count)
+    fail("Parsing failed even at max reallocation count");
+}
+END_TEST
+
+START_TEST(test_alloc_long_base) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY e SYSTEM 'foo'>\n"
+                     "]>\n"
+                     "<doc>&e;</doc>";
+  char entity_text[] = "Hello world";
+  const XML_Char *base =
+      /* 64 characters per line */
+      /* clang-format off */
+        XCS("LongBaseURI/that/will/overflow/an/internal/buffer/and/cause/it/t")
+        XCS("o/have/to/grow/PQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/");
+  /* clang-format on */
+  int i;
+  const int max_alloc_count = 25;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetUserData(g_parser, entity_text);
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_alloc);
+    if (XML_SetBase(g_parser, base) == XML_STATUS_ERROR) {
+      XML_ParserReset(g_parser, NULL);
+      continue;
+    }
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing allocations");
+  else if (i == max_alloc_count)
+    fail("Parsing failed even at max allocation count");
+}
+END_TEST
+
+START_TEST(test_alloc_long_public_id) {
+  const char *text
+      = "<!DOCTYPE doc [\n"
+        "  <!ENTITY e PUBLIC '"
+        /* 64 characters per line */
+        "LongPublicIDThatShouldResultInAnInternalStringPoolGrowingAtASpec"
+        "ificMomentKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "' 'bar'>\n"
+        "]>\n"
+        "<doc>&e;</doc>";
+  char entity_text[] = "Hello world";
+  int i;
+  const int max_alloc_count = 40;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetUserData(g_parser, entity_text);
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_alloc);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing allocations");
+  else if (i == max_alloc_count)
+    fail("Parsing failed even at max allocation count");
+}
+END_TEST
+
+START_TEST(test_alloc_long_entity_value) {
+  const char *text
+      = "<!DOCTYPE doc [\n"
+        "  <!ENTITY e1 '"
+        /* 64 characters per line */
+        "Long entity value that should provoke a string pool to grow whil"
+        "e setting up to parse the external entity below. xyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "'>\n"
+        "  <!ENTITY e2 SYSTEM 'bar'>\n"
+        "]>\n"
+        "<doc>&e2;</doc>";
+  char entity_text[] = "Hello world";
+  int i;
+  const int max_alloc_count = 40;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetUserData(g_parser, entity_text);
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_alloc);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing allocations");
+  else if (i == max_alloc_count)
+    fail("Parsing failed even at max allocation count");
+}
+END_TEST
+
+START_TEST(test_alloc_long_notation) {
+  const char *text
+      = "<!DOCTYPE doc [\n"
+        "  <!NOTATION note SYSTEM '"
+        /* 64 characters per line */
+        "ALongNotationNameThatShouldProvokeStringPoolGrowthWhileCallingAn"
+        "ExternalEntityParserUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "'>\n"
+        "  <!ENTITY e1 SYSTEM 'foo' NDATA "
+        /* 64 characters per line */
+        "ALongNotationNameThatShouldProvokeStringPoolGrowthWhileCallingAn"
+        "ExternalEntityParserUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
+        ">\n"
+        "  <!ENTITY e2 SYSTEM 'bar'>\n"
+        "]>\n"
+        "<doc>&e2;</doc>";
+  ExtOption options[]
+      = {{XCS("foo"), "Entity Foo"}, {XCS("bar"), "Entity Bar"}, {NULL, NULL}};
+  int i;
+  const int max_alloc_count = 40;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetUserData(g_parser, options);
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_optioner);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+
+    /* See comment in test_alloc_parse_xdecl() */
+    alloc_teardown();
+    alloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing allocations");
+  else if (i == max_alloc_count)
+    fail("Parsing failed even at max allocation count");
+}
+END_TEST
+
+START_TEST(test_alloc_reset_after_external_entity_parser_create_fail) {
+  const char *const text = "<!DOCTYPE doc SYSTEM 'foo'><doc/>";
+
+  XML_SetExternalEntityRefHandler(
+      g_parser, external_entity_parser_create_alloc_fail_handler);
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail("Call to parse was expected to fail");
+
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_EXTERNAL_ENTITY_HANDLING)
+    fail("Call to parse was expected to fail from the external entity handler");
+
+  XML_ParserReset(g_parser, NULL);
+}
+END_TEST
+
+void
+make_alloc_test_case(Suite *s) {
+  TCase *tc_alloc = tcase_create("allocation tests");
+
+  suite_add_tcase(s, tc_alloc);
+  tcase_add_checked_fixture(tc_alloc, alloc_setup, alloc_teardown);
+
+  tcase_add_test(tc_alloc, test_alloc_parse_xdecl);
+  tcase_add_test(tc_alloc, test_alloc_parse_xdecl_2);
+  tcase_add_test(tc_alloc, test_alloc_parse_pi);
+  tcase_add_test(tc_alloc, test_alloc_parse_pi_2);
+  tcase_add_test(tc_alloc, test_alloc_parse_pi_3);
+  tcase_add_test(tc_alloc, test_alloc_parse_comment);
+  tcase_add_test(tc_alloc, test_alloc_parse_comment_2);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_create_external_parser);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_run_external_parser);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_dtd_copy_default_atts);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_external_entity);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_ext_entity_set_encoding);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_internal_entity);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_dtd_default_handling);
+  tcase_add_test(tc_alloc, test_alloc_explicit_encoding);
+  tcase_add_test(tc_alloc, test_alloc_set_base);
+  tcase_add_test(tc_alloc, test_alloc_realloc_buffer);
+  tcase_add_test__if_xml_ge(tc_alloc, test_alloc_ext_entity_realloc_buffer);
+  tcase_add_test(tc_alloc, test_alloc_realloc_many_attributes);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_public_entity_value);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc,
+                                test_alloc_realloc_subst_public_entity_value);
+  tcase_add_test(tc_alloc, test_alloc_parse_public_doctype);
+  tcase_add_test(tc_alloc, test_alloc_parse_public_doctype_long_name);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_set_foreign_dtd);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_attribute_enum_value);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc,
+                                test_alloc_realloc_attribute_enum_value);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_realloc_implied_attribute);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_realloc_default_attribute);
+  tcase_add_test__if_xml_ge(tc_alloc, test_alloc_notation);
+  tcase_add_test(tc_alloc, test_alloc_public_notation);
+  tcase_add_test(tc_alloc, test_alloc_system_notation);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_nested_groups);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_realloc_nested_groups);
+  tcase_add_test(tc_alloc, test_alloc_large_group);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_realloc_group_choice);
+  tcase_add_test(tc_alloc, test_alloc_pi_in_epilog);
+  tcase_add_test(tc_alloc, test_alloc_comment_in_epilog);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc,
+                                test_alloc_realloc_long_attribute_value);
+  tcase_add_test(tc_alloc, test_alloc_attribute_whitespace);
+  tcase_add_test(tc_alloc, test_alloc_attribute_predefined_entity);
+  tcase_add_test(tc_alloc, test_alloc_long_attr_default_with_char_ref);
+  tcase_add_test__if_xml_ge(tc_alloc, test_alloc_long_attr_value);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_nested_entities);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc,
+                                test_alloc_realloc_param_entity_newline);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_realloc_ce_extends_pe);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_realloc_attributes);
+  tcase_add_test(tc_alloc, test_alloc_long_doc_name);
+  tcase_add_test__if_xml_ge(tc_alloc, test_alloc_long_base);
+  tcase_add_test__if_xml_ge(tc_alloc, test_alloc_long_public_id);
+  tcase_add_test__if_xml_ge(tc_alloc, test_alloc_long_entity_value);
+  tcase_add_test__if_xml_ge(tc_alloc, test_alloc_long_notation);
+
+  tcase_add_test__ifdef_xml_dtd(
+      tc_alloc, test_alloc_reset_after_external_entity_parser_create_fail);
+}
diff --git a/tests/alloc_tests.h b/tests/alloc_tests.h
new file mode 100644 (file)
index 0000000..1eae130
--- /dev/null
@@ -0,0 +1,56 @@
+/* Tests in the "allocation" test case for the Expat test suite
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
+   Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Joe Orton <jorton@redhat.com>
+   Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2020      Tim Gates <tim.gates@iress.com>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef XML_ALLOC_TESTS_H
+#  define XML_ALLOC_TESTS_H
+
+extern void make_alloc_test_case(Suite *s);
+
+#endif /* XML_ALLOC_TESTS_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/alloc_tests_cxx.cpp b/tests/alloc_tests_cxx.cpp
new file mode 100644 (file)
index 0000000..3270b1a
--- /dev/null
@@ -0,0 +1,32 @@
+/* C++ compilation harness for the test suite.
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2023 Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "alloc_tests.c"
diff --git a/tests/basic_tests.c b/tests/basic_tests.c
new file mode 100644 (file)
index 0000000..91c8dd7
--- /dev/null
@@ -0,0 +1,6093 @@
+/* Tests in the "basic" test case for the Expat test suite
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
+   Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Joe Orton <jorton@redhat.com>
+   Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2020      Tim Gates <tim.gates@iress.com>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
+   Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <snild@sony.com>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#if defined(NDEBUG)
+#  undef NDEBUG /* because test suite relies on assert(...) at the moment */
+#endif
+
+#include <assert.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#if ! defined(__cplusplus)
+#  include <stdbool.h>
+#endif
+
+#include "expat_config.h"
+
+#include "expat.h"
+#include "internal.h"
+#include "minicheck.h"
+#include "structdata.h"
+#include "common.h"
+#include "dummy.h"
+#include "handlers.h"
+#include "siphash.h"
+#include "basic_tests.h"
+
+static void
+basic_setup(void) {
+  g_parser = XML_ParserCreate(NULL);
+  if (g_parser == NULL)
+    fail("Parser not created.");
+}
+
+/*
+ * Character & encoding tests.
+ */
+
+START_TEST(test_nul_byte) {
+  char text[] = "<doc>\0</doc>";
+
+  /* test that a NUL byte (in US-ASCII data) is an error */
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_OK)
+    fail("Parser did not report error on NUL-byte.");
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_INVALID_TOKEN)
+    xml_failure(g_parser);
+}
+END_TEST
+
+START_TEST(test_u0000_char) {
+  /* test that a NUL byte (in US-ASCII data) is an error */
+  expect_failure("<doc>&#0;</doc>", XML_ERROR_BAD_CHAR_REF,
+                 "Parser did not report error on NUL-byte.");
+}
+END_TEST
+
+START_TEST(test_siphash_self) {
+  if (! sip24_valid())
+    fail("SipHash self-test failed");
+}
+END_TEST
+
+START_TEST(test_siphash_spec) {
+  /* https://131002.net/siphash/siphash.pdf (page 19, "Test values") */
+  const char message[] = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09"
+                         "\x0a\x0b\x0c\x0d\x0e";
+  const size_t len = sizeof(message) - 1;
+  const uint64_t expected = SIP_ULL(0xa129ca61U, 0x49be45e5U);
+  struct siphash state;
+  struct sipkey key;
+
+  sip_tokey(&key, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09"
+                  "\x0a\x0b\x0c\x0d\x0e\x0f");
+  sip24_init(&state, &key);
+
+  /* Cover spread across calls */
+  sip24_update(&state, message, 4);
+  sip24_update(&state, message + 4, len - 4);
+
+  /* Cover null length */
+  sip24_update(&state, message, 0);
+
+  if (sip24_final(&state) != expected)
+    fail("sip24_final failed spec test\n");
+
+  /* Cover wrapper */
+  if (siphash24(message, len, &key) != expected)
+    fail("siphash24 failed spec test\n");
+}
+END_TEST
+
+START_TEST(test_bom_utf8) {
+  /* This test is really just making sure we don't core on a UTF-8 BOM. */
+  const char *text = "\357\273\277<e/>";
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+START_TEST(test_bom_utf16_be) {
+  char text[] = "\376\377\0<\0e\0/\0>";
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+START_TEST(test_bom_utf16_le) {
+  char text[] = "\377\376<\0e\0/\0>\0";
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+START_TEST(test_nobom_utf16_le) {
+  char text[] = " \0<\0e\0/\0>\0";
+
+  if (g_chunkSize == 1) {
+    // TODO: with just the first byte, we can't tell the difference between
+    // UTF-16-LE and UTF-8. Avoid the failure for now.
+    return;
+  }
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+START_TEST(test_hash_collision) {
+  /* For full coverage of the lookup routine, we need to ensure a
+   * hash collision even though we can only tell that we have one
+   * through breakpoint debugging or coverage statistics.  The
+   * following will cause a hash collision on machines with a 64-bit
+   * long type; others will have to experiment.  The full coverage
+   * tests invoked from qa.sh usually provide a hash collision, but
+   * not always.  This is an attempt to provide insurance.
+   */
+#define COLLIDING_HASH_SALT (unsigned long)SIP_ULL(0xffffffffU, 0xff99fc90U)
+  const char *text
+      = "<doc>\n"
+        "<a1/><a2/><a3/><a4/><a5/><a6/><a7/><a8/>\n"
+        "<b1></b1><b2 attr='foo'>This is a foo</b2><b3></b3><b4></b4>\n"
+        "<b5></b5><b6></b6><b7></b7><b8></b8>\n"
+        "<c1/><c2/><c3/><c4/><c5/><c6/><c7/><c8/>\n"
+        "<d1/><d2/><d3/><d4/><d5/><d6/><d7/>\n"
+        "<d8>This triggers the table growth and collides with b2</d8>\n"
+        "</doc>\n";
+
+  XML_SetHashSalt(g_parser, COLLIDING_HASH_SALT);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+#undef COLLIDING_HASH_SALT
+
+/* Regression test for SF bug #491986. */
+START_TEST(test_danish_latin1) {
+  const char *text = "<?xml version='1.0' encoding='iso-8859-1'?>\n"
+                     "<e>J\xF8rgen \xE6\xF8\xE5\xC6\xD8\xC5</e>";
+#ifdef XML_UNICODE
+  const XML_Char *expected
+      = XCS("J\x00f8rgen \x00e6\x00f8\x00e5\x00c6\x00d8\x00c5");
+#else
+  const XML_Char *expected
+      = XCS("J\xC3\xB8rgen \xC3\xA6\xC3\xB8\xC3\xA5\xC3\x86\xC3\x98\xC3\x85");
+#endif
+  run_character_check(text, expected);
+}
+END_TEST
+
+/* Regression test for SF bug #514281. */
+START_TEST(test_french_charref_hexidecimal) {
+  const char *text = "<?xml version='1.0' encoding='iso-8859-1'?>\n"
+                     "<doc>&#xE9;&#xE8;&#xE0;&#xE7;&#xEA;&#xC8;</doc>";
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("\x00e9\x00e8\x00e0\x00e7\x00ea\x00c8");
+#else
+  const XML_Char *expected
+      = XCS("\xC3\xA9\xC3\xA8\xC3\xA0\xC3\xA7\xC3\xAA\xC3\x88");
+#endif
+  run_character_check(text, expected);
+}
+END_TEST
+
+START_TEST(test_french_charref_decimal) {
+  const char *text = "<?xml version='1.0' encoding='iso-8859-1'?>\n"
+                     "<doc>&#233;&#232;&#224;&#231;&#234;&#200;</doc>";
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("\x00e9\x00e8\x00e0\x00e7\x00ea\x00c8");
+#else
+  const XML_Char *expected
+      = XCS("\xC3\xA9\xC3\xA8\xC3\xA0\xC3\xA7\xC3\xAA\xC3\x88");
+#endif
+  run_character_check(text, expected);
+}
+END_TEST
+
+START_TEST(test_french_latin1) {
+  const char *text = "<?xml version='1.0' encoding='iso-8859-1'?>\n"
+                     "<doc>\xE9\xE8\xE0\xE7\xEa\xC8</doc>";
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("\x00e9\x00e8\x00e0\x00e7\x00ea\x00c8");
+#else
+  const XML_Char *expected
+      = XCS("\xC3\xA9\xC3\xA8\xC3\xA0\xC3\xA7\xC3\xAA\xC3\x88");
+#endif
+  run_character_check(text, expected);
+}
+END_TEST
+
+START_TEST(test_french_utf8) {
+  const char *text = "<?xml version='1.0' encoding='utf-8'?>\n"
+                     "<doc>\xC3\xA9</doc>";
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("\x00e9");
+#else
+  const XML_Char *expected = XCS("\xC3\xA9");
+#endif
+  run_character_check(text, expected);
+}
+END_TEST
+
+/* Regression test for SF bug #600479.
+   XXX There should be a test that exercises all legal XML Unicode
+   characters as PCDATA and attribute value content, and XML Name
+   characters as part of element and attribute names.
+*/
+START_TEST(test_utf8_false_rejection) {
+  const char *text = "<doc>\xEF\xBA\xBF</doc>";
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("\xfebf");
+#else
+  const XML_Char *expected = XCS("\xEF\xBA\xBF");
+#endif
+  run_character_check(text, expected);
+}
+END_TEST
+
+/* Regression test for SF bug #477667.
+   This test assures that any 8-bit character followed by a 7-bit
+   character will not be mistakenly interpreted as a valid UTF-8
+   sequence.
+*/
+START_TEST(test_illegal_utf8) {
+  char text[100];
+  int i;
+
+  for (i = 128; i <= 255; ++i) {
+    snprintf(text, sizeof(text), "<e>%ccd</e>", i);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        == XML_STATUS_OK) {
+      snprintf(text, sizeof(text),
+               "expected token error for '%c' (ordinal %d) in UTF-8 text", i,
+               i);
+      fail(text);
+    } else if (XML_GetErrorCode(g_parser) != XML_ERROR_INVALID_TOKEN)
+      xml_failure(g_parser);
+    /* Reset the parser since we use the same parser repeatedly. */
+    XML_ParserReset(g_parser, NULL);
+  }
+}
+END_TEST
+
+/* Examples, not masks: */
+#define UTF8_LEAD_1 "\x7f" /* 0b01111111 */
+#define UTF8_LEAD_2 "\xdf" /* 0b11011111 */
+#define UTF8_LEAD_3 "\xef" /* 0b11101111 */
+#define UTF8_LEAD_4 "\xf7" /* 0b11110111 */
+#define UTF8_FOLLOW "\xbf" /* 0b10111111 */
+
+START_TEST(test_utf8_auto_align) {
+  struct TestCase {
+    ptrdiff_t expectedMovementInChars;
+    const char *input;
+  };
+
+  struct TestCase cases[] = {
+      {00, ""},
+
+      {00, UTF8_LEAD_1},
+
+      {-1, UTF8_LEAD_2},
+      {00, UTF8_LEAD_2 UTF8_FOLLOW},
+
+      {-1, UTF8_LEAD_3},
+      {-2, UTF8_LEAD_3 UTF8_FOLLOW},
+      {00, UTF8_LEAD_3 UTF8_FOLLOW UTF8_FOLLOW},
+
+      {-1, UTF8_LEAD_4},
+      {-2, UTF8_LEAD_4 UTF8_FOLLOW},
+      {-3, UTF8_LEAD_4 UTF8_FOLLOW UTF8_FOLLOW},
+      {00, UTF8_LEAD_4 UTF8_FOLLOW UTF8_FOLLOW UTF8_FOLLOW},
+  };
+
+  size_t i = 0;
+  bool success = true;
+  for (; i < sizeof(cases) / sizeof(*cases); i++) {
+    const char *fromLim = cases[i].input + strlen(cases[i].input);
+    const char *const fromLimInitially = fromLim;
+    ptrdiff_t actualMovementInChars;
+
+    _INTERNAL_trim_to_complete_utf8_characters(cases[i].input, &fromLim);
+
+    actualMovementInChars = (fromLim - fromLimInitially);
+    if (actualMovementInChars != cases[i].expectedMovementInChars) {
+      size_t j = 0;
+      success = false;
+      printf("[-] UTF-8 case %2u: Expected movement by %2d chars"
+             ", actually moved by %2d chars: \"",
+             (unsigned)(i + 1), (int)cases[i].expectedMovementInChars,
+             (int)actualMovementInChars);
+      for (; j < strlen(cases[i].input); j++) {
+        printf("\\x%02x", (unsigned char)cases[i].input[j]);
+      }
+      printf("\"\n");
+    }
+  }
+
+  if (! success) {
+    fail("UTF-8 auto-alignment is not bullet-proof\n");
+  }
+}
+END_TEST
+
+START_TEST(test_utf16) {
+  /* <?xml version="1.0" encoding="UTF-16"?>
+   *  <doc a='123'>some {A} text</doc>
+   *
+   * where {A} is U+FF21, FULLWIDTH LATIN CAPITAL LETTER A
+   */
+  char text[]
+      = "\000<\000?\000x\000m\000\154\000 \000v\000e\000r\000s\000i\000o"
+        "\000n\000=\000'\0001\000.\000\060\000'\000 \000e\000n\000c\000o"
+        "\000d\000i\000n\000g\000=\000'\000U\000T\000F\000-\0001\000\066"
+        "\000'\000?\000>\000\n"
+        "\000<\000d\000o\000c\000 \000a\000=\000'\0001\0002\0003\000'\000>"
+        "\000s\000o\000m\000e\000 \xff\x21\000 \000t\000e\000x\000t\000"
+        "<\000/\000d\000o\000c\000>";
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("some \xff21 text");
+#else
+  const XML_Char *expected = XCS("some \357\274\241 text");
+#endif
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetUserData(g_parser, &storage);
+  XML_SetCharacterDataHandler(g_parser, accumulate_characters);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_utf16_le_epilog_newline) {
+  unsigned int first_chunk_bytes = 17;
+  char text[] = "\xFF\xFE"                  /* BOM */
+                "<\000e\000/\000>\000"      /* document element */
+                "\r\000\n\000\r\000\n\000"; /* epilog */
+
+  if (first_chunk_bytes >= sizeof(text) - 1)
+    fail("bad value of first_chunk_bytes");
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, first_chunk_bytes, XML_FALSE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  else {
+    enum XML_Status rc;
+    rc = _XML_Parse_SINGLE_BYTES(g_parser, text + first_chunk_bytes,
+                                 sizeof(text) - first_chunk_bytes - 1,
+                                 XML_TRUE);
+    if (rc == XML_STATUS_ERROR)
+      xml_failure(g_parser);
+  }
+}
+END_TEST
+
+/* Test that an outright lie in the encoding is faulted */
+START_TEST(test_not_utf16) {
+  const char *text = "<?xml version='1.0' encoding='utf-16'?>"
+                     "<doc>Hi</doc>";
+
+  /* Use a handler to provoke the appropriate code paths */
+  XML_SetXmlDeclHandler(g_parser, dummy_xdecl_handler);
+  expect_failure(text, XML_ERROR_INCORRECT_ENCODING,
+                 "UTF-16 declared in UTF-8 not faulted");
+}
+END_TEST
+
+/* Test that an unknown encoding is rejected */
+START_TEST(test_bad_encoding) {
+  const char *text = "<doc>Hi</doc>";
+
+  if (! XML_SetEncoding(g_parser, XCS("unknown-encoding")))
+    fail("XML_SetEncoding failed");
+  expect_failure(text, XML_ERROR_UNKNOWN_ENCODING,
+                 "Unknown encoding not faulted");
+}
+END_TEST
+
+/* Regression test for SF bug #481609, #774028. */
+START_TEST(test_latin1_umlauts) {
+  const char *text
+      = "<?xml version='1.0' encoding='iso-8859-1'?>\n"
+        "<e a='\xE4 \xF6 \xFC &#228; &#246; &#252; &#x00E4; &#x0F6; &#xFC; >'\n"
+        "  >\xE4 \xF6 \xFC &#228; &#246; &#252; &#x00E4; &#x0F6; &#xFC; ></e>";
+#ifdef XML_UNICODE
+  /* Expected results in UTF-16 */
+  const XML_Char *expected = XCS("\x00e4 \x00f6 \x00fc ")
+      XCS("\x00e4 \x00f6 \x00fc ") XCS("\x00e4 \x00f6 \x00fc >");
+#else
+  /* Expected results in UTF-8 */
+  const XML_Char *expected = XCS("\xC3\xA4 \xC3\xB6 \xC3\xBC ")
+      XCS("\xC3\xA4 \xC3\xB6 \xC3\xBC ") XCS("\xC3\xA4 \xC3\xB6 \xC3\xBC >");
+#endif
+
+  run_character_check(text, expected);
+  XML_ParserReset(g_parser, NULL);
+  run_attribute_check(text, expected);
+  /* Repeat with a default handler */
+  XML_ParserReset(g_parser, NULL);
+  XML_SetDefaultHandler(g_parser, dummy_default_handler);
+  run_character_check(text, expected);
+  XML_ParserReset(g_parser, NULL);
+  XML_SetDefaultHandler(g_parser, dummy_default_handler);
+  run_attribute_check(text, expected);
+}
+END_TEST
+
+/* Test that an element name with a 4-byte UTF-8 character is rejected */
+START_TEST(test_long_utf8_character) {
+  const char *text
+      = "<?xml version='1.0' encoding='utf-8'?>\n"
+        /* 0xf0 0x90 0x80 0x80 = U+10000, the first Linear B character */
+        "<do\xf0\x90\x80\x80/>";
+  expect_failure(text, XML_ERROR_INVALID_TOKEN,
+                 "4-byte UTF-8 character in element name not faulted");
+}
+END_TEST
+
+/* Test that a long latin-1 attribute (too long to convert in one go)
+ * is correctly converted
+ */
+START_TEST(test_long_latin1_attribute) {
+  const char *text
+      = "<?xml version='1.0' encoding='iso-8859-1'?>\n"
+        "<doc att='"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
+        /* Last character splits across a buffer boundary */
+        "\xe4'>\n</doc>";
+
+  const XML_Char *expected =
+      /* 64 characters per line */
+      /* clang-format off */
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO")
+  /* clang-format on */
+#ifdef XML_UNICODE
+                                                  XCS("\x00e4");
+#else
+                                                  XCS("\xc3\xa4");
+#endif
+
+  run_attribute_check(text, expected);
+}
+END_TEST
+
+/* Test that a long ASCII attribute (too long to convert in one go)
+ * is correctly converted
+ */
+START_TEST(test_long_ascii_attribute) {
+  const char *text
+      = "<?xml version='1.0' encoding='us-ascii'?>\n"
+        "<doc att='"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "01234'>\n</doc>";
+  const XML_Char *expected =
+      /* 64 characters per line */
+      /* clang-format off */
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("01234");
+  /* clang-format on */
+
+  run_attribute_check(text, expected);
+}
+END_TEST
+
+/* Regression test #1 for SF bug #653180. */
+START_TEST(test_line_number_after_parse) {
+  const char *text = "<tag>\n"
+                     "\n"
+                     "\n</tag>";
+  XML_Size lineno;
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  lineno = XML_GetCurrentLineNumber(g_parser);
+  if (lineno != 4) {
+    char buffer[100];
+    snprintf(buffer, sizeof(buffer),
+             "expected 4 lines, saw %" XML_FMT_INT_MOD "u", lineno);
+    fail(buffer);
+  }
+}
+END_TEST
+
+/* Regression test #2 for SF bug #653180. */
+START_TEST(test_column_number_after_parse) {
+  const char *text = "<tag></tag>";
+  XML_Size colno;
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  colno = XML_GetCurrentColumnNumber(g_parser);
+  if (colno != 11) {
+    char buffer[100];
+    snprintf(buffer, sizeof(buffer),
+             "expected 11 columns, saw %" XML_FMT_INT_MOD "u", colno);
+    fail(buffer);
+  }
+}
+END_TEST
+
+/* Regression test #3 for SF bug #653180. */
+START_TEST(test_line_and_column_numbers_inside_handlers) {
+  const char *text = "<a>\n"      /* Unix end-of-line */
+                     "  <b>\r\n"  /* Windows end-of-line */
+                     "    <c/>\r" /* Mac OS end-of-line */
+                     "  </b>\n"
+                     "  <d>\n"
+                     "    <f/>\n"
+                     "  </d>\n"
+                     "</a>";
+  const StructDataEntry expected[]
+      = {{XCS("a"), 0, 1, STRUCT_START_TAG}, {XCS("b"), 2, 2, STRUCT_START_TAG},
+         {XCS("c"), 4, 3, STRUCT_START_TAG}, {XCS("c"), 8, 3, STRUCT_END_TAG},
+         {XCS("b"), 2, 4, STRUCT_END_TAG},   {XCS("d"), 2, 5, STRUCT_START_TAG},
+         {XCS("f"), 4, 6, STRUCT_START_TAG}, {XCS("f"), 8, 6, STRUCT_END_TAG},
+         {XCS("d"), 2, 7, STRUCT_END_TAG},   {XCS("a"), 0, 8, STRUCT_END_TAG}};
+  const int expected_count = sizeof(expected) / sizeof(StructDataEntry);
+  StructData storage;
+
+  StructData_Init(&storage);
+  XML_SetUserData(g_parser, &storage);
+  XML_SetStartElementHandler(g_parser, start_element_event_handler2);
+  XML_SetEndElementHandler(g_parser, end_element_event_handler2);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+
+  StructData_CheckItems(&storage, expected, expected_count);
+  StructData_Dispose(&storage);
+}
+END_TEST
+
+/* Regression test #4 for SF bug #653180. */
+START_TEST(test_line_number_after_error) {
+  const char *text = "<a>\n"
+                     "  <b>\n"
+                     "  </a>"; /* missing </b> */
+  XML_Size lineno;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail("Expected a parse error");
+
+  lineno = XML_GetCurrentLineNumber(g_parser);
+  if (lineno != 3) {
+    char buffer[100];
+    snprintf(buffer, sizeof(buffer),
+             "expected 3 lines, saw %" XML_FMT_INT_MOD "u", lineno);
+    fail(buffer);
+  }
+}
+END_TEST
+
+/* Regression test #5 for SF bug #653180. */
+START_TEST(test_column_number_after_error) {
+  const char *text = "<a>\n"
+                     "  <b>\n"
+                     "  </a>"; /* missing </b> */
+  XML_Size colno;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail("Expected a parse error");
+
+  colno = XML_GetCurrentColumnNumber(g_parser);
+  if (colno != 4) {
+    char buffer[100];
+    snprintf(buffer, sizeof(buffer),
+             "expected 4 columns, saw %" XML_FMT_INT_MOD "u", colno);
+    fail(buffer);
+  }
+}
+END_TEST
+
+/* Regression test for SF bug #478332. */
+START_TEST(test_really_long_lines) {
+  /* This parses an input line longer than INIT_DATA_BUF_SIZE
+     characters long (defined to be 1024 in xmlparse.c).  We take a
+     really cheesy approach to building the input buffer, because
+     this avoids writing bugs in buffer-filling code.
+  */
+  const char *text
+      = "<e>"
+        /* 64 chars */
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        /* until we have at least 1024 characters on the line: */
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "</e>";
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test cdata processing across a buffer boundary */
+START_TEST(test_really_long_encoded_lines) {
+  /* As above, except that we want to provoke an output buffer
+   * overflow with a non-trivial encoding.  For this we need to pass
+   * the whole cdata in one go, not byte-by-byte.
+   */
+  void *buffer;
+  const char *text
+      = "<?xml version='1.0' encoding='iso-8859-1'?>"
+        "<e>"
+        /* 64 chars */
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        /* until we have at least 1024 characters on the line: */
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
+        "</e>";
+  int parse_len = (int)strlen(text);
+
+  /* Need a cdata handler to provoke the code path we want to test */
+  XML_SetCharacterDataHandler(g_parser, dummy_cdata_handler);
+  buffer = XML_GetBuffer(g_parser, parse_len);
+  if (buffer == NULL)
+    fail("Could not allocate parse buffer");
+  assert(buffer != NULL);
+  memcpy(buffer, text, parse_len);
+  if (XML_ParseBuffer(g_parser, parse_len, XML_TRUE) == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/*
+ * Element event tests.
+ */
+
+START_TEST(test_end_element_events) {
+  const char *text = "<a><b><c/></b><d><f/></d></a>";
+  const XML_Char *expected = XCS("/c/b/f/d/a");
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetUserData(g_parser, &storage);
+  XML_SetEndElementHandler(g_parser, end_element_event_handler);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+/*
+ * Attribute tests.
+ */
+
+/* Helper used by the following tests; this checks any "attr" and "refs"
+   attributes to make sure whitespace has been normalized.
+
+   Return true if whitespace has been normalized in a string, using
+   the rules for attribute value normalization.  The 'is_cdata' flag
+   is needed since CDATA attributes don't need to have multiple
+   whitespace characters collapsed to a single space, while other
+   attribute data types do.  (Section 3.3.3 of the recommendation.)
+*/
+static int
+is_whitespace_normalized(const XML_Char *s, int is_cdata) {
+  int blanks = 0;
+  int at_start = 1;
+  while (*s) {
+    if (*s == XCS(' '))
+      ++blanks;
+    else if (*s == XCS('\t') || *s == XCS('\n') || *s == XCS('\r'))
+      return 0;
+    else {
+      if (at_start) {
+        at_start = 0;
+        if (blanks && ! is_cdata)
+          /* illegal leading blanks */
+          return 0;
+      } else if (blanks > 1 && ! is_cdata)
+        return 0;
+      blanks = 0;
+    }
+    ++s;
+  }
+  if (blanks && ! is_cdata)
+    return 0;
+  return 1;
+}
+
+/* Check the attribute whitespace checker: */
+START_TEST(test_helper_is_whitespace_normalized) {
+  assert(is_whitespace_normalized(XCS("abc"), 0));
+  assert(is_whitespace_normalized(XCS("abc"), 1));
+  assert(is_whitespace_normalized(XCS("abc def ghi"), 0));
+  assert(is_whitespace_normalized(XCS("abc def ghi"), 1));
+  assert(! is_whitespace_normalized(XCS(" abc def ghi"), 0));
+  assert(is_whitespace_normalized(XCS(" abc def ghi"), 1));
+  assert(! is_whitespace_normalized(XCS("abc  def ghi"), 0));
+  assert(is_whitespace_normalized(XCS("abc  def ghi"), 1));
+  assert(! is_whitespace_normalized(XCS("abc def ghi "), 0));
+  assert(is_whitespace_normalized(XCS("abc def ghi "), 1));
+  assert(! is_whitespace_normalized(XCS(" "), 0));
+  assert(is_whitespace_normalized(XCS(" "), 1));
+  assert(! is_whitespace_normalized(XCS("\t"), 0));
+  assert(! is_whitespace_normalized(XCS("\t"), 1));
+  assert(! is_whitespace_normalized(XCS("\n"), 0));
+  assert(! is_whitespace_normalized(XCS("\n"), 1));
+  assert(! is_whitespace_normalized(XCS("\r"), 0));
+  assert(! is_whitespace_normalized(XCS("\r"), 1));
+  assert(! is_whitespace_normalized(XCS("abc\t def"), 1));
+}
+END_TEST
+
+static void XMLCALL
+check_attr_contains_normalized_whitespace(void *userData, const XML_Char *name,
+                                          const XML_Char **atts) {
+  int i;
+  UNUSED_P(userData);
+  UNUSED_P(name);
+  for (i = 0; atts[i] != NULL; i += 2) {
+    const XML_Char *attrname = atts[i];
+    const XML_Char *value = atts[i + 1];
+    if (xcstrcmp(XCS("attr"), attrname) == 0
+        || xcstrcmp(XCS("ents"), attrname) == 0
+        || xcstrcmp(XCS("refs"), attrname) == 0) {
+      if (! is_whitespace_normalized(value, 0)) {
+        char buffer[256];
+        snprintf(buffer, sizeof(buffer),
+                 "attribute value not normalized: %" XML_FMT_STR
+                 "='%" XML_FMT_STR "'",
+                 attrname, value);
+        fail(buffer);
+      }
+    }
+  }
+}
+
+START_TEST(test_attr_whitespace_normalization) {
+  const char *text
+      = "<!DOCTYPE doc [\n"
+        "  <!ATTLIST doc\n"
+        "            attr NMTOKENS #REQUIRED\n"
+        "            ents ENTITIES #REQUIRED\n"
+        "            refs IDREFS   #REQUIRED>\n"
+        "]>\n"
+        "<doc attr='    a  b c\t\td\te\t' refs=' id-1   \t  id-2\t\t'  \n"
+        "     ents=' ent-1   \t\r\n"
+        "            ent-2  ' >\n"
+        "  <e id='id-1'/>\n"
+        "  <e id='id-2'/>\n"
+        "</doc>";
+
+  XML_SetStartElementHandler(g_parser,
+                             check_attr_contains_normalized_whitespace);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/*
+ * XML declaration tests.
+ */
+
+START_TEST(test_xmldecl_misplaced) {
+  expect_failure("\n"
+                 "<?xml version='1.0'?>\n"
+                 "<a/>",
+                 XML_ERROR_MISPLACED_XML_PI,
+                 "failed to report misplaced XML declaration");
+}
+END_TEST
+
+START_TEST(test_xmldecl_invalid) {
+  expect_failure("<?xml version='1.0' \xc3\xa7?>\n<doc/>", XML_ERROR_XML_DECL,
+                 "Failed to report invalid XML declaration");
+}
+END_TEST
+
+START_TEST(test_xmldecl_missing_attr) {
+  expect_failure("<?xml ='1.0'?>\n<doc/>\n", XML_ERROR_XML_DECL,
+                 "Failed to report missing XML declaration attribute");
+}
+END_TEST
+
+START_TEST(test_xmldecl_missing_value) {
+  expect_failure("<?xml version='1.0' encoding='us-ascii' standalone?>\n"
+                 "<doc/>",
+                 XML_ERROR_XML_DECL,
+                 "Failed to report missing attribute value");
+}
+END_TEST
+
+/* Regression test for SF bug #584832. */
+START_TEST(test_unknown_encoding_internal_entity) {
+  const char *text = "<?xml version='1.0' encoding='unsupported-encoding'?>\n"
+                     "<!DOCTYPE test [<!ENTITY foo 'bar'>]>\n"
+                     "<test a='&foo;'/>";
+
+  XML_SetUnknownEncodingHandler(g_parser, UnknownEncodingHandler, NULL);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test unrecognised encoding handler */
+START_TEST(test_unrecognised_encoding_internal_entity) {
+  const char *text = "<?xml version='1.0' encoding='unsupported-encoding'?>\n"
+                     "<!DOCTYPE test [<!ENTITY foo 'bar'>]>\n"
+                     "<test a='&foo;'/>";
+
+  XML_SetUnknownEncodingHandler(g_parser, UnrecognisedEncodingHandler, NULL);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail("Unrecognised encoding not rejected");
+}
+END_TEST
+
+/* Regression test for SF bug #620106. */
+START_TEST(test_ext_entity_set_encoding) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
+                     "]>\n"
+                     "<doc>&en;</doc>";
+  ExtTest test_data
+      = {/* This text says it's an unsupported encoding, but it's really
+            UTF-8, which we tell Expat using XML_SetEncoding().
+         */
+         "<?xml encoding='iso-8859-3'?>\xC3\xA9", XCS("utf-8"), NULL};
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("\x00e9");
+#else
+  const XML_Char *expected = XCS("\xc3\xa9");
+#endif
+
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
+  run_ext_character_check(text, &test_data, expected);
+}
+END_TEST
+
+/* Test external entities with no handler */
+START_TEST(test_ext_entity_no_handler) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
+                     "]>\n"
+                     "<doc>&en;</doc>";
+
+  XML_SetDefaultHandler(g_parser, dummy_default_handler);
+  run_character_check(text, XCS(""));
+}
+END_TEST
+
+/* Test UTF-8 BOM is accepted */
+START_TEST(test_ext_entity_set_bom) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
+                     "]>\n"
+                     "<doc>&en;</doc>";
+  ExtTest test_data = {"\xEF\xBB\xBF" /* BOM */
+                       "<?xml encoding='iso-8859-3'?>"
+                       "\xC3\xA9",
+                       XCS("utf-8"), NULL};
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("\x00e9");
+#else
+  const XML_Char *expected = XCS("\xc3\xa9");
+#endif
+
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
+  run_ext_character_check(text, &test_data, expected);
+}
+END_TEST
+
+/* Test that bad encodings are faulted */
+START_TEST(test_ext_entity_bad_encoding) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
+                     "]>\n"
+                     "<doc>&en;</doc>";
+  ExtFaults fault
+      = {"<?xml encoding='iso-8859-3'?>u", "Unsupported encoding not faulted",
+         XCS("unknown"), XML_ERROR_UNKNOWN_ENCODING};
+
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_faulter);
+  XML_SetUserData(g_parser, &fault);
+  expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
+                 "Bad encoding should not have been accepted");
+}
+END_TEST
+
+/* Try handing an invalid encoding to an external entity parser */
+START_TEST(test_ext_entity_bad_encoding_2) {
+  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
+                     "<!DOCTYPE doc SYSTEM 'foo'>\n"
+                     "<doc>&entity;</doc>";
+  ExtFaults fault
+      = {"<!ELEMENT doc (#PCDATA)*>", "Unknown encoding not faulted",
+         XCS("unknown-encoding"), XML_ERROR_UNKNOWN_ENCODING};
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_faulter);
+  XML_SetUserData(g_parser, &fault);
+  expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
+                 "Bad encoding not faulted in external entity handler");
+}
+END_TEST
+
+/* Test that no error is reported for unknown entities if we don't
+   read an external subset.  This was fixed in Expat 1.95.5.
+*/
+START_TEST(test_wfc_undeclared_entity_unread_external_subset) {
+  const char *text = "<!DOCTYPE doc SYSTEM 'foo'>\n"
+                     "<doc>&entity;</doc>";
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test that an error is reported for unknown entities if we don't
+   have an external subset.
+*/
+START_TEST(test_wfc_undeclared_entity_no_external_subset) {
+  expect_failure("<doc>&entity;</doc>", XML_ERROR_UNDEFINED_ENTITY,
+                 "Parser did not report undefined entity w/out a DTD.");
+}
+END_TEST
+
+/* Test that an error is reported for unknown entities if we don't
+   read an external subset, but have been declared standalone.
+*/
+START_TEST(test_wfc_undeclared_entity_standalone) {
+  const char *text
+      = "<?xml version='1.0' encoding='us-ascii' standalone='yes'?>\n"
+        "<!DOCTYPE doc SYSTEM 'foo'>\n"
+        "<doc>&entity;</doc>";
+
+  expect_failure(text, XML_ERROR_UNDEFINED_ENTITY,
+                 "Parser did not report undefined entity (standalone).");
+}
+END_TEST
+
+/* Test that an error is reported for unknown entities if we have read
+   an external subset, and standalone is true.
+*/
+START_TEST(test_wfc_undeclared_entity_with_external_subset_standalone) {
+  const char *text
+      = "<?xml version='1.0' encoding='us-ascii' standalone='yes'?>\n"
+        "<!DOCTYPE doc SYSTEM 'foo'>\n"
+        "<doc>&entity;</doc>";
+  ExtTest test_data = {"<!ELEMENT doc (#PCDATA)*>", NULL, NULL};
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetUserData(g_parser, &test_data);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
+  expect_failure(text, XML_ERROR_UNDEFINED_ENTITY,
+                 "Parser did not report undefined entity (external DTD).");
+}
+END_TEST
+
+/* Test that external entity handling is not done if the parsing flag
+ * is set to UNLESS_STANDALONE
+ */
+START_TEST(test_entity_with_external_subset_unless_standalone) {
+  const char *text
+      = "<?xml version='1.0' encoding='us-ascii' standalone='yes'?>\n"
+        "<!DOCTYPE doc SYSTEM 'foo'>\n"
+        "<doc>&entity;</doc>";
+  ExtTest test_data = {"<!ENTITY entity 'bar'>", NULL, NULL};
+
+  XML_SetParamEntityParsing(g_parser,
+                            XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE);
+  XML_SetUserData(g_parser, &test_data);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
+  expect_failure(text, XML_ERROR_UNDEFINED_ENTITY,
+                 "Parser did not report undefined entity");
+}
+END_TEST
+
+/* Test that no error is reported for unknown entities if we have read
+   an external subset, and standalone is false.
+*/
+START_TEST(test_wfc_undeclared_entity_with_external_subset) {
+  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
+                     "<!DOCTYPE doc SYSTEM 'foo'>\n"
+                     "<doc>&entity;</doc>";
+  ExtTest test_data = {"<!ELEMENT doc (#PCDATA)*>", NULL, NULL};
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
+  run_ext_character_check(text, &test_data, XCS(""));
+}
+END_TEST
+
+/* Test that an error is reported if our NotStandalone handler fails */
+START_TEST(test_not_standalone_handler_reject) {
+  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
+                     "<!DOCTYPE doc SYSTEM 'foo'>\n"
+                     "<doc>&entity;</doc>";
+  ExtTest test_data = {"<!ELEMENT doc (#PCDATA)*>", NULL, NULL};
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetUserData(g_parser, &test_data);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
+  XML_SetNotStandaloneHandler(g_parser, reject_not_standalone_handler);
+  expect_failure(text, XML_ERROR_NOT_STANDALONE,
+                 "NotStandalone handler failed to reject");
+
+  /* Try again but without external entity handling */
+  XML_ParserReset(g_parser, NULL);
+  XML_SetNotStandaloneHandler(g_parser, reject_not_standalone_handler);
+  expect_failure(text, XML_ERROR_NOT_STANDALONE,
+                 "NotStandalone handler failed to reject");
+}
+END_TEST
+
+/* Test that no error is reported if our NotStandalone handler succeeds */
+START_TEST(test_not_standalone_handler_accept) {
+  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
+                     "<!DOCTYPE doc SYSTEM 'foo'>\n"
+                     "<doc>&entity;</doc>";
+  ExtTest test_data = {"<!ELEMENT doc (#PCDATA)*>", NULL, NULL};
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
+  XML_SetNotStandaloneHandler(g_parser, accept_not_standalone_handler);
+  run_ext_character_check(text, &test_data, XCS(""));
+
+  /* Repeat without the external entity handler */
+  XML_ParserReset(g_parser, NULL);
+  XML_SetNotStandaloneHandler(g_parser, accept_not_standalone_handler);
+  run_character_check(text, XCS(""));
+}
+END_TEST
+
+START_TEST(test_wfc_no_recursive_entity_refs) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY entity '&#38;entity;'>\n"
+                     "]>\n"
+                     "<doc>&entity;</doc>";
+
+  expect_failure(text, XML_ERROR_RECURSIVE_ENTITY_REF,
+                 "Parser did not report recursive entity reference.");
+}
+END_TEST
+
+START_TEST(test_recursive_external_parameter_entity_2) {
+  struct TestCase {
+    const char *doc;
+    enum XML_Status expectedStatus;
+  };
+
+  struct TestCase cases[] = {
+      {"<!ENTITY % p1 '%p1;'>", XML_STATUS_ERROR},
+      {"<!ENTITY % p1 '%p1;'>"
+       "<!ENTITY % p1 'first declaration wins'>",
+       XML_STATUS_ERROR},
+      {"<!ENTITY % p1 'first declaration wins'>"
+       "<!ENTITY % p1 '%p1;'>",
+       XML_STATUS_OK},
+      {"<!ENTITY % p1 '&#37;p1;'>", XML_STATUS_OK},
+  };
+
+  for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
+    const char *const doc = cases[i].doc;
+    const enum XML_Status expectedStatus = cases[i].expectedStatus;
+    set_subtest("%s", doc);
+
+    XML_Parser parser = XML_ParserCreate(NULL);
+    assert_true(parser != NULL);
+
+    XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL);
+    assert_true(ext_parser != NULL);
+
+    const enum XML_Status actualStatus
+        = _XML_Parse_SINGLE_BYTES(ext_parser, doc, (int)strlen(doc), XML_TRUE);
+
+    assert_true(actualStatus == expectedStatus);
+    if (actualStatus != XML_STATUS_OK) {
+      assert_true(XML_GetErrorCode(ext_parser)
+                  == XML_ERROR_RECURSIVE_ENTITY_REF);
+    }
+
+    XML_ParserFree(ext_parser);
+    XML_ParserFree(parser);
+  }
+}
+END_TEST
+
+/* Test incomplete external entities are faulted */
+START_TEST(test_ext_entity_invalid_parse) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
+                     "]>\n"
+                     "<doc>&en;</doc>";
+  const ExtFaults faults[]
+      = {{"<", "Incomplete element declaration not faulted", NULL,
+          XML_ERROR_UNCLOSED_TOKEN},
+         {"<\xe2\x82", /* First two bytes of a three-byte char */
+          "Incomplete character not faulted", NULL, XML_ERROR_PARTIAL_CHAR},
+         {"<tag>\xe2\x82", "Incomplete character in CDATA not faulted", NULL,
+          XML_ERROR_PARTIAL_CHAR},
+         {NULL, NULL, NULL, XML_ERROR_NONE}};
+  const ExtFaults *fault = faults;
+
+  for (; fault->parse_text != NULL; fault++) {
+    set_subtest("\"%s\"", fault->parse_text);
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_faulter);
+    XML_SetUserData(g_parser, (void *)fault);
+    expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
+                   "Parser did not report external entity error");
+    XML_ParserReset(g_parser, NULL);
+  }
+}
+END_TEST
+
+/* Regression test for SF bug #483514. */
+START_TEST(test_dtd_default_handling) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "<!ENTITY e SYSTEM 'http://example.org/e'>\n"
+                     "<!NOTATION n SYSTEM 'http://example.org/n'>\n"
+                     "<!ELEMENT doc EMPTY>\n"
+                     "<!ATTLIST doc a CDATA #IMPLIED>\n"
+                     "<?pi in dtd?>\n"
+                     "<!--comment in dtd-->\n"
+                     "]><doc/>";
+
+  XML_SetDefaultHandler(g_parser, accumulate_characters);
+  XML_SetStartDoctypeDeclHandler(g_parser, dummy_start_doctype_handler);
+  XML_SetEndDoctypeDeclHandler(g_parser, dummy_end_doctype_handler);
+  XML_SetEntityDeclHandler(g_parser, dummy_entity_decl_handler);
+  XML_SetNotationDeclHandler(g_parser, dummy_notation_decl_handler);
+  XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
+  XML_SetAttlistDeclHandler(g_parser, dummy_attlist_decl_handler);
+  XML_SetProcessingInstructionHandler(g_parser, dummy_pi_handler);
+  XML_SetCommentHandler(g_parser, dummy_comment_handler);
+  XML_SetStartCdataSectionHandler(g_parser, dummy_start_cdata_handler);
+  XML_SetEndCdataSectionHandler(g_parser, dummy_end_cdata_handler);
+  run_character_check(text, XCS("\n\n\n\n\n\n\n<doc/>"));
+}
+END_TEST
+
+/* Test handling of attribute declarations */
+START_TEST(test_dtd_attr_handling) {
+  const char *prolog = "<!DOCTYPE doc [\n"
+                       "<!ELEMENT doc EMPTY>\n";
+  AttTest attr_data[]
+      = {{"<!ATTLIST doc a ( one | two | three ) #REQUIRED>\n"
+          "]>"
+          "<doc a='two'/>",
+          XCS("doc"), XCS("a"),
+          XCS("(one|two|three)"), /* Extraneous spaces will be removed */
+          NULL, XML_TRUE},
+         {"<!NOTATION foo SYSTEM 'http://example.org/foo'>\n"
+          "<!ATTLIST doc a NOTATION (foo) #IMPLIED>\n"
+          "]>"
+          "<doc/>",
+          XCS("doc"), XCS("a"), XCS("NOTATION(foo)"), NULL, XML_FALSE},
+         {"<!ATTLIST doc a NOTATION (foo) 'bar'>\n"
+          "]>"
+          "<doc/>",
+          XCS("doc"), XCS("a"), XCS("NOTATION(foo)"), XCS("bar"), XML_FALSE},
+         {"<!ATTLIST doc a CDATA '\xdb\xb2'>\n"
+          "]>"
+          "<doc/>",
+          XCS("doc"), XCS("a"), XCS("CDATA"),
+#ifdef XML_UNICODE
+          XCS("\x06f2"),
+#else
+          XCS("\xdb\xb2"),
+#endif
+          XML_FALSE},
+         {NULL, NULL, NULL, NULL, NULL, XML_FALSE}};
+  AttTest *test;
+
+  for (test = attr_data; test->definition != NULL; test++) {
+    set_subtest("%s", test->definition);
+    XML_SetAttlistDeclHandler(g_parser, verify_attlist_decl_handler);
+    XML_SetUserData(g_parser, test);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, prolog, (int)strlen(prolog),
+                                XML_FALSE)
+        == XML_STATUS_ERROR)
+      xml_failure(g_parser);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, test->definition,
+                                (int)strlen(test->definition), XML_TRUE)
+        == XML_STATUS_ERROR)
+      xml_failure(g_parser);
+    XML_ParserReset(g_parser, NULL);
+  }
+}
+END_TEST
+
+/* See related SF bug #673791.
+   When namespace processing is enabled, setting the namespace URI for
+   a prefix is not allowed; this test ensures that it *is* allowed
+   when namespace processing is not enabled.
+   (See Namespaces in XML, section 2.)
+*/
+START_TEST(test_empty_ns_without_namespaces) {
+  const char *text = "<doc xmlns:prefix='http://example.org/'>\n"
+                     "  <e xmlns:prefix=''/>\n"
+                     "</doc>";
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Regression test for SF bug #824420.
+   Checks that an xmlns:prefix attribute set in an attribute's default
+   value isn't misinterpreted.
+*/
+START_TEST(test_ns_in_attribute_default_without_namespaces) {
+  const char *text = "<!DOCTYPE e:element [\n"
+                     "  <!ATTLIST e:element\n"
+                     "    xmlns:e CDATA 'http://example.org/'>\n"
+                     "      ]>\n"
+                     "<e:element/>";
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Regression test for SF bug #1515266: missing check of stopped
+   parser in doContext() 'for' loop. */
+START_TEST(test_stop_parser_between_char_data_calls) {
+  /* The sample data must be big enough that there are two calls to
+     the character data handler from within the inner "for" loop of
+     the XML_TOK_DATA_CHARS case in doContent(), and the character
+     handler must stop the parser and clear the character data
+     handler.
+  */
+  const char *text = long_character_data_text;
+
+  XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler);
+  g_resumable = XML_FALSE;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_ABORTED)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Regression test for SF bug #1515266: missing check of stopped
+   parser in doContext() 'for' loop. */
+START_TEST(test_suspend_parser_between_char_data_calls) {
+  /* The sample data must be big enough that there are two calls to
+     the character data handler from within the inner "for" loop of
+     the XML_TOK_DATA_CHARS case in doContent(), and the character
+     handler must stop the parser and clear the character data
+     handler.
+  */
+  const char *text = long_character_data_text;
+
+  XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler);
+  g_resumable = XML_TRUE;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_SUSPENDED)
+    xml_failure(g_parser);
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_NONE)
+    xml_failure(g_parser);
+  /* Try parsing directly */
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail("Attempt to continue parse while suspended not faulted");
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_SUSPENDED)
+    fail("Suspended parse not faulted with correct error");
+}
+END_TEST
+
+/* Test repeated calls to XML_StopParser are handled correctly */
+START_TEST(test_repeated_stop_parser_between_char_data_calls) {
+  const char *text = long_character_data_text;
+
+  XML_SetCharacterDataHandler(g_parser, parser_stop_character_handler);
+  g_resumable = XML_FALSE;
+  g_abortable = XML_FALSE;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail("Failed to double-stop parser");
+
+  XML_ParserReset(g_parser, NULL);
+  XML_SetCharacterDataHandler(g_parser, parser_stop_character_handler);
+  g_resumable = XML_TRUE;
+  g_abortable = XML_FALSE;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_SUSPENDED)
+    fail("Failed to double-suspend parser");
+
+  XML_ParserReset(g_parser, NULL);
+  XML_SetCharacterDataHandler(g_parser, parser_stop_character_handler);
+  g_resumable = XML_TRUE;
+  g_abortable = XML_TRUE;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail("Failed to suspend-abort parser");
+}
+END_TEST
+
+START_TEST(test_good_cdata_ascii) {
+  const char *text = "<a><![CDATA[<greeting>Hello, world!</greeting>]]></a>";
+  const XML_Char *expected = XCS("<greeting>Hello, world!</greeting>");
+
+  CharData storage;
+  CharData_Init(&storage);
+  XML_SetUserData(g_parser, &storage);
+  XML_SetCharacterDataHandler(g_parser, accumulate_characters);
+  /* Add start and end handlers for coverage */
+  XML_SetStartCdataSectionHandler(g_parser, dummy_start_cdata_handler);
+  XML_SetEndCdataSectionHandler(g_parser, dummy_end_cdata_handler);
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+
+  /* Try again, this time with a default handler */
+  XML_ParserReset(g_parser, NULL);
+  CharData_Init(&storage);
+  XML_SetUserData(g_parser, &storage);
+  XML_SetCharacterDataHandler(g_parser, accumulate_characters);
+  XML_SetDefaultHandler(g_parser, dummy_default_handler);
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_good_cdata_utf16) {
+  /* Test data is:
+   *   <?xml version='1.0' encoding='utf-16'?>
+   *   <a><![CDATA[hello]]></a>
+   */
+  const char text[]
+      = "\0<\0?\0x\0m\0l\0"
+        " \0v\0e\0r\0s\0i\0o\0n\0=\0'\0\x31\0.\0\x30\0'\0"
+        " \0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\0"
+        "1\0"
+        "6\0'"
+        "\0?\0>\0\n"
+        "\0<\0a\0>\0<\0!\0[\0C\0D\0A\0T\0A\0[\0h\0e\0l\0l\0o\0]\0]\0>\0<\0/\0a\0>";
+  const XML_Char *expected = XCS("hello");
+
+  CharData storage;
+  CharData_Init(&storage);
+  XML_SetUserData(g_parser, &storage);
+  XML_SetCharacterDataHandler(g_parser, accumulate_characters);
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_good_cdata_utf16_le) {
+  /* Test data is:
+   *   <?xml version='1.0' encoding='utf-16'?>
+   *   <a><![CDATA[hello]]></a>
+   */
+  const char text[]
+      = "<\0?\0x\0m\0l\0"
+        " \0v\0e\0r\0s\0i\0o\0n\0=\0'\0\x31\0.\0\x30\0'\0"
+        " \0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\0"
+        "1\0"
+        "6\0'"
+        "\0?\0>\0\n"
+        "\0<\0a\0>\0<\0!\0[\0C\0D\0A\0T\0A\0[\0h\0e\0l\0l\0o\0]\0]\0>\0<\0/\0a\0>\0";
+  const XML_Char *expected = XCS("hello");
+
+  CharData storage;
+  CharData_Init(&storage);
+  XML_SetUserData(g_parser, &storage);
+  XML_SetCharacterDataHandler(g_parser, accumulate_characters);
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+/* Test UTF16 conversion of a long cdata string */
+
+/* 16 characters: handy macro to reduce visual clutter */
+#define A_TO_P_IN_UTF16 "\0A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0K\0L\0M\0N\0O\0P"
+
+START_TEST(test_long_cdata_utf16) {
+  /* Test data is:
+   * <?xlm version='1.0' encoding='utf-16'?>
+   * <a><![CDATA[
+   * ABCDEFGHIJKLMNOP
+   * ]]></a>
+   */
+  const char text[]
+      = "\0<\0?\0x\0m\0l\0 "
+        "\0v\0e\0r\0s\0i\0o\0n\0=\0'\0\x31\0.\0\x30\0'\0 "
+        "\0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\0\x31\0\x36\0'\0?\0>"
+        "\0<\0a\0>\0<\0!\0[\0C\0D\0A\0T\0A\0["
+      /* 64 characters per line */
+      /* clang-format off */
+        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
+        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
+        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
+        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
+        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
+        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
+        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
+        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
+        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
+        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
+        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
+        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
+        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
+        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
+        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
+        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
+        A_TO_P_IN_UTF16
+        /* clang-format on */
+        "\0]\0]\0>\0<\0/\0a\0>";
+  const XML_Char *expected =
+      /* clang-format off */
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
+        XCS("ABCDEFGHIJKLMNOP");
+  /* clang-format on */
+  CharData storage;
+  void *buffer;
+
+  CharData_Init(&storage);
+  XML_SetUserData(g_parser, &storage);
+  XML_SetCharacterDataHandler(g_parser, accumulate_characters);
+  buffer = XML_GetBuffer(g_parser, sizeof(text) - 1);
+  if (buffer == NULL)
+    fail("Could not allocate parse buffer");
+  assert(buffer != NULL);
+  memcpy(buffer, text, sizeof(text) - 1);
+  if (XML_ParseBuffer(g_parser, sizeof(text) - 1, XML_TRUE) == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+/* Test handling of multiple unit UTF-16 characters */
+START_TEST(test_multichar_cdata_utf16) {
+  /* Test data is:
+   *   <?xml version='1.0' encoding='utf-16'?>
+   *   <a><![CDATA[{MINIM}{CROTCHET}]]></a>
+   *
+   * where {MINIM} is U+1d15e (a minim or half-note)
+   *   UTF-16: 0xd834 0xdd5e
+   *   UTF-8:  0xf0 0x9d 0x85 0x9e
+   * and {CROTCHET} is U+1d15f (a crotchet or quarter-note)
+   *   UTF-16: 0xd834 0xdd5f
+   *   UTF-8:  0xf0 0x9d 0x85 0x9f
+   */
+  const char text[] = "\0<\0?\0x\0m\0l\0"
+                      " \0v\0e\0r\0s\0i\0o\0n\0=\0'\0\x31\0.\0\x30\0'\0"
+                      " \0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\0"
+                      "1\0"
+                      "6\0'"
+                      "\0?\0>\0\n"
+                      "\0<\0a\0>\0<\0!\0[\0C\0D\0A\0T\0A\0["
+                      "\xd8\x34\xdd\x5e\xd8\x34\xdd\x5f"
+                      "\0]\0]\0>\0<\0/\0a\0>";
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("\xd834\xdd5e\xd834\xdd5f");
+#else
+  const XML_Char *expected = XCS("\xf0\x9d\x85\x9e\xf0\x9d\x85\x9f");
+#endif
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetUserData(g_parser, &storage);
+  XML_SetCharacterDataHandler(g_parser, accumulate_characters);
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+/* Test that an element name with a UTF-16 surrogate pair is rejected */
+START_TEST(test_utf16_bad_surrogate_pair) {
+  /* Test data is:
+   *   <?xml version='1.0' encoding='utf-16'?>
+   *   <a><![CDATA[{BADLINB}]]></a>
+   *
+   * where {BADLINB} is U+10000 (the first Linear B character)
+   * with the UTF-16 surrogate pair in the wrong order, i.e.
+   *   0xdc00 0xd800
+   */
+  const char text[] = "\0<\0?\0x\0m\0l\0"
+                      " \0v\0e\0r\0s\0i\0o\0n\0=\0'\0\x31\0.\0\x30\0'\0"
+                      " \0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\0"
+                      "1\0"
+                      "6\0'"
+                      "\0?\0>\0\n"
+                      "\0<\0a\0>\0<\0!\0[\0C\0D\0A\0T\0A\0["
+                      "\xdc\x00\xd8\x00"
+                      "\0]\0]\0>\0<\0/\0a\0>";
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail("Reversed UTF-16 surrogate pair not faulted");
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_INVALID_TOKEN)
+    xml_failure(g_parser);
+}
+END_TEST
+
+START_TEST(test_bad_cdata) {
+  struct CaseData {
+    const char *text;
+    enum XML_Error expectedError;
+  };
+
+  struct CaseData cases[]
+      = {{"<a><", XML_ERROR_UNCLOSED_TOKEN},
+         {"<a><!", XML_ERROR_UNCLOSED_TOKEN},
+         {"<a><![", XML_ERROR_UNCLOSED_TOKEN},
+         {"<a><![C", XML_ERROR_UNCLOSED_TOKEN},
+         {"<a><![CD", XML_ERROR_UNCLOSED_TOKEN},
+         {"<a><![CDA", XML_ERROR_UNCLOSED_TOKEN},
+         {"<a><![CDAT", XML_ERROR_UNCLOSED_TOKEN},
+         {"<a><![CDATA", XML_ERROR_UNCLOSED_TOKEN},
+
+         {"<a><![CDATA[", XML_ERROR_UNCLOSED_CDATA_SECTION},
+         {"<a><![CDATA[]", XML_ERROR_UNCLOSED_CDATA_SECTION},
+         {"<a><![CDATA[]]", XML_ERROR_UNCLOSED_CDATA_SECTION},
+
+         {"<a><!<a/>", XML_ERROR_INVALID_TOKEN},
+         {"<a><![<a/>", XML_ERROR_UNCLOSED_TOKEN},  /* ?! */
+         {"<a><![C<a/>", XML_ERROR_UNCLOSED_TOKEN}, /* ?! */
+         {"<a><![CD<a/>", XML_ERROR_INVALID_TOKEN},
+         {"<a><![CDA<a/>", XML_ERROR_INVALID_TOKEN},
+         {"<a><![CDAT<a/>", XML_ERROR_INVALID_TOKEN},
+         {"<a><![CDATA<a/>", XML_ERROR_INVALID_TOKEN},
+
+         {"<a><![CDATA[<a/>", XML_ERROR_UNCLOSED_CDATA_SECTION},
+         {"<a><![CDATA[]<a/>", XML_ERROR_UNCLOSED_CDATA_SECTION},
+         {"<a><![CDATA[]]<a/>", XML_ERROR_UNCLOSED_CDATA_SECTION}};
+
+  size_t i = 0;
+  for (; i < sizeof(cases) / sizeof(struct CaseData); i++) {
+    set_subtest("%s", cases[i].text);
+    const enum XML_Status actualStatus = _XML_Parse_SINGLE_BYTES(
+        g_parser, cases[i].text, (int)strlen(cases[i].text), XML_TRUE);
+    const enum XML_Error actualError = XML_GetErrorCode(g_parser);
+
+    assert(actualStatus == XML_STATUS_ERROR);
+
+    if (actualError != cases[i].expectedError) {
+      char message[100];
+      snprintf(message, sizeof(message),
+               "Expected error %d but got error %d for case %u: \"%s\"\n",
+               cases[i].expectedError, actualError, (unsigned int)i + 1,
+               cases[i].text);
+      fail(message);
+    }
+
+    XML_ParserReset(g_parser, NULL);
+  }
+}
+END_TEST
+
+/* Test failures in UTF-16 CDATA */
+START_TEST(test_bad_cdata_utf16) {
+  struct CaseData {
+    size_t text_bytes;
+    const char *text;
+    enum XML_Error expected_error;
+  };
+
+  const char prolog[] = "\0<\0?\0x\0m\0l\0"
+                        " \0v\0e\0r\0s\0i\0o\0n\0=\0'\0\x31\0.\0\x30\0'\0"
+                        " \0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\0"
+                        "1\0"
+                        "6\0'"
+                        "\0?\0>\0\n"
+                        "\0<\0a\0>";
+  struct CaseData cases[] = {
+      {1, "\0", XML_ERROR_UNCLOSED_TOKEN},
+      {2, "\0<", XML_ERROR_UNCLOSED_TOKEN},
+      {3, "\0<\0", XML_ERROR_UNCLOSED_TOKEN},
+      {4, "\0<\0!", XML_ERROR_UNCLOSED_TOKEN},
+      {5, "\0<\0!\0", XML_ERROR_UNCLOSED_TOKEN},
+      {6, "\0<\0!\0[", XML_ERROR_UNCLOSED_TOKEN},
+      {7, "\0<\0!\0[\0", XML_ERROR_UNCLOSED_TOKEN},
+      {8, "\0<\0!\0[\0C", XML_ERROR_UNCLOSED_TOKEN},
+      {9, "\0<\0!\0[\0C\0", XML_ERROR_UNCLOSED_TOKEN},
+      {10, "\0<\0!\0[\0C\0D", XML_ERROR_UNCLOSED_TOKEN},
+      {11, "\0<\0!\0[\0C\0D\0", XML_ERROR_UNCLOSED_TOKEN},
+      {12, "\0<\0!\0[\0C\0D\0A", XML_ERROR_UNCLOSED_TOKEN},
+      {13, "\0<\0!\0[\0C\0D\0A\0", XML_ERROR_UNCLOSED_TOKEN},
+      {14, "\0<\0!\0[\0C\0D\0A\0T", XML_ERROR_UNCLOSED_TOKEN},
+      {15, "\0<\0!\0[\0C\0D\0A\0T\0", XML_ERROR_UNCLOSED_TOKEN},
+      {16, "\0<\0!\0[\0C\0D\0A\0T\0A", XML_ERROR_UNCLOSED_TOKEN},
+      {17, "\0<\0!\0[\0C\0D\0A\0T\0A\0", XML_ERROR_UNCLOSED_TOKEN},
+      {18, "\0<\0!\0[\0C\0D\0A\0T\0A\0[", XML_ERROR_UNCLOSED_CDATA_SECTION},
+      {19, "\0<\0!\0[\0C\0D\0A\0T\0A\0[\0", XML_ERROR_UNCLOSED_CDATA_SECTION},
+      {20, "\0<\0!\0[\0C\0D\0A\0T\0A\0[\0Z", XML_ERROR_UNCLOSED_CDATA_SECTION},
+      /* Now add a four-byte UTF-16 character */
+      {21, "\0<\0!\0[\0C\0D\0A\0T\0A\0[\0Z\xd8",
+       XML_ERROR_UNCLOSED_CDATA_SECTION},
+      {22, "\0<\0!\0[\0C\0D\0A\0T\0A\0[\0Z\xd8\x34", XML_ERROR_PARTIAL_CHAR},
+      {23, "\0<\0!\0[\0C\0D\0A\0T\0A\0[\0Z\xd8\x34\xdd",
+       XML_ERROR_PARTIAL_CHAR},
+      {24, "\0<\0!\0[\0C\0D\0A\0T\0A\0[\0Z\xd8\x34\xdd\x5e",
+       XML_ERROR_UNCLOSED_CDATA_SECTION}};
+  size_t i;
+
+  for (i = 0; i < sizeof(cases) / sizeof(struct CaseData); i++) {
+    set_subtest("case %lu", (long unsigned)(i + 1));
+    enum XML_Status actual_status;
+    enum XML_Error actual_error;
+
+    if (_XML_Parse_SINGLE_BYTES(g_parser, prolog, (int)sizeof(prolog) - 1,
+                                XML_FALSE)
+        == XML_STATUS_ERROR)
+      xml_failure(g_parser);
+    actual_status = _XML_Parse_SINGLE_BYTES(g_parser, cases[i].text,
+                                            (int)cases[i].text_bytes, XML_TRUE);
+    assert(actual_status == XML_STATUS_ERROR);
+    actual_error = XML_GetErrorCode(g_parser);
+    if (actual_error != cases[i].expected_error) {
+      char message[1024];
+
+      snprintf(message, sizeof(message),
+               "Expected error %d (%" XML_FMT_STR "), got %d (%" XML_FMT_STR
+               ") for case %lu\n",
+               cases[i].expected_error,
+               XML_ErrorString(cases[i].expected_error), actual_error,
+               XML_ErrorString(actual_error), (long unsigned)(i + 1));
+      fail(message);
+    }
+    XML_ParserReset(g_parser, NULL);
+  }
+}
+END_TEST
+
+/* Test stopping the parser in cdata handler */
+START_TEST(test_stop_parser_between_cdata_calls) {
+  const char *text = long_cdata_text;
+
+  XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler);
+  g_resumable = XML_FALSE;
+  expect_failure(text, XML_ERROR_ABORTED, "Parse not aborted in CDATA handler");
+}
+END_TEST
+
+/* Test suspending the parser in cdata handler */
+START_TEST(test_suspend_parser_between_cdata_calls) {
+  const char *text = long_cdata_text;
+  enum XML_Status result;
+
+  XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler);
+  g_resumable = XML_TRUE;
+  result = _XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE);
+  if (result != XML_STATUS_SUSPENDED) {
+    if (result == XML_STATUS_ERROR)
+      xml_failure(g_parser);
+    fail("Parse not suspended in CDATA handler");
+  }
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_NONE)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test memory allocation functions */
+START_TEST(test_memory_allocation) {
+  char *buffer = (char *)XML_MemMalloc(g_parser, 256);
+  char *p;
+
+  if (buffer == NULL) {
+    fail("Allocation failed");
+  } else {
+    /* Try writing to memory; some OSes try to cheat! */
+    buffer[0] = 'T';
+    buffer[1] = 'E';
+    buffer[2] = 'S';
+    buffer[3] = 'T';
+    buffer[4] = '\0';
+    if (strcmp(buffer, "TEST") != 0) {
+      fail("Memory not writable");
+    } else {
+      p = (char *)XML_MemRealloc(g_parser, buffer, 512);
+      if (p == NULL) {
+        fail("Reallocation failed");
+      } else {
+        /* Write again, just to be sure */
+        buffer = p;
+        buffer[0] = 'V';
+        if (strcmp(buffer, "VEST") != 0) {
+          fail("Reallocated memory not writable");
+        }
+      }
+    }
+    XML_MemFree(g_parser, buffer);
+  }
+}
+END_TEST
+
+/* Test XML_DefaultCurrent() passes handling on correctly */
+START_TEST(test_default_current) {
+  const char *text = "<doc>hell]</doc>";
+  const char *entity_text = "<!DOCTYPE doc [\n"
+                            "<!ENTITY entity '&#37;'>\n"
+                            "]>\n"
+                            "<doc>&entity;</doc>";
+
+  set_subtest("with defaulting");
+  {
+    struct handler_record_list storage;
+    storage.count = 0;
+    XML_SetDefaultHandler(g_parser, record_default_handler);
+    XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
+    XML_SetUserData(g_parser, &storage);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        == XML_STATUS_ERROR)
+      xml_failure(g_parser);
+    int i = 0;
+    assert_record_handler_called(&storage, i++, "record_default_handler", 5);
+    // we should have gotten one or more cdata callbacks, totaling 5 chars
+    int cdata_len_remaining = 5;
+    while (cdata_len_remaining > 0) {
+      const struct handler_record_entry *c_entry
+          = handler_record_get(&storage, i++);
+      assert_true(strcmp(c_entry->name, "record_cdata_handler") == 0);
+      assert_true(c_entry->arg > 0);
+      assert_true(c_entry->arg <= cdata_len_remaining);
+      cdata_len_remaining -= c_entry->arg;
+      // default handler must follow, with the exact same len argument.
+      assert_record_handler_called(&storage, i++, "record_default_handler",
+                                   c_entry->arg);
+    }
+    assert_record_handler_called(&storage, i++, "record_default_handler", 6);
+    assert_true(storage.count == i);
+  }
+
+  /* Again, without the defaulting */
+  set_subtest("no defaulting");
+  {
+    struct handler_record_list storage;
+    storage.count = 0;
+    XML_ParserReset(g_parser, NULL);
+    XML_SetDefaultHandler(g_parser, record_default_handler);
+    XML_SetCharacterDataHandler(g_parser, record_cdata_nodefault_handler);
+    XML_SetUserData(g_parser, &storage);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        == XML_STATUS_ERROR)
+      xml_failure(g_parser);
+    int i = 0;
+    assert_record_handler_called(&storage, i++, "record_default_handler", 5);
+    // we should have gotten one or more cdata callbacks, totaling 5 chars
+    int cdata_len_remaining = 5;
+    while (cdata_len_remaining > 0) {
+      const struct handler_record_entry *c_entry
+          = handler_record_get(&storage, i++);
+      assert_true(strcmp(c_entry->name, "record_cdata_nodefault_handler") == 0);
+      assert_true(c_entry->arg > 0);
+      assert_true(c_entry->arg <= cdata_len_remaining);
+      cdata_len_remaining -= c_entry->arg;
+    }
+    assert_record_handler_called(&storage, i++, "record_default_handler", 6);
+    assert_true(storage.count == i);
+  }
+
+  /* Now with an internal entity to complicate matters */
+  set_subtest("with internal entity");
+  {
+    struct handler_record_list storage;
+    storage.count = 0;
+    XML_ParserReset(g_parser, NULL);
+    XML_SetDefaultHandler(g_parser, record_default_handler);
+    XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
+    XML_SetUserData(g_parser, &storage);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
+                                XML_TRUE)
+        == XML_STATUS_ERROR)
+      xml_failure(g_parser);
+    /* The default handler suppresses the entity */
+    assert_record_handler_called(&storage, 0, "record_default_handler", 9);
+    assert_record_handler_called(&storage, 1, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 2, "record_default_handler", 3);
+    assert_record_handler_called(&storage, 3, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 4, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 5, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 6, "record_default_handler", 8);
+    assert_record_handler_called(&storage, 7, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 8, "record_default_handler", 6);
+    assert_record_handler_called(&storage, 9, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 10, "record_default_handler", 7);
+    assert_record_handler_called(&storage, 11, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 12, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 13, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 14, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 15, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 16, "record_default_handler", 5);
+    assert_record_handler_called(&storage, 17, "record_default_handler", 8);
+    assert_record_handler_called(&storage, 18, "record_default_handler", 6);
+    assert_true(storage.count == 19);
+  }
+
+  /* Again, with a skip handler */
+  set_subtest("with skip handler");
+  {
+    struct handler_record_list storage;
+    storage.count = 0;
+    XML_ParserReset(g_parser, NULL);
+    XML_SetDefaultHandler(g_parser, record_default_handler);
+    XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
+    XML_SetSkippedEntityHandler(g_parser, record_skip_handler);
+    XML_SetUserData(g_parser, &storage);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
+                                XML_TRUE)
+        == XML_STATUS_ERROR)
+      xml_failure(g_parser);
+    /* The default handler suppresses the entity */
+    assert_record_handler_called(&storage, 0, "record_default_handler", 9);
+    assert_record_handler_called(&storage, 1, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 2, "record_default_handler", 3);
+    assert_record_handler_called(&storage, 3, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 4, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 5, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 6, "record_default_handler", 8);
+    assert_record_handler_called(&storage, 7, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 8, "record_default_handler", 6);
+    assert_record_handler_called(&storage, 9, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 10, "record_default_handler", 7);
+    assert_record_handler_called(&storage, 11, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 12, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 13, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 14, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 15, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 16, "record_default_handler", 5);
+    assert_record_handler_called(&storage, 17, "record_skip_handler", 0);
+    assert_record_handler_called(&storage, 18, "record_default_handler", 6);
+    assert_true(storage.count == 19);
+  }
+
+  /* This time, allow the entity through */
+  set_subtest("allow entity");
+  {
+    struct handler_record_list storage;
+    storage.count = 0;
+    XML_ParserReset(g_parser, NULL);
+    XML_SetDefaultHandlerExpand(g_parser, record_default_handler);
+    XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
+    XML_SetUserData(g_parser, &storage);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
+                                XML_TRUE)
+        == XML_STATUS_ERROR)
+      xml_failure(g_parser);
+    assert_record_handler_called(&storage, 0, "record_default_handler", 9);
+    assert_record_handler_called(&storage, 1, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 2, "record_default_handler", 3);
+    assert_record_handler_called(&storage, 3, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 4, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 5, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 6, "record_default_handler", 8);
+    assert_record_handler_called(&storage, 7, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 8, "record_default_handler", 6);
+    assert_record_handler_called(&storage, 9, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 10, "record_default_handler", 7);
+    assert_record_handler_called(&storage, 11, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 12, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 13, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 14, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 15, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 16, "record_default_handler", 5);
+    assert_record_handler_called(&storage, 17, "record_cdata_handler", 1);
+    assert_record_handler_called(&storage, 18, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 19, "record_default_handler", 6);
+    assert_true(storage.count == 20);
+  }
+
+  /* Finally, without passing the cdata to the default handler */
+  set_subtest("not passing cdata");
+  {
+    struct handler_record_list storage;
+    storage.count = 0;
+    XML_ParserReset(g_parser, NULL);
+    XML_SetDefaultHandlerExpand(g_parser, record_default_handler);
+    XML_SetCharacterDataHandler(g_parser, record_cdata_nodefault_handler);
+    XML_SetUserData(g_parser, &storage);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
+                                XML_TRUE)
+        == XML_STATUS_ERROR)
+      xml_failure(g_parser);
+    assert_record_handler_called(&storage, 0, "record_default_handler", 9);
+    assert_record_handler_called(&storage, 1, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 2, "record_default_handler", 3);
+    assert_record_handler_called(&storage, 3, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 4, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 5, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 6, "record_default_handler", 8);
+    assert_record_handler_called(&storage, 7, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 8, "record_default_handler", 6);
+    assert_record_handler_called(&storage, 9, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 10, "record_default_handler", 7);
+    assert_record_handler_called(&storage, 11, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 12, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 13, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 14, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 15, "record_default_handler", 1);
+    assert_record_handler_called(&storage, 16, "record_default_handler", 5);
+    assert_record_handler_called(&storage, 17, "record_cdata_nodefault_handler",
+                                 1);
+    assert_record_handler_called(&storage, 18, "record_default_handler", 6);
+    assert_true(storage.count == 19);
+  }
+}
+END_TEST
+
+/* Test DTD element parsing code paths */
+START_TEST(test_dtd_elements) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "<!ELEMENT doc (chapter)>\n"
+                     "<!ELEMENT chapter (#PCDATA)>\n"
+                     "]>\n"
+                     "<doc><chapter>Wombats are go</chapter></doc>";
+
+  XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+static void XMLCALL
+element_decl_check_model(void *userData, const XML_Char *name,
+                         XML_Content *model) {
+  UNUSED_P(userData);
+  uint32_t errorFlags = 0;
+
+  /* Expected model array structure is this:
+   * [0] (type 6, quant 0)
+   *   [1] (type 5, quant 0)
+   *     [3] (type 4, quant 0, name "bar")
+   *     [4] (type 4, quant 0, name "foo")
+   *     [5] (type 4, quant 3, name "xyz")
+   *   [2] (type 4, quant 2, name "zebra")
+   */
+  errorFlags |= ((xcstrcmp(name, XCS("junk")) == 0) ? 0 : (1u << 0));
+  errorFlags |= ((model != NULL) ? 0 : (1u << 1));
+
+  if (model != NULL) {
+    errorFlags |= ((model[0].type == XML_CTYPE_SEQ) ? 0 : (1u << 2));
+    errorFlags |= ((model[0].quant == XML_CQUANT_NONE) ? 0 : (1u << 3));
+    errorFlags |= ((model[0].numchildren == 2) ? 0 : (1u << 4));
+    errorFlags |= ((model[0].children == &model[1]) ? 0 : (1u << 5));
+    errorFlags |= ((model[0].name == NULL) ? 0 : (1u << 6));
+
+    errorFlags |= ((model[1].type == XML_CTYPE_CHOICE) ? 0 : (1u << 7));
+    errorFlags |= ((model[1].quant == XML_CQUANT_NONE) ? 0 : (1u << 8));
+    errorFlags |= ((model[1].numchildren == 3) ? 0 : (1u << 9));
+    errorFlags |= ((model[1].children == &model[3]) ? 0 : (1u << 10));
+    errorFlags |= ((model[1].name == NULL) ? 0 : (1u << 11));
+
+    errorFlags |= ((model[2].type == XML_CTYPE_NAME) ? 0 : (1u << 12));
+    errorFlags |= ((model[2].quant == XML_CQUANT_REP) ? 0 : (1u << 13));
+    errorFlags |= ((model[2].numchildren == 0) ? 0 : (1u << 14));
+    errorFlags |= ((model[2].children == NULL) ? 0 : (1u << 15));
+    errorFlags
+        |= ((xcstrcmp(model[2].name, XCS("zebra")) == 0) ? 0 : (1u << 16));
+
+    errorFlags |= ((model[3].type == XML_CTYPE_NAME) ? 0 : (1u << 17));
+    errorFlags |= ((model[3].quant == XML_CQUANT_NONE) ? 0 : (1u << 18));
+    errorFlags |= ((model[3].numchildren == 0) ? 0 : (1u << 19));
+    errorFlags |= ((model[3].children == NULL) ? 0 : (1u << 20));
+    errorFlags |= ((xcstrcmp(model[3].name, XCS("bar")) == 0) ? 0 : (1u << 21));
+
+    errorFlags |= ((model[4].type == XML_CTYPE_NAME) ? 0 : (1u << 22));
+    errorFlags |= ((model[4].quant == XML_CQUANT_NONE) ? 0 : (1u << 23));
+    errorFlags |= ((model[4].numchildren == 0) ? 0 : (1u << 24));
+    errorFlags |= ((model[4].children == NULL) ? 0 : (1u << 25));
+    errorFlags |= ((xcstrcmp(model[4].name, XCS("foo")) == 0) ? 0 : (1u << 26));
+
+    errorFlags |= ((model[5].type == XML_CTYPE_NAME) ? 0 : (1u << 27));
+    errorFlags |= ((model[5].quant == XML_CQUANT_PLUS) ? 0 : (1u << 28));
+    errorFlags |= ((model[5].numchildren == 0) ? 0 : (1u << 29));
+    errorFlags |= ((model[5].children == NULL) ? 0 : (1u << 30));
+    errorFlags |= ((xcstrcmp(model[5].name, XCS("xyz")) == 0) ? 0 : (1u << 31));
+  }
+
+  XML_SetUserData(g_parser, (void *)(uintptr_t)errorFlags);
+  XML_FreeContentModel(g_parser, model);
+}
+
+START_TEST(test_dtd_elements_nesting) {
+  // Payload inspired by a test in Perl's XML::Parser
+  const char *text = "<!DOCTYPE foo [\n"
+                     "<!ELEMENT junk ((bar|foo|xyz+), zebra*)>\n"
+                     "]>\n"
+                     "<foo/>";
+
+  XML_SetUserData(g_parser, (void *)(uintptr_t)-1);
+
+  XML_SetElementDeclHandler(g_parser, element_decl_check_model);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+
+  if ((uint32_t)(uintptr_t)XML_GetUserData(g_parser) != 0)
+    fail("Element declaration model regression detected");
+}
+END_TEST
+
+/* Test foreign DTD handling */
+START_TEST(test_set_foreign_dtd) {
+  const char *text1 = "<?xml version='1.0' encoding='us-ascii'?>\n";
+  const char *text2 = "<doc>&entity;</doc>";
+  ExtTest test_data = {"<!ELEMENT doc (#PCDATA)*>", NULL, NULL};
+
+  /* Check hash salt is passed through too */
+  XML_SetHashSalt(g_parser, 0x12345678);
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetUserData(g_parser, &test_data);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
+  /* Add a default handler to exercise more code paths */
+  XML_SetDefaultHandler(g_parser, dummy_default_handler);
+  if (XML_UseForeignDTD(g_parser, XML_TRUE) != XML_ERROR_NONE)
+    fail("Could not set foreign DTD");
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text1, (int)strlen(text1), XML_FALSE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+
+  /* Ensure that trying to set the DTD after parsing has started
+   * is faulted, even if it's the same setting.
+   */
+  if (XML_UseForeignDTD(g_parser, XML_TRUE)
+      != XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING)
+    fail("Failed to reject late foreign DTD setting");
+  /* Ditto for the hash salt */
+  if (XML_SetHashSalt(g_parser, 0x23456789))
+    fail("Failed to reject late hash salt change");
+
+  /* Now finish the parse */
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text2, (int)strlen(text2), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test foreign DTD handling with a failing NotStandalone handler */
+START_TEST(test_foreign_dtd_not_standalone) {
+  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
+                     "<doc>&entity;</doc>";
+  ExtTest test_data = {"<!ELEMENT doc (#PCDATA)*>", NULL, NULL};
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetUserData(g_parser, &test_data);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
+  XML_SetNotStandaloneHandler(g_parser, reject_not_standalone_handler);
+  if (XML_UseForeignDTD(g_parser, XML_TRUE) != XML_ERROR_NONE)
+    fail("Could not set foreign DTD");
+  expect_failure(text, XML_ERROR_NOT_STANDALONE,
+                 "NotStandalonehandler failed to reject");
+}
+END_TEST
+
+/* Test invalid character in a foreign DTD is faulted */
+START_TEST(test_invalid_foreign_dtd) {
+  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
+                     "<doc>&entity;</doc>";
+  ExtFaults test_data
+      = {"$", "Dollar not faulted", NULL, XML_ERROR_INVALID_TOKEN};
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetUserData(g_parser, &test_data);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_faulter);
+  XML_UseForeignDTD(g_parser, XML_TRUE);
+  expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
+                 "Bad DTD should not have been accepted");
+}
+END_TEST
+
+/* Test foreign DTD use with a doctype */
+START_TEST(test_foreign_dtd_with_doctype) {
+  const char *text1 = "<?xml version='1.0' encoding='us-ascii'?>\n"
+                      "<!DOCTYPE doc [<!ENTITY entity 'hello world'>]>\n";
+  const char *text2 = "<doc>&entity;</doc>";
+  ExtTest test_data = {"<!ELEMENT doc (#PCDATA)*>", NULL, NULL};
+
+  /* Check hash salt is passed through too */
+  XML_SetHashSalt(g_parser, 0x12345678);
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetUserData(g_parser, &test_data);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
+  /* Add a default handler to exercise more code paths */
+  XML_SetDefaultHandler(g_parser, dummy_default_handler);
+  if (XML_UseForeignDTD(g_parser, XML_TRUE) != XML_ERROR_NONE)
+    fail("Could not set foreign DTD");
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text1, (int)strlen(text1), XML_FALSE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+
+  /* Ensure that trying to set the DTD after parsing has started
+   * is faulted, even if it's the same setting.
+   */
+  if (XML_UseForeignDTD(g_parser, XML_TRUE)
+      != XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING)
+    fail("Failed to reject late foreign DTD setting");
+  /* Ditto for the hash salt */
+  if (XML_SetHashSalt(g_parser, 0x23456789))
+    fail("Failed to reject late hash salt change");
+
+  /* Now finish the parse */
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text2, (int)strlen(text2), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test XML_UseForeignDTD with no external subset present */
+START_TEST(test_foreign_dtd_without_external_subset) {
+  const char *text = "<!DOCTYPE doc [<!ENTITY foo 'bar'>]>\n"
+                     "<doc>&foo;</doc>";
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetUserData(g_parser, NULL);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_null_loader);
+  XML_UseForeignDTD(g_parser, XML_TRUE);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+START_TEST(test_empty_foreign_dtd) {
+  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
+                     "<doc>&entity;</doc>";
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_null_loader);
+  XML_UseForeignDTD(g_parser, XML_TRUE);
+  expect_failure(text, XML_ERROR_UNDEFINED_ENTITY,
+                 "Undefined entity not faulted");
+}
+END_TEST
+
+/* Test XML Base is set and unset appropriately */
+START_TEST(test_set_base) {
+  const XML_Char *old_base;
+  const XML_Char *new_base = XCS("/local/file/name.xml");
+
+  old_base = XML_GetBase(g_parser);
+  if (XML_SetBase(g_parser, new_base) != XML_STATUS_OK)
+    fail("Unable to set base");
+  if (xcstrcmp(XML_GetBase(g_parser), new_base) != 0)
+    fail("Base setting not correct");
+  if (XML_SetBase(g_parser, NULL) != XML_STATUS_OK)
+    fail("Unable to NULL base");
+  if (XML_GetBase(g_parser) != NULL)
+    fail("Base setting not nulled");
+  XML_SetBase(g_parser, old_base);
+}
+END_TEST
+
+/* Test attribute counts, indexing, etc */
+START_TEST(test_attributes) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "<!ELEMENT doc (tag)>\n"
+                     "<!ATTLIST doc id ID #REQUIRED>\n"
+                     "]>"
+                     "<doc a='1' id='one' b='2'>"
+                     "<tag c='3'/>"
+                     "</doc>";
+  AttrInfo doc_info[] = {{XCS("a"), XCS("1")},
+                         {XCS("b"), XCS("2")},
+                         {XCS("id"), XCS("one")},
+                         {NULL, NULL}};
+  AttrInfo tag_info[] = {{XCS("c"), XCS("3")}, {NULL, NULL}};
+  ElementInfo info[] = {{XCS("doc"), 3, XCS("id"), NULL},
+                        {XCS("tag"), 1, NULL, NULL},
+                        {NULL, 0, NULL, NULL}};
+  info[0].attributes = doc_info;
+  info[1].attributes = tag_info;
+
+  XML_SetStartElementHandler(g_parser, counting_start_element_handler);
+  XML_SetUserData(g_parser, info);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test reset works correctly in the middle of processing an internal
+ * entity.  Exercises some obscure code in XML_ParserReset().
+ */
+START_TEST(test_reset_in_entity) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "<!ENTITY wombat 'wom'>\n"
+                     "<!ENTITY entity 'hi &wom; there'>\n"
+                     "]>\n"
+                     "<doc>&entity;</doc>";
+  XML_ParsingStatus status;
+
+  g_resumable = XML_TRUE;
+  XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  XML_GetParsingStatus(g_parser, &status);
+  if (status.parsing != XML_SUSPENDED)
+    fail("Parsing status not SUSPENDED");
+  XML_ParserReset(g_parser, NULL);
+  XML_GetParsingStatus(g_parser, &status);
+  if (status.parsing != XML_INITIALIZED)
+    fail("Parsing status doesn't reset to INITIALIZED");
+}
+END_TEST
+
+/* Test that resume correctly passes through parse errors */
+START_TEST(test_resume_invalid_parse) {
+  const char *text = "<doc>Hello</doc"; /* Missing closing wedge */
+
+  g_resumable = XML_TRUE;
+  XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler);
+  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  if (XML_ResumeParser(g_parser) == XML_STATUS_OK)
+    fail("Resumed invalid parse not faulted");
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_UNCLOSED_TOKEN)
+    fail("Invalid parse not correctly faulted");
+}
+END_TEST
+
+/* Test that re-suspended parses are correctly passed through */
+START_TEST(test_resume_resuspended) {
+  const char *text = "<doc>Hello<meep/>world</doc>";
+
+  g_resumable = XML_TRUE;
+  XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler);
+  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  g_resumable = XML_TRUE;
+  XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler);
+  if (XML_ResumeParser(g_parser) != XML_STATUS_SUSPENDED)
+    fail("Resumption not suspended");
+  /* This one should succeed and finish up */
+  if (XML_ResumeParser(g_parser) != XML_STATUS_OK)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test that CDATA shows up correctly through a default handler */
+START_TEST(test_cdata_default) {
+  const char *text = "<doc><![CDATA[Hello\nworld]]></doc>";
+  const XML_Char *expected = XCS("<doc><![CDATA[Hello\nworld]]></doc>");
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetUserData(g_parser, &storage);
+  XML_SetDefaultHandler(g_parser, accumulate_characters);
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+/* Test resetting a subordinate parser does exactly nothing */
+START_TEST(test_subordinate_reset) {
+  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
+                     "<!DOCTYPE doc SYSTEM 'foo'>\n"
+                     "<doc>&entity;</doc>";
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_resetter);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test suspending a subordinate parser */
+START_TEST(test_subordinate_suspend) {
+  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
+                     "<!DOCTYPE doc SYSTEM 'foo'>\n"
+                     "<doc>&entity;</doc>";
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_suspender);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test suspending a subordinate parser from an XML declaration */
+/* Increases code coverage of the tests */
+
+START_TEST(test_subordinate_xdecl_suspend) {
+  const char *text
+      = "<!DOCTYPE doc [\n"
+        "  <!ENTITY entity SYSTEM 'http://example.org/dummy.ent'>\n"
+        "]>\n"
+        "<doc>&entity;</doc>";
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_suspend_xmldecl);
+  g_resumable = XML_TRUE;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+START_TEST(test_subordinate_xdecl_abort) {
+  const char *text
+      = "<!DOCTYPE doc [\n"
+        "  <!ENTITY entity SYSTEM 'http://example.org/dummy.ent'>\n"
+        "]>\n"
+        "<doc>&entity;</doc>";
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_suspend_xmldecl);
+  g_resumable = XML_FALSE;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test external entity fault handling with suspension */
+START_TEST(test_ext_entity_invalid_suspended_parse) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
+                     "]>\n"
+                     "<doc>&en;</doc>";
+  ExtFaults faults[]
+      = {{"<?xml version='1.0' encoding='us-ascii'?><",
+          "Incomplete element declaration not faulted", NULL,
+          XML_ERROR_UNCLOSED_TOKEN},
+         {/* First two bytes of a three-byte char */
+          "<?xml version='1.0' encoding='utf-8'?>\xe2\x82",
+          "Incomplete character not faulted", NULL, XML_ERROR_PARTIAL_CHAR},
+         {NULL, NULL, NULL, XML_ERROR_NONE}};
+  ExtFaults *fault;
+
+  for (fault = &faults[0]; fault->parse_text != NULL; fault++) {
+    set_subtest("%s", fault->parse_text);
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetExternalEntityRefHandler(g_parser,
+                                    external_entity_suspending_faulter);
+    XML_SetUserData(g_parser, fault);
+    expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
+                   "Parser did not report external entity error");
+    XML_ParserReset(g_parser, NULL);
+  }
+}
+END_TEST
+
+/* Test setting an explicit encoding */
+START_TEST(test_explicit_encoding) {
+  const char *text1 = "<doc>Hello ";
+  const char *text2 = " World</doc>";
+
+  /* Just check that we can set the encoding to NULL before starting */
+  if (XML_SetEncoding(g_parser, NULL) != XML_STATUS_OK)
+    fail("Failed to initialise encoding to NULL");
+  /* Say we are UTF-8 */
+  if (XML_SetEncoding(g_parser, XCS("utf-8")) != XML_STATUS_OK)
+    fail("Failed to set explicit encoding");
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text1, (int)strlen(text1), XML_FALSE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  /* Try to switch encodings mid-parse */
+  if (XML_SetEncoding(g_parser, XCS("us-ascii")) != XML_STATUS_ERROR)
+    fail("Allowed encoding change");
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text2, (int)strlen(text2), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  /* Try now the parse is over */
+  if (XML_SetEncoding(g_parser, NULL) != XML_STATUS_OK)
+    fail("Failed to unset encoding");
+}
+END_TEST
+
+/* Test handling of trailing CR (rather than newline) */
+START_TEST(test_trailing_cr) {
+  const char *text = "<doc>\r";
+  int found_cr;
+
+  /* Try with a character handler, for code coverage */
+  XML_SetCharacterDataHandler(g_parser, cr_cdata_handler);
+  XML_SetUserData(g_parser, &found_cr);
+  found_cr = 0;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_OK)
+    fail("Failed to fault unclosed doc");
+  if (found_cr == 0)
+    fail("Did not catch the carriage return");
+  XML_ParserReset(g_parser, NULL);
+
+  /* Now with a default handler instead */
+  XML_SetDefaultHandler(g_parser, cr_cdata_handler);
+  XML_SetUserData(g_parser, &found_cr);
+  found_cr = 0;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_OK)
+    fail("Failed to fault unclosed doc");
+  if (found_cr == 0)
+    fail("Did not catch default carriage return");
+}
+END_TEST
+
+/* Test trailing CR in an external entity parse */
+START_TEST(test_ext_entity_trailing_cr) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
+                     "]>\n"
+                     "<doc>&en;</doc>";
+  int found_cr;
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_cr_catcher);
+  XML_SetUserData(g_parser, &found_cr);
+  found_cr = 0;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_OK)
+    xml_failure(g_parser);
+  if (found_cr == 0)
+    fail("No carriage return found");
+  XML_ParserReset(g_parser, NULL);
+
+  /* Try again with a different trailing CR */
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_bad_cr_catcher);
+  XML_SetUserData(g_parser, &found_cr);
+  found_cr = 0;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_OK)
+    xml_failure(g_parser);
+  if (found_cr == 0)
+    fail("No carriage return found");
+}
+END_TEST
+
+/* Test handling of trailing square bracket */
+START_TEST(test_trailing_rsqb) {
+  const char *text8 = "<doc>]";
+  const char text16[] = "\xFF\xFE<\000d\000o\000c\000>\000]\000";
+  int found_rsqb;
+  int text8_len = (int)strlen(text8);
+
+  XML_SetCharacterDataHandler(g_parser, rsqb_handler);
+  XML_SetUserData(g_parser, &found_rsqb);
+  found_rsqb = 0;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text8, text8_len, XML_TRUE)
+      == XML_STATUS_OK)
+    fail("Failed to fault unclosed doc");
+  if (found_rsqb == 0)
+    fail("Did not catch the right square bracket");
+
+  /* Try again with a different encoding */
+  XML_ParserReset(g_parser, NULL);
+  XML_SetCharacterDataHandler(g_parser, rsqb_handler);
+  XML_SetUserData(g_parser, &found_rsqb);
+  found_rsqb = 0;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text16, (int)sizeof(text16) - 1,
+                              XML_TRUE)
+      == XML_STATUS_OK)
+    fail("Failed to fault unclosed doc");
+  if (found_rsqb == 0)
+    fail("Did not catch the right square bracket");
+
+  /* And finally with a default handler */
+  XML_ParserReset(g_parser, NULL);
+  XML_SetDefaultHandler(g_parser, rsqb_handler);
+  XML_SetUserData(g_parser, &found_rsqb);
+  found_rsqb = 0;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text16, (int)sizeof(text16) - 1,
+                              XML_TRUE)
+      == XML_STATUS_OK)
+    fail("Failed to fault unclosed doc");
+  if (found_rsqb == 0)
+    fail("Did not catch the right square bracket");
+}
+END_TEST
+
+/* Test trailing right square bracket in an external entity parse */
+START_TEST(test_ext_entity_trailing_rsqb) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
+                     "]>\n"
+                     "<doc>&en;</doc>";
+  int found_rsqb;
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_rsqb_catcher);
+  XML_SetUserData(g_parser, &found_rsqb);
+  found_rsqb = 0;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_OK)
+    xml_failure(g_parser);
+  if (found_rsqb == 0)
+    fail("No right square bracket found");
+}
+END_TEST
+
+/* Test CDATA handling in an external entity */
+START_TEST(test_ext_entity_good_cdata) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
+                     "]>\n"
+                     "<doc>&en;</doc>";
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_good_cdata_ascii);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_OK)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test user parameter settings */
+START_TEST(test_user_parameters) {
+  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
+                     "<!-- Primary parse -->\n"
+                     "<!DOCTYPE doc SYSTEM 'foo'>\n"
+                     "<doc>&entity;";
+  const char *epilog = "<!-- Back to primary parser -->\n"
+                       "</doc>";
+
+  g_comment_count = 0;
+  g_skip_count = 0;
+  g_xdecl_count = 0;
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetXmlDeclHandler(g_parser, xml_decl_handler);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_param_checker);
+  XML_SetCommentHandler(g_parser, data_check_comment_handler);
+  XML_SetSkippedEntityHandler(g_parser, param_check_skip_handler);
+  XML_UseParserAsHandlerArg(g_parser);
+  XML_SetUserData(g_parser, (void *)1);
+  g_handler_data = g_parser;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  /* Ensure we can't change policy mid-parse */
+  if (XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_NEVER))
+    fail("Changed param entity parsing policy while parsing");
+  if (_XML_Parse_SINGLE_BYTES(g_parser, epilog, (int)strlen(epilog), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  if (g_comment_count != 3)
+    fail("Comment handler not invoked enough times");
+  if (g_skip_count != 1)
+    fail("Skip handler not invoked enough times");
+  if (g_xdecl_count != 1)
+    fail("XML declaration handler not invoked");
+}
+END_TEST
+
+/* Test that an explicit external entity handler argument replaces
+ * the parser as the first argument.
+ *
+ * We do not call the first parameter to the external entity handler
+ * 'parser' for once, since the first time the handler is called it
+ * will actually be a text string.  We need to be able to access the
+ * global 'parser' variable to create our external entity parser from,
+ * since there are code paths we need to ensure get executed.
+ */
+START_TEST(test_ext_entity_ref_parameter) {
+  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
+                     "<!DOCTYPE doc SYSTEM 'foo'>\n"
+                     "<doc>&entity;</doc>";
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_ref_param_checker);
+  /* Set a handler arg that is not NULL and not parser (which is
+   * what NULL would cause to be passed.
+   */
+  XML_SetExternalEntityRefHandlerArg(g_parser, (void *)text);
+  g_handler_data = text;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+
+  /* Now try again with unset args */
+  XML_ParserReset(g_parser, NULL);
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_ref_param_checker);
+  XML_SetExternalEntityRefHandlerArg(g_parser, NULL);
+  g_handler_data = g_parser;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test the parsing of an empty string */
+START_TEST(test_empty_parse) {
+  const char *text = "<doc></doc>";
+  const char *partial = "<doc>";
+
+  if (XML_Parse(g_parser, NULL, 0, XML_FALSE) == XML_STATUS_ERROR)
+    fail("Parsing empty string faulted");
+  if (XML_Parse(g_parser, NULL, 0, XML_TRUE) != XML_STATUS_ERROR)
+    fail("Parsing final empty string not faulted");
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_NO_ELEMENTS)
+    fail("Parsing final empty string faulted for wrong reason");
+
+  /* Now try with valid text before the empty end */
+  XML_ParserReset(g_parser, NULL);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  if (XML_Parse(g_parser, NULL, 0, XML_TRUE) == XML_STATUS_ERROR)
+    fail("Parsing final empty string faulted");
+
+  /* Now try with invalid text before the empty end */
+  XML_ParserReset(g_parser, NULL);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, partial, (int)strlen(partial),
+                              XML_FALSE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  if (XML_Parse(g_parser, NULL, 0, XML_TRUE) != XML_STATUS_ERROR)
+    fail("Parsing final incomplete empty string not faulted");
+}
+END_TEST
+
+/* Test odd corners of the XML_GetBuffer interface */
+static enum XML_Status
+get_feature(enum XML_FeatureEnum feature_id, long *presult) {
+  const XML_Feature *feature = XML_GetFeatureList();
+
+  if (feature == NULL)
+    return XML_STATUS_ERROR;
+  for (; feature->feature != XML_FEATURE_END; feature++) {
+    if (feature->feature == feature_id) {
+      *presult = feature->value;
+      return XML_STATUS_OK;
+    }
+  }
+  return XML_STATUS_ERROR;
+}
+
+/* Test odd corners of the XML_GetBuffer interface */
+START_TEST(test_get_buffer_1) {
+  const char *text = get_buffer_test_text;
+  void *buffer;
+  long context_bytes;
+
+  /* Attempt to allocate a negative length buffer */
+  if (XML_GetBuffer(g_parser, -12) != NULL)
+    fail("Negative length buffer not failed");
+
+  /* Now get a small buffer and extend it past valid length */
+  buffer = XML_GetBuffer(g_parser, 1536);
+  if (buffer == NULL)
+    fail("1.5K buffer failed");
+  assert(buffer != NULL);
+  memcpy(buffer, text, strlen(text));
+  if (XML_ParseBuffer(g_parser, (int)strlen(text), XML_FALSE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  if (XML_GetBuffer(g_parser, INT_MAX) != NULL)
+    fail("INT_MAX buffer not failed");
+
+  /* Now try extending it a more reasonable but still too large
+   * amount.  The allocator in XML_GetBuffer() doubles the buffer
+   * size until it exceeds the requested amount or INT_MAX.  If it
+   * exceeds INT_MAX, it rejects the request, so we want a request
+   * between INT_MAX and INT_MAX/2.  A gap of 1K seems comfortable,
+   * with an extra byte just to ensure that the request is off any
+   * boundary.  The request will be inflated internally by
+   * XML_CONTEXT_BYTES (if >=1), so we subtract that from our
+   * request.
+   */
+  if (get_feature(XML_FEATURE_CONTEXT_BYTES, &context_bytes) != XML_STATUS_OK)
+    context_bytes = 0;
+  if (XML_GetBuffer(g_parser, INT_MAX - (context_bytes + 1025)) != NULL)
+    fail("INT_MAX- buffer not failed");
+
+  /* Now try extending it a carefully crafted amount */
+  if (XML_GetBuffer(g_parser, 1000) == NULL)
+    fail("1000 buffer failed");
+}
+END_TEST
+
+/* Test more corners of the XML_GetBuffer interface */
+START_TEST(test_get_buffer_2) {
+  const char *text = get_buffer_test_text;
+  void *buffer;
+
+  /* Now get a decent buffer */
+  buffer = XML_GetBuffer(g_parser, 1536);
+  if (buffer == NULL)
+    fail("1.5K buffer failed");
+  assert(buffer != NULL);
+  memcpy(buffer, text, strlen(text));
+  if (XML_ParseBuffer(g_parser, (int)strlen(text), XML_FALSE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+
+  /* Extend it, to catch a different code path */
+  if (XML_GetBuffer(g_parser, 1024) == NULL)
+    fail("1024 buffer failed");
+}
+END_TEST
+
+/* Test for signed integer overflow CVE-2022-23852 */
+#if XML_CONTEXT_BYTES > 0
+START_TEST(test_get_buffer_3_overflow) {
+  XML_Parser parser = XML_ParserCreate(NULL);
+  assert(parser != NULL);
+
+  const char *const text = "\n";
+  const int expectedKeepValue = (int)strlen(text);
+
+  // After this call, variable "keep" in XML_GetBuffer will
+  // have value expectedKeepValue
+  if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text),
+                              XML_FALSE /* isFinal */)
+      == XML_STATUS_ERROR)
+    xml_failure(parser);
+
+  assert(expectedKeepValue > 0);
+  if (XML_GetBuffer(parser, INT_MAX - expectedKeepValue + 1) != NULL)
+    fail("enlarging buffer not failed");
+
+  XML_ParserFree(parser);
+}
+END_TEST
+#endif // XML_CONTEXT_BYTES > 0
+
+START_TEST(test_buffer_can_grow_to_max) {
+  const char *const prefixes[] = {
+      "",
+      "<",
+      "<x a='",
+      "<doc><x a='",
+      "<document><x a='",
+      "<averylongelementnamesuchthatitwillhopefullystretchacrossmultiplelinesand"
+      "lookprettyridiculousitsalsoveryhardtoreadandifyouredoingitihavetowonderif"
+      "youreallydonthaveanythingbettertodoofcourseiguessicouldveputsomethingbadin"
+      "herebutipromisethatididntheybtwhowgreatarespacesandpunctuationforhelping"
+      "withreadabilityprettygreatithinkanywaysthisisprobablylongenoughbye><x a='"};
+  const int num_prefixes = sizeof(prefixes) / sizeof(prefixes[0]);
+  int maxbuf = INT_MAX / 2 + (INT_MAX & 1); // round up without overflow
+#if defined(__MINGW32__) && ! defined(__MINGW64__)
+  // workaround for mingw/wine32 on GitHub CI not being able to reach 1GiB
+  // Can we make a big allocation?
+  void *big = malloc(maxbuf);
+  if (! big) {
+    // The big allocation failed. Let's be a little lenient.
+    maxbuf = maxbuf / 2;
+  }
+  free(big);
+#endif
+
+  for (int i = 0; i < num_prefixes; ++i) {
+    set_subtest("\"%s\"", prefixes[i]);
+    XML_Parser parser = XML_ParserCreate(NULL);
+    const int prefix_len = (int)strlen(prefixes[i]);
+    const enum XML_Status s
+        = _XML_Parse_SINGLE_BYTES(parser, prefixes[i], prefix_len, XML_FALSE);
+    if (s != XML_STATUS_OK)
+      xml_failure(parser);
+
+    // XML_CONTEXT_BYTES of the prefix may remain in the buffer;
+    // subtracting the whole prefix is easiest, and close enough.
+    assert_true(XML_GetBuffer(parser, maxbuf - prefix_len) != NULL);
+    // The limit should be consistent; no prefix should allow us to
+    // reach above the max buffer size.
+    assert_true(XML_GetBuffer(parser, maxbuf + 1) == NULL);
+    XML_ParserFree(parser);
+  }
+}
+END_TEST
+
+START_TEST(test_getbuffer_allocates_on_zero_len) {
+  for (int first_len = 1; first_len >= 0; first_len--) {
+    set_subtest("with len=%d first", first_len);
+    XML_Parser parser = XML_ParserCreate(NULL);
+    assert_true(parser != NULL);
+    assert_true(XML_GetBuffer(parser, first_len) != NULL);
+    assert_true(XML_GetBuffer(parser, 0) != NULL);
+    if (XML_ParseBuffer(parser, 0, XML_FALSE) != XML_STATUS_OK)
+      xml_failure(parser);
+    XML_ParserFree(parser);
+  }
+}
+END_TEST
+
+/* Test position information macros */
+START_TEST(test_byte_info_at_end) {
+  const char *text = "<doc></doc>";
+
+  if (XML_GetCurrentByteIndex(g_parser) != -1
+      || XML_GetCurrentByteCount(g_parser) != 0)
+    fail("Byte index/count incorrect at start of parse");
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  /* At end, the count will be zero and the index the end of string */
+  if (XML_GetCurrentByteCount(g_parser) != 0)
+    fail("Terminal byte count incorrect");
+  if (XML_GetCurrentByteIndex(g_parser) != (XML_Index)strlen(text))
+    fail("Terminal byte index incorrect");
+}
+END_TEST
+
+/* Test position information from errors */
+#define PRE_ERROR_STR "<doc></"
+#define POST_ERROR_STR "wombat></doc>"
+START_TEST(test_byte_info_at_error) {
+  const char *text = PRE_ERROR_STR POST_ERROR_STR;
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_OK)
+    fail("Syntax error not faulted");
+  if (XML_GetCurrentByteCount(g_parser) != 0)
+    fail("Error byte count incorrect");
+  if (XML_GetCurrentByteIndex(g_parser) != strlen(PRE_ERROR_STR))
+    fail("Error byte index incorrect");
+}
+END_TEST
+#undef PRE_ERROR_STR
+#undef POST_ERROR_STR
+
+/* Test position information in handler */
+#define START_ELEMENT "<e>"
+#define CDATA_TEXT "Hello"
+#define END_ELEMENT "</e>"
+START_TEST(test_byte_info_at_cdata) {
+  const char *text = START_ELEMENT CDATA_TEXT END_ELEMENT;
+  int offset, size;
+  ByteTestData data;
+
+  /* Check initial context is empty */
+  if (XML_GetInputContext(g_parser, &offset, &size) != NULL)
+    fail("Unexpected context at start of parse");
+
+  data.start_element_len = (int)strlen(START_ELEMENT);
+  data.cdata_len = (int)strlen(CDATA_TEXT);
+  data.total_string_len = (int)strlen(text);
+  XML_SetCharacterDataHandler(g_parser, byte_character_handler);
+  XML_SetUserData(g_parser, &data);
+  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE) != XML_STATUS_OK)
+    xml_failure(g_parser);
+}
+END_TEST
+#undef START_ELEMENT
+#undef CDATA_TEXT
+#undef END_ELEMENT
+
+/* Test predefined entities are correctly recognised */
+START_TEST(test_predefined_entities) {
+  const char *text = "<doc>&lt;&gt;&amp;&quot;&apos;</doc>";
+  const XML_Char *expected = XCS("<doc>&lt;&gt;&amp;&quot;&apos;</doc>");
+  const XML_Char *result = XCS("<>&\"'");
+  CharData storage;
+
+  XML_SetDefaultHandler(g_parser, accumulate_characters);
+  /* run_character_check uses XML_SetCharacterDataHandler(), which
+   * unfortunately heads off a code path that we need to exercise.
+   */
+  CharData_Init(&storage);
+  XML_SetUserData(g_parser, &storage);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  /* The default handler doesn't translate the entities */
+  CharData_CheckXMLChars(&storage, expected);
+
+  /* Now try again and check the translation */
+  XML_ParserReset(g_parser, NULL);
+  run_character_check(text, result);
+}
+END_TEST
+
+/* Regression test that an invalid tag in an external parameter
+ * reference in an external DTD is correctly faulted.
+ *
+ * Only a few specific tags are legal in DTDs ignoring comments and
+ * processing instructions, all of which begin with an exclamation
+ * mark.  "<el/>" is not one of them, so the parser should raise an
+ * error on encountering it.
+ */
+START_TEST(test_invalid_tag_in_dtd) {
+  const char *text = "<!DOCTYPE doc SYSTEM '004-1.ent'>\n"
+                     "<doc></doc>\n";
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_param);
+  expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
+                 "Invalid tag IN DTD external param not rejected");
+}
+END_TEST
+
+/* Test entities not quite the predefined ones are not mis-recognised */
+START_TEST(test_not_predefined_entities) {
+  const char *text[] = {"<doc>&pt;</doc>", "<doc>&amo;</doc>",
+                        "<doc>&quid;</doc>", "<doc>&apod;</doc>", NULL};
+  int i = 0;
+
+  while (text[i] != NULL) {
+    expect_failure(text[i], XML_ERROR_UNDEFINED_ENTITY,
+                   "Undefined entity not rejected");
+    XML_ParserReset(g_parser, NULL);
+    i++;
+  }
+}
+END_TEST
+
+/* Test conditional inclusion (IGNORE) */
+START_TEST(test_ignore_section) {
+  const char *text = "<!DOCTYPE doc SYSTEM 'foo'>\n"
+                     "<doc><e>&entity;</e></doc>";
+  const XML_Char *expected
+      = XCS("<![IGNORE[<!ELEMENT e (#PCDATA)*>]]>\n&entity;");
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetUserData(g_parser, &storage);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_load_ignore);
+  XML_SetDefaultHandler(g_parser, accumulate_characters);
+  XML_SetStartDoctypeDeclHandler(g_parser, dummy_start_doctype_handler);
+  XML_SetEndDoctypeDeclHandler(g_parser, dummy_end_doctype_handler);
+  XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
+  XML_SetStartElementHandler(g_parser, dummy_start_element);
+  XML_SetEndElementHandler(g_parser, dummy_end_element);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_ignore_section_utf16) {
+  const char text[] =
+      /* <!DOCTYPE d SYSTEM 's'> */
+      "<\0!\0D\0O\0C\0T\0Y\0P\0E\0 \0d\0 "
+      "\0S\0Y\0S\0T\0E\0M\0 \0'\0s\0'\0>\0\n\0"
+      /* <d><e>&en;</e></d> */
+      "<\0d\0>\0<\0e\0>\0&\0e\0n\0;\0<\0/\0e\0>\0<\0/\0d\0>\0";
+  const XML_Char *expected = XCS("<![IGNORE[<!ELEMENT e (#PCDATA)*>]]>\n&en;");
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetUserData(g_parser, &storage);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_load_ignore_utf16);
+  XML_SetDefaultHandler(g_parser, accumulate_characters);
+  XML_SetStartDoctypeDeclHandler(g_parser, dummy_start_doctype_handler);
+  XML_SetEndDoctypeDeclHandler(g_parser, dummy_end_doctype_handler);
+  XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
+  XML_SetStartElementHandler(g_parser, dummy_start_element);
+  XML_SetEndElementHandler(g_parser, dummy_end_element);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_ignore_section_utf16_be) {
+  const char text[] =
+      /* <!DOCTYPE d SYSTEM 's'> */
+      "\0<\0!\0D\0O\0C\0T\0Y\0P\0E\0 \0d\0 "
+      "\0S\0Y\0S\0T\0E\0M\0 \0'\0s\0'\0>\0\n"
+      /* <d><e>&en;</e></d> */
+      "\0<\0d\0>\0<\0e\0>\0&\0e\0n\0;\0<\0/\0e\0>\0<\0/\0d\0>";
+  const XML_Char *expected = XCS("<![IGNORE[<!ELEMENT e (#PCDATA)*>]]>\n&en;");
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetUserData(g_parser, &storage);
+  XML_SetExternalEntityRefHandler(g_parser,
+                                  external_entity_load_ignore_utf16_be);
+  XML_SetDefaultHandler(g_parser, accumulate_characters);
+  XML_SetStartDoctypeDeclHandler(g_parser, dummy_start_doctype_handler);
+  XML_SetEndDoctypeDeclHandler(g_parser, dummy_end_doctype_handler);
+  XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
+  XML_SetStartElementHandler(g_parser, dummy_start_element);
+  XML_SetEndElementHandler(g_parser, dummy_end_element);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+/* Test mis-formatted conditional exclusion */
+START_TEST(test_bad_ignore_section) {
+  const char *text = "<!DOCTYPE doc SYSTEM 'foo'>\n"
+                     "<doc><e>&entity;</e></doc>";
+  ExtFaults faults[]
+      = {{"<![IGNORE[<!ELEM", "Broken-off declaration not faulted", NULL,
+          XML_ERROR_SYNTAX},
+         {"<![IGNORE[\x01]]>", "Invalid XML character not faulted", NULL,
+          XML_ERROR_INVALID_TOKEN},
+         {/* FIrst two bytes of a three-byte char */
+          "<![IGNORE[\xe2\x82", "Partial XML character not faulted", NULL,
+          XML_ERROR_PARTIAL_CHAR},
+         {NULL, NULL, NULL, XML_ERROR_NONE}};
+  ExtFaults *fault;
+
+  for (fault = &faults[0]; fault->parse_text != NULL; fault++) {
+    set_subtest("%s", fault->parse_text);
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_faulter);
+    XML_SetUserData(g_parser, fault);
+    expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
+                   "Incomplete IGNORE section not failed");
+    XML_ParserReset(g_parser, NULL);
+  }
+}
+END_TEST
+
+struct bom_testdata {
+  const char *external;
+  int split;
+  XML_Bool nested_callback_happened;
+};
+
+static int XMLCALL
+external_bom_checker(XML_Parser parser, const XML_Char *context,
+                     const XML_Char *base, const XML_Char *systemId,
+                     const XML_Char *publicId) {
+  const char *text;
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+
+  XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could not create external entity parser");
+
+  if (! xcstrcmp(systemId, XCS("004-2.ent"))) {
+    struct bom_testdata *const testdata
+        = (struct bom_testdata *)XML_GetUserData(parser);
+    const char *const external = testdata->external;
+    const int split = testdata->split;
+    testdata->nested_callback_happened = XML_TRUE;
+
+    if (_XML_Parse_SINGLE_BYTES(ext_parser, external, split, XML_FALSE)
+        != XML_STATUS_OK) {
+      xml_failure(ext_parser);
+    }
+    text = external + split; // the parse below will continue where we left off.
+  } else if (! xcstrcmp(systemId, XCS("004-1.ent"))) {
+    text = "<!ELEMENT doc EMPTY>\n"
+           "<!ENTITY % e1 SYSTEM '004-2.ent'>\n"
+           "<!ENTITY % e2 '%e1;'>\n";
+  } else {
+    fail("unknown systemId");
+  }
+
+  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_OK)
+    xml_failure(ext_parser);
+
+  XML_ParserFree(ext_parser);
+  return XML_STATUS_OK;
+}
+
+/* regression test: BOM should be consumed when followed by a partial token. */
+START_TEST(test_external_bom_consumed) {
+  const char *const text = "<!DOCTYPE doc SYSTEM '004-1.ent'>\n"
+                           "<doc></doc>\n";
+  const char *const external = "\xEF\xBB\xBF<!ATTLIST doc a1 CDATA 'value'>";
+  const int len = (int)strlen(external);
+  for (int split = 0; split <= len; ++split) {
+    set_subtest("split at byte %d", split);
+
+    struct bom_testdata testdata;
+    testdata.external = external;
+    testdata.split = split;
+    testdata.nested_callback_happened = XML_FALSE;
+
+    XML_Parser parser = XML_ParserCreate(NULL);
+    if (parser == NULL) {
+      fail("Couldn't create parser");
+    }
+    XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetExternalEntityRefHandler(parser, external_bom_checker);
+    XML_SetUserData(parser, &testdata);
+    if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
+        == XML_STATUS_ERROR)
+      xml_failure(parser);
+    if (! testdata.nested_callback_happened) {
+      fail("ref handler not called");
+    }
+    XML_ParserFree(parser);
+  }
+}
+END_TEST
+
+/* Test recursive parsing */
+START_TEST(test_external_entity_values) {
+  const char *text = "<!DOCTYPE doc SYSTEM '004-1.ent'>\n"
+                     "<doc></doc>\n";
+  ExtFaults data_004_2[] = {
+      {"<!ATTLIST doc a1 CDATA 'value'>", NULL, NULL, XML_ERROR_NONE},
+      {"<!ATTLIST $doc a1 CDATA 'value'>", "Invalid token not faulted", NULL,
+       XML_ERROR_INVALID_TOKEN},
+      {"'wombat", "Unterminated string not faulted", NULL,
+       XML_ERROR_UNCLOSED_TOKEN},
+      {"\xe2\x82", "Partial UTF-8 character not faulted", NULL,
+       XML_ERROR_PARTIAL_CHAR},
+      {"<?xml version='1.0' encoding='utf-8'?>\n", NULL, NULL, XML_ERROR_NONE},
+      {"<?xml?>", "Malformed XML declaration not faulted", NULL,
+       XML_ERROR_XML_DECL},
+      {/* UTF-8 BOM */
+       "\xEF\xBB\xBF<!ATTLIST doc a1 CDATA 'value'>", NULL, NULL,
+       XML_ERROR_NONE},
+      {"<?xml version='1.0' encoding='utf-8'?>\n$",
+       "Invalid token after text declaration not faulted", NULL,
+       XML_ERROR_INVALID_TOKEN},
+      {"<?xml version='1.0' encoding='utf-8'?>\n'wombat",
+       "Unterminated string after text decl not faulted", NULL,
+       XML_ERROR_UNCLOSED_TOKEN},
+      {"<?xml version='1.0' encoding='utf-8'?>\n\xe2\x82",
+       "Partial UTF-8 character after text decl not faulted", NULL,
+       XML_ERROR_PARTIAL_CHAR},
+      {"%e1;", "Recursive parameter entity not faulted", NULL,
+       XML_ERROR_RECURSIVE_ENTITY_REF},
+      {NULL, NULL, NULL, XML_ERROR_NONE}};
+  int i;
+
+  for (i = 0; data_004_2[i].parse_text != NULL; i++) {
+    set_subtest("%s", data_004_2[i].parse_text);
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_valuer);
+    XML_SetUserData(g_parser, &data_004_2[i]);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        == XML_STATUS_ERROR)
+      xml_failure(g_parser);
+    XML_ParserReset(g_parser, NULL);
+  }
+}
+END_TEST
+
+/* Test the recursive parse interacts with a not standalone handler */
+START_TEST(test_ext_entity_not_standalone) {
+  const char *text = "<!DOCTYPE doc SYSTEM 'foo'>\n"
+                     "<doc></doc>";
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_not_standalone);
+  expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
+                 "Standalone rejection not caught");
+}
+END_TEST
+
+START_TEST(test_ext_entity_value_abort) {
+  const char *text = "<!DOCTYPE doc SYSTEM '004-1.ent'>\n"
+                     "<doc></doc>\n";
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_value_aborter);
+  g_resumable = XML_FALSE;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+START_TEST(test_bad_public_doctype) {
+  const char *text = "<?xml version='1.0' encoding='utf-8'?>\n"
+                     "<!DOCTYPE doc PUBLIC '{BadName}' 'test'>\n"
+                     "<doc></doc>";
+
+  /* Setting a handler provokes a particular code path */
+  XML_SetDoctypeDeclHandler(g_parser, dummy_start_doctype_handler,
+                            dummy_end_doctype_handler);
+  expect_failure(text, XML_ERROR_PUBLICID, "Bad Public ID not failed");
+}
+END_TEST
+
+/* Test based on ibm/valid/P32/ibm32v04.xml */
+START_TEST(test_attribute_enum_value) {
+  const char *text = "<?xml version='1.0' standalone='no'?>\n"
+                     "<!DOCTYPE animal SYSTEM 'test.dtd'>\n"
+                     "<animal>This is a \n    <a/>  \n\nyellow tiger</animal>";
+  ExtTest dtd_data
+      = {"<!ELEMENT animal (#PCDATA|a)*>\n"
+         "<!ELEMENT a EMPTY>\n"
+         "<!ATTLIST animal xml:space (default|preserve) 'preserve'>",
+         NULL, NULL};
+  const XML_Char *expected = XCS("This is a \n      \n\nyellow tiger");
+
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
+  XML_SetUserData(g_parser, &dtd_data);
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  /* An attribute list handler provokes a different code path */
+  XML_SetAttlistDeclHandler(g_parser, dummy_attlist_decl_handler);
+  run_ext_character_check(text, &dtd_data, expected);
+}
+END_TEST
+
+/* Slightly bizarrely, the library seems to silently ignore entity
+ * definitions for predefined entities, even when they are wrong.  The
+ * language of the XML 1.0 spec is somewhat unhelpful as to what ought
+ * to happen, so this is currently treated as acceptable.
+ */
+START_TEST(test_predefined_entity_redefinition) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "<!ENTITY apos 'foo'>\n"
+                     "]>\n"
+                     "<doc>&apos;</doc>";
+  run_character_check(text, XCS("'"));
+}
+END_TEST
+
+/* Test that the parser stops processing the DTD after an unresolved
+ * parameter entity is encountered.
+ */
+START_TEST(test_dtd_stop_processing) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "%foo;\n"
+                     "<!ENTITY bar 'bas'>\n"
+                     "]><doc/>";
+
+  XML_SetEntityDeclHandler(g_parser, dummy_entity_decl_handler);
+  init_dummy_handlers();
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  if (get_dummy_handler_flags() != 0)
+    fail("DTD processing still going after undefined PE");
+}
+END_TEST
+
+/* Test public notations with no system ID */
+START_TEST(test_public_notation_no_sysid) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "<!NOTATION note PUBLIC 'foo'>\n"
+                     "<!ELEMENT doc EMPTY>\n"
+                     "]>\n<doc/>";
+
+  init_dummy_handlers();
+  XML_SetNotationDeclHandler(g_parser, dummy_notation_decl_handler);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  if (get_dummy_handler_flags() != DUMMY_NOTATION_DECL_HANDLER_FLAG)
+    fail("Notation declaration handler not called");
+}
+END_TEST
+
+START_TEST(test_nested_groups) {
+  const char *text
+      = "<!DOCTYPE doc [\n"
+        "<!ELEMENT doc "
+        /* Sixteen elements per line */
+        "(e,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,"
+        "(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?"
+        "))))))))))))))))))))))))))))))))>\n"
+        "<!ELEMENT e EMPTY>"
+        "]>\n"
+        "<doc><e/></doc>";
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
+  XML_SetStartElementHandler(g_parser, record_element_start_handler);
+  XML_SetUserData(g_parser, &storage);
+  init_dummy_handlers();
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, XCS("doce"));
+  if (get_dummy_handler_flags() != DUMMY_ELEMENT_DECL_HANDLER_FLAG)
+    fail("Element handler not fired");
+}
+END_TEST
+
+START_TEST(test_group_choice) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "<!ELEMENT doc (a|b|c)+>\n"
+                     "<!ELEMENT a EMPTY>\n"
+                     "<!ELEMENT b (#PCDATA)>\n"
+                     "<!ELEMENT c ANY>\n"
+                     "]>\n"
+                     "<doc>\n"
+                     "<a/>\n"
+                     "<b attr='foo'>This is a foo</b>\n"
+                     "<c></c>\n"
+                     "</doc>\n";
+
+  XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
+  init_dummy_handlers();
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  if (get_dummy_handler_flags() != DUMMY_ELEMENT_DECL_HANDLER_FLAG)
+    fail("Element handler flag not raised");
+}
+END_TEST
+
+START_TEST(test_standalone_parameter_entity) {
+  const char *text = "<?xml version='1.0' standalone='yes'?>\n"
+                     "<!DOCTYPE doc SYSTEM 'http://example.org/' [\n"
+                     "<!ENTITY % entity '<!ELEMENT doc (#PCDATA)>'>\n"
+                     "%entity;\n"
+                     "]>\n"
+                     "<doc></doc>";
+  char dtd_data[] = "<!ENTITY % e1 'foo'>\n";
+
+  XML_SetUserData(g_parser, dtd_data);
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_public);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test skipping of parameter entity in an external DTD */
+/* Derived from ibm/invalid/P69/ibm69i01.xml */
+START_TEST(test_skipped_parameter_entity) {
+  const char *text = "<?xml version='1.0'?>\n"
+                     "<!DOCTYPE root SYSTEM 'http://example.org/dtd.ent' [\n"
+                     "<!ELEMENT root (#PCDATA|a)* >\n"
+                     "]>\n"
+                     "<root></root>";
+  ExtTest dtd_data = {"%pe2;", NULL, NULL};
+
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
+  XML_SetUserData(g_parser, &dtd_data);
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetSkippedEntityHandler(g_parser, dummy_skip_handler);
+  init_dummy_handlers();
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  if (get_dummy_handler_flags() != DUMMY_SKIP_HANDLER_FLAG)
+    fail("Skip handler not executed");
+}
+END_TEST
+
+/* Test recursive parameter entity definition rejected in external DTD */
+START_TEST(test_recursive_external_parameter_entity) {
+  const char *text = "<?xml version='1.0'?>\n"
+                     "<!DOCTYPE root SYSTEM 'http://example.org/dtd.ent' [\n"
+                     "<!ELEMENT root (#PCDATA|a)* >\n"
+                     "]>\n"
+                     "<root></root>";
+  ExtFaults dtd_data = {"<!ENTITY % pe2 '&#37;pe2;'>\n%pe2;",
+                        "Recursive external parameter entity not faulted", NULL,
+                        XML_ERROR_RECURSIVE_ENTITY_REF};
+
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_faulter);
+  XML_SetUserData(g_parser, &dtd_data);
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
+                 "Recursive external parameter not spotted");
+}
+END_TEST
+
+/* Test undefined parameter entity in external entity handler */
+START_TEST(test_undefined_ext_entity_in_external_dtd) {
+  const char *text = "<!DOCTYPE doc SYSTEM 'foo'>\n"
+                     "<doc></doc>\n";
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_devaluer);
+  XML_SetUserData(g_parser, NULL);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+
+  /* Now repeat without the external entity ref handler invoking
+   * another copy of itself.
+   */
+  XML_ParserReset(g_parser, NULL);
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_devaluer);
+  XML_SetUserData(g_parser, g_parser); /* Any non-NULL value will do */
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test suspending the parse on receiving an XML declaration works */
+START_TEST(test_suspend_xdecl) {
+  const char *text = long_character_data_text;
+
+  XML_SetXmlDeclHandler(g_parser, entity_suspending_xdecl_handler);
+  XML_SetUserData(g_parser, g_parser);
+  g_resumable = XML_TRUE;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_SUSPENDED)
+    xml_failure(g_parser);
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_NONE)
+    xml_failure(g_parser);
+  /* Attempt to start a new parse while suspended */
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail("Attempt to parse while suspended not faulted");
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_SUSPENDED)
+    fail("Suspended parse not faulted with correct error");
+}
+END_TEST
+
+/* Test aborting the parse in an epilog works */
+START_TEST(test_abort_epilog) {
+  const char *text = "<doc></doc>\n\r\n";
+  XML_Char trigger_char = XCS('\r');
+
+  XML_SetDefaultHandler(g_parser, selective_aborting_default_handler);
+  XML_SetUserData(g_parser, &trigger_char);
+  g_resumable = XML_FALSE;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail("Abort not triggered");
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_ABORTED)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test a different code path for abort in the epilog */
+START_TEST(test_abort_epilog_2) {
+  const char *text = "<doc></doc>\n";
+  XML_Char trigger_char = XCS('\n');
+
+  XML_SetDefaultHandler(g_parser, selective_aborting_default_handler);
+  XML_SetUserData(g_parser, &trigger_char);
+  g_resumable = XML_FALSE;
+  expect_failure(text, XML_ERROR_ABORTED, "Abort not triggered");
+}
+END_TEST
+
+/* Test suspension from the epilog */
+START_TEST(test_suspend_epilog) {
+  const char *text = "<doc></doc>\n";
+  XML_Char trigger_char = XCS('\n');
+
+  XML_SetDefaultHandler(g_parser, selective_aborting_default_handler);
+  XML_SetUserData(g_parser, &trigger_char);
+  g_resumable = XML_TRUE;
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_SUSPENDED)
+    xml_failure(g_parser);
+}
+END_TEST
+
+START_TEST(test_suspend_in_sole_empty_tag) {
+  const char *text = "<doc/>";
+  enum XML_Status rc;
+
+  XML_SetEndElementHandler(g_parser, suspending_end_handler);
+  XML_SetUserData(g_parser, g_parser);
+  rc = _XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE);
+  if (rc == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  else if (rc != XML_STATUS_SUSPENDED)
+    fail("Suspend not triggered");
+  rc = XML_ResumeParser(g_parser);
+  if (rc == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  else if (rc != XML_STATUS_OK)
+    fail("Resume failed");
+}
+END_TEST
+
+START_TEST(test_unfinished_epilog) {
+  const char *text = "<doc></doc><";
+
+  expect_failure(text, XML_ERROR_UNCLOSED_TOKEN,
+                 "Incomplete epilog entry not faulted");
+}
+END_TEST
+
+START_TEST(test_partial_char_in_epilog) {
+  const char *text = "<doc></doc>\xe2\x82";
+
+  /* First check that no fault is raised if the parse is not finished */
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  /* Now check that it is faulted once we finish */
+  if (XML_ParseBuffer(g_parser, 0, XML_TRUE) != XML_STATUS_ERROR)
+    fail("Partial character in epilog not faulted");
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_PARTIAL_CHAR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test resuming a parse suspended in entity substitution */
+START_TEST(test_suspend_resume_internal_entity) {
+  const char *text
+      = "<!DOCTYPE doc [\n"
+        "<!ENTITY foo '<suspend>Hi<suspend>Ho</suspend></suspend>'>\n"
+        "]>\n"
+        "<doc>&foo;</doc>\n";
+  const XML_Char *expected1 = XCS("Hi");
+  const XML_Char *expected2 = XCS("HiHo");
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetStartElementHandler(g_parser, start_element_suspender);
+  XML_SetCharacterDataHandler(g_parser, accumulate_characters);
+  XML_SetUserData(g_parser, &storage);
+  // can't use SINGLE_BYTES here, because it'll return early on suspension, and
+  // we won't know exactly how much input we actually managed to give Expat.
+  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_SUSPENDED)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, XCS(""));
+  if (XML_ResumeParser(g_parser) != XML_STATUS_SUSPENDED)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected1);
+  if (XML_ResumeParser(g_parser) != XML_STATUS_OK)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected2);
+}
+END_TEST
+
+START_TEST(test_suspend_resume_internal_entity_issue_629) {
+  const char *const text
+      = "<!DOCTYPE a [<!ENTITY e '<!--COMMENT-->a'>]><a>&e;<b>\n"
+        "<"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        "/>"
+        "</b></a>";
+  const size_t firstChunkSizeBytes = 54;
+
+  XML_Parser parser = XML_ParserCreate(NULL);
+  XML_SetUserData(parser, parser);
+  XML_SetCommentHandler(parser, suspending_comment_handler);
+
+  if (XML_Parse(parser, text, (int)firstChunkSizeBytes, XML_FALSE)
+      != XML_STATUS_SUSPENDED)
+    xml_failure(parser);
+  if (XML_ResumeParser(parser) != XML_STATUS_OK)
+    xml_failure(parser);
+  if (_XML_Parse_SINGLE_BYTES(parser, text + firstChunkSizeBytes,
+                              (int)(strlen(text) - firstChunkSizeBytes),
+                              XML_TRUE)
+      != XML_STATUS_OK)
+    xml_failure(parser);
+  XML_ParserFree(parser);
+}
+END_TEST
+
+/* Test syntax error is caught at parse resumption */
+START_TEST(test_resume_entity_with_syntax_error) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "<!ENTITY foo '<suspend>Hi</wombat>'>\n"
+                     "]>\n"
+                     "<doc>&foo;</doc>\n";
+
+  XML_SetStartElementHandler(g_parser, start_element_suspender);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_SUSPENDED)
+    xml_failure(g_parser);
+  if (XML_ResumeParser(g_parser) != XML_STATUS_ERROR)
+    fail("Syntax error in entity not faulted");
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_TAG_MISMATCH)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test suspending and resuming in a parameter entity substitution */
+START_TEST(test_suspend_resume_parameter_entity) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "<!ENTITY % foo '<!ELEMENT doc (#PCDATA)*>'>\n"
+                     "%foo;\n"
+                     "]>\n"
+                     "<doc>Hello, world</doc>";
+  const XML_Char *expected = XCS("Hello, world");
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetElementDeclHandler(g_parser, element_decl_suspender);
+  XML_SetCharacterDataHandler(g_parser, accumulate_characters);
+  XML_SetUserData(g_parser, &storage);
+  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_SUSPENDED)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, XCS(""));
+  if (XML_ResumeParser(g_parser) != XML_STATUS_OK)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+/* Test attempting to use parser after an error is faulted */
+START_TEST(test_restart_on_error) {
+  const char *text = "<$doc><doc></doc>";
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail("Invalid tag name not faulted");
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_INVALID_TOKEN)
+    xml_failure(g_parser);
+  if (XML_Parse(g_parser, NULL, 0, XML_TRUE) != XML_STATUS_ERROR)
+    fail("Restarting invalid parse not faulted");
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_INVALID_TOKEN)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test that angle brackets in an attribute default value are faulted */
+START_TEST(test_reject_lt_in_attribute_value) {
+  const char *text = "<!DOCTYPE doc [<!ATTLIST doc a CDATA '<bar>'>]>\n"
+                     "<doc></doc>";
+
+  expect_failure(text, XML_ERROR_INVALID_TOKEN,
+                 "Bad attribute default not faulted");
+}
+END_TEST
+
+START_TEST(test_reject_unfinished_param_in_att_value) {
+  const char *text = "<!DOCTYPE doc [<!ATTLIST doc a CDATA '&foo'>]>\n"
+                     "<doc></doc>";
+
+  expect_failure(text, XML_ERROR_INVALID_TOKEN,
+                 "Bad attribute default not faulted");
+}
+END_TEST
+
+START_TEST(test_trailing_cr_in_att_value) {
+  const char *text = "<doc a='value\r'/>";
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Try parsing a general entity within a parameter entity in a
+ * standalone internal DTD.  Covers a corner case in the parser.
+ */
+START_TEST(test_standalone_internal_entity) {
+  const char *text = "<?xml version='1.0' standalone='yes' ?>\n"
+                     "<!DOCTYPE doc [\n"
+                     "  <!ELEMENT doc (#PCDATA)>\n"
+                     "  <!ENTITY % pe '<!ATTLIST doc att2 CDATA \"&ge;\">'>\n"
+                     "  <!ENTITY ge 'AttDefaultValue'>\n"
+                     "  %pe;\n"
+                     "]>\n"
+                     "<doc att2='any'/>";
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test that a reference to an unknown external entity is skipped */
+START_TEST(test_skipped_external_entity) {
+  const char *text = "<!DOCTYPE doc SYSTEM 'http://example.org/'>\n"
+                     "<doc></doc>\n";
+  ExtTest test_data = {"<!ELEMENT doc EMPTY>\n"
+                       "<!ENTITY % e2 '%e1;'>\n",
+                       NULL, NULL};
+
+  XML_SetUserData(g_parser, &test_data);
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test a different form of unknown external entity */
+START_TEST(test_skipped_null_loaded_ext_entity) {
+  const char *text = "<!DOCTYPE doc SYSTEM 'http://example.org/one.ent'>\n"
+                     "<doc />";
+  ExtHdlrData test_data
+      = {"<!ENTITY % pe1 SYSTEM 'http://example.org/two.ent'>\n"
+         "<!ENTITY % pe2 '%pe1;'>\n"
+         "%pe2;\n",
+         external_entity_null_loader};
+
+  XML_SetUserData(g_parser, &test_data);
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_oneshot_loader);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+START_TEST(test_skipped_unloaded_ext_entity) {
+  const char *text = "<!DOCTYPE doc SYSTEM 'http://example.org/one.ent'>\n"
+                     "<doc />";
+  ExtHdlrData test_data
+      = {"<!ENTITY % pe1 SYSTEM 'http://example.org/two.ent'>\n"
+         "<!ENTITY % pe2 '%pe1;'>\n"
+         "%pe2;\n",
+         NULL};
+
+  XML_SetUserData(g_parser, &test_data);
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_oneshot_loader);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test that a parameter entity value ending with a carriage return
+ * has it translated internally into a newline.
+ */
+START_TEST(test_param_entity_with_trailing_cr) {
+#define PARAM_ENTITY_NAME "pe"
+#define PARAM_ENTITY_CORE_VALUE "<!ATTLIST doc att CDATA \"default\">"
+  const char *text = "<!DOCTYPE doc SYSTEM 'http://example.org/'>\n"
+                     "<doc/>";
+  ExtTest test_data
+      = {"<!ENTITY % " PARAM_ENTITY_NAME " '" PARAM_ENTITY_CORE_VALUE "\r'>\n"
+         "%" PARAM_ENTITY_NAME ";\n",
+         NULL, NULL};
+
+  XML_SetUserData(g_parser, &test_data);
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
+  XML_SetEntityDeclHandler(g_parser, param_entity_match_handler);
+  param_entity_match_init(XCS(PARAM_ENTITY_NAME),
+                          XCS(PARAM_ENTITY_CORE_VALUE) XCS("\n"));
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  int entity_match_flag = get_param_entity_match_flag();
+  if (entity_match_flag == ENTITY_MATCH_FAIL)
+    fail("Parameter entity CR->NEWLINE conversion failed");
+  else if (entity_match_flag == ENTITY_MATCH_NOT_FOUND)
+    fail("Parameter entity not parsed");
+}
+#undef PARAM_ENTITY_NAME
+#undef PARAM_ENTITY_CORE_VALUE
+END_TEST
+
+START_TEST(test_invalid_character_entity) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY entity '&#x110000;'>\n"
+                     "]>\n"
+                     "<doc>&entity;</doc>";
+
+  expect_failure(text, XML_ERROR_BAD_CHAR_REF,
+                 "Out of range character reference not faulted");
+}
+END_TEST
+
+START_TEST(test_invalid_character_entity_2) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY entity '&#xg0;'>\n"
+                     "]>\n"
+                     "<doc>&entity;</doc>";
+
+  expect_failure(text, XML_ERROR_INVALID_TOKEN,
+                 "Out of range character reference not faulted");
+}
+END_TEST
+
+START_TEST(test_invalid_character_entity_3) {
+  const char text[] =
+      /* <!DOCTYPE doc [\n */
+      "\0<\0!\0D\0O\0C\0T\0Y\0P\0E\0 \0d\0o\0c\0 \0[\0\n"
+      /* U+0E04 = KHO KHWAI
+       * U+0E08 = CHO CHAN */
+      /* <!ENTITY entity '&\u0e04\u0e08;'>\n */
+      "\0<\0!\0E\0N\0T\0I\0T\0Y\0 \0e\0n\0t\0i\0t\0y\0 "
+      "\0'\0&\x0e\x04\x0e\x08\0;\0'\0>\0\n"
+      /* ]>\n */
+      "\0]\0>\0\n"
+      /* <doc>&entity;</doc> */
+      "\0<\0d\0o\0c\0>\0&\0e\0n\0t\0i\0t\0y\0;\0<\0/\0d\0o\0c\0>";
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail("Invalid start of entity name not faulted");
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_UNDEFINED_ENTITY)
+    xml_failure(g_parser);
+}
+END_TEST
+
+START_TEST(test_invalid_character_entity_4) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY entity '&#1114112;'>\n" /* = &#x110000 */
+                     "]>\n"
+                     "<doc>&entity;</doc>";
+
+  expect_failure(text, XML_ERROR_BAD_CHAR_REF,
+                 "Out of range character reference not faulted");
+}
+END_TEST
+
+/* Test that processing instructions are picked up by a default handler */
+START_TEST(test_pi_handled_in_default) {
+  const char *text = "<?test processing instruction?>\n<doc/>";
+  const XML_Char *expected = XCS("<?test processing instruction?>\n<doc/>");
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetDefaultHandler(g_parser, accumulate_characters);
+  XML_SetUserData(g_parser, &storage);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+/* Test that comments are picked up by a default handler */
+START_TEST(test_comment_handled_in_default) {
+  const char *text = "<!-- This is a comment -->\n<doc/>";
+  const XML_Char *expected = XCS("<!-- This is a comment -->\n<doc/>");
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetDefaultHandler(g_parser, accumulate_characters);
+  XML_SetUserData(g_parser, &storage);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+/* Test PIs that look almost but not quite like XML declarations */
+START_TEST(test_pi_yml) {
+  const char *text = "<?yml something like data?><doc/>";
+  const XML_Char *expected = XCS("yml: something like data\n");
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetProcessingInstructionHandler(g_parser, accumulate_pi_characters);
+  XML_SetUserData(g_parser, &storage);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_pi_xnl) {
+  const char *text = "<?xnl nothing like data?><doc/>";
+  const XML_Char *expected = XCS("xnl: nothing like data\n");
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetProcessingInstructionHandler(g_parser, accumulate_pi_characters);
+  XML_SetUserData(g_parser, &storage);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_pi_xmm) {
+  const char *text = "<?xmm everything like data?><doc/>";
+  const XML_Char *expected = XCS("xmm: everything like data\n");
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetProcessingInstructionHandler(g_parser, accumulate_pi_characters);
+  XML_SetUserData(g_parser, &storage);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_utf16_pi) {
+  const char text[] =
+      /* <?{KHO KHWAI}{CHO CHAN}?>
+       * where {KHO KHWAI} = U+0E04
+       * and   {CHO CHAN}  = U+0E08
+       */
+      "<\0?\0\x04\x0e\x08\x0e?\0>\0"
+      /* <q/> */
+      "<\0q\0/\0>\0";
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("\x0e04\x0e08: \n");
+#else
+  const XML_Char *expected = XCS("\xe0\xb8\x84\xe0\xb8\x88: \n");
+#endif
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetProcessingInstructionHandler(g_parser, accumulate_pi_characters);
+  XML_SetUserData(g_parser, &storage);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_utf16_be_pi) {
+  const char text[] =
+      /* <?{KHO KHWAI}{CHO CHAN}?>
+       * where {KHO KHWAI} = U+0E04
+       * and   {CHO CHAN}  = U+0E08
+       */
+      "\0<\0?\x0e\x04\x0e\x08\0?\0>"
+      /* <q/> */
+      "\0<\0q\0/\0>";
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("\x0e04\x0e08: \n");
+#else
+  const XML_Char *expected = XCS("\xe0\xb8\x84\xe0\xb8\x88: \n");
+#endif
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetProcessingInstructionHandler(g_parser, accumulate_pi_characters);
+  XML_SetUserData(g_parser, &storage);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+/* Test that comments can be picked up and translated */
+START_TEST(test_utf16_be_comment) {
+  const char text[] =
+      /* <!-- Comment A --> */
+      "\0<\0!\0-\0-\0 \0C\0o\0m\0m\0e\0n\0t\0 \0A\0 \0-\0-\0>\0\n"
+      /* <doc/> */
+      "\0<\0d\0o\0c\0/\0>";
+  const XML_Char *expected = XCS(" Comment A ");
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetCommentHandler(g_parser, accumulate_comment);
+  XML_SetUserData(g_parser, &storage);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_utf16_le_comment) {
+  const char text[] =
+      /* <!-- Comment B --> */
+      "<\0!\0-\0-\0 \0C\0o\0m\0m\0e\0n\0t\0 \0B\0 \0-\0-\0>\0\n\0"
+      /* <doc/> */
+      "<\0d\0o\0c\0/\0>\0";
+  const XML_Char *expected = XCS(" Comment B ");
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetCommentHandler(g_parser, accumulate_comment);
+  XML_SetUserData(g_parser, &storage);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+/* Test that the unknown encoding handler with map entries that expect
+ * conversion but no conversion function is faulted
+ */
+START_TEST(test_missing_encoding_conversion_fn) {
+  const char *text = "<?xml version='1.0' encoding='no-conv'?>\n"
+                     "<doc>\x81</doc>";
+
+  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
+  /* MiscEncodingHandler sets up an encoding with every top-bit-set
+   * character introducing a two-byte sequence.  For this, it
+   * requires a convert function.  The above function call doesn't
+   * pass one through, so when BadEncodingHandler actually gets
+   * called it should supply an invalid encoding.
+   */
+  expect_failure(text, XML_ERROR_UNKNOWN_ENCODING,
+                 "Encoding with missing convert() not faulted");
+}
+END_TEST
+
+START_TEST(test_failing_encoding_conversion_fn) {
+  const char *text = "<?xml version='1.0' encoding='failing-conv'?>\n"
+                     "<doc>\x81</doc>";
+
+  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
+  /* BadEncodingHandler sets up an encoding with every top-bit-set
+   * character introducing a two-byte sequence.  For this, it
+   * requires a convert function.  The above function call passes
+   * one that insists all possible sequences are invalid anyway.
+   */
+  expect_failure(text, XML_ERROR_INVALID_TOKEN,
+                 "Encoding with failing convert() not faulted");
+}
+END_TEST
+
+/* Test unknown encoding conversions */
+START_TEST(test_unknown_encoding_success) {
+  const char *text = "<?xml version='1.0' encoding='prefix-conv'?>\n"
+                     /* Equivalent to <eoc>Hello, world</eoc> */
+                     "<\x81\x64\x80oc>Hello, world</\x81\x64\x80oc>";
+
+  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
+  run_character_check(text, XCS("Hello, world"));
+}
+END_TEST
+
+/* Test bad name character in unknown encoding */
+START_TEST(test_unknown_encoding_bad_name) {
+  const char *text = "<?xml version='1.0' encoding='prefix-conv'?>\n"
+                     "<\xff\x64oc>Hello, world</\xff\x64oc>";
+
+  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
+  expect_failure(text, XML_ERROR_INVALID_TOKEN,
+                 "Bad name start in unknown encoding not faulted");
+}
+END_TEST
+
+/* Test bad mid-name character in unknown encoding */
+START_TEST(test_unknown_encoding_bad_name_2) {
+  const char *text = "<?xml version='1.0' encoding='prefix-conv'?>\n"
+                     "<d\xffoc>Hello, world</d\xffoc>";
+
+  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
+  expect_failure(text, XML_ERROR_INVALID_TOKEN,
+                 "Bad name in unknown encoding not faulted");
+}
+END_TEST
+
+/* Test element name that is long enough to fill the conversion buffer
+ * in an unknown encoding, finishing with an encoded character.
+ */
+START_TEST(test_unknown_encoding_long_name_1) {
+  const char *text = "<?xml version='1.0' encoding='prefix-conv'?>\n"
+                     "<abcdefghabcdefghabcdefghijkl\x80m\x80n\x80o\x80p>"
+                     "Hi"
+                     "</abcdefghabcdefghabcdefghijkl\x80m\x80n\x80o\x80p>";
+  const XML_Char *expected = XCS("abcdefghabcdefghabcdefghijklmnop");
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
+  XML_SetStartElementHandler(g_parser, record_element_start_handler);
+  XML_SetUserData(g_parser, &storage);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+/* Test element name that is long enough to fill the conversion buffer
+ * in an unknown encoding, finishing with an simple character.
+ */
+START_TEST(test_unknown_encoding_long_name_2) {
+  const char *text = "<?xml version='1.0' encoding='prefix-conv'?>\n"
+                     "<abcdefghabcdefghabcdefghijklmnop>"
+                     "Hi"
+                     "</abcdefghabcdefghabcdefghijklmnop>";
+  const XML_Char *expected = XCS("abcdefghabcdefghabcdefghijklmnop");
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
+  XML_SetStartElementHandler(g_parser, record_element_start_handler);
+  XML_SetUserData(g_parser, &storage);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_invalid_unknown_encoding) {
+  const char *text = "<?xml version='1.0' encoding='invalid-9'?>\n"
+                     "<doc>Hello world</doc>";
+
+  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
+  expect_failure(text, XML_ERROR_UNKNOWN_ENCODING,
+                 "Invalid unknown encoding not faulted");
+}
+END_TEST
+
+START_TEST(test_unknown_ascii_encoding_ok) {
+  const char *text = "<?xml version='1.0' encoding='ascii-like'?>\n"
+                     "<doc>Hello, world</doc>";
+
+  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
+  run_character_check(text, XCS("Hello, world"));
+}
+END_TEST
+
+START_TEST(test_unknown_ascii_encoding_fail) {
+  const char *text = "<?xml version='1.0' encoding='ascii-like'?>\n"
+                     "<doc>Hello, \x80 world</doc>";
+
+  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
+  expect_failure(text, XML_ERROR_INVALID_TOKEN,
+                 "Invalid character not faulted");
+}
+END_TEST
+
+START_TEST(test_unknown_encoding_invalid_length) {
+  const char *text = "<?xml version='1.0' encoding='invalid-len'?>\n"
+                     "<doc>Hello, world</doc>";
+
+  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
+  expect_failure(text, XML_ERROR_UNKNOWN_ENCODING,
+                 "Invalid unknown encoding not faulted");
+}
+END_TEST
+
+START_TEST(test_unknown_encoding_invalid_topbit) {
+  const char *text = "<?xml version='1.0' encoding='invalid-a'?>\n"
+                     "<doc>Hello, world</doc>";
+
+  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
+  expect_failure(text, XML_ERROR_UNKNOWN_ENCODING,
+                 "Invalid unknown encoding not faulted");
+}
+END_TEST
+
+START_TEST(test_unknown_encoding_invalid_surrogate) {
+  const char *text = "<?xml version='1.0' encoding='invalid-surrogate'?>\n"
+                     "<doc>Hello, \x82 world</doc>";
+
+  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
+  expect_failure(text, XML_ERROR_INVALID_TOKEN,
+                 "Invalid unknown encoding not faulted");
+}
+END_TEST
+
+START_TEST(test_unknown_encoding_invalid_high) {
+  const char *text = "<?xml version='1.0' encoding='invalid-high'?>\n"
+                     "<doc>Hello, world</doc>";
+
+  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
+  expect_failure(text, XML_ERROR_UNKNOWN_ENCODING,
+                 "Invalid unknown encoding not faulted");
+}
+END_TEST
+
+START_TEST(test_unknown_encoding_invalid_attr_value) {
+  const char *text = "<?xml version='1.0' encoding='prefix-conv'?>\n"
+                     "<doc attr='\xff\x30'/>";
+
+  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
+  expect_failure(text, XML_ERROR_INVALID_TOKEN,
+                 "Invalid attribute valid not faulted");
+}
+END_TEST
+
+/* Test an external entity parser set to use latin-1 detects UTF-16
+ * BOMs correctly.
+ */
+/* Test that UTF-16 BOM does not select UTF-16 given explicit encoding */
+START_TEST(test_ext_entity_latin1_utf16le_bom) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
+                     "]>\n"
+                     "<doc>&en;</doc>";
+  ExtTest2 test_data
+      = {/* If UTF-16, 0xfeff is the BOM and 0x204c is black left bullet */
+         /* If Latin-1, 0xff = Y-diaeresis, 0xfe = lowercase thorn,
+          *   0x4c = L and 0x20 is a space
+          */
+         "\xff\xfe\x4c\x20", 4, XCS("iso-8859-1"), NULL};
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("\x00ff\x00feL ");
+#else
+  /* In UTF-8, y-diaeresis is 0xc3 0xbf, lowercase thorn is 0xc3 0xbe */
+  const XML_Char *expected = XCS("\xc3\xbf\xc3\xbeL ");
+#endif
+  CharData storage;
+
+  CharData_Init(&storage);
+  test_data.storage = &storage;
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader2);
+  XML_SetUserData(g_parser, &test_data);
+  XML_SetCharacterDataHandler(g_parser, ext2_accumulate_characters);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_ext_entity_latin1_utf16be_bom) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
+                     "]>\n"
+                     "<doc>&en;</doc>";
+  ExtTest2 test_data
+      = {/* If UTF-16, 0xfeff is the BOM and 0x204c is black left bullet */
+         /* If Latin-1, 0xff = Y-diaeresis, 0xfe = lowercase thorn,
+          *   0x4c = L and 0x20 is a space
+          */
+         "\xfe\xff\x20\x4c", 4, XCS("iso-8859-1"), NULL};
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("\x00fe\x00ff L");
+#else
+  /* In UTF-8, y-diaeresis is 0xc3 0xbf, lowercase thorn is 0xc3 0xbe */
+  const XML_Char *expected = XCS("\xc3\xbe\xc3\xbf L");
+#endif
+  CharData storage;
+
+  CharData_Init(&storage);
+  test_data.storage = &storage;
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader2);
+  XML_SetUserData(g_parser, &test_data);
+  XML_SetCharacterDataHandler(g_parser, ext2_accumulate_characters);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+/* Parsing the full buffer rather than a byte at a time makes a
+ * difference to the encoding scanning code, so repeat the above tests
+ * without breaking them down by byte.
+ */
+START_TEST(test_ext_entity_latin1_utf16le_bom2) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
+                     "]>\n"
+                     "<doc>&en;</doc>";
+  ExtTest2 test_data
+      = {/* If UTF-16, 0xfeff is the BOM and 0x204c is black left bullet */
+         /* If Latin-1, 0xff = Y-diaeresis, 0xfe = lowercase thorn,
+          *   0x4c = L and 0x20 is a space
+          */
+         "\xff\xfe\x4c\x20", 4, XCS("iso-8859-1"), NULL};
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("\x00ff\x00feL ");
+#else
+  /* In UTF-8, y-diaeresis is 0xc3 0xbf, lowercase thorn is 0xc3 0xbe */
+  const XML_Char *expected = XCS("\xc3\xbf\xc3\xbeL ");
+#endif
+  CharData storage;
+
+  CharData_Init(&storage);
+  test_data.storage = &storage;
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader2);
+  XML_SetUserData(g_parser, &test_data);
+  XML_SetCharacterDataHandler(g_parser, ext2_accumulate_characters);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_ext_entity_latin1_utf16be_bom2) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
+                     "]>\n"
+                     "<doc>&en;</doc>";
+  ExtTest2 test_data
+      = {/* If UTF-16, 0xfeff is the BOM and 0x204c is black left bullet */
+         /* If Latin-1, 0xff = Y-diaeresis, 0xfe = lowercase thorn,
+          *   0x4c = L and 0x20 is a space
+          */
+         "\xfe\xff\x20\x4c", 4, XCS("iso-8859-1"), NULL};
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("\x00fe\x00ff L");
+#else
+  /* In UTF-8, y-diaeresis is 0xc3 0xbf, lowercase thorn is 0xc3 0xbe */
+  const XML_Char *expected = "\xc3\xbe\xc3\xbf L";
+#endif
+  CharData storage;
+
+  CharData_Init(&storage);
+  test_data.storage = &storage;
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader2);
+  XML_SetUserData(g_parser, &test_data);
+  XML_SetCharacterDataHandler(g_parser, ext2_accumulate_characters);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+/* Test little-endian UTF-16 given an explicit big-endian encoding */
+START_TEST(test_ext_entity_utf16_be) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
+                     "]>\n"
+                     "<doc>&en;</doc>";
+  ExtTest2 test_data = {"<\0e\0/\0>\0", 8, XCS("utf-16be"), NULL};
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("\x3c00\x6500\x2f00\x3e00");
+#else
+  const XML_Char *expected = XCS("\xe3\xb0\x80"   /* U+3C00 */
+                                 "\xe6\x94\x80"   /* U+6500 */
+                                 "\xe2\xbc\x80"   /* U+2F00 */
+                                 "\xe3\xb8\x80"); /* U+3E00 */
+#endif
+  CharData storage;
+
+  CharData_Init(&storage);
+  test_data.storage = &storage;
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader2);
+  XML_SetUserData(g_parser, &test_data);
+  XML_SetCharacterDataHandler(g_parser, ext2_accumulate_characters);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+/* Test big-endian UTF-16 given an explicit little-endian encoding */
+START_TEST(test_ext_entity_utf16_le) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
+                     "]>\n"
+                     "<doc>&en;</doc>";
+  ExtTest2 test_data = {"\0<\0e\0/\0>", 8, XCS("utf-16le"), NULL};
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("\x3c00\x6500\x2f00\x3e00");
+#else
+  const XML_Char *expected = XCS("\xe3\xb0\x80"   /* U+3C00 */
+                                 "\xe6\x94\x80"   /* U+6500 */
+                                 "\xe2\xbc\x80"   /* U+2F00 */
+                                 "\xe3\xb8\x80"); /* U+3E00 */
+#endif
+  CharData storage;
+
+  CharData_Init(&storage);
+  test_data.storage = &storage;
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader2);
+  XML_SetUserData(g_parser, &test_data);
+  XML_SetCharacterDataHandler(g_parser, ext2_accumulate_characters);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+/* Test little-endian UTF-16 given no explicit encoding.
+ * The existing default encoding (UTF-8) is assumed to hold without a
+ * BOM to contradict it, so the entity value will in fact provoke an
+ * error because 0x00 is not a valid XML character.  We parse the
+ * whole buffer in one go rather than feeding it in byte by byte to
+ * exercise different code paths in the initial scanning routines.
+ */
+START_TEST(test_ext_entity_utf16_unknown) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
+                     "]>\n"
+                     "<doc>&en;</doc>";
+  ExtFaults2 test_data
+      = {"a\0b\0c\0", 6, "Invalid character in entity not faulted", NULL,
+         XML_ERROR_INVALID_TOKEN};
+
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_faulter2);
+  XML_SetUserData(g_parser, &test_data);
+  expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
+                 "Invalid character should not have been accepted");
+}
+END_TEST
+
+/* Test not-quite-UTF-8 BOM (0xEF 0xBB 0xBF) */
+START_TEST(test_ext_entity_utf8_non_bom) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
+                     "]>\n"
+                     "<doc>&en;</doc>";
+  ExtTest2 test_data
+      = {"\xef\xbb\x80", /* Arabic letter DAD medial form, U+FEC0 */
+         3, NULL, NULL};
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("\xfec0");
+#else
+  const XML_Char *expected = XCS("\xef\xbb\x80");
+#endif
+  CharData storage;
+
+  CharData_Init(&storage);
+  test_data.storage = &storage;
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader2);
+  XML_SetUserData(g_parser, &test_data);
+  XML_SetCharacterDataHandler(g_parser, ext2_accumulate_characters);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+/* Test that UTF-8 in a CDATA section is correctly passed through */
+START_TEST(test_utf8_in_cdata_section) {
+  const char *text = "<doc><![CDATA[one \xc3\xa9 two]]></doc>";
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("one \x00e9 two");
+#else
+  const XML_Char *expected = XCS("one \xc3\xa9 two");
+#endif
+
+  run_character_check(text, expected);
+}
+END_TEST
+
+/* Test that little-endian UTF-16 in a CDATA section is handled */
+START_TEST(test_utf8_in_cdata_section_2) {
+  const char *text = "<doc><![CDATA[\xc3\xa9]\xc3\xa9two]]></doc>";
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("\x00e9]\x00e9two");
+#else
+  const XML_Char *expected = XCS("\xc3\xa9]\xc3\xa9two");
+#endif
+
+  run_character_check(text, expected);
+}
+END_TEST
+
+START_TEST(test_utf8_in_start_tags) {
+  struct test_case {
+    bool goodName;
+    bool goodNameStart;
+    const char *tagName;
+  };
+
+  // The idea with the tests below is this:
+  // We want to cover 1-, 2- and 3-byte sequences, 4-byte sequences
+  // go to isNever and are hence not a concern.
+  //
+  // We start with a character that is a valid name character
+  // (or even name-start character, see XML 1.0r4 spec) and then we flip
+  // single bits at places where (1) the result leaves the UTF-8 encoding space
+  // and (2) we stay in the same n-byte sequence family.
+  //
+  // The flipped bits are highlighted in angle brackets in comments,
+  // e.g. "[<1>011 1001]" means we had [0011 1001] but we now flipped
+  // the most significant bit to 1 to leave UTF-8 encoding space.
+  struct test_case cases[] = {
+      // 1-byte UTF-8: [0xxx xxxx]
+      {true, true, "\x3A"},   // [0011 1010] = ASCII colon ':'
+      {false, false, "\xBA"}, // [<1>011 1010]
+      {true, false, "\x39"},  // [0011 1001] = ASCII nine '9'
+      {false, false, "\xB9"}, // [<1>011 1001]
+
+      // 2-byte UTF-8: [110x xxxx] [10xx xxxx]
+      {true, true, "\xDB\xA5"},   // [1101 1011] [1010 0101] =
+                                  // Arabic small waw U+06E5
+      {false, false, "\x9B\xA5"}, // [1<0>01 1011] [1010 0101]
+      {false, false, "\xDB\x25"}, // [1101 1011] [<0>010 0101]
+      {false, false, "\xDB\xE5"}, // [1101 1011] [1<1>10 0101]
+      {true, false, "\xCC\x81"},  // [1100 1100] [1000 0001] =
+                                  // combining char U+0301
+      {false, false, "\x8C\x81"}, // [1<0>00 1100] [1000 0001]
+      {false, false, "\xCC\x01"}, // [1100 1100] [<0>000 0001]
+      {false, false, "\xCC\xC1"}, // [1100 1100] [1<1>00 0001]
+
+      // 3-byte UTF-8: [1110 xxxx] [10xx xxxx] [10xxxxxx]
+      {true, true, "\xE0\xA4\x85"},   // [1110 0000] [1010 0100] [1000 0101] =
+                                      // Devanagari Letter A U+0905
+      {false, false, "\xA0\xA4\x85"}, // [1<0>10 0000] [1010 0100] [1000 0101]
+      {false, false, "\xE0\x24\x85"}, // [1110 0000] [<0>010 0100] [1000 0101]
+      {false, false, "\xE0\xE4\x85"}, // [1110 0000] [1<1>10 0100] [1000 0101]
+      {false, false, "\xE0\xA4\x05"}, // [1110 0000] [1010 0100] [<0>000 0101]
+      {false, false, "\xE0\xA4\xC5"}, // [1110 0000] [1010 0100] [1<1>00 0101]
+      {true, false, "\xE0\xA4\x81"},  // [1110 0000] [1010 0100] [1000 0001] =
+                                      // combining char U+0901
+      {false, false, "\xA0\xA4\x81"}, // [1<0>10 0000] [1010 0100] [1000 0001]
+      {false, false, "\xE0\x24\x81"}, // [1110 0000] [<0>010 0100] [1000 0001]
+      {false, false, "\xE0\xE4\x81"}, // [1110 0000] [1<1>10 0100] [1000 0001]
+      {false, false, "\xE0\xA4\x01"}, // [1110 0000] [1010 0100] [<0>000 0001]
+      {false, false, "\xE0\xA4\xC1"}, // [1110 0000] [1010 0100] [1<1>00 0001]
+  };
+  const bool atNameStart[] = {true, false};
+
+  size_t i = 0;
+  char doc[1024];
+  size_t failCount = 0;
+
+  // we need all the bytes to be parsed, but we don't want the errors that can
+  // trigger on isFinal=XML_TRUE, so we skip the test if the heuristic is on.
+  if (g_reparseDeferralEnabledDefault) {
+    return;
+  }
+
+  for (; i < sizeof(cases) / sizeof(cases[0]); i++) {
+    size_t j = 0;
+    for (; j < sizeof(atNameStart) / sizeof(atNameStart[0]); j++) {
+      const bool expectedSuccess
+          = atNameStart[j] ? cases[i].goodNameStart : cases[i].goodName;
+      snprintf(doc, sizeof(doc), "<%s%s><!--", atNameStart[j] ? "" : "a",
+               cases[i].tagName);
+      XML_Parser parser = XML_ParserCreate(NULL);
+
+      const enum XML_Status status = _XML_Parse_SINGLE_BYTES(
+          parser, doc, (int)strlen(doc), /*isFinal=*/XML_FALSE);
+
+      bool success = true;
+      if ((status == XML_STATUS_OK) != expectedSuccess) {
+        success = false;
+      }
+      if ((status == XML_STATUS_ERROR)
+          && (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN)) {
+        success = false;
+      }
+
+      if (! success) {
+        fprintf(
+            stderr,
+            "FAIL case %2u (%sat name start, %u-byte sequence, error code %d)\n",
+            (unsigned)i + 1u, atNameStart[j] ? "    " : "not ",
+            (unsigned)strlen(cases[i].tagName), XML_GetErrorCode(parser));
+        failCount++;
+      }
+
+      XML_ParserFree(parser);
+    }
+  }
+
+  if (failCount > 0) {
+    fail("UTF-8 regression detected");
+  }
+}
+END_TEST
+
+/* Test trailing spaces in elements are accepted */
+START_TEST(test_trailing_spaces_in_elements) {
+  const char *text = "<doc   >Hi</doc >";
+  const XML_Char *expected = XCS("doc/doc");
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetElementHandler(g_parser, record_element_start_handler,
+                        record_element_end_handler);
+  XML_SetUserData(g_parser, &storage);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_utf16_attribute) {
+  const char text[] =
+      /* <d {KHO KHWAI}{CHO CHAN}='a'/>
+       * where {KHO KHWAI} = U+0E04 = 0xe0 0xb8 0x84 in UTF-8
+       * and   {CHO CHAN}  = U+0E08 = 0xe0 0xb8 0x88 in UTF-8
+       */
+      "<\0d\0 \0\x04\x0e\x08\x0e=\0'\0a\0'\0/\0>\0";
+  const XML_Char *expected = XCS("a");
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetStartElementHandler(g_parser, accumulate_attribute);
+  XML_SetUserData(g_parser, &storage);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_utf16_second_attr) {
+  /* <d a='1' {KHO KHWAI}{CHO CHAN}='2'/>
+   * where {KHO KHWAI} = U+0E04 = 0xe0 0xb8 0x84 in UTF-8
+   * and   {CHO CHAN}  = U+0E08 = 0xe0 0xb8 0x88 in UTF-8
+   */
+  const char text[] = "<\0d\0 \0a\0=\0'\0\x31\0'\0 \0"
+                      "\x04\x0e\x08\x0e=\0'\0\x32\0'\0/\0>\0";
+  const XML_Char *expected = XCS("1");
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetStartElementHandler(g_parser, accumulate_attribute);
+  XML_SetUserData(g_parser, &storage);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_attr_after_solidus) {
+  const char *text = "<doc attr1='a' / attr2='b'>";
+
+  expect_failure(text, XML_ERROR_INVALID_TOKEN, "Misplaced / not faulted");
+}
+END_TEST
+
+START_TEST(test_utf16_pe) {
+  /* <!DOCTYPE doc [
+   * <!ENTITY % {KHO KHWAI}{CHO CHAN} '<!ELEMENT doc (#PCDATA)>'>
+   * %{KHO KHWAI}{CHO CHAN};
+   * ]>
+   * <doc></doc>
+   *
+   * where {KHO KHWAI} = U+0E04 = 0xe0 0xb8 0x84 in UTF-8
+   * and   {CHO CHAN}  = U+0E08 = 0xe0 0xb8 0x88 in UTF-8
+   */
+  const char text[] = "\0<\0!\0D\0O\0C\0T\0Y\0P\0E\0 \0d\0o\0c\0 \0[\0\n"
+                      "\0<\0!\0E\0N\0T\0I\0T\0Y\0 \0%\0 \x0e\x04\x0e\x08\0 "
+                      "\0'\0<\0!\0E\0L\0E\0M\0E\0N\0T\0 "
+                      "\0d\0o\0c\0 \0(\0#\0P\0C\0D\0A\0T\0A\0)\0>\0'\0>\0\n"
+                      "\0%\x0e\x04\x0e\x08\0;\0\n"
+                      "\0]\0>\0\n"
+                      "\0<\0d\0o\0c\0>\0<\0/\0d\0o\0c\0>";
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("\x0e04\x0e08=<!ELEMENT doc (#PCDATA)>\n");
+#else
+  const XML_Char *expected
+      = XCS("\xe0\xb8\x84\xe0\xb8\x88=<!ELEMENT doc (#PCDATA)>\n");
+#endif
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetUserData(g_parser, &storage);
+  XML_SetEntityDeclHandler(g_parser, accumulate_entity_decl);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+/* Test that duff attribute description keywords are rejected */
+START_TEST(test_bad_attr_desc_keyword) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ATTLIST doc attr CDATA #!IMPLIED>\n"
+                     "]>\n"
+                     "<doc />";
+
+  expect_failure(text, XML_ERROR_INVALID_TOKEN,
+                 "Bad keyword !IMPLIED not faulted");
+}
+END_TEST
+
+/* Test that an invalid attribute description keyword consisting of
+ * UTF-16 characters with their top bytes non-zero are correctly
+ * faulted
+ */
+START_TEST(test_bad_attr_desc_keyword_utf16) {
+  /* <!DOCTYPE d [
+   * <!ATTLIST d a CDATA #{KHO KHWAI}{CHO CHAN}>
+   * ]><d/>
+   *
+   * where {KHO KHWAI} = U+0E04 = 0xe0 0xb8 0x84 in UTF-8
+   * and   {CHO CHAN}  = U+0E08 = 0xe0 0xb8 0x88 in UTF-8
+   */
+  const char text[]
+      = "\0<\0!\0D\0O\0C\0T\0Y\0P\0E\0 \0d\0 \0[\0\n"
+        "\0<\0!\0A\0T\0T\0L\0I\0S\0T\0 \0d\0 \0a\0 \0C\0D\0A\0T\0A\0 "
+        "\0#\x0e\x04\x0e\x08\0>\0\n"
+        "\0]\0>\0<\0d\0/\0>";
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail("Invalid UTF16 attribute keyword not faulted");
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_SYNTAX)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test that invalid syntax in a <!DOCTYPE> is rejected.  Do this
+ * using prefix-encoding (see above) to trigger specific code paths
+ */
+START_TEST(test_bad_doctype) {
+  const char *text = "<?xml version='1.0' encoding='prefix-conv'?>\n"
+                     "<!DOCTYPE doc [ \x80\x44 ]><doc/>";
+
+  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
+  expect_failure(text, XML_ERROR_SYNTAX,
+                 "Invalid bytes in DOCTYPE not faulted");
+}
+END_TEST
+
+START_TEST(test_bad_doctype_utf8) {
+  const char *text = "<!DOCTYPE \xDB\x25"
+                     "doc><doc/>"; // [1101 1011] [<0>010 0101]
+  expect_failure(text, XML_ERROR_INVALID_TOKEN,
+                 "Invalid UTF-8 in DOCTYPE not faulted");
+}
+END_TEST
+
+START_TEST(test_bad_doctype_utf16) {
+  const char text[] =
+      /* <!DOCTYPE doc [ \x06f2 ]><doc/>
+       *
+       * U+06F2 = EXTENDED ARABIC-INDIC DIGIT TWO, a valid number
+       * (name character) but not a valid letter (name start character)
+       */
+      "\0<\0!\0D\0O\0C\0T\0Y\0P\0E\0 \0d\0o\0c\0 \0[\0 "
+      "\x06\xf2"
+      "\0 \0]\0>\0<\0d\0o\0c\0/\0>";
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail("Invalid bytes in DOCTYPE not faulted");
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_SYNTAX)
+    xml_failure(g_parser);
+}
+END_TEST
+
+START_TEST(test_bad_doctype_plus) {
+  const char *text = "<!DOCTYPE 1+ [ <!ENTITY foo 'bar'> ]>\n"
+                     "<1+>&foo;</1+>";
+
+  expect_failure(text, XML_ERROR_INVALID_TOKEN,
+                 "'+' in document name not faulted");
+}
+END_TEST
+
+START_TEST(test_bad_doctype_star) {
+  const char *text = "<!DOCTYPE 1* [ <!ENTITY foo 'bar'> ]>\n"
+                     "<1*>&foo;</1*>";
+
+  expect_failure(text, XML_ERROR_INVALID_TOKEN,
+                 "'*' in document name not faulted");
+}
+END_TEST
+
+START_TEST(test_bad_doctype_query) {
+  const char *text = "<!DOCTYPE 1? [ <!ENTITY foo 'bar'> ]>\n"
+                     "<1?>&foo;</1?>";
+
+  expect_failure(text, XML_ERROR_INVALID_TOKEN,
+                 "'?' in document name not faulted");
+}
+END_TEST
+
+START_TEST(test_unknown_encoding_bad_ignore) {
+  const char *text = "<?xml version='1.0' encoding='prefix-conv'?>"
+                     "<!DOCTYPE doc SYSTEM 'foo'>"
+                     "<doc><e>&entity;</e></doc>";
+  ExtFaults fault = {"<![IGNORE[<!ELEMENT \xffG (#PCDATA)*>]]>",
+                     "Invalid character not faulted", XCS("prefix-conv"),
+                     XML_ERROR_INVALID_TOKEN};
+
+  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_faulter);
+  XML_SetUserData(g_parser, &fault);
+  expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
+                 "Bad IGNORE section with unknown encoding not failed");
+}
+END_TEST
+
+START_TEST(test_entity_in_utf16_be_attr) {
+  const char text[] =
+      /* <e a='&#228; &#x00E4;'></e> */
+      "\0<\0e\0 \0a\0=\0'\0&\0#\0\x32\0\x32\0\x38\0;\0 "
+      "\0&\0#\0x\0\x30\0\x30\0E\0\x34\0;\0'\0>\0<\0/\0e\0>";
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("\x00e4 \x00e4");
+#else
+  const XML_Char *expected = XCS("\xc3\xa4 \xc3\xa4");
+#endif
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetUserData(g_parser, &storage);
+  XML_SetStartElementHandler(g_parser, accumulate_attribute);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_entity_in_utf16_le_attr) {
+  const char text[] =
+      /* <e a='&#228; &#x00E4;'></e> */
+      "<\0e\0 \0a\0=\0'\0&\0#\0\x32\0\x32\0\x38\0;\0 \0"
+      "&\0#\0x\0\x30\0\x30\0E\0\x34\0;\0'\0>\0<\0/\0e\0>\0";
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("\x00e4 \x00e4");
+#else
+  const XML_Char *expected = XCS("\xc3\xa4 \xc3\xa4");
+#endif
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetUserData(g_parser, &storage);
+  XML_SetStartElementHandler(g_parser, accumulate_attribute);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_entity_public_utf16_be) {
+  const char text[] =
+      /* <!DOCTYPE d [ */
+      "\0<\0!\0D\0O\0C\0T\0Y\0P\0E\0 \0d\0 \0[\0\n"
+      /* <!ENTITY % e PUBLIC 'foo' 'bar.ent'> */
+      "\0<\0!\0E\0N\0T\0I\0T\0Y\0 \0%\0 \0e\0 \0P\0U\0B\0L\0I\0C\0 "
+      "\0'\0f\0o\0o\0'\0 \0'\0b\0a\0r\0.\0e\0n\0t\0'\0>\0\n"
+      /* %e; */
+      "\0%\0e\0;\0\n"
+      /* ]> */
+      "\0]\0>\0\n"
+      /* <d>&j;</d> */
+      "\0<\0d\0>\0&\0j\0;\0<\0/\0d\0>";
+  ExtTest2 test_data
+      = {/* <!ENTITY j 'baz'> */
+         "\0<\0!\0E\0N\0T\0I\0T\0Y\0 \0j\0 \0'\0b\0a\0z\0'\0>", 34, NULL, NULL};
+  const XML_Char *expected = XCS("baz");
+  CharData storage;
+
+  CharData_Init(&storage);
+  test_data.storage = &storage;
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader2);
+  XML_SetUserData(g_parser, &test_data);
+  XML_SetCharacterDataHandler(g_parser, ext2_accumulate_characters);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_entity_public_utf16_le) {
+  const char text[] =
+      /* <!DOCTYPE d [ */
+      "<\0!\0D\0O\0C\0T\0Y\0P\0E\0 \0d\0 \0[\0\n\0"
+      /* <!ENTITY % e PUBLIC 'foo' 'bar.ent'> */
+      "<\0!\0E\0N\0T\0I\0T\0Y\0 \0%\0 \0e\0 \0P\0U\0B\0L\0I\0C\0 \0"
+      "'\0f\0o\0o\0'\0 \0'\0b\0a\0r\0.\0e\0n\0t\0'\0>\0\n\0"
+      /* %e; */
+      "%\0e\0;\0\n\0"
+      /* ]> */
+      "]\0>\0\n\0"
+      /* <d>&j;</d> */
+      "<\0d\0>\0&\0j\0;\0<\0/\0d\0>\0";
+  ExtTest2 test_data
+      = {/* <!ENTITY j 'baz'> */
+         "<\0!\0E\0N\0T\0I\0T\0Y\0 \0j\0 \0'\0b\0a\0z\0'\0>\0", 34, NULL, NULL};
+  const XML_Char *expected = XCS("baz");
+  CharData storage;
+
+  CharData_Init(&storage);
+  test_data.storage = &storage;
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader2);
+  XML_SetUserData(g_parser, &test_data);
+  XML_SetCharacterDataHandler(g_parser, ext2_accumulate_characters);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+/* Test that a doctype with neither an internal nor external subset is
+ * faulted
+ */
+START_TEST(test_short_doctype) {
+  const char *text = "<!DOCTYPE doc></doc>";
+  expect_failure(text, XML_ERROR_INVALID_TOKEN,
+                 "DOCTYPE without subset not rejected");
+}
+END_TEST
+
+START_TEST(test_short_doctype_2) {
+  const char *text = "<!DOCTYPE doc PUBLIC></doc>";
+  expect_failure(text, XML_ERROR_SYNTAX,
+                 "DOCTYPE without Public ID not rejected");
+}
+END_TEST
+
+START_TEST(test_short_doctype_3) {
+  const char *text = "<!DOCTYPE doc SYSTEM></doc>";
+  expect_failure(text, XML_ERROR_SYNTAX,
+                 "DOCTYPE without System ID not rejected");
+}
+END_TEST
+
+START_TEST(test_long_doctype) {
+  const char *text = "<!DOCTYPE doc PUBLIC 'foo' 'bar' 'baz'></doc>";
+  expect_failure(text, XML_ERROR_SYNTAX, "DOCTYPE with extra ID not rejected");
+}
+END_TEST
+
+START_TEST(test_bad_entity) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY foo PUBLIC>\n"
+                     "]>\n"
+                     "<doc/>";
+  expect_failure(text, XML_ERROR_SYNTAX,
+                 "ENTITY without Public ID is not rejected");
+}
+END_TEST
+
+/* Test unquoted value is faulted */
+START_TEST(test_bad_entity_2) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY % foo bar>\n"
+                     "]>\n"
+                     "<doc/>";
+  expect_failure(text, XML_ERROR_SYNTAX,
+                 "ENTITY without Public ID is not rejected");
+}
+END_TEST
+
+START_TEST(test_bad_entity_3) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY % foo PUBLIC>\n"
+                     "]>\n"
+                     "<doc/>";
+  expect_failure(text, XML_ERROR_SYNTAX,
+                 "Parameter ENTITY without Public ID is not rejected");
+}
+END_TEST
+
+START_TEST(test_bad_entity_4) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ENTITY % foo SYSTEM>\n"
+                     "]>\n"
+                     "<doc/>";
+  expect_failure(text, XML_ERROR_SYNTAX,
+                 "Parameter ENTITY without Public ID is not rejected");
+}
+END_TEST
+
+START_TEST(test_bad_notation) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!NOTATION n SYSTEM>\n"
+                     "]>\n"
+                     "<doc/>";
+  expect_failure(text, XML_ERROR_SYNTAX,
+                 "Notation without System ID is not rejected");
+}
+END_TEST
+
+/* Test for issue #11, wrongly suppressed default handler */
+START_TEST(test_default_doctype_handler) {
+  const char *text = "<!DOCTYPE doc PUBLIC 'pubname' 'test.dtd' [\n"
+                     "  <!ENTITY foo 'bar'>\n"
+                     "]>\n"
+                     "<doc>&foo;</doc>";
+  DefaultCheck test_data[] = {{XCS("'pubname'"), 9, XML_FALSE},
+                              {XCS("'test.dtd'"), 10, XML_FALSE},
+                              {NULL, 0, XML_FALSE}};
+  int i;
+
+  XML_SetUserData(g_parser, &test_data);
+  XML_SetDefaultHandler(g_parser, checking_default_handler);
+  XML_SetEntityDeclHandler(g_parser, dummy_entity_decl_handler);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  for (i = 0; test_data[i].expected != NULL; i++)
+    if (! test_data[i].seen)
+      fail("Default handler not run for public !DOCTYPE");
+}
+END_TEST
+
+START_TEST(test_empty_element_abort) {
+  const char *text = "<abort/>";
+
+  XML_SetStartElementHandler(g_parser, start_element_suspender);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail("Expected to error on abort");
+}
+END_TEST
+
+/* Regression test for GH issue #612: unfinished m_declAttributeType
+ * allocation in ->m_tempPool can corrupt following allocation.
+ */
+START_TEST(test_pool_integrity_with_unfinished_attr) {
+  const char *text = "<?xml version='1.0' encoding='UTF-8'?>\n"
+                     "<!DOCTYPE foo [\n"
+                     "<!ELEMENT foo ANY>\n"
+                     "<!ENTITY % entp SYSTEM \"external.dtd\">\n"
+                     "%entp;\n"
+                     "]>\n"
+                     "<a></a>\n";
+  const XML_Char *expected = XCS("COMMENT");
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_unfinished_attlist);
+  XML_SetAttlistDeclHandler(g_parser, dummy_attlist_decl_handler);
+  XML_SetCommentHandler(g_parser, accumulate_comment);
+  XML_SetUserData(g_parser, &storage);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_nested_entity_suspend) {
+  const char *const text = "<!DOCTYPE a [\n"
+                           "  <!ENTITY e1 '<!--e1-->'>\n"
+                           "  <!ENTITY e2 '<!--e2 head-->&e1;<!--e2 tail-->'>\n"
+                           "  <!ENTITY e3 '<!--e3 head-->&e2;<!--e3 tail-->'>\n"
+                           "]>\n"
+                           "<a><!--start-->&e3;<!--end--></a>";
+  const XML_Char *const expected = XCS("start") XCS("e3 head") XCS("e2 head")
+      XCS("e1") XCS("e2 tail") XCS("e3 tail") XCS("end");
+  CharData storage;
+  CharData_Init(&storage);
+  XML_Parser parser = XML_ParserCreate(NULL);
+  ParserPlusStorage parserPlusStorage = {parser, &storage};
+
+  XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetCommentHandler(parser, accumulate_and_suspend_comment_handler);
+  XML_SetUserData(parser, &parserPlusStorage);
+
+  enum XML_Status status = XML_Parse(parser, text, (int)strlen(text), XML_TRUE);
+  while (status == XML_STATUS_SUSPENDED) {
+    status = XML_ResumeParser(parser);
+  }
+  if (status != XML_STATUS_OK)
+    xml_failure(parser);
+
+  CharData_CheckXMLChars(&storage, expected);
+  XML_ParserFree(parser);
+}
+END_TEST
+
+/* Regression test for quadratic parsing on large tokens */
+START_TEST(test_big_tokens_scale_linearly) {
+  const struct {
+    const char *pre;
+    const char *post;
+  } text[] = {
+      {"<a>", "</a>"},                      // assumed good, used as baseline
+      {"<b><![CDATA[ value: ", " ]]></b>"}, // CDATA, performed OK before patch
+      {"<c attr='", "'></c>"},              // big attribute, used to be O(N²)
+      {"<d><!-- ", " --></d>"},             // long comment, used to be O(N²)
+      {"<e><", "/></e>"},                   // big elem name, used to be O(N²)
+  };
+  const int num_cases = sizeof(text) / sizeof(text[0]);
+  char aaaaaa[4096];
+  const int fillsize = (int)sizeof(aaaaaa);
+  const int fillcount = 100;
+  const unsigned approx_bytes = fillsize * fillcount; // ignore pre/post.
+  const unsigned max_factor = 4;
+  const unsigned max_scanned = max_factor * approx_bytes;
+
+  memset(aaaaaa, 'a', fillsize);
+
+  if (! g_reparseDeferralEnabledDefault) {
+    return; // heuristic is disabled; we would get O(n^2) and fail.
+  }
+
+  for (int i = 0; i < num_cases; ++i) {
+    XML_Parser parser = XML_ParserCreate(NULL);
+    assert_true(parser != NULL);
+    enum XML_Status status;
+    set_subtest("text=\"%saaaaaa%s\"", text[i].pre, text[i].post);
+
+    // parse the start text
+    g_bytesScanned = 0;
+    status = _XML_Parse_SINGLE_BYTES(parser, text[i].pre,
+                                     (int)strlen(text[i].pre), XML_FALSE);
+    if (status != XML_STATUS_OK) {
+      xml_failure(parser);
+    }
+
+    // parse lots of 'a', failing the test early if it takes too long
+    unsigned past_max_count = 0;
+    for (int f = 0; f < fillcount; ++f) {
+      status = _XML_Parse_SINGLE_BYTES(parser, aaaaaa, fillsize, XML_FALSE);
+      if (status != XML_STATUS_OK) {
+        xml_failure(parser);
+      }
+      if (g_bytesScanned > max_scanned) {
+        // We're not done, and have already passed the limit -- the test will
+        // definitely fail. This block allows us to save time by failing early.
+        const unsigned pushed
+            = (unsigned)strlen(text[i].pre) + (f + 1) * fillsize;
+        fprintf(
+            stderr,
+            "after %d/%d loops: pushed=%u scanned=%u (factor ~%.2f) max_scanned: %u (factor ~%u)\n",
+            f + 1, fillcount, pushed, g_bytesScanned,
+            g_bytesScanned / (double)pushed, max_scanned, max_factor);
+        past_max_count++;
+        // We are failing, but allow a few log prints first. If we don't reach
+        // a count of five, the test will fail after the loop instead.
+        assert_true(past_max_count < 5);
+      }
+    }
+
+    // parse the end text
+    status = _XML_Parse_SINGLE_BYTES(parser, text[i].post,
+                                     (int)strlen(text[i].post), XML_TRUE);
+    if (status != XML_STATUS_OK) {
+      xml_failure(parser);
+    }
+
+    assert_true(g_bytesScanned > approx_bytes); // or the counter isn't working
+    if (g_bytesScanned > max_scanned) {
+      fprintf(
+          stderr,
+          "after all input: scanned=%u (factor ~%.2f) max_scanned: %u (factor ~%u)\n",
+          g_bytesScanned, g_bytesScanned / (double)approx_bytes, max_scanned,
+          max_factor);
+      fail("scanned too many bytes");
+    }
+
+    XML_ParserFree(parser);
+  }
+}
+END_TEST
+
+START_TEST(test_set_reparse_deferral) {
+  const char *const pre = "<d>";
+  const char *const start = "<x attr='";
+  const char *const end = "'></x>";
+  char eeeeee[100];
+  const int fillsize = (int)sizeof(eeeeee);
+  memset(eeeeee, 'e', fillsize);
+
+  for (int enabled = 0; enabled <= 1; enabled += 1) {
+    set_subtest("deferral=%d", enabled);
+
+    XML_Parser parser = XML_ParserCreate(NULL);
+    assert_true(parser != NULL);
+    assert_true(XML_SetReparseDeferralEnabled(parser, enabled));
+    // pre-grow the buffer to avoid reparsing due to almost-fullness
+    assert_true(XML_GetBuffer(parser, fillsize * 10103) != NULL);
+
+    CharData storage;
+    CharData_Init(&storage);
+    XML_SetUserData(parser, &storage);
+    XML_SetStartElementHandler(parser, start_element_event_handler);
+
+    enum XML_Status status;
+    // parse the start text
+    status = XML_Parse(parser, pre, (int)strlen(pre), XML_FALSE);
+    if (status != XML_STATUS_OK) {
+      xml_failure(parser);
+    }
+    CharData_CheckXMLChars(&storage, XCS("d")); // first element should be done
+
+    // ..and the start of the token
+    status = XML_Parse(parser, start, (int)strlen(start), XML_FALSE);
+    if (status != XML_STATUS_OK) {
+      xml_failure(parser);
+    }
+    CharData_CheckXMLChars(&storage, XCS("d")); // still just the first one
+
+    // try to parse lots of 'e', but the token isn't finished
+    for (int c = 0; c < 100; ++c) {
+      status = XML_Parse(parser, eeeeee, fillsize, XML_FALSE);
+      if (status != XML_STATUS_OK) {
+        xml_failure(parser);
+      }
+    }
+    CharData_CheckXMLChars(&storage, XCS("d")); // *still* just the first one
+
+    // end the <x> token.
+    status = XML_Parse(parser, end, (int)strlen(end), XML_FALSE);
+    if (status != XML_STATUS_OK) {
+      xml_failure(parser);
+    }
+
+    if (enabled) {
+      // In general, we may need to push more data to trigger a reparse attempt,
+      // but in this test, the data is constructed to always require it.
+      CharData_CheckXMLChars(&storage, XCS("d")); // or the test is incorrect
+      // 2x the token length should suffice; the +1 covers the start and end.
+      for (int c = 0; c < 101; ++c) {
+        status = XML_Parse(parser, eeeeee, fillsize, XML_FALSE);
+        if (status != XML_STATUS_OK) {
+          xml_failure(parser);
+        }
+      }
+    }
+    CharData_CheckXMLChars(&storage, XCS("dx")); // the <x> should be done
+
+    XML_ParserFree(parser);
+  }
+}
+END_TEST
+
+struct element_decl_data {
+  XML_Parser parser;
+  int count;
+};
+
+static void
+element_decl_counter(void *userData, const XML_Char *name, XML_Content *model) {
+  UNUSED_P(name);
+  struct element_decl_data *testdata = (struct element_decl_data *)userData;
+  testdata->count += 1;
+  XML_FreeContentModel(testdata->parser, model);
+}
+
+static int
+external_inherited_parser(XML_Parser p, const XML_Char *context,
+                          const XML_Char *base, const XML_Char *systemId,
+                          const XML_Char *publicId) {
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  const char *const pre = "<!ELEMENT document ANY>\n";
+  const char *const start = "<!ELEMENT ";
+  const char *const end = " ANY>\n";
+  const char *const post = "<!ELEMENT xyz ANY>\n";
+  const int enabled = *(int *)XML_GetUserData(p);
+  char eeeeee[100];
+  char spaces[100];
+  const int fillsize = (int)sizeof(eeeeee);
+  assert_true(fillsize == (int)sizeof(spaces));
+  memset(eeeeee, 'e', fillsize);
+  memset(spaces, ' ', fillsize);
+
+  XML_Parser parser = XML_ExternalEntityParserCreate(p, context, NULL);
+  assert_true(parser != NULL);
+  // pre-grow the buffer to avoid reparsing due to almost-fullness
+  assert_true(XML_GetBuffer(parser, fillsize * 10103) != NULL);
+
+  struct element_decl_data testdata;
+  testdata.parser = parser;
+  testdata.count = 0;
+  XML_SetUserData(parser, &testdata);
+  XML_SetElementDeclHandler(parser, element_decl_counter);
+
+  enum XML_Status status;
+  // parse the initial text
+  status = XML_Parse(parser, pre, (int)strlen(pre), XML_FALSE);
+  if (status != XML_STATUS_OK) {
+    xml_failure(parser);
+  }
+  assert_true(testdata.count == 1); // first element should be done
+
+  // ..and the start of the big token
+  status = XML_Parse(parser, start, (int)strlen(start), XML_FALSE);
+  if (status != XML_STATUS_OK) {
+    xml_failure(parser);
+  }
+  assert_true(testdata.count == 1); // still just the first one
+
+  // try to parse lots of 'e', but the token isn't finished
+  for (int c = 0; c < 100; ++c) {
+    status = XML_Parse(parser, eeeeee, fillsize, XML_FALSE);
+    if (status != XML_STATUS_OK) {
+      xml_failure(parser);
+    }
+  }
+  assert_true(testdata.count == 1); // *still* just the first one
+
+  // end the big token.
+  status = XML_Parse(parser, end, (int)strlen(end), XML_FALSE);
+  if (status != XML_STATUS_OK) {
+    xml_failure(parser);
+  }
+
+  if (enabled) {
+    // In general, we may need to push more data to trigger a reparse attempt,
+    // but in this test, the data is constructed to always require it.
+    assert_true(testdata.count == 1); // or the test is incorrect
+    // 2x the token length should suffice; the +1 covers the start and end.
+    for (int c = 0; c < 101; ++c) {
+      status = XML_Parse(parser, spaces, fillsize, XML_FALSE);
+      if (status != XML_STATUS_OK) {
+        xml_failure(parser);
+      }
+    }
+  }
+  assert_true(testdata.count == 2); // the big token should be done
+
+  // parse the final text
+  status = XML_Parse(parser, post, (int)strlen(post), XML_TRUE);
+  if (status != XML_STATUS_OK) {
+    xml_failure(parser);
+  }
+  assert_true(testdata.count == 3); // after isFinal=XML_TRUE, all must be done
+
+  XML_ParserFree(parser);
+  return XML_STATUS_OK;
+}
+
+START_TEST(test_reparse_deferral_is_inherited) {
+  const char *const text
+      = "<!DOCTYPE document SYSTEM 'something.ext'><document/>";
+  for (int enabled = 0; enabled <= 1; ++enabled) {
+    set_subtest("deferral=%d", enabled);
+
+    XML_Parser parser = XML_ParserCreate(NULL);
+    assert_true(parser != NULL);
+    XML_SetUserData(parser, (void *)&enabled);
+    XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    // this handler creates a sub-parser and checks that its deferral behavior
+    // is what we expected, based on the value of `enabled` (in userdata).
+    XML_SetExternalEntityRefHandler(parser, external_inherited_parser);
+    assert_true(XML_SetReparseDeferralEnabled(parser, enabled));
+    if (XML_Parse(parser, text, (int)strlen(text), XML_TRUE) != XML_STATUS_OK)
+      xml_failure(parser);
+
+    XML_ParserFree(parser);
+  }
+}
+END_TEST
+
+START_TEST(test_set_reparse_deferral_on_null_parser) {
+  assert_true(XML_SetReparseDeferralEnabled(NULL, 0) == XML_FALSE);
+  assert_true(XML_SetReparseDeferralEnabled(NULL, 1) == XML_FALSE);
+  assert_true(XML_SetReparseDeferralEnabled(NULL, 10) == XML_FALSE);
+  assert_true(XML_SetReparseDeferralEnabled(NULL, 100) == XML_FALSE);
+  assert_true(XML_SetReparseDeferralEnabled(NULL, (XML_Bool)INT_MIN)
+              == XML_FALSE);
+  assert_true(XML_SetReparseDeferralEnabled(NULL, (XML_Bool)INT_MAX)
+              == XML_FALSE);
+}
+END_TEST
+
+START_TEST(test_set_reparse_deferral_on_the_fly) {
+  const char *const pre = "<d><x attr='";
+  const char *const end = "'></x>";
+  char iiiiii[100];
+  const int fillsize = (int)sizeof(iiiiii);
+  memset(iiiiii, 'i', fillsize);
+
+  XML_Parser parser = XML_ParserCreate(NULL);
+  assert_true(parser != NULL);
+  assert_true(XML_SetReparseDeferralEnabled(parser, XML_TRUE));
+
+  CharData storage;
+  CharData_Init(&storage);
+  XML_SetUserData(parser, &storage);
+  XML_SetStartElementHandler(parser, start_element_event_handler);
+
+  enum XML_Status status;
+  // parse the start text
+  status = XML_Parse(parser, pre, (int)strlen(pre), XML_FALSE);
+  if (status != XML_STATUS_OK) {
+    xml_failure(parser);
+  }
+  CharData_CheckXMLChars(&storage, XCS("d")); // first element should be done
+
+  // try to parse some 'i', but the token isn't finished
+  status = XML_Parse(parser, iiiiii, fillsize, XML_FALSE);
+  if (status != XML_STATUS_OK) {
+    xml_failure(parser);
+  }
+  CharData_CheckXMLChars(&storage, XCS("d")); // *still* just the first one
+
+  // end the <x> token.
+  status = XML_Parse(parser, end, (int)strlen(end), XML_FALSE);
+  if (status != XML_STATUS_OK) {
+    xml_failure(parser);
+  }
+  CharData_CheckXMLChars(&storage, XCS("d")); // not yet.
+
+  // now change the heuristic setting and add *no* data
+  assert_true(XML_SetReparseDeferralEnabled(parser, XML_FALSE));
+  // we avoid isFinal=XML_TRUE, because that would force-bypass the heuristic.
+  status = XML_Parse(parser, "", 0, XML_FALSE);
+  if (status != XML_STATUS_OK) {
+    xml_failure(parser);
+  }
+  CharData_CheckXMLChars(&storage, XCS("dx"));
+
+  XML_ParserFree(parser);
+}
+END_TEST
+
+START_TEST(test_set_bad_reparse_option) {
+  XML_Parser parser = XML_ParserCreate(NULL);
+  assert_true(XML_FALSE == XML_SetReparseDeferralEnabled(parser, 2));
+  assert_true(XML_FALSE == XML_SetReparseDeferralEnabled(parser, 3));
+  assert_true(XML_FALSE == XML_SetReparseDeferralEnabled(parser, 99));
+  assert_true(XML_FALSE == XML_SetReparseDeferralEnabled(parser, 127));
+  assert_true(XML_FALSE == XML_SetReparseDeferralEnabled(parser, 128));
+  assert_true(XML_FALSE == XML_SetReparseDeferralEnabled(parser, 129));
+  assert_true(XML_FALSE == XML_SetReparseDeferralEnabled(parser, 255));
+  assert_true(XML_TRUE == XML_SetReparseDeferralEnabled(parser, 0));
+  assert_true(XML_TRUE == XML_SetReparseDeferralEnabled(parser, 1));
+  XML_ParserFree(parser);
+}
+END_TEST
+
+static size_t g_totalAlloc = 0;
+static size_t g_biggestAlloc = 0;
+
+static void *
+counting_realloc(void *ptr, size_t size) {
+  g_totalAlloc += size;
+  if (size > g_biggestAlloc) {
+    g_biggestAlloc = size;
+  }
+  return realloc(ptr, size);
+}
+
+static void *
+counting_malloc(size_t size) {
+  return counting_realloc(NULL, size);
+}
+
+START_TEST(test_bypass_heuristic_when_close_to_bufsize) {
+  if (g_chunkSize != 0) {
+    // this test does not use SINGLE_BYTES, because it depends on very precise
+    // buffer fills.
+    return;
+  }
+  if (! g_reparseDeferralEnabledDefault) {
+    return; // this test is irrelevant when the deferral heuristic is disabled.
+  }
+
+  const int document_length = 65536;
+  char *const document = (char *)malloc(document_length);
+
+  const XML_Memory_Handling_Suite memfuncs = {
+      counting_malloc,
+      counting_realloc,
+      free,
+  };
+
+  const int leading_list[] = {0, 3, 61, 96, 400, 401, 4000, 4010, 4099, -1};
+  const int bigtoken_list[] = {3000, 4000, 4001, 4096, 4099, 5000, 20000, -1};
+  const int fillsize_list[] = {131, 256, 399, 400, 401, 1025, 4099, 4321, -1};
+
+  for (const int *leading = leading_list; *leading >= 0; leading++) {
+    for (const int *bigtoken = bigtoken_list; *bigtoken >= 0; bigtoken++) {
+      for (const int *fillsize = fillsize_list; *fillsize >= 0; fillsize++) {
+        set_subtest("leading=%d bigtoken=%d fillsize=%d", *leading, *bigtoken,
+                    *fillsize);
+        // start by checking that the test looks reasonably valid
+        assert_true(*leading + *bigtoken <= document_length);
+
+        // put 'x' everywhere; some will be overwritten by elements.
+        memset(document, 'x', document_length);
+        // maybe add an initial tag
+        if (*leading) {
+          assert_true(*leading >= 3); // or the test case is invalid
+          memcpy(document, "<a>", 3);
+        }
+        // add the large token
+        document[*leading + 0] = '<';
+        document[*leading + 1] = 'b';
+        memset(&document[*leading + 2], ' ', *bigtoken - 2); // a spacy token
+        document[*leading + *bigtoken - 1] = '>';
+
+        // 1 for 'b', plus 1 or 0 depending on the presence of 'a'
+        const int expected_elem_total = 1 + (*leading ? 1 : 0);
+
+        XML_Parser parser = XML_ParserCreate_MM(NULL, &memfuncs, NULL);
+        assert_true(parser != NULL);
+
+        CharData storage;
+        CharData_Init(&storage);
+        XML_SetUserData(parser, &storage);
+        XML_SetStartElementHandler(parser, start_element_event_handler);
+
+        g_biggestAlloc = 0;
+        g_totalAlloc = 0;
+        int offset = 0;
+        // fill data until the big token is covered (but not necessarily parsed)
+        while (offset < *leading + *bigtoken) {
+          assert_true(offset + *fillsize <= document_length);
+          const enum XML_Status status
+              = XML_Parse(parser, &document[offset], *fillsize, XML_FALSE);
+          if (status != XML_STATUS_OK) {
+            xml_failure(parser);
+          }
+          offset += *fillsize;
+        }
+        // Now, check that we've had a buffer allocation that could fit the
+        // context bytes and our big token. In order to detect a special case,
+        // we need to know how many bytes of our big token were included in the
+        // first push that contained _any_ bytes of the big token:
+        const int bigtok_first_chunk_bytes = *fillsize - (*leading % *fillsize);
+        if (bigtok_first_chunk_bytes >= *bigtoken && XML_CONTEXT_BYTES == 0) {
+          // Special case: we aren't saving any context, and the whole big token
+          // was covered by a single fill, so Expat may have parsed directly
+          // from our input pointer, without allocating an internal buffer.
+        } else if (*leading < XML_CONTEXT_BYTES) {
+          assert_true(g_biggestAlloc >= *leading + (size_t)*bigtoken);
+        } else {
+          assert_true(g_biggestAlloc >= XML_CONTEXT_BYTES + (size_t)*bigtoken);
+        }
+        // fill data until the big token is actually parsed
+        while (storage.count < expected_elem_total) {
+          const size_t alloc_before = g_totalAlloc;
+          assert_true(offset + *fillsize <= document_length);
+          const enum XML_Status status
+              = XML_Parse(parser, &document[offset], *fillsize, XML_FALSE);
+          if (status != XML_STATUS_OK) {
+            xml_failure(parser);
+          }
+          offset += *fillsize;
+          // since all the bytes of the big token are already in the buffer,
+          // the bufsize ceiling should make us finish its parsing without any
+          // further buffer allocations. We assume that there will be no other
+          // large allocations in this test.
+          assert_true(g_totalAlloc - alloc_before < 4096);
+        }
+        // test-the-test: was our alloc even called?
+        assert_true(g_totalAlloc > 0);
+        // test-the-test: there shouldn't be any extra start elements
+        assert_true(storage.count == expected_elem_total);
+
+        XML_ParserFree(parser);
+      }
+    }
+  }
+  free(document);
+}
+END_TEST
+
+START_TEST(test_varying_buffer_fills) {
+  const int KiB = 1024;
+  const int MiB = 1024 * KiB;
+  const int document_length = 16 * MiB;
+  const int big = 7654321; // arbitrarily chosen between 4 and 8 MiB
+
+  if (g_chunkSize != 0) {
+    return; // this test is slow, and doesn't use _XML_Parse_SINGLE_BYTES().
+  }
+
+  char *const document = (char *)malloc(document_length);
+  assert_true(document != NULL);
+  memset(document, 'x', document_length);
+  document[0] = '<';
+  document[1] = 't';
+  memset(&document[2], ' ', big - 2); // a very spacy token
+  document[big - 1] = '>';
+
+  // Each testcase is a list of buffer fill sizes, terminated by a value < 0.
+  // When reparse deferral is enabled, the final (negated) value is the expected
+  // maximum number of bytes scanned in parse attempts.
+  const int testcases[][30] = {
+      {8 * MiB, -8 * MiB},
+      {4 * MiB, 4 * MiB, -12 * MiB}, // try at 4MB, then 8MB = 12 MB total
+      // zero-size fills shouldn't trigger the bypass
+      {4 * MiB, 0, 4 * MiB, -12 * MiB},
+      {4 * MiB, 0, 0, 4 * MiB, -12 * MiB},
+      {4 * MiB, 0, 1 * MiB, 0, 3 * MiB, -12 * MiB},
+      // try to hit the buffer ceiling only once (at the end)
+      {4 * MiB, 2 * MiB, 1 * MiB, 512 * KiB, 256 * KiB, 256 * KiB, -12 * MiB},
+      // try to hit the same buffer ceiling multiple times
+      {4 * MiB + 1, 2 * MiB, 1 * MiB, 512 * KiB, -25 * MiB},
+
+      // try to hit every ceiling, by always landing 1K shy of the buffer size
+      {1 * KiB, 2 * KiB, 4 * KiB, 8 * KiB, 16 * KiB, 32 * KiB, 64 * KiB,
+       128 * KiB, 256 * KiB, 512 * KiB, 1 * MiB, 2 * MiB, 4 * MiB, -16 * MiB},
+
+      // try to avoid every ceiling, by always landing 1B past the buffer size
+      // the normal 2x heuristic threshold still forces parse attempts.
+      {2 * KiB + 1,          // will attempt 2KiB + 1 ==> total 2KiB + 1
+       2 * KiB, 4 * KiB,     // will attempt 8KiB + 1 ==> total 10KiB + 2
+       8 * KiB, 16 * KiB,    // will attempt 32KiB + 1 ==> total 42KiB + 3
+       32 * KiB, 64 * KiB,   // will attempt 128KiB + 1 ==> total 170KiB + 4
+       128 * KiB, 256 * KiB, // will attempt 512KiB + 1 ==> total 682KiB + 5
+       512 * KiB, 1 * MiB,   // will attempt 2MiB + 1 ==> total 2M + 682K + 6
+       2 * MiB, 4 * MiB,     // will attempt 8MiB + 1 ==> total 10M + 682K + 7
+       -(10 * MiB + 682 * KiB + 7)},
+      // try to avoid every ceiling again, except on our last fill.
+      {2 * KiB + 1,          // will attempt 2KiB + 1 ==> total 2KiB + 1
+       2 * KiB, 4 * KiB,     // will attempt 8KiB + 1 ==> total 10KiB + 2
+       8 * KiB, 16 * KiB,    // will attempt 32KiB + 1 ==> total 42KiB + 3
+       32 * KiB, 64 * KiB,   // will attempt 128KiB + 1 ==> total 170KiB + 4
+       128 * KiB, 256 * KiB, // will attempt 512KiB + 1 ==> total 682KiB + 5
+       512 * KiB, 1 * MiB,   // will attempt 2MiB + 1 ==> total 2M + 682K + 6
+       2 * MiB, 4 * MiB - 1, // will attempt 8MiB ==> total 10M + 682K + 6
+       -(10 * MiB + 682 * KiB + 6)},
+
+      // try to hit ceilings on the way multiple times
+      {512 * KiB + 1, 256 * KiB, 128 * KiB, 128 * KiB - 1, // 1 MiB buffer
+       512 * KiB + 1, 256 * KiB, 128 * KiB, 128 * KiB - 1, // 2 MiB buffer
+       1 * MiB + 1, 512 * KiB, 256 * KiB, 256 * KiB - 1,   // 4 MiB buffer
+       2 * MiB + 1, 1 * MiB, 512 * KiB,                    // 8 MiB buffer
+       // we'll make a parse attempt at every parse call
+       -(45 * MiB + 12)},
+  };
+  const int testcount = sizeof(testcases) / sizeof(testcases[0]);
+  for (int test_i = 0; test_i < testcount; test_i++) {
+    const int *fillsize = testcases[test_i];
+    set_subtest("#%d {%d %d %d %d ...}", test_i, fillsize[0], fillsize[1],
+                fillsize[2], fillsize[3]);
+    XML_Parser parser = XML_ParserCreate(NULL);
+    assert_true(parser != NULL);
+
+    CharData storage;
+    CharData_Init(&storage);
+    XML_SetUserData(parser, &storage);
+    XML_SetStartElementHandler(parser, start_element_event_handler);
+
+    g_bytesScanned = 0;
+    int worstcase_bytes = 0; // sum of (buffered bytes at each XML_Parse call)
+    int offset = 0;
+    while (*fillsize >= 0) {
+      assert_true(offset + *fillsize <= document_length); // or test is invalid
+      const enum XML_Status status
+          = XML_Parse(parser, &document[offset], *fillsize, XML_FALSE);
+      if (status != XML_STATUS_OK) {
+        xml_failure(parser);
+      }
+      offset += *fillsize;
+      fillsize++;
+      assert_true(offset <= INT_MAX - worstcase_bytes); // avoid overflow
+      worstcase_bytes += offset; // we might've tried to parse all pending bytes
+    }
+    assert_true(storage.count == 1); // the big token should've been parsed
+    assert_true(g_bytesScanned > 0); // test-the-test: does our counter work?
+    if (g_reparseDeferralEnabledDefault) {
+      // heuristic is enabled; some XML_Parse calls may have deferred reparsing
+      const unsigned max_bytes_scanned = -*fillsize;
+      if (g_bytesScanned > max_bytes_scanned) {
+        fprintf(stderr,
+                "bytes scanned in parse attempts: actual=%u limit=%u \n",
+                g_bytesScanned, max_bytes_scanned);
+        fail("too many bytes scanned in parse attempts");
+      }
+    }
+    assert_true(g_bytesScanned <= (unsigned)worstcase_bytes);
+
+    XML_ParserFree(parser);
+  }
+  free(document);
+}
+END_TEST
+
+void
+make_basic_test_case(Suite *s) {
+  TCase *tc_basic = tcase_create("basic tests");
+
+  suite_add_tcase(s, tc_basic);
+  tcase_add_checked_fixture(tc_basic, basic_setup, basic_teardown);
+
+  tcase_add_test(tc_basic, test_nul_byte);
+  tcase_add_test(tc_basic, test_u0000_char);
+  tcase_add_test(tc_basic, test_siphash_self);
+  tcase_add_test(tc_basic, test_siphash_spec);
+  tcase_add_test(tc_basic, test_bom_utf8);
+  tcase_add_test(tc_basic, test_bom_utf16_be);
+  tcase_add_test(tc_basic, test_bom_utf16_le);
+  tcase_add_test(tc_basic, test_nobom_utf16_le);
+  tcase_add_test(tc_basic, test_hash_collision);
+  tcase_add_test(tc_basic, test_illegal_utf8);
+  tcase_add_test(tc_basic, test_utf8_auto_align);
+  tcase_add_test(tc_basic, test_utf16);
+  tcase_add_test(tc_basic, test_utf16_le_epilog_newline);
+  tcase_add_test(tc_basic, test_not_utf16);
+  tcase_add_test(tc_basic, test_bad_encoding);
+  tcase_add_test(tc_basic, test_latin1_umlauts);
+  tcase_add_test(tc_basic, test_long_utf8_character);
+  tcase_add_test(tc_basic, test_long_latin1_attribute);
+  tcase_add_test(tc_basic, test_long_ascii_attribute);
+  /* Regression test for SF bug #491986. */
+  tcase_add_test(tc_basic, test_danish_latin1);
+  /* Regression test for SF bug #514281. */
+  tcase_add_test(tc_basic, test_french_charref_hexidecimal);
+  tcase_add_test(tc_basic, test_french_charref_decimal);
+  tcase_add_test(tc_basic, test_french_latin1);
+  tcase_add_test(tc_basic, test_french_utf8);
+  tcase_add_test(tc_basic, test_utf8_false_rejection);
+  tcase_add_test(tc_basic, test_line_number_after_parse);
+  tcase_add_test(tc_basic, test_column_number_after_parse);
+  tcase_add_test(tc_basic, test_line_and_column_numbers_inside_handlers);
+  tcase_add_test(tc_basic, test_line_number_after_error);
+  tcase_add_test(tc_basic, test_column_number_after_error);
+  tcase_add_test(tc_basic, test_really_long_lines);
+  tcase_add_test(tc_basic, test_really_long_encoded_lines);
+  tcase_add_test(tc_basic, test_end_element_events);
+  tcase_add_test(tc_basic, test_helper_is_whitespace_normalized);
+  tcase_add_test(tc_basic, test_attr_whitespace_normalization);
+  tcase_add_test(tc_basic, test_xmldecl_misplaced);
+  tcase_add_test(tc_basic, test_xmldecl_invalid);
+  tcase_add_test(tc_basic, test_xmldecl_missing_attr);
+  tcase_add_test(tc_basic, test_xmldecl_missing_value);
+  tcase_add_test__if_xml_ge(tc_basic, test_unknown_encoding_internal_entity);
+  tcase_add_test(tc_basic, test_unrecognised_encoding_internal_entity);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_set_encoding);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_no_handler);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_set_bom);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_bad_encoding);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_bad_encoding_2);
+  tcase_add_test(tc_basic, test_wfc_undeclared_entity_unread_external_subset);
+  tcase_add_test(tc_basic, test_wfc_undeclared_entity_no_external_subset);
+  tcase_add_test(tc_basic, test_wfc_undeclared_entity_standalone);
+  tcase_add_test(tc_basic,
+                 test_wfc_undeclared_entity_with_external_subset_standalone);
+  tcase_add_test(tc_basic, test_entity_with_external_subset_unless_standalone);
+  tcase_add_test(tc_basic, test_wfc_undeclared_entity_with_external_subset);
+  tcase_add_test(tc_basic, test_not_standalone_handler_reject);
+  tcase_add_test(tc_basic, test_not_standalone_handler_accept);
+  tcase_add_test__if_xml_ge(tc_basic, test_wfc_no_recursive_entity_refs);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_invalid_parse);
+  tcase_add_test__if_xml_ge(tc_basic, test_dtd_default_handling);
+  tcase_add_test(tc_basic, test_dtd_attr_handling);
+  tcase_add_test(tc_basic, test_empty_ns_without_namespaces);
+  tcase_add_test(tc_basic, test_ns_in_attribute_default_without_namespaces);
+  tcase_add_test(tc_basic, test_stop_parser_between_char_data_calls);
+  tcase_add_test(tc_basic, test_suspend_parser_between_char_data_calls);
+  tcase_add_test(tc_basic, test_repeated_stop_parser_between_char_data_calls);
+  tcase_add_test(tc_basic, test_good_cdata_ascii);
+  tcase_add_test(tc_basic, test_good_cdata_utf16);
+  tcase_add_test(tc_basic, test_good_cdata_utf16_le);
+  tcase_add_test(tc_basic, test_long_cdata_utf16);
+  tcase_add_test(tc_basic, test_multichar_cdata_utf16);
+  tcase_add_test(tc_basic, test_utf16_bad_surrogate_pair);
+  tcase_add_test(tc_basic, test_bad_cdata);
+  tcase_add_test(tc_basic, test_bad_cdata_utf16);
+  tcase_add_test(tc_basic, test_stop_parser_between_cdata_calls);
+  tcase_add_test(tc_basic, test_suspend_parser_between_cdata_calls);
+  tcase_add_test(tc_basic, test_memory_allocation);
+  tcase_add_test__if_xml_ge(tc_basic, test_default_current);
+  tcase_add_test(tc_basic, test_dtd_elements);
+  tcase_add_test(tc_basic, test_dtd_elements_nesting);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_set_foreign_dtd);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_foreign_dtd_not_standalone);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_invalid_foreign_dtd);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_foreign_dtd_with_doctype);
+  tcase_add_test__ifdef_xml_dtd(tc_basic,
+                                test_foreign_dtd_without_external_subset);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_empty_foreign_dtd);
+  tcase_add_test(tc_basic, test_set_base);
+  tcase_add_test(tc_basic, test_attributes);
+  tcase_add_test__if_xml_ge(tc_basic, test_reset_in_entity);
+  tcase_add_test(tc_basic, test_resume_invalid_parse);
+  tcase_add_test(tc_basic, test_resume_resuspended);
+  tcase_add_test(tc_basic, test_cdata_default);
+  tcase_add_test(tc_basic, test_subordinate_reset);
+  tcase_add_test(tc_basic, test_subordinate_suspend);
+  tcase_add_test__if_xml_ge(tc_basic, test_subordinate_xdecl_suspend);
+  tcase_add_test__if_xml_ge(tc_basic, test_subordinate_xdecl_abort);
+  tcase_add_test__ifdef_xml_dtd(tc_basic,
+                                test_ext_entity_invalid_suspended_parse);
+  tcase_add_test(tc_basic, test_explicit_encoding);
+  tcase_add_test(tc_basic, test_trailing_cr);
+  tcase_add_test__if_xml_ge(tc_basic, test_ext_entity_trailing_cr);
+  tcase_add_test(tc_basic, test_trailing_rsqb);
+  tcase_add_test__if_xml_ge(tc_basic, test_ext_entity_trailing_rsqb);
+  tcase_add_test__if_xml_ge(tc_basic, test_ext_entity_good_cdata);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_user_parameters);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_ref_parameter);
+  tcase_add_test(tc_basic, test_empty_parse);
+  tcase_add_test(tc_basic, test_get_buffer_1);
+  tcase_add_test(tc_basic, test_get_buffer_2);
+#if XML_CONTEXT_BYTES > 0
+  tcase_add_test(tc_basic, test_get_buffer_3_overflow);
+#endif
+  tcase_add_test(tc_basic, test_buffer_can_grow_to_max);
+  tcase_add_test(tc_basic, test_getbuffer_allocates_on_zero_len);
+  tcase_add_test(tc_basic, test_byte_info_at_end);
+  tcase_add_test(tc_basic, test_byte_info_at_error);
+  tcase_add_test(tc_basic, test_byte_info_at_cdata);
+  tcase_add_test(tc_basic, test_predefined_entities);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_invalid_tag_in_dtd);
+  tcase_add_test(tc_basic, test_not_predefined_entities);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ignore_section);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ignore_section_utf16);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ignore_section_utf16_be);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_bad_ignore_section);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_external_bom_consumed);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_external_entity_values);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_not_standalone);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_value_abort);
+  tcase_add_test(tc_basic, test_bad_public_doctype);
+  tcase_add_test(tc_basic, test_attribute_enum_value);
+  tcase_add_test(tc_basic, test_predefined_entity_redefinition);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_dtd_stop_processing);
+  tcase_add_test(tc_basic, test_public_notation_no_sysid);
+  tcase_add_test(tc_basic, test_nested_groups);
+  tcase_add_test(tc_basic, test_group_choice);
+  tcase_add_test(tc_basic, test_standalone_parameter_entity);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_skipped_parameter_entity);
+  tcase_add_test__ifdef_xml_dtd(tc_basic,
+                                test_recursive_external_parameter_entity);
+  tcase_add_test__ifdef_xml_dtd(tc_basic,
+                                test_recursive_external_parameter_entity_2);
+  tcase_add_test(tc_basic, test_undefined_ext_entity_in_external_dtd);
+  tcase_add_test(tc_basic, test_suspend_xdecl);
+  tcase_add_test(tc_basic, test_abort_epilog);
+  tcase_add_test(tc_basic, test_abort_epilog_2);
+  tcase_add_test(tc_basic, test_suspend_epilog);
+  tcase_add_test(tc_basic, test_suspend_in_sole_empty_tag);
+  tcase_add_test(tc_basic, test_unfinished_epilog);
+  tcase_add_test(tc_basic, test_partial_char_in_epilog);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_suspend_resume_internal_entity);
+  tcase_add_test__ifdef_xml_dtd(tc_basic,
+                                test_suspend_resume_internal_entity_issue_629);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_resume_entity_with_syntax_error);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_suspend_resume_parameter_entity);
+  tcase_add_test(tc_basic, test_restart_on_error);
+  tcase_add_test(tc_basic, test_reject_lt_in_attribute_value);
+  tcase_add_test(tc_basic, test_reject_unfinished_param_in_att_value);
+  tcase_add_test(tc_basic, test_trailing_cr_in_att_value);
+  tcase_add_test(tc_basic, test_standalone_internal_entity);
+  tcase_add_test(tc_basic, test_skipped_external_entity);
+  tcase_add_test(tc_basic, test_skipped_null_loaded_ext_entity);
+  tcase_add_test(tc_basic, test_skipped_unloaded_ext_entity);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_param_entity_with_trailing_cr);
+  tcase_add_test__if_xml_ge(tc_basic, test_invalid_character_entity);
+  tcase_add_test__if_xml_ge(tc_basic, test_invalid_character_entity_2);
+  tcase_add_test__if_xml_ge(tc_basic, test_invalid_character_entity_3);
+  tcase_add_test__if_xml_ge(tc_basic, test_invalid_character_entity_4);
+  tcase_add_test(tc_basic, test_pi_handled_in_default);
+  tcase_add_test(tc_basic, test_comment_handled_in_default);
+  tcase_add_test(tc_basic, test_pi_yml);
+  tcase_add_test(tc_basic, test_pi_xnl);
+  tcase_add_test(tc_basic, test_pi_xmm);
+  tcase_add_test(tc_basic, test_utf16_pi);
+  tcase_add_test(tc_basic, test_utf16_be_pi);
+  tcase_add_test(tc_basic, test_utf16_be_comment);
+  tcase_add_test(tc_basic, test_utf16_le_comment);
+  tcase_add_test(tc_basic, test_missing_encoding_conversion_fn);
+  tcase_add_test(tc_basic, test_failing_encoding_conversion_fn);
+  tcase_add_test(tc_basic, test_unknown_encoding_success);
+  tcase_add_test(tc_basic, test_unknown_encoding_bad_name);
+  tcase_add_test(tc_basic, test_unknown_encoding_bad_name_2);
+  tcase_add_test(tc_basic, test_unknown_encoding_long_name_1);
+  tcase_add_test(tc_basic, test_unknown_encoding_long_name_2);
+  tcase_add_test(tc_basic, test_invalid_unknown_encoding);
+  tcase_add_test(tc_basic, test_unknown_ascii_encoding_ok);
+  tcase_add_test(tc_basic, test_unknown_ascii_encoding_fail);
+  tcase_add_test(tc_basic, test_unknown_encoding_invalid_length);
+  tcase_add_test(tc_basic, test_unknown_encoding_invalid_topbit);
+  tcase_add_test(tc_basic, test_unknown_encoding_invalid_surrogate);
+  tcase_add_test(tc_basic, test_unknown_encoding_invalid_high);
+  tcase_add_test(tc_basic, test_unknown_encoding_invalid_attr_value);
+  tcase_add_test__if_xml_ge(tc_basic, test_ext_entity_latin1_utf16le_bom);
+  tcase_add_test__if_xml_ge(tc_basic, test_ext_entity_latin1_utf16be_bom);
+  tcase_add_test__if_xml_ge(tc_basic, test_ext_entity_latin1_utf16le_bom2);
+  tcase_add_test__if_xml_ge(tc_basic, test_ext_entity_latin1_utf16be_bom2);
+  tcase_add_test__if_xml_ge(tc_basic, test_ext_entity_utf16_be);
+  tcase_add_test__if_xml_ge(tc_basic, test_ext_entity_utf16_le);
+  tcase_add_test__if_xml_ge(tc_basic, test_ext_entity_utf16_unknown);
+  tcase_add_test__if_xml_ge(tc_basic, test_ext_entity_utf8_non_bom);
+  tcase_add_test(tc_basic, test_utf8_in_cdata_section);
+  tcase_add_test(tc_basic, test_utf8_in_cdata_section_2);
+  tcase_add_test(tc_basic, test_utf8_in_start_tags);
+  tcase_add_test(tc_basic, test_trailing_spaces_in_elements);
+  tcase_add_test(tc_basic, test_utf16_attribute);
+  tcase_add_test(tc_basic, test_utf16_second_attr);
+  tcase_add_test(tc_basic, test_attr_after_solidus);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_utf16_pe);
+  tcase_add_test(tc_basic, test_bad_attr_desc_keyword);
+  tcase_add_test(tc_basic, test_bad_attr_desc_keyword_utf16);
+  tcase_add_test(tc_basic, test_bad_doctype);
+  tcase_add_test(tc_basic, test_bad_doctype_utf8);
+  tcase_add_test(tc_basic, test_bad_doctype_utf16);
+  tcase_add_test(tc_basic, test_bad_doctype_plus);
+  tcase_add_test(tc_basic, test_bad_doctype_star);
+  tcase_add_test(tc_basic, test_bad_doctype_query);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_unknown_encoding_bad_ignore);
+  tcase_add_test(tc_basic, test_entity_in_utf16_be_attr);
+  tcase_add_test(tc_basic, test_entity_in_utf16_le_attr);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_entity_public_utf16_be);
+  tcase_add_test__ifdef_xml_dtd(tc_basic, test_entity_public_utf16_le);
+  tcase_add_test(tc_basic, test_short_doctype);
+  tcase_add_test(tc_basic, test_short_doctype_2);
+  tcase_add_test(tc_basic, test_short_doctype_3);
+  tcase_add_test(tc_basic, test_long_doctype);
+  tcase_add_test(tc_basic, test_bad_entity);
+  tcase_add_test(tc_basic, test_bad_entity_2);
+  tcase_add_test(tc_basic, test_bad_entity_3);
+  tcase_add_test(tc_basic, test_bad_entity_4);
+  tcase_add_test(tc_basic, test_bad_notation);
+  tcase_add_test(tc_basic, test_default_doctype_handler);
+  tcase_add_test(tc_basic, test_empty_element_abort);
+  tcase_add_test__ifdef_xml_dtd(tc_basic,
+                                test_pool_integrity_with_unfinished_attr);
+  tcase_add_test__if_xml_ge(tc_basic, test_nested_entity_suspend);
+  tcase_add_test(tc_basic, test_big_tokens_scale_linearly);
+  tcase_add_test(tc_basic, test_set_reparse_deferral);
+  tcase_add_test(tc_basic, test_reparse_deferral_is_inherited);
+  tcase_add_test(tc_basic, test_set_reparse_deferral_on_null_parser);
+  tcase_add_test(tc_basic, test_set_reparse_deferral_on_the_fly);
+  tcase_add_test(tc_basic, test_set_bad_reparse_option);
+  tcase_add_test(tc_basic, test_bypass_heuristic_when_close_to_bufsize);
+  tcase_add_test(tc_basic, test_varying_buffer_fills);
+}
diff --git a/tests/basic_tests.h b/tests/basic_tests.h
new file mode 100644 (file)
index 0000000..4c16562
--- /dev/null
@@ -0,0 +1,56 @@
+/* Tests in the "basic" test case for the Expat test suite
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
+   Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Joe Orton <jorton@redhat.com>
+   Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2020      Tim Gates <tim.gates@iress.com>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef XML_BASIC_TESTS_H
+#  define XML_BASIC_TESTS_H
+
+extern void make_basic_test_case(Suite *s);
+
+#endif /* XML_BASIC_TESTS_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/basic_tests_cxx.cpp b/tests/basic_tests_cxx.cpp
new file mode 100644 (file)
index 0000000..d7e75a9
--- /dev/null
@@ -0,0 +1,32 @@
+/* C++ compilation harness for the test suite.
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2023 Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "basic_tests.c"
index 0079e7a..18f3b7b 100644 (file)
@@ -133,6 +133,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
        $(top_srcdir)/conftools/ax-append-compile-flags.m4 \
        $(top_srcdir)/conftools/ax-append-link-flags.m4 \
        $(top_srcdir)/conftools/expatcfg-compiler-supports-visibility.m4 \
+       $(top_srcdir)/conftools/ax-cxx-compile-stdcxx.m4 \
+       $(top_srcdir)/conftools/ax-cxx-compile-stdcxx-11.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
@@ -259,6 +261,7 @@ FGREP = @FGREP@
 FILECMD = @FILECMD@
 FILEMAP = @FILEMAP@
 GREP = @GREP@
+HAVE_CXX11 = @HAVE_CXX11@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
@@ -278,6 +281,7 @@ LIPO = @LIPO@
 LN_S = @LN_S@
 LTLIBOBJS = @LTLIBOBJS@
 LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
 MAKEINFO = @MAKEINFO@
 MANIFEST_TOOL = @MANIFEST_TOOL@
 MKDIR_P = @MKDIR_P@
@@ -368,7 +372,7 @@ all: all-am
 
 .SUFFIXES:
 .SUFFIXES: .c .lo .o .obj
-$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
        @for dep in $?; do \
          case '$(am__configure_deps)' in \
            *$$dep*) \
@@ -392,9 +396,9 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
        cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
 
-$(top_srcdir)/configure:  $(am__configure_deps)
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
        cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
        cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
 $(am__aclocal_m4_deps):
 
index 2c4eb78..355d83f 100644 (file)
@@ -8,7 +8,7 @@
 
    Copyright (c) 2003-2006 Karl Waclawek <karl@waclawek.net>
    Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
-   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2023 Sebastian Pipping <sebastian@pipping.org>
    Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Licensed under the MIT license:
 
@@ -33,6 +33,8 @@
 */
 
 #include <sys/stat.h>
+#include <assert.h>
+#include <stddef.h> // ptrdiff_t
 #include <stdlib.h>
 #include <stdio.h>
 #include <time.h>
@@ -62,7 +64,8 @@ main(int argc, char *argv[]) {
   char *XMLBuf, *XMLBufEnd, *XMLBufPtr;
   FILE *fd;
   struct stat fileAttr;
-  int nrOfLoops, bufferSize, fileSize, i, isFinal;
+  int nrOfLoops, bufferSize, i, isFinal;
+  size_t fileSize;
   int j = 0, ns = 0;
   clock_t tstart, tend;
   double cpuTime = 0.0;
@@ -114,12 +117,13 @@ main(int argc, char *argv[]) {
     isFinal = 0;
     tstart = clock();
     do {
-      int parseBufferSize = XMLBufEnd - XMLBufPtr;
-      if (parseBufferSize <= bufferSize)
+      ptrdiff_t parseBufferSize = XMLBufEnd - XMLBufPtr;
+      if (parseBufferSize <= (ptrdiff_t)bufferSize)
         isFinal = 1;
       else
         parseBufferSize = bufferSize;
-      if (! XML_Parse(parser, XMLBufPtr, parseBufferSize, isFinal)) {
+      assert(parseBufferSize <= (ptrdiff_t)bufferSize);
+      if (! XML_Parse(parser, XMLBufPtr, (int)parseBufferSize, isFinal)) {
         fprintf(stderr,
                 "error '%" XML_FMT_STR "' at line %" XML_FMT_INT_MOD
                 "u character %" XML_FMT_INT_MOD "u\n",
index d1989a8..2adb2c5 100644 (file)
@@ -9,9 +9,10 @@
    Copyright (c) 2002-2004 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
    Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
    Copyright (c) 2016      Gilles Espinasse <g.esp@free.fr>
-   Copyright (c) 2016-2021 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016-2023 Sebastian Pipping <sebastian@pipping.org>
    Copyright (c) 2017      Joe Orton <jorton@redhat.com>
    Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2022      Sean McBride <sean@rogue-research.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
    USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
-#include <expat_config.h>
+#if defined(NDEBUG)
+#  undef NDEBUG /* because test suite relies on assert(...) at the moment */
+#endif
+
+#include "expat_config.h"
 #include "minicheck.h"
 
 #include <assert.h>
@@ -80,15 +85,16 @@ CharData_AppendXMLChars(CharData *storage, const XML_Char *s, int len) {
 
 int
 CharData_CheckXMLChars(CharData *storage, const XML_Char *expected) {
-  char buffer[1024];
   int len = xmlstrlen(expected);
   int count;
 
   assert(storage != NULL);
   count = (storage->count < 0) ? 0 : storage->count;
   if (len != count) {
-    sprintf(buffer, "wrong number of data characters: got %d, expected %d",
-            count, len);
+    char buffer[1024];
+    snprintf(buffer, sizeof(buffer),
+             "wrong number of data characters: got %d, expected %d", count,
+             len);
     fail(buffer);
     return 0;
   }
diff --git a/tests/chardata_cxx.cpp b/tests/chardata_cxx.cpp
new file mode 100644 (file)
index 0000000..81820c7
--- /dev/null
@@ -0,0 +1,32 @@
+/* C++ compilation harness for the test suite.
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2023 Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "chardata.c"
diff --git a/tests/common.c b/tests/common.c
new file mode 100644 (file)
index 0000000..26d0c54
--- /dev/null
@@ -0,0 +1,325 @@
+/* Commonly used functions for the Expat test suite
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
+   Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2023 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Joe Orton <jorton@redhat.com>
+   Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2020      Tim Gates <tim.gates@iress.com>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
+   Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <snild@sony.com>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "expat_config.h"
+#include "expat.h"
+#include "internal.h"
+#include "chardata.h"
+#include "minicheck.h"
+#include "common.h"
+
+/* Common test data */
+
+const char *long_character_data_text
+    = "<?xml version='1.0' encoding='iso-8859-1'?><s>"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "</s>";
+
+const char *long_cdata_text
+    = "<s><![CDATA["
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "012345678901234567890123456789012345678901234567890123456789"
+      "]]></s>";
+
+/* Having an element name longer than 1024 characters exercises some
+ * of the pool allocation code in the parser that otherwise does not
+ * get executed.  The count at the end of the line is the number of
+ * characters (bytes) in the element name by that point.x
+ */
+const char *get_buffer_test_text
+    = "<documentwitharidiculouslylongelementnametotease"  /* 0x030 */
+      "aparticularcorneroftheallocationinXML_GetBuffers"  /* 0x060 */
+      "othatwecanimprovethecoverageyetagain012345678901"  /* 0x090 */
+      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x0c0 */
+      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x0f0 */
+      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x120 */
+      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x150 */
+      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x180 */
+      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x1b0 */
+      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x1e0 */
+      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x210 */
+      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x240 */
+      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x270 */
+      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x2a0 */
+      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x2d0 */
+      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x300 */
+      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x330 */
+      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x360 */
+      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x390 */
+      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x3c0 */
+      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x3f0 */
+      "123456789abcdef0123456789abcdef0123456789>\n<ef0"; /* 0x420 */
+
+/* Test control globals */
+
+/* Used as the "resumable" parameter to XML_StopParser by some tests */
+XML_Bool g_resumable = XML_FALSE;
+
+/* Used to control abort checks in some tests */
+XML_Bool g_abortable = XML_FALSE;
+
+/* Used to control _XML_Parse_SINGLE_BYTES() chunk size */
+int g_chunkSize = 1;
+
+/* Common test functions */
+
+void
+tcase_add_test__ifdef_xml_dtd(TCase *tc, tcase_test_function test) {
+#ifdef XML_DTD
+  tcase_add_test(tc, test);
+#else
+  UNUSED_P(tc);
+  UNUSED_P(test);
+#endif
+}
+
+void
+tcase_add_test__if_xml_ge(TCase *tc, tcase_test_function test) {
+#if XML_GE == 1
+  tcase_add_test(tc, test);
+#else
+  UNUSED_P(tc);
+  UNUSED_P(test);
+#endif
+}
+
+void
+basic_teardown(void) {
+  if (g_parser != NULL) {
+    XML_ParserFree(g_parser);
+    g_parser = NULL;
+  }
+}
+
+/* Generate a failure using the parser state to create an error message;
+   this should be used when the parser reports an error we weren't
+   expecting.
+*/
+void
+_xml_failure(XML_Parser parser, const char *file, int line) {
+  char buffer[1024];
+  enum XML_Error err = XML_GetErrorCode(parser);
+  snprintf(buffer, sizeof(buffer),
+           "    %d: %" XML_FMT_STR " (line %" XML_FMT_INT_MOD
+           "u, offset %" XML_FMT_INT_MOD "u)\n    reported from %s, line %d\n",
+           err, XML_ErrorString(err), XML_GetCurrentLineNumber(parser),
+           XML_GetCurrentColumnNumber(parser), file, line);
+  _fail(file, line, buffer);
+}
+
+enum XML_Status
+_XML_Parse_SINGLE_BYTES(XML_Parser parser, const char *s, int len,
+                        int isFinal) {
+  // This ensures that tests have to run pathological parse cases
+  // (e.g. when `s` is NULL) against plain XML_Parse rather than
+  // chunking _XML_Parse_SINGLE_BYTES.
+  assert((parser != NULL) && (s != NULL) && (len >= 0));
+  const int chunksize = g_chunkSize;
+  if (chunksize > 0) {
+    // parse in chunks of `chunksize` bytes as long as not exhausting
+    for (; len > chunksize; len -= chunksize, s += chunksize) {
+      enum XML_Status res = XML_Parse(parser, s, chunksize, XML_FALSE);
+      if (res != XML_STATUS_OK) {
+        return res;
+      }
+    }
+  }
+  // parse the final chunk, the size of which will be <= chunksize
+  return XML_Parse(parser, s, len, isFinal);
+}
+
+void
+_expect_failure(const char *text, enum XML_Error errorCode,
+                const char *errorMessage, const char *file, int lineno) {
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_OK)
+    /* Hackish use of _fail() macro, but lets us report
+       the right filename and line number. */
+    _fail(file, lineno, errorMessage);
+  if (XML_GetErrorCode(g_parser) != errorCode)
+    _xml_failure(g_parser, file, lineno);
+}
+
+/* Character data support for handlers, built on top of the code in
+ * chardata.c
+ */
+void XMLCALL
+accumulate_characters(void *userData, const XML_Char *s, int len) {
+  CharData_AppendXMLChars((CharData *)userData, s, len);
+}
+
+void XMLCALL
+accumulate_attribute(void *userData, const XML_Char *name,
+                     const XML_Char **atts) {
+  CharData *storage = (CharData *)userData;
+  UNUSED_P(name);
+  /* Check there are attributes to deal with */
+  if (atts == NULL)
+    return;
+
+  while (storage->count < 0 && atts[0] != NULL) {
+    /* "accumulate" the value of the first attribute we see */
+    CharData_AppendXMLChars(storage, atts[1], -1);
+    atts += 2;
+  }
+}
+
+void
+_run_character_check(const char *text, const XML_Char *expected,
+                     const char *file, int line) {
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetUserData(g_parser, &storage);
+  XML_SetCharacterDataHandler(g_parser, accumulate_characters);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    _xml_failure(g_parser, file, line);
+  CharData_CheckXMLChars(&storage, expected);
+}
+
+void
+_run_attribute_check(const char *text, const XML_Char *expected,
+                     const char *file, int line) {
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetUserData(g_parser, &storage);
+  XML_SetStartElementHandler(g_parser, accumulate_attribute);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    _xml_failure(g_parser, file, line);
+  CharData_CheckXMLChars(&storage, expected);
+}
+
+void XMLCALL
+ext_accumulate_characters(void *userData, const XML_Char *s, int len) {
+  ExtTest *test_data = (ExtTest *)userData;
+  accumulate_characters(test_data->storage, s, len);
+}
+
+void
+_run_ext_character_check(const char *text, ExtTest *test_data,
+                         const XML_Char *expected, const char *file, int line) {
+  CharData *const storage = (CharData *)malloc(sizeof(CharData));
+
+  CharData_Init(storage);
+  test_data->storage = storage;
+  XML_SetUserData(g_parser, test_data);
+  XML_SetCharacterDataHandler(g_parser, ext_accumulate_characters);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    _xml_failure(g_parser, file, line);
+  CharData_CheckXMLChars(storage, expected);
+
+  free(storage);
+}
+
+/* Control variable; the number of times duff_allocator() will successfully
+ * allocate */
+#define ALLOC_ALWAYS_SUCCEED (-1)
+#define REALLOC_ALWAYS_SUCCEED (-1)
+
+int g_allocation_count = ALLOC_ALWAYS_SUCCEED;
+int g_reallocation_count = REALLOC_ALWAYS_SUCCEED;
+
+/* Crocked allocator for allocation failure tests */
+void *
+duff_allocator(size_t size) {
+  if (g_allocation_count == 0)
+    return NULL;
+  if (g_allocation_count != ALLOC_ALWAYS_SUCCEED)
+    g_allocation_count--;
+  return malloc(size);
+}
+
+/* Crocked reallocator for allocation failure tests */
+void *
+duff_reallocator(void *ptr, size_t size) {
+  if (g_reallocation_count == 0)
+    return NULL;
+  if (g_reallocation_count != REALLOC_ALWAYS_SUCCEED)
+    g_reallocation_count--;
+  return realloc(ptr, size);
+}
diff --git a/tests/common.h b/tests/common.h
new file mode 100644 (file)
index 0000000..52f00cc
--- /dev/null
@@ -0,0 +1,162 @@
+/* Commonly used functions for the Expat test suite
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
+   Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2023 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Joe Orton <jorton@redhat.com>
+   Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2020      Tim Gates <tim.gates@iress.com>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
+   Copyright (c) 2023      Sony Corporation / Snild Dolkow <snild@sony.com>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef XML_COMMON_H
+#  define XML_COMMON_H
+
+#  include "expat_config.h"
+#  include "minicheck.h"
+#  include "chardata.h"
+
+#  ifdef XML_LARGE_SIZE
+#    define XML_FMT_INT_MOD "ll"
+#  else
+#    define XML_FMT_INT_MOD "l"
+#  endif
+
+#  ifdef XML_UNICODE_WCHAR_T
+#    define XML_FMT_STR "ls"
+#    include <wchar.h>
+#    define xcstrlen(s) wcslen(s)
+#    define xcstrcmp(s, t) wcscmp((s), (t))
+#    define xcstrncmp(s, t, n) wcsncmp((s), (t), (n))
+#    define XCS(s) _XCS(s)
+#    define _XCS(s) L##s
+#  else
+#    ifdef XML_UNICODE
+#      error "No support for UTF-16 character without wchar_t in tests"
+#    else
+#      define XML_FMT_STR "s"
+#      define xcstrlen(s) strlen(s)
+#      define xcstrcmp(s, t) strcmp((s), (t))
+#      define xcstrncmp(s, t, n) strncmp((s), (t), (n))
+#      define XCS(s) s
+#    endif /* XML_UNICODE */
+#  endif   /* XML_UNICODE_WCHAR_T */
+
+extern XML_Parser g_parser;
+
+extern XML_Bool g_resumable;
+extern XML_Bool g_abortable;
+
+extern int g_chunkSize;
+
+extern const char *long_character_data_text;
+extern const char *long_cdata_text;
+extern const char *get_buffer_test_text;
+
+extern void tcase_add_test__ifdef_xml_dtd(TCase *tc, tcase_test_function test);
+extern void tcase_add_test__if_xml_ge(TCase *tc, tcase_test_function test);
+
+extern void basic_teardown(void);
+
+extern void _xml_failure(XML_Parser parser, const char *file, int line);
+
+#  define xml_failure(parser) _xml_failure((parser), __FILE__, __LINE__)
+
+extern enum XML_Status _XML_Parse_SINGLE_BYTES(XML_Parser parser, const char *s,
+                                               int len, int isFinal);
+
+extern void _expect_failure(const char *text, enum XML_Error errorCode,
+                            const char *errorMessage, const char *file,
+                            int lineno);
+
+#  define expect_failure(text, errorCode, errorMessage)                        \
+    _expect_failure((text), (errorCode), (errorMessage), __FILE__, __LINE__)
+
+/* Support functions for handlers to collect up character and attribute data.
+ */
+
+extern void XMLCALL accumulate_characters(void *userData, const XML_Char *s,
+                                          int len);
+
+extern void XMLCALL accumulate_attribute(void *userData, const XML_Char *name,
+                                         const XML_Char **atts);
+
+extern void _run_character_check(const char *text, const XML_Char *expected,
+                                 const char *file, int line);
+
+#  define run_character_check(text, expected)                                  \
+    _run_character_check(text, expected, __FILE__, __LINE__)
+
+extern void _run_attribute_check(const char *text, const XML_Char *expected,
+                                 const char *file, int line);
+
+#  define run_attribute_check(text, expected)                                  \
+    _run_attribute_check(text, expected, __FILE__, __LINE__)
+
+typedef struct ExtTest {
+  const char *parse_text;
+  const XML_Char *encoding;
+  CharData *storage;
+} ExtTest;
+
+extern void XMLCALL ext_accumulate_characters(void *userData, const XML_Char *s,
+                                              int len);
+
+extern void _run_ext_character_check(const char *text, ExtTest *test_data,
+                                     const XML_Char *expected, const char *file,
+                                     int line);
+
+#  define run_ext_character_check(text, test_data, expected)                   \
+    _run_ext_character_check(text, test_data, expected, __FILE__, __LINE__)
+
+#  define ALLOC_ALWAYS_SUCCEED (-1)
+#  define REALLOC_ALWAYS_SUCCEED (-1)
+
+extern int g_allocation_count;
+extern int g_reallocation_count;
+
+extern void *duff_allocator(size_t size);
+
+extern void *duff_reallocator(void *ptr, size_t size);
+
+#endif /* XML_COMMON_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/common_cxx.cpp b/tests/common_cxx.cpp
new file mode 100644 (file)
index 0000000..698a0cc
--- /dev/null
@@ -0,0 +1,32 @@
+/* C++ compilation harness for the test suite.
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2023 Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "common.c"
diff --git a/tests/dummy.c b/tests/dummy.c
new file mode 100644 (file)
index 0000000..4ab57ed
--- /dev/null
@@ -0,0 +1,261 @@
+/* Dummy handler functions for the Expat test suite
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
+   Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Joe Orton <jorton@redhat.com>
+   Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2020      Tim Gates <tim.gates@iress.com>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "expat.h"
+#include "internal.h"
+#include "common.h"
+#include "dummy.h"
+
+/* Dummy handlers for when we need to set a handler to tickle a bug,
+   but it doesn't need to do anything.
+*/
+static unsigned long dummy_handler_flags = 0;
+
+void
+init_dummy_handlers(void) {
+  dummy_handler_flags = 0;
+}
+
+unsigned long
+get_dummy_handler_flags(void) {
+  return dummy_handler_flags;
+}
+
+void XMLCALL
+dummy_xdecl_handler(void *userData, const XML_Char *version,
+                    const XML_Char *encoding, int standalone) {
+  UNUSED_P(userData);
+  UNUSED_P(version);
+  UNUSED_P(encoding);
+  UNUSED_P(standalone);
+}
+
+void XMLCALL
+dummy_start_doctype_handler(void *userData, const XML_Char *doctypeName,
+                            const XML_Char *sysid, const XML_Char *pubid,
+                            int has_internal_subset) {
+  UNUSED_P(userData);
+  UNUSED_P(doctypeName);
+  UNUSED_P(sysid);
+  UNUSED_P(pubid);
+  UNUSED_P(has_internal_subset);
+  dummy_handler_flags |= DUMMY_START_DOCTYPE_HANDLER_FLAG;
+}
+
+void XMLCALL
+dummy_end_doctype_handler(void *userData) {
+  UNUSED_P(userData);
+  dummy_handler_flags |= DUMMY_END_DOCTYPE_HANDLER_FLAG;
+}
+
+void XMLCALL
+dummy_entity_decl_handler(void *userData, const XML_Char *entityName,
+                          int is_parameter_entity, const XML_Char *value,
+                          int value_length, const XML_Char *base,
+                          const XML_Char *systemId, const XML_Char *publicId,
+                          const XML_Char *notationName) {
+  UNUSED_P(userData);
+  UNUSED_P(entityName);
+  UNUSED_P(is_parameter_entity);
+  UNUSED_P(value);
+  UNUSED_P(value_length);
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  UNUSED_P(notationName);
+  dummy_handler_flags |= DUMMY_ENTITY_DECL_HANDLER_FLAG;
+}
+
+void XMLCALL
+dummy_notation_decl_handler(void *userData, const XML_Char *notationName,
+                            const XML_Char *base, const XML_Char *systemId,
+                            const XML_Char *publicId) {
+  UNUSED_P(userData);
+  UNUSED_P(notationName);
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  dummy_handler_flags |= DUMMY_NOTATION_DECL_HANDLER_FLAG;
+}
+
+void XMLCALL
+dummy_element_decl_handler(void *userData, const XML_Char *name,
+                           XML_Content *model) {
+  UNUSED_P(userData);
+  UNUSED_P(name);
+  /* The content model must be freed by the handler.  Unfortunately
+   * we cannot pass the parser as the userData because this is used
+   * with other handlers that require other userData.
+   */
+  XML_FreeContentModel(g_parser, model);
+  dummy_handler_flags |= DUMMY_ELEMENT_DECL_HANDLER_FLAG;
+}
+
+void XMLCALL
+dummy_attlist_decl_handler(void *userData, const XML_Char *elname,
+                           const XML_Char *attname, const XML_Char *att_type,
+                           const XML_Char *dflt, int isrequired) {
+  UNUSED_P(userData);
+  UNUSED_P(elname);
+  UNUSED_P(attname);
+  UNUSED_P(att_type);
+  UNUSED_P(dflt);
+  UNUSED_P(isrequired);
+  dummy_handler_flags |= DUMMY_ATTLIST_DECL_HANDLER_FLAG;
+}
+
+void XMLCALL
+dummy_comment_handler(void *userData, const XML_Char *data) {
+  UNUSED_P(userData);
+  UNUSED_P(data);
+  dummy_handler_flags |= DUMMY_COMMENT_HANDLER_FLAG;
+}
+
+void XMLCALL
+dummy_pi_handler(void *userData, const XML_Char *target, const XML_Char *data) {
+  UNUSED_P(userData);
+  UNUSED_P(target);
+  UNUSED_P(data);
+  dummy_handler_flags |= DUMMY_PI_HANDLER_FLAG;
+}
+
+void XMLCALL
+dummy_start_element(void *userData, const XML_Char *name,
+                    const XML_Char **atts) {
+  UNUSED_P(userData);
+  UNUSED_P(name);
+  UNUSED_P(atts);
+  dummy_handler_flags |= DUMMY_START_ELEMENT_HANDLER_FLAG;
+}
+
+void XMLCALL
+dummy_end_element(void *userData, const XML_Char *name) {
+  UNUSED_P(userData);
+  UNUSED_P(name);
+}
+
+void XMLCALL
+dummy_start_cdata_handler(void *userData) {
+  UNUSED_P(userData);
+  dummy_handler_flags |= DUMMY_START_CDATA_HANDLER_FLAG;
+}
+
+void XMLCALL
+dummy_end_cdata_handler(void *userData) {
+  UNUSED_P(userData);
+  dummy_handler_flags |= DUMMY_END_CDATA_HANDLER_FLAG;
+}
+
+void XMLCALL
+dummy_cdata_handler(void *userData, const XML_Char *s, int len) {
+  UNUSED_P(userData);
+  UNUSED_P(s);
+  UNUSED_P(len);
+}
+
+void XMLCALL
+dummy_start_namespace_decl_handler(void *userData, const XML_Char *prefix,
+                                   const XML_Char *uri) {
+  UNUSED_P(userData);
+  UNUSED_P(prefix);
+  UNUSED_P(uri);
+  dummy_handler_flags |= DUMMY_START_NS_DECL_HANDLER_FLAG;
+}
+
+void XMLCALL
+dummy_end_namespace_decl_handler(void *userData, const XML_Char *prefix) {
+  UNUSED_P(userData);
+  UNUSED_P(prefix);
+  dummy_handler_flags |= DUMMY_END_NS_DECL_HANDLER_FLAG;
+}
+
+/* This handler is obsolete, but while the code exists we should
+ * ensure that dealing with the handler is covered by tests.
+ */
+void XMLCALL
+dummy_unparsed_entity_decl_handler(void *userData, const XML_Char *entityName,
+                                   const XML_Char *base,
+                                   const XML_Char *systemId,
+                                   const XML_Char *publicId,
+                                   const XML_Char *notationName) {
+  UNUSED_P(userData);
+  UNUSED_P(entityName);
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  UNUSED_P(notationName);
+  dummy_handler_flags |= DUMMY_UNPARSED_ENTITY_DECL_HANDLER_FLAG;
+}
+
+void XMLCALL
+dummy_default_handler(void *userData, const XML_Char *s, int len) {
+  UNUSED_P(userData);
+  UNUSED_P(s);
+  UNUSED_P(len);
+}
+
+void XMLCALL
+dummy_start_doctype_decl_handler(void *userData, const XML_Char *doctypeName,
+                                 const XML_Char *sysid, const XML_Char *pubid,
+                                 int has_internal_subset) {
+  UNUSED_P(userData);
+  UNUSED_P(doctypeName);
+  UNUSED_P(sysid);
+  UNUSED_P(pubid);
+  UNUSED_P(has_internal_subset);
+  dummy_handler_flags |= DUMMY_START_DOCTYPE_DECL_HANDLER_FLAG;
+}
+
+void XMLCALL
+dummy_end_doctype_decl_handler(void *userData) {
+  UNUSED_P(userData);
+  dummy_handler_flags |= DUMMY_END_DOCTYPE_DECL_HANDLER_FLAG;
+}
+
+void XMLCALL
+dummy_skip_handler(void *userData, const XML_Char *entityName,
+                   int is_parameter_entity) {
+  UNUSED_P(userData);
+  UNUSED_P(entityName);
+  UNUSED_P(is_parameter_entity);
+  dummy_handler_flags |= DUMMY_SKIP_HANDLER_FLAG;
+}
diff --git a/tests/dummy.h b/tests/dummy.h
new file mode 100644 (file)
index 0000000..3d7ec63
--- /dev/null
@@ -0,0 +1,150 @@
+/* Dummy handler functions for the Expat test suite
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
+   Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Joe Orton <jorton@redhat.com>
+   Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2020      Tim Gates <tim.gates@iress.com>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef XML_DUMMY_H
+#  define XML_DUMMY_H
+
+#  define DUMMY_START_DOCTYPE_HANDLER_FLAG (1UL << 0)
+#  define DUMMY_END_DOCTYPE_HANDLER_FLAG (1UL << 1)
+#  define DUMMY_ENTITY_DECL_HANDLER_FLAG (1UL << 2)
+#  define DUMMY_NOTATION_DECL_HANDLER_FLAG (1UL << 3)
+#  define DUMMY_ELEMENT_DECL_HANDLER_FLAG (1UL << 4)
+#  define DUMMY_ATTLIST_DECL_HANDLER_FLAG (1UL << 5)
+#  define DUMMY_COMMENT_HANDLER_FLAG (1UL << 6)
+#  define DUMMY_PI_HANDLER_FLAG (1UL << 7)
+#  define DUMMY_START_ELEMENT_HANDLER_FLAG (1UL << 8)
+#  define DUMMY_START_CDATA_HANDLER_FLAG (1UL << 9)
+#  define DUMMY_END_CDATA_HANDLER_FLAG (1UL << 10)
+#  define DUMMY_UNPARSED_ENTITY_DECL_HANDLER_FLAG (1UL << 11)
+#  define DUMMY_START_NS_DECL_HANDLER_FLAG (1UL << 12)
+#  define DUMMY_END_NS_DECL_HANDLER_FLAG (1UL << 13)
+#  define DUMMY_START_DOCTYPE_DECL_HANDLER_FLAG (1UL << 14)
+#  define DUMMY_END_DOCTYPE_DECL_HANDLER_FLAG (1UL << 15)
+#  define DUMMY_SKIP_HANDLER_FLAG (1UL << 16)
+#  define DUMMY_DEFAULT_HANDLER_FLAG (1UL << 17)
+
+extern void init_dummy_handlers(void);
+extern unsigned long get_dummy_handler_flags(void);
+
+extern void XMLCALL dummy_xdecl_handler(void *userData, const XML_Char *version,
+                                        const XML_Char *encoding,
+                                        int standalone);
+
+extern void XMLCALL dummy_start_doctype_handler(void *userData,
+                                                const XML_Char *doctypeName,
+                                                const XML_Char *sysid,
+                                                const XML_Char *pubid,
+                                                int has_internal_subset);
+
+extern void XMLCALL dummy_end_doctype_handler(void *userData);
+
+extern void XMLCALL dummy_entity_decl_handler(
+    void *userData, const XML_Char *entityName, int is_parameter_entity,
+    const XML_Char *value, int value_length, const XML_Char *base,
+    const XML_Char *systemId, const XML_Char *publicId,
+    const XML_Char *notationName);
+
+extern void XMLCALL dummy_notation_decl_handler(void *userData,
+                                                const XML_Char *notationName,
+                                                const XML_Char *base,
+                                                const XML_Char *systemId,
+                                                const XML_Char *publicId);
+
+extern void XMLCALL dummy_element_decl_handler(void *userData,
+                                               const XML_Char *name,
+                                               XML_Content *model);
+
+extern void XMLCALL dummy_attlist_decl_handler(
+    void *userData, const XML_Char *elname, const XML_Char *attname,
+    const XML_Char *att_type, const XML_Char *dflt, int isrequired);
+
+extern void XMLCALL dummy_comment_handler(void *userData, const XML_Char *data);
+
+extern void XMLCALL dummy_pi_handler(void *userData, const XML_Char *target,
+                                     const XML_Char *data);
+
+extern void XMLCALL dummy_start_element(void *userData, const XML_Char *name,
+                                        const XML_Char **atts);
+
+extern void XMLCALL dummy_end_element(void *userData, const XML_Char *name);
+
+extern void XMLCALL dummy_start_cdata_handler(void *userData);
+
+extern void XMLCALL dummy_end_cdata_handler(void *userData);
+
+extern void XMLCALL dummy_cdata_handler(void *userData, const XML_Char *s,
+                                        int len);
+
+extern void XMLCALL dummy_start_namespace_decl_handler(void *userData,
+                                                       const XML_Char *prefix,
+                                                       const XML_Char *uri);
+
+extern void XMLCALL dummy_end_namespace_decl_handler(void *userData,
+                                                     const XML_Char *prefix);
+
+extern void XMLCALL dummy_unparsed_entity_decl_handler(
+    void *userData, const XML_Char *entityName, const XML_Char *base,
+    const XML_Char *systemId, const XML_Char *publicId,
+    const XML_Char *notationName);
+
+extern void XMLCALL dummy_default_handler(void *userData, const XML_Char *s,
+                                          int len);
+
+extern void XMLCALL dummy_start_doctype_decl_handler(
+    void *userData, const XML_Char *doctypeName, const XML_Char *sysid,
+    const XML_Char *pubid, int has_internal_subset);
+
+extern void XMLCALL dummy_end_doctype_decl_handler(void *userData);
+
+extern void XMLCALL dummy_skip_handler(void *userData,
+                                       const XML_Char *entityName,
+                                       int is_parameter_entity);
+
+#endif /* XML_DUMMY_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/dummy_cxx.cpp b/tests/dummy_cxx.cpp
new file mode 100644 (file)
index 0000000..27c9f43
--- /dev/null
@@ -0,0 +1,32 @@
+/* C++ compilation harness for the test suite.
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2023 Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "dummy.c"
diff --git a/tests/handlers.c b/tests/handlers.c
new file mode 100644 (file)
index 0000000..449ada7
--- /dev/null
@@ -0,0 +1,1932 @@
+/* XML handler functions for the Expat test suite
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
+   Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Joe Orton <jorton@redhat.com>
+   Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2020      Tim Gates <tim.gates@iress.com>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
+   Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <snild@sony.com>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#if defined(NDEBUG)
+#  undef NDEBUG /* because test suite relies on assert(...) at the moment */
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "expat_config.h"
+
+#include "expat.h"
+#include "internal.h"
+#include "chardata.h"
+#include "structdata.h"
+#include "common.h"
+#include "handlers.h"
+
+/* Global variables for user parameter settings tests */
+/* Variable holding the expected handler userData */
+const void *g_handler_data = NULL;
+/* Count of the number of times the comment handler has been invoked */
+int g_comment_count = 0;
+/* Count of the number of skipped entities */
+int g_skip_count = 0;
+/* Count of the number of times the XML declaration handler is invoked */
+int g_xdecl_count = 0;
+
+/* Start/End Element Handlers */
+
+void XMLCALL
+start_element_event_handler(void *userData, const XML_Char *name,
+                            const XML_Char **atts) {
+  UNUSED_P(atts);
+  CharData_AppendXMLChars((CharData *)userData, name, -1);
+}
+
+void XMLCALL
+end_element_event_handler(void *userData, const XML_Char *name) {
+  CharData *storage = (CharData *)userData;
+  CharData_AppendXMLChars(storage, XCS("/"), 1);
+  CharData_AppendXMLChars(storage, name, -1);
+}
+
+void XMLCALL
+start_element_event_handler2(void *userData, const XML_Char *name,
+                             const XML_Char **attr) {
+  StructData *storage = (StructData *)userData;
+  UNUSED_P(attr);
+  StructData_AddItem(storage, name, XML_GetCurrentColumnNumber(g_parser),
+                     XML_GetCurrentLineNumber(g_parser), STRUCT_START_TAG);
+}
+
+void XMLCALL
+end_element_event_handler2(void *userData, const XML_Char *name) {
+  StructData *storage = (StructData *)userData;
+  StructData_AddItem(storage, name, XML_GetCurrentColumnNumber(g_parser),
+                     XML_GetCurrentLineNumber(g_parser), STRUCT_END_TAG);
+}
+
+void XMLCALL
+counting_start_element_handler(void *userData, const XML_Char *name,
+                               const XML_Char **atts) {
+  ElementInfo *info = (ElementInfo *)userData;
+  AttrInfo *attr;
+  int count, id, i;
+
+  while (info->name != NULL) {
+    if (! xcstrcmp(name, info->name))
+      break;
+    info++;
+  }
+  if (info->name == NULL)
+    fail("Element not recognised");
+  /* The attribute count is twice what you might expect.  It is a
+   * count of items in atts, an array which contains alternating
+   * attribute names and attribute values.  For the naive user this
+   * is possibly a little unexpected, but it is what the
+   * documentation in expat.h tells us to expect.
+   */
+  count = XML_GetSpecifiedAttributeCount(g_parser);
+  if (info->attr_count * 2 != count) {
+    fail("Not got expected attribute count");
+    return;
+  }
+  id = XML_GetIdAttributeIndex(g_parser);
+  if (id == -1 && info->id_name != NULL) {
+    fail("ID not present");
+    return;
+  }
+  if (id != -1 && xcstrcmp(atts[id], info->id_name)) {
+    fail("ID does not have the correct name");
+    return;
+  }
+  for (i = 0; i < info->attr_count; i++) {
+    attr = info->attributes;
+    while (attr->name != NULL) {
+      if (! xcstrcmp(atts[0], attr->name))
+        break;
+      attr++;
+    }
+    if (attr->name == NULL) {
+      fail("Attribute not recognised");
+      return;
+    }
+    if (xcstrcmp(atts[1], attr->value)) {
+      fail("Attribute has wrong value");
+      return;
+    }
+    /* Remember, two entries in atts per attribute (see above) */
+    atts += 2;
+  }
+}
+
+void XMLCALL
+suspending_end_handler(void *userData, const XML_Char *s) {
+  UNUSED_P(s);
+  XML_StopParser((XML_Parser)userData, 1);
+}
+
+void XMLCALL
+start_element_suspender(void *userData, const XML_Char *name,
+                        const XML_Char **atts) {
+  UNUSED_P(userData);
+  UNUSED_P(atts);
+  if (! xcstrcmp(name, XCS("suspend")))
+    XML_StopParser(g_parser, XML_TRUE);
+  if (! xcstrcmp(name, XCS("abort")))
+    XML_StopParser(g_parser, XML_FALSE);
+}
+
+/* Check that an element name and attribute name match the expected values.
+   The expected values are passed as an array reference of string pointers
+   provided as the userData argument; the first is the expected
+   element name, and the second is the expected attribute name.
+*/
+int g_triplet_start_flag = XML_FALSE;
+int g_triplet_end_flag = XML_FALSE;
+
+void XMLCALL
+triplet_start_checker(void *userData, const XML_Char *name,
+                      const XML_Char **atts) {
+  XML_Char **elemstr = (XML_Char **)userData;
+  char buffer[1024];
+  if (xcstrcmp(elemstr[0], name) != 0) {
+    snprintf(buffer, sizeof(buffer),
+             "unexpected start string: '%" XML_FMT_STR "'", name);
+    fail(buffer);
+  }
+  if (xcstrcmp(elemstr[1], atts[0]) != 0) {
+    snprintf(buffer, sizeof(buffer),
+             "unexpected attribute string: '%" XML_FMT_STR "'", atts[0]);
+    fail(buffer);
+  }
+  g_triplet_start_flag = XML_TRUE;
+}
+
+/* Check that the element name passed to the end-element handler matches
+   the expected value.  The expected value is passed as the first element
+   in an array of strings passed as the userData argument.
+*/
+void XMLCALL
+triplet_end_checker(void *userData, const XML_Char *name) {
+  XML_Char **elemstr = (XML_Char **)userData;
+  if (xcstrcmp(elemstr[0], name) != 0) {
+    char buffer[1024];
+    snprintf(buffer, sizeof(buffer),
+             "unexpected end string: '%" XML_FMT_STR "'", name);
+    fail(buffer);
+  }
+  g_triplet_end_flag = XML_TRUE;
+}
+
+void XMLCALL
+overwrite_start_checker(void *userData, const XML_Char *name,
+                        const XML_Char **atts) {
+  CharData *storage = (CharData *)userData;
+  CharData_AppendXMLChars(storage, XCS("start "), 6);
+  CharData_AppendXMLChars(storage, name, -1);
+  while (*atts != NULL) {
+    CharData_AppendXMLChars(storage, XCS("\nattribute "), 11);
+    CharData_AppendXMLChars(storage, *atts, -1);
+    atts += 2;
+  }
+  CharData_AppendXMLChars(storage, XCS("\n"), 1);
+}
+
+void XMLCALL
+overwrite_end_checker(void *userData, const XML_Char *name) {
+  CharData *storage = (CharData *)userData;
+  CharData_AppendXMLChars(storage, XCS("end "), 4);
+  CharData_AppendXMLChars(storage, name, -1);
+  CharData_AppendXMLChars(storage, XCS("\n"), 1);
+}
+
+void XMLCALL
+start_element_fail(void *userData, const XML_Char *name,
+                   const XML_Char **atts) {
+  UNUSED_P(userData);
+  UNUSED_P(name);
+  UNUSED_P(atts);
+
+  /* We should never get here. */
+  fail("should never reach start_element_fail()");
+}
+
+void XMLCALL
+start_ns_clearing_start_element(void *userData, const XML_Char *prefix,
+                                const XML_Char *uri) {
+  UNUSED_P(prefix);
+  UNUSED_P(uri);
+  XML_SetStartElementHandler((XML_Parser)userData, NULL);
+}
+
+void XMLCALL
+start_element_issue_240(void *userData, const XML_Char *name,
+                        const XML_Char **atts) {
+  DataIssue240 *mydata = (DataIssue240 *)userData;
+  UNUSED_P(name);
+  UNUSED_P(atts);
+  mydata->deep++;
+}
+
+void XMLCALL
+end_element_issue_240(void *userData, const XML_Char *name) {
+  DataIssue240 *mydata = (DataIssue240 *)userData;
+
+  UNUSED_P(name);
+  mydata->deep--;
+  if (mydata->deep == 0) {
+    XML_StopParser(mydata->parser, 0);
+  }
+}
+
+/* Text encoding handlers */
+
+int XMLCALL
+UnknownEncodingHandler(void *data, const XML_Char *encoding,
+                       XML_Encoding *info) {
+  UNUSED_P(data);
+  if (xcstrcmp(encoding, XCS("unsupported-encoding")) == 0) {
+    int i;
+    for (i = 0; i < 256; ++i)
+      info->map[i] = i;
+    info->data = NULL;
+    info->convert = NULL;
+    info->release = NULL;
+    return XML_STATUS_OK;
+  }
+  return XML_STATUS_ERROR;
+}
+
+static void
+dummy_release(void *data) {
+  UNUSED_P(data);
+}
+
+int XMLCALL
+UnrecognisedEncodingHandler(void *data, const XML_Char *encoding,
+                            XML_Encoding *info) {
+  UNUSED_P(data);
+  UNUSED_P(encoding);
+  info->data = NULL;
+  info->convert = NULL;
+  info->release = dummy_release;
+  return XML_STATUS_ERROR;
+}
+
+int XMLCALL
+unknown_released_encoding_handler(void *data, const XML_Char *encoding,
+                                  XML_Encoding *info) {
+  UNUSED_P(data);
+  if (! xcstrcmp(encoding, XCS("unsupported-encoding"))) {
+    int i;
+
+    for (i = 0; i < 256; i++)
+      info->map[i] = i;
+    info->data = NULL;
+    info->convert = NULL;
+    info->release = dummy_release;
+    return XML_STATUS_OK;
+  }
+  return XML_STATUS_ERROR;
+}
+
+static int XMLCALL
+failing_converter(void *data, const char *s) {
+  UNUSED_P(data);
+  UNUSED_P(s);
+  /* Always claim to have failed */
+  return -1;
+}
+
+static int XMLCALL
+prefix_converter(void *data, const char *s) {
+  UNUSED_P(data);
+  /* If the first byte is 0xff, raise an error */
+  if (s[0] == (char)-1)
+    return -1;
+  /* Just add the low bits of the first byte to the second */
+  return (s[1] + (s[0] & 0x7f)) & 0x01ff;
+}
+
+int XMLCALL
+MiscEncodingHandler(void *data, const XML_Char *encoding, XML_Encoding *info) {
+  int i;
+  int high_map = -2; /* Assume a 2-byte sequence */
+
+  if (! xcstrcmp(encoding, XCS("invalid-9"))
+      || ! xcstrcmp(encoding, XCS("ascii-like"))
+      || ! xcstrcmp(encoding, XCS("invalid-len"))
+      || ! xcstrcmp(encoding, XCS("invalid-a"))
+      || ! xcstrcmp(encoding, XCS("invalid-surrogate"))
+      || ! xcstrcmp(encoding, XCS("invalid-high")))
+    high_map = -1;
+
+  for (i = 0; i < 128; ++i)
+    info->map[i] = i;
+  for (; i < 256; ++i)
+    info->map[i] = high_map;
+
+  /* If required, put an invalid value in the ASCII entries */
+  if (! xcstrcmp(encoding, XCS("invalid-9")))
+    info->map[9] = 5;
+  /* If required, have a top-bit set character starts a 5-byte sequence */
+  if (! xcstrcmp(encoding, XCS("invalid-len")))
+    info->map[0x81] = -5;
+  /* If required, make a top-bit set character a valid ASCII character */
+  if (! xcstrcmp(encoding, XCS("invalid-a")))
+    info->map[0x82] = 'a';
+  /* If required, give a top-bit set character a forbidden value,
+   * what would otherwise be the first of a surrogate pair.
+   */
+  if (! xcstrcmp(encoding, XCS("invalid-surrogate")))
+    info->map[0x83] = 0xd801;
+  /* If required, give a top-bit set character too high a value */
+  if (! xcstrcmp(encoding, XCS("invalid-high")))
+    info->map[0x84] = 0x010101;
+
+  info->data = data;
+  info->release = NULL;
+  if (! xcstrcmp(encoding, XCS("failing-conv")))
+    info->convert = failing_converter;
+  else if (! xcstrcmp(encoding, XCS("prefix-conv")))
+    info->convert = prefix_converter;
+  else
+    info->convert = NULL;
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+long_encoding_handler(void *userData, const XML_Char *encoding,
+                      XML_Encoding *info) {
+  int i;
+
+  UNUSED_P(userData);
+  UNUSED_P(encoding);
+  for (i = 0; i < 256; i++)
+    info->map[i] = i;
+  info->data = NULL;
+  info->convert = NULL;
+  info->release = NULL;
+  return XML_STATUS_OK;
+}
+
+/* External Entity Handlers */
+
+int XMLCALL
+external_entity_optioner(XML_Parser parser, const XML_Char *context,
+                         const XML_Char *base, const XML_Char *systemId,
+                         const XML_Char *publicId) {
+  ExtOption *options = (ExtOption *)XML_GetUserData(parser);
+  XML_Parser ext_parser;
+
+  UNUSED_P(base);
+  UNUSED_P(publicId);
+  while (options->parse_text != NULL) {
+    if (! xcstrcmp(systemId, options->system_id)) {
+      enum XML_Status rc;
+      ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+      if (ext_parser == NULL)
+        return XML_STATUS_ERROR;
+      rc = _XML_Parse_SINGLE_BYTES(ext_parser, options->parse_text,
+                                   (int)strlen(options->parse_text), XML_TRUE);
+      XML_ParserFree(ext_parser);
+      return rc;
+    }
+    options++;
+  }
+  fail("No suitable option found");
+  return XML_STATUS_ERROR;
+}
+
+int XMLCALL
+external_entity_loader(XML_Parser parser, const XML_Char *context,
+                       const XML_Char *base, const XML_Char *systemId,
+                       const XML_Char *publicId) {
+  ExtTest *test_data = (ExtTest *)XML_GetUserData(parser);
+  XML_Parser extparser;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  extparser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (extparser == NULL)
+    fail("Could not create external entity parser.");
+  if (test_data->encoding != NULL) {
+    if (! XML_SetEncoding(extparser, test_data->encoding))
+      fail("XML_SetEncoding() ignored for external entity");
+  }
+  if (_XML_Parse_SINGLE_BYTES(extparser, test_data->parse_text,
+                              (int)strlen(test_data->parse_text), XML_TRUE)
+      == XML_STATUS_ERROR) {
+    xml_failure(extparser);
+    return XML_STATUS_ERROR;
+  }
+  XML_ParserFree(extparser);
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_faulter(XML_Parser parser, const XML_Char *context,
+                        const XML_Char *base, const XML_Char *systemId,
+                        const XML_Char *publicId) {
+  XML_Parser ext_parser;
+  ExtFaults *fault = (ExtFaults *)XML_GetUserData(parser);
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could not create external entity parser");
+  if (fault->encoding != NULL) {
+    if (! XML_SetEncoding(ext_parser, fault->encoding))
+      fail("XML_SetEncoding failed");
+  }
+  if (_XML_Parse_SINGLE_BYTES(ext_parser, fault->parse_text,
+                              (int)strlen(fault->parse_text), XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail(fault->fail_text);
+  if (XML_GetErrorCode(ext_parser) != fault->error)
+    xml_failure(ext_parser);
+
+  XML_ParserFree(ext_parser);
+  return XML_STATUS_ERROR;
+}
+
+int XMLCALL
+external_entity_null_loader(XML_Parser parser, const XML_Char *context,
+                            const XML_Char *base, const XML_Char *systemId,
+                            const XML_Char *publicId) {
+  UNUSED_P(parser);
+  UNUSED_P(context);
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_resetter(XML_Parser parser, const XML_Char *context,
+                         const XML_Char *base, const XML_Char *systemId,
+                         const XML_Char *publicId) {
+  const char *text = "<!ELEMENT doc (#PCDATA)*>";
+  XML_Parser ext_parser;
+  XML_ParsingStatus status;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could not create external entity parser");
+  XML_GetParsingStatus(ext_parser, &status);
+  if (status.parsing != XML_INITIALIZED) {
+    fail("Parsing status is not INITIALIZED");
+    return XML_STATUS_ERROR;
+  }
+  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR) {
+    xml_failure(parser);
+    return XML_STATUS_ERROR;
+  }
+  XML_GetParsingStatus(ext_parser, &status);
+  if (status.parsing != XML_FINISHED) {
+    fail("Parsing status is not FINISHED");
+    return XML_STATUS_ERROR;
+  }
+  /* Check we can't parse here */
+  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail("Parsing when finished not faulted");
+  if (XML_GetErrorCode(ext_parser) != XML_ERROR_FINISHED)
+    fail("Parsing when finished faulted with wrong code");
+  XML_ParserReset(ext_parser, NULL);
+  XML_GetParsingStatus(ext_parser, &status);
+  if (status.parsing != XML_FINISHED) {
+    fail("Parsing status not still FINISHED");
+    return XML_STATUS_ERROR;
+  }
+  XML_ParserFree(ext_parser);
+  return XML_STATUS_OK;
+}
+
+void XMLCALL
+entity_suspending_decl_handler(void *userData, const XML_Char *name,
+                               XML_Content *model) {
+  XML_Parser ext_parser = (XML_Parser)userData;
+
+  UNUSED_P(name);
+  if (XML_StopParser(ext_parser, XML_TRUE) != XML_STATUS_ERROR)
+    fail("Attempting to suspend a subordinate parser not faulted");
+  if (XML_GetErrorCode(ext_parser) != XML_ERROR_SUSPEND_PE)
+    fail("Suspending subordinate parser get wrong code");
+  XML_SetElementDeclHandler(ext_parser, NULL);
+  XML_FreeContentModel(g_parser, model);
+}
+
+int XMLCALL
+external_entity_suspender(XML_Parser parser, const XML_Char *context,
+                          const XML_Char *base, const XML_Char *systemId,
+                          const XML_Char *publicId) {
+  const char *text = "<!ELEMENT doc (#PCDATA)*>";
+  XML_Parser ext_parser;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could not create external entity parser");
+  XML_SetElementDeclHandler(ext_parser, entity_suspending_decl_handler);
+  XML_SetUserData(ext_parser, ext_parser);
+  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR) {
+    xml_failure(ext_parser);
+    return XML_STATUS_ERROR;
+  }
+  XML_ParserFree(ext_parser);
+  return XML_STATUS_OK;
+}
+
+void XMLCALL
+entity_suspending_xdecl_handler(void *userData, const XML_Char *version,
+                                const XML_Char *encoding, int standalone) {
+  XML_Parser ext_parser = (XML_Parser)userData;
+
+  UNUSED_P(version);
+  UNUSED_P(encoding);
+  UNUSED_P(standalone);
+  XML_StopParser(ext_parser, g_resumable);
+  XML_SetXmlDeclHandler(ext_parser, NULL);
+}
+
+int XMLCALL
+external_entity_suspend_xmldecl(XML_Parser parser, const XML_Char *context,
+                                const XML_Char *base, const XML_Char *systemId,
+                                const XML_Char *publicId) {
+  const char *text = "<?xml version='1.0' encoding='us-ascii'?>";
+  XML_Parser ext_parser;
+  XML_ParsingStatus status;
+  enum XML_Status rc;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could not create external entity parser");
+  XML_SetXmlDeclHandler(ext_parser, entity_suspending_xdecl_handler);
+  XML_SetUserData(ext_parser, ext_parser);
+  rc = _XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE);
+  XML_GetParsingStatus(ext_parser, &status);
+  if (g_resumable) {
+    if (rc == XML_STATUS_ERROR)
+      xml_failure(ext_parser);
+    if (status.parsing != XML_SUSPENDED)
+      fail("Ext Parsing status not SUSPENDED");
+  } else {
+    if (rc != XML_STATUS_ERROR)
+      fail("Ext parsing not aborted");
+    if (XML_GetErrorCode(ext_parser) != XML_ERROR_ABORTED)
+      xml_failure(ext_parser);
+    if (status.parsing != XML_FINISHED)
+      fail("Ext Parsing status not FINISHED");
+  }
+
+  XML_ParserFree(ext_parser);
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_suspending_faulter(XML_Parser parser, const XML_Char *context,
+                                   const XML_Char *base,
+                                   const XML_Char *systemId,
+                                   const XML_Char *publicId) {
+  XML_Parser ext_parser;
+  ExtFaults *fault = (ExtFaults *)XML_GetUserData(parser);
+  void *buffer;
+  int parse_len = (int)strlen(fault->parse_text);
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could not create external entity parser");
+  XML_SetXmlDeclHandler(ext_parser, entity_suspending_xdecl_handler);
+  XML_SetUserData(ext_parser, ext_parser);
+  g_resumable = XML_TRUE;
+  buffer = XML_GetBuffer(ext_parser, parse_len);
+  if (buffer == NULL)
+    fail("Could not allocate parse buffer");
+  assert(buffer != NULL);
+  memcpy(buffer, fault->parse_text, parse_len);
+  if (XML_ParseBuffer(ext_parser, parse_len, XML_FALSE) != XML_STATUS_SUSPENDED)
+    fail("XML declaration did not suspend");
+  if (XML_ResumeParser(ext_parser) != XML_STATUS_OK)
+    xml_failure(ext_parser);
+  if (XML_ParseBuffer(ext_parser, 0, XML_TRUE) != XML_STATUS_ERROR)
+    fail(fault->fail_text);
+  if (XML_GetErrorCode(ext_parser) != fault->error)
+    xml_failure(ext_parser);
+
+  XML_ParserFree(ext_parser);
+  return XML_STATUS_ERROR;
+}
+
+int XMLCALL
+external_entity_failer__if_not_xml_ge(XML_Parser parser,
+                                      const XML_Char *context,
+                                      const XML_Char *base,
+                                      const XML_Char *systemId,
+                                      const XML_Char *publicId) {
+  UNUSED_P(parser);
+  UNUSED_P(context);
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+#if XML_GE == 0
+  fail(
+      "Function external_entity_suspending_failer was called despite XML_GE==0.");
+#endif
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_cr_catcher(XML_Parser parser, const XML_Char *context,
+                           const XML_Char *base, const XML_Char *systemId,
+                           const XML_Char *publicId) {
+  const char *text = "\r";
+  XML_Parser ext_parser;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could not create external entity parser");
+  XML_SetCharacterDataHandler(ext_parser, cr_cdata_handler);
+  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(ext_parser);
+  XML_ParserFree(ext_parser);
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_bad_cr_catcher(XML_Parser parser, const XML_Char *context,
+                               const XML_Char *base, const XML_Char *systemId,
+                               const XML_Char *publicId) {
+  const char *text = "<tag>\r";
+  XML_Parser ext_parser;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could not create external entity parser");
+  XML_SetCharacterDataHandler(ext_parser, cr_cdata_handler);
+  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_OK)
+    fail("Async entity error not caught");
+  if (XML_GetErrorCode(ext_parser) != XML_ERROR_ASYNC_ENTITY)
+    xml_failure(ext_parser);
+  XML_ParserFree(ext_parser);
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_rsqb_catcher(XML_Parser parser, const XML_Char *context,
+                             const XML_Char *base, const XML_Char *systemId,
+                             const XML_Char *publicId) {
+  const char *text = "<tag>]";
+  XML_Parser ext_parser;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could not create external entity parser");
+  XML_SetCharacterDataHandler(ext_parser, rsqb_handler);
+  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail("Async entity error not caught");
+  if (XML_GetErrorCode(ext_parser) != XML_ERROR_ASYNC_ENTITY)
+    xml_failure(ext_parser);
+  XML_ParserFree(ext_parser);
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_good_cdata_ascii(XML_Parser parser, const XML_Char *context,
+                                 const XML_Char *base, const XML_Char *systemId,
+                                 const XML_Char *publicId) {
+  const char *text = "<a><![CDATA[<greeting>Hello, world!</greeting>]]></a>";
+  const XML_Char *expected = XCS("<greeting>Hello, world!</greeting>");
+  CharData storage;
+  XML_Parser ext_parser;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  CharData_Init(&storage);
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could not create external entity parser");
+  XML_SetUserData(ext_parser, &storage);
+  XML_SetCharacterDataHandler(ext_parser, accumulate_characters);
+
+  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(ext_parser);
+  CharData_CheckXMLChars(&storage, expected);
+
+  XML_ParserFree(ext_parser);
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_param_checker(XML_Parser parser, const XML_Char *context,
+                              const XML_Char *base, const XML_Char *systemId,
+                              const XML_Char *publicId) {
+  const char *text = "<!-- Subordinate parser -->\n"
+                     "<!ELEMENT doc (#PCDATA)*>";
+  XML_Parser ext_parser;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could not create external entity parser");
+  g_handler_data = ext_parser;
+  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR) {
+    xml_failure(parser);
+    return XML_STATUS_ERROR;
+  }
+  g_handler_data = parser;
+  XML_ParserFree(ext_parser);
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_ref_param_checker(XML_Parser parameter, const XML_Char *context,
+                                  const XML_Char *base,
+                                  const XML_Char *systemId,
+                                  const XML_Char *publicId) {
+  const char *text = "<!ELEMENT doc (#PCDATA)*>";
+  XML_Parser ext_parser;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  if ((void *)parameter != g_handler_data)
+    fail("External entity ref handler parameter not correct");
+
+  /* Here we use the global 'parser' variable */
+  ext_parser = XML_ExternalEntityParserCreate(g_parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could not create external entity parser");
+  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(ext_parser);
+
+  XML_ParserFree(ext_parser);
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_param(XML_Parser parser, const XML_Char *context,
+                      const XML_Char *base, const XML_Char *systemId,
+                      const XML_Char *publicId) {
+  const char *text1 = "<!ELEMENT doc EMPTY>\n"
+                      "<!ENTITY % e1 SYSTEM '004-2.ent'>\n"
+                      "<!ENTITY % e2 '%e1;'>\n"
+                      "%e1;\n";
+  const char *text2 = "<!ELEMENT el EMPTY>\n"
+                      "<el/>\n";
+  XML_Parser ext_parser;
+
+  UNUSED_P(base);
+  UNUSED_P(publicId);
+  if (systemId == NULL)
+    return XML_STATUS_OK;
+
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could not create external entity parser");
+
+  if (! xcstrcmp(systemId, XCS("004-1.ent"))) {
+    if (_XML_Parse_SINGLE_BYTES(ext_parser, text1, (int)strlen(text1), XML_TRUE)
+        != XML_STATUS_ERROR)
+      fail("Inner DTD with invalid tag not rejected");
+    if (XML_GetErrorCode(ext_parser) != XML_ERROR_EXTERNAL_ENTITY_HANDLING)
+      xml_failure(ext_parser);
+  } else if (! xcstrcmp(systemId, XCS("004-2.ent"))) {
+    if (_XML_Parse_SINGLE_BYTES(ext_parser, text2, (int)strlen(text2), XML_TRUE)
+        != XML_STATUS_ERROR)
+      fail("Invalid tag in external param not rejected");
+    if (XML_GetErrorCode(ext_parser) != XML_ERROR_SYNTAX)
+      xml_failure(ext_parser);
+  } else {
+    fail("Unknown system ID");
+  }
+
+  XML_ParserFree(ext_parser);
+  return XML_STATUS_ERROR;
+}
+
+int XMLCALL
+external_entity_load_ignore(XML_Parser parser, const XML_Char *context,
+                            const XML_Char *base, const XML_Char *systemId,
+                            const XML_Char *publicId) {
+  const char *text = "<![IGNORE[<!ELEMENT e (#PCDATA)*>]]>";
+  XML_Parser ext_parser;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could not create external entity parser");
+  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(parser);
+
+  XML_ParserFree(ext_parser);
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_load_ignore_utf16(XML_Parser parser, const XML_Char *context,
+                                  const XML_Char *base,
+                                  const XML_Char *systemId,
+                                  const XML_Char *publicId) {
+  const char text[] =
+      /* <![IGNORE[<!ELEMENT e (#PCDATA)*>]]> */
+      "<\0!\0[\0I\0G\0N\0O\0R\0E\0[\0"
+      "<\0!\0E\0L\0E\0M\0E\0N\0T\0 \0e\0 \0"
+      "(\0#\0P\0C\0D\0A\0T\0A\0)\0*\0>\0]\0]\0>\0";
+  XML_Parser ext_parser;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could not create external entity parser");
+  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(parser);
+
+  XML_ParserFree(ext_parser);
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_load_ignore_utf16_be(XML_Parser parser, const XML_Char *context,
+                                     const XML_Char *base,
+                                     const XML_Char *systemId,
+                                     const XML_Char *publicId) {
+  const char text[] =
+      /* <![IGNORE[<!ELEMENT e (#PCDATA)*>]]> */
+      "\0<\0!\0[\0I\0G\0N\0O\0R\0E\0["
+      "\0<\0!\0E\0L\0E\0M\0E\0N\0T\0 \0e\0 "
+      "\0(\0#\0P\0C\0D\0A\0T\0A\0)\0*\0>\0]\0]\0>";
+  XML_Parser ext_parser;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could not create external entity parser");
+  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(parser);
+
+  XML_ParserFree(ext_parser);
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_valuer(XML_Parser parser, const XML_Char *context,
+                       const XML_Char *base, const XML_Char *systemId,
+                       const XML_Char *publicId) {
+  const char *text1 = "<!ELEMENT doc EMPTY>\n"
+                      "<!ENTITY % e1 SYSTEM '004-2.ent'>\n"
+                      "<!ENTITY % e2 '%e1;'>\n"
+                      "%e1;\n";
+  XML_Parser ext_parser;
+
+  UNUSED_P(base);
+  UNUSED_P(publicId);
+  if (systemId == NULL)
+    return XML_STATUS_OK;
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could not create external entity parser");
+  if (! xcstrcmp(systemId, XCS("004-1.ent"))) {
+    if (_XML_Parse_SINGLE_BYTES(ext_parser, text1, (int)strlen(text1), XML_TRUE)
+        == XML_STATUS_ERROR)
+      xml_failure(ext_parser);
+  } else if (! xcstrcmp(systemId, XCS("004-2.ent"))) {
+    ExtFaults *fault = (ExtFaults *)XML_GetUserData(parser);
+    enum XML_Status status;
+    enum XML_Error error;
+
+    status = _XML_Parse_SINGLE_BYTES(ext_parser, fault->parse_text,
+                                     (int)strlen(fault->parse_text), XML_TRUE);
+    if (fault->error == XML_ERROR_NONE) {
+      if (status == XML_STATUS_ERROR)
+        xml_failure(ext_parser);
+    } else {
+      if (status != XML_STATUS_ERROR)
+        fail(fault->fail_text);
+      error = XML_GetErrorCode(ext_parser);
+      if (error != fault->error
+          && (fault->error != XML_ERROR_XML_DECL
+              || error != XML_ERROR_TEXT_DECL))
+        xml_failure(ext_parser);
+    }
+  }
+
+  XML_ParserFree(ext_parser);
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_not_standalone(XML_Parser parser, const XML_Char *context,
+                               const XML_Char *base, const XML_Char *systemId,
+                               const XML_Char *publicId) {
+  const char *text1 = "<!ELEMENT doc EMPTY>\n"
+                      "<!ENTITY % e1 SYSTEM 'bar'>\n"
+                      "%e1;\n";
+  const char *text2 = "<!ATTLIST doc a1 CDATA 'value'>";
+  XML_Parser ext_parser;
+
+  UNUSED_P(base);
+  UNUSED_P(publicId);
+  if (systemId == NULL)
+    return XML_STATUS_OK;
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could not create external entity parser");
+  if (! xcstrcmp(systemId, XCS("foo"))) {
+    XML_SetNotStandaloneHandler(ext_parser, reject_not_standalone_handler);
+    if (_XML_Parse_SINGLE_BYTES(ext_parser, text1, (int)strlen(text1), XML_TRUE)
+        != XML_STATUS_ERROR)
+      fail("Expected not standalone rejection");
+    if (XML_GetErrorCode(ext_parser) != XML_ERROR_NOT_STANDALONE)
+      xml_failure(ext_parser);
+    XML_SetNotStandaloneHandler(ext_parser, NULL);
+    XML_ParserFree(ext_parser);
+    return XML_STATUS_ERROR;
+  } else if (! xcstrcmp(systemId, XCS("bar"))) {
+    if (_XML_Parse_SINGLE_BYTES(ext_parser, text2, (int)strlen(text2), XML_TRUE)
+        == XML_STATUS_ERROR)
+      xml_failure(ext_parser);
+  }
+
+  XML_ParserFree(ext_parser);
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_value_aborter(XML_Parser parser, const XML_Char *context,
+                              const XML_Char *base, const XML_Char *systemId,
+                              const XML_Char *publicId) {
+  const char *text1 = "<!ELEMENT doc EMPTY>\n"
+                      "<!ENTITY % e1 SYSTEM '004-2.ent'>\n"
+                      "<!ENTITY % e2 '%e1;'>\n"
+                      "%e1;\n";
+  const char *text2 = "<?xml version='1.0' encoding='utf-8'?>";
+  XML_Parser ext_parser;
+
+  UNUSED_P(base);
+  UNUSED_P(publicId);
+  if (systemId == NULL)
+    return XML_STATUS_OK;
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could not create external entity parser");
+  if (! xcstrcmp(systemId, XCS("004-1.ent"))) {
+    if (_XML_Parse_SINGLE_BYTES(ext_parser, text1, (int)strlen(text1), XML_TRUE)
+        == XML_STATUS_ERROR)
+      xml_failure(ext_parser);
+  }
+  if (! xcstrcmp(systemId, XCS("004-2.ent"))) {
+    XML_SetXmlDeclHandler(ext_parser, entity_suspending_xdecl_handler);
+    XML_SetUserData(ext_parser, ext_parser);
+    if (_XML_Parse_SINGLE_BYTES(ext_parser, text2, (int)strlen(text2), XML_TRUE)
+        != XML_STATUS_ERROR)
+      fail("Aborted parse not faulted");
+    if (XML_GetErrorCode(ext_parser) != XML_ERROR_ABORTED)
+      xml_failure(ext_parser);
+  }
+
+  XML_ParserFree(ext_parser);
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_public(XML_Parser parser, const XML_Char *context,
+                       const XML_Char *base, const XML_Char *systemId,
+                       const XML_Char *publicId) {
+  const char *text1 = (const char *)XML_GetUserData(parser);
+  const char *text2 = "<!ATTLIST doc a CDATA 'value'>";
+  const char *text = NULL;
+  XML_Parser ext_parser;
+  int parse_res;
+
+  UNUSED_P(base);
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    return XML_STATUS_ERROR;
+  if (systemId != NULL && ! xcstrcmp(systemId, XCS("http://example.org/"))) {
+    text = text1;
+  } else if (publicId != NULL && ! xcstrcmp(publicId, XCS("foo"))) {
+    text = text2;
+  } else
+    fail("Unexpected parameters to external entity parser");
+  assert(text != NULL);
+  parse_res
+      = _XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE);
+  XML_ParserFree(ext_parser);
+  return parse_res;
+}
+
+int XMLCALL
+external_entity_devaluer(XML_Parser parser, const XML_Char *context,
+                         const XML_Char *base, const XML_Char *systemId,
+                         const XML_Char *publicId) {
+  const char *text = "<!ELEMENT doc EMPTY>\n"
+                     "<!ENTITY % e1 SYSTEM 'bar'>\n"
+                     "%e1;\n";
+  XML_Parser ext_parser;
+  int clear_handler_flag = (XML_GetUserData(parser) != NULL);
+
+  UNUSED_P(base);
+  UNUSED_P(publicId);
+  if (systemId == NULL || ! xcstrcmp(systemId, XCS("bar")))
+    return XML_STATUS_OK;
+  if (xcstrcmp(systemId, XCS("foo")))
+    fail("Unexpected system ID");
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could note create external entity parser");
+  if (clear_handler_flag)
+    XML_SetExternalEntityRefHandler(ext_parser, NULL);
+  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(ext_parser);
+
+  XML_ParserFree(ext_parser);
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_oneshot_loader(XML_Parser parser, const XML_Char *context,
+                               const XML_Char *base, const XML_Char *systemId,
+                               const XML_Char *publicId) {
+  ExtHdlrData *test_data = (ExtHdlrData *)XML_GetUserData(parser);
+  XML_Parser ext_parser;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could not create external entity parser.");
+  /* Use the requested entity parser for further externals */
+  XML_SetExternalEntityRefHandler(ext_parser, test_data->handler);
+  if (_XML_Parse_SINGLE_BYTES(ext_parser, test_data->parse_text,
+                              (int)strlen(test_data->parse_text), XML_TRUE)
+      == XML_STATUS_ERROR) {
+    xml_failure(ext_parser);
+  }
+
+  XML_ParserFree(ext_parser);
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_loader2(XML_Parser parser, const XML_Char *context,
+                        const XML_Char *base, const XML_Char *systemId,
+                        const XML_Char *publicId) {
+  ExtTest2 *test_data = (ExtTest2 *)XML_GetUserData(parser);
+  XML_Parser extparser;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  extparser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (extparser == NULL)
+    fail("Coulr not create external entity parser");
+  if (test_data->encoding != NULL) {
+    if (! XML_SetEncoding(extparser, test_data->encoding))
+      fail("XML_SetEncoding() ignored for external entity");
+  }
+  if (_XML_Parse_SINGLE_BYTES(extparser, test_data->parse_text,
+                              test_data->parse_len, XML_TRUE)
+      == XML_STATUS_ERROR) {
+    xml_failure(extparser);
+  }
+
+  XML_ParserFree(extparser);
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_faulter2(XML_Parser parser, const XML_Char *context,
+                         const XML_Char *base, const XML_Char *systemId,
+                         const XML_Char *publicId) {
+  ExtFaults2 *test_data = (ExtFaults2 *)XML_GetUserData(parser);
+  XML_Parser extparser;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  extparser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (extparser == NULL)
+    fail("Could not create external entity parser");
+  if (test_data->encoding != NULL) {
+    if (! XML_SetEncoding(extparser, test_data->encoding))
+      fail("XML_SetEncoding() ignored for external entity");
+  }
+  if (_XML_Parse_SINGLE_BYTES(extparser, test_data->parse_text,
+                              test_data->parse_len, XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail(test_data->fail_text);
+  if (XML_GetErrorCode(extparser) != test_data->error)
+    xml_failure(extparser);
+
+  XML_ParserFree(extparser);
+  return XML_STATUS_ERROR;
+}
+
+int XMLCALL
+external_entity_unfinished_attlist(XML_Parser parser, const XML_Char *context,
+                                   const XML_Char *base,
+                                   const XML_Char *systemId,
+                                   const XML_Char *publicId) {
+  const char *text = "<!ELEMENT barf ANY>\n"
+                     "<!ATTLIST barf my_attr (blah|%blah;a|foo) #REQUIRED>\n"
+                     "<!--COMMENT-->\n";
+  XML_Parser ext_parser;
+
+  UNUSED_P(base);
+  UNUSED_P(publicId);
+  if (systemId == NULL)
+    return XML_STATUS_OK;
+
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could not create external entity parser");
+
+  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(ext_parser);
+
+  XML_ParserFree(ext_parser);
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_handler(XML_Parser parser, const XML_Char *context,
+                        const XML_Char *base, const XML_Char *systemId,
+                        const XML_Char *publicId) {
+  void *user_data = XML_GetUserData(parser);
+  const char *text;
+  XML_Parser p2;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  if (user_data == NULL)
+    text = ("<!ELEMENT doc (e+)>\n"
+            "<!ATTLIST doc xmlns CDATA #IMPLIED>\n"
+            "<!ELEMENT e EMPTY>\n");
+  else
+    text = ("<?xml version='1.0' encoding='us-ascii'?>"
+            "<e/>");
+
+  /* Set user data to any non-NULL value */
+  XML_SetUserData(parser, parser);
+  p2 = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (_XML_Parse_SINGLE_BYTES(p2, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR) {
+    xml_failure(p2);
+    return XML_STATUS_ERROR;
+  }
+  XML_ParserFree(p2);
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_duff_loader(XML_Parser parser, const XML_Char *context,
+                            const XML_Char *base, const XML_Char *systemId,
+                            const XML_Char *publicId) {
+  XML_Parser new_parser;
+  unsigned int i;
+  const unsigned int max_alloc_count = 10;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  /* Try a few different allocation levels */
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    new_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+    if (new_parser != NULL) {
+      XML_ParserFree(new_parser);
+      break;
+    }
+  }
+  if (i == 0)
+    fail("External parser creation ignored failing allocator");
+  else if (i == max_alloc_count)
+    fail("Extern parser not created with max allocation count");
+
+  /* Make sure other random allocation doesn't now fail */
+  g_allocation_count = ALLOC_ALWAYS_SUCCEED;
+
+  /* Make sure the failure code path is executed too */
+  return XML_STATUS_ERROR;
+}
+
+int XMLCALL
+external_entity_dbl_handler(XML_Parser parser, const XML_Char *context,
+                            const XML_Char *base, const XML_Char *systemId,
+                            const XML_Char *publicId) {
+  int *pcallno = (int *)XML_GetUserData(parser);
+  int callno = *pcallno;
+  const char *text;
+  XML_Parser new_parser = NULL;
+  int i;
+  const int max_alloc_count = 20;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  if (callno == 0) {
+    /* First time through, check how many calls to malloc occur */
+    text = ("<!ELEMENT doc (e+)>\n"
+            "<!ATTLIST doc xmlns CDATA #IMPLIED>\n"
+            "<!ELEMENT e EMPTY>\n");
+    g_allocation_count = 10000;
+    new_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+    if (new_parser == NULL) {
+      fail("Unable to allocate first external parser");
+      return XML_STATUS_ERROR;
+    }
+    /* Stash the number of calls in the user data */
+    *pcallno = 10000 - g_allocation_count;
+  } else {
+    text = ("<?xml version='1.0' encoding='us-ascii'?>"
+            "<e/>");
+    /* Try at varying levels to exercise more code paths */
+    for (i = 0; i < max_alloc_count; i++) {
+      g_allocation_count = callno + i;
+      new_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+      if (new_parser != NULL)
+        break;
+    }
+    if (i == 0) {
+      fail("Second external parser unexpectedly created");
+      XML_ParserFree(new_parser);
+      return XML_STATUS_ERROR;
+    } else if (i == max_alloc_count) {
+      fail("Second external parser not created");
+      return XML_STATUS_ERROR;
+    }
+  }
+
+  g_allocation_count = ALLOC_ALWAYS_SUCCEED;
+  if (_XML_Parse_SINGLE_BYTES(new_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR) {
+    xml_failure(new_parser);
+    return XML_STATUS_ERROR;
+  }
+  XML_ParserFree(new_parser);
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_dbl_handler_2(XML_Parser parser, const XML_Char *context,
+                              const XML_Char *base, const XML_Char *systemId,
+                              const XML_Char *publicId) {
+  int *pcallno = (int *)XML_GetUserData(parser);
+  int callno = *pcallno;
+  const char *text;
+  XML_Parser new_parser;
+  enum XML_Status rv;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  if (callno == 0) {
+    /* Try different allocation levels for whole exercise */
+    text = ("<!ELEMENT doc (e+)>\n"
+            "<!ATTLIST doc xmlns CDATA #IMPLIED>\n"
+            "<!ELEMENT e EMPTY>\n");
+    *pcallno = 1;
+    new_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+    if (new_parser == NULL)
+      return XML_STATUS_ERROR;
+    rv = _XML_Parse_SINGLE_BYTES(new_parser, text, (int)strlen(text), XML_TRUE);
+  } else {
+    /* Just run through once */
+    text = ("<?xml version='1.0' encoding='us-ascii'?>"
+            "<e/>");
+    new_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+    if (new_parser == NULL)
+      return XML_STATUS_ERROR;
+    rv = _XML_Parse_SINGLE_BYTES(new_parser, text, (int)strlen(text), XML_TRUE);
+  }
+  XML_ParserFree(new_parser);
+  if (rv == XML_STATUS_ERROR)
+    return XML_STATUS_ERROR;
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_alloc_set_encoding(XML_Parser parser, const XML_Char *context,
+                                   const XML_Char *base,
+                                   const XML_Char *systemId,
+                                   const XML_Char *publicId) {
+  /* As for external_entity_loader() */
+  const char *text = "<?xml encoding='iso-8859-3'?>"
+                     "\xC3\xA9";
+  XML_Parser ext_parser;
+  enum XML_Status status;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    return XML_STATUS_ERROR;
+  if (! XML_SetEncoding(ext_parser, XCS("utf-8"))) {
+    XML_ParserFree(ext_parser);
+    return XML_STATUS_ERROR;
+  }
+  status
+      = _XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE);
+  XML_ParserFree(ext_parser);
+  if (status == XML_STATUS_ERROR)
+    return XML_STATUS_ERROR;
+  return XML_STATUS_OK;
+}
+
+int XMLCALL
+external_entity_reallocator(XML_Parser parser, const XML_Char *context,
+                            const XML_Char *base, const XML_Char *systemId,
+                            const XML_Char *publicId) {
+  const char *text = get_buffer_test_text;
+  XML_Parser ext_parser;
+  void *buffer;
+  enum XML_Status status;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    fail("Could not create external entity parser");
+
+  g_reallocation_count = *(int *)XML_GetUserData(parser);
+  buffer = XML_GetBuffer(ext_parser, 1536);
+  if (buffer == NULL)
+    fail("Buffer allocation failed");
+  assert(buffer != NULL);
+  memcpy(buffer, text, strlen(text));
+  status = XML_ParseBuffer(ext_parser, (int)strlen(text), XML_FALSE);
+  g_reallocation_count = -1;
+  XML_ParserFree(ext_parser);
+  return (status == XML_STATUS_OK) ? XML_STATUS_OK : XML_STATUS_ERROR;
+}
+
+int XMLCALL
+external_entity_alloc(XML_Parser parser, const XML_Char *context,
+                      const XML_Char *base, const XML_Char *systemId,
+                      const XML_Char *publicId) {
+  const char *text = (const char *)XML_GetUserData(parser);
+  XML_Parser ext_parser;
+  int parse_res;
+
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
+  if (ext_parser == NULL)
+    return XML_STATUS_ERROR;
+  parse_res
+      = _XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE);
+  XML_ParserFree(ext_parser);
+  return parse_res;
+}
+
+int XMLCALL
+external_entity_parser_create_alloc_fail_handler(XML_Parser parser,
+                                                 const XML_Char *context,
+                                                 const XML_Char *base,
+                                                 const XML_Char *systemId,
+                                                 const XML_Char *publicId) {
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+
+  if (context != NULL)
+    fail("Unexpected non-NULL context");
+
+  // The following number intends to fail the upcoming allocation in line
+  // "parser->m_protocolEncodingName = copyString(encodingName,
+  // &(parser->m_mem));" in function parserInit.
+  g_allocation_count = 3;
+
+  const XML_Char *const encodingName = XCS("UTF-8"); // needs something non-NULL
+  const XML_Parser ext_parser
+      = XML_ExternalEntityParserCreate(parser, context, encodingName);
+  if (ext_parser != NULL)
+    fail(
+        "Call to XML_ExternalEntityParserCreate was expected to fail out-of-memory");
+
+  g_allocation_count = ALLOC_ALWAYS_SUCCEED;
+  return XML_STATUS_ERROR;
+}
+
+#if XML_GE == 1
+int
+accounting_external_entity_ref_handler(XML_Parser parser,
+                                       const XML_Char *context,
+                                       const XML_Char *base,
+                                       const XML_Char *systemId,
+                                       const XML_Char *publicId) {
+  UNUSED_P(base);
+  UNUSED_P(publicId);
+
+  const struct AccountingTestCase *const testCase
+      = (const struct AccountingTestCase *)XML_GetUserData(parser);
+
+  const char *externalText = NULL;
+  if (xcstrcmp(systemId, XCS("first.ent")) == 0) {
+    externalText = testCase->firstExternalText;
+  } else if (xcstrcmp(systemId, XCS("second.ent")) == 0) {
+    externalText = testCase->secondExternalText;
+  } else {
+    assert(! "systemId is neither \"first.ent\" nor \"second.ent\"");
+  }
+  assert(externalText);
+
+  XML_Parser entParser = XML_ExternalEntityParserCreate(parser, context, 0);
+  assert(entParser);
+
+  const enum XML_Status status = _XML_Parse_SINGLE_BYTES(
+      entParser, externalText, (int)strlen(externalText), XML_TRUE);
+
+  XML_ParserFree(entParser);
+  return status;
+}
+#endif /* XML_GE == 1 */
+
+/* NotStandalone handlers */
+
+int XMLCALL
+reject_not_standalone_handler(void *userData) {
+  UNUSED_P(userData);
+  return XML_STATUS_ERROR;
+}
+
+int XMLCALL
+accept_not_standalone_handler(void *userData) {
+  UNUSED_P(userData);
+  return XML_STATUS_OK;
+}
+
+/* Attribute List handlers */
+void XMLCALL
+verify_attlist_decl_handler(void *userData, const XML_Char *element_name,
+                            const XML_Char *attr_name,
+                            const XML_Char *attr_type,
+                            const XML_Char *default_value, int is_required) {
+  AttTest *at = (AttTest *)userData;
+
+  if (xcstrcmp(element_name, at->element_name))
+    fail("Unexpected element name in attribute declaration");
+  if (xcstrcmp(attr_name, at->attr_name))
+    fail("Unexpected attribute name in attribute declaration");
+  if (xcstrcmp(attr_type, at->attr_type))
+    fail("Unexpected attribute type in attribute declaration");
+  if ((default_value == NULL && at->default_value != NULL)
+      || (default_value != NULL && at->default_value == NULL)
+      || (default_value != NULL && xcstrcmp(default_value, at->default_value)))
+    fail("Unexpected default value in attribute declaration");
+  if (is_required != at->is_required)
+    fail("Requirement mismatch in attribute declaration");
+}
+
+/* Character Data handlers */
+
+void XMLCALL
+clearing_aborting_character_handler(void *userData, const XML_Char *s,
+                                    int len) {
+  UNUSED_P(userData);
+  UNUSED_P(s);
+  UNUSED_P(len);
+  XML_StopParser(g_parser, g_resumable);
+  XML_SetCharacterDataHandler(g_parser, NULL);
+}
+
+void XMLCALL
+parser_stop_character_handler(void *userData, const XML_Char *s, int len) {
+  UNUSED_P(userData);
+  UNUSED_P(s);
+  UNUSED_P(len);
+  XML_ParsingStatus status;
+  XML_GetParsingStatus(g_parser, &status);
+  if (status.parsing == XML_FINISHED) {
+    return; // the parser was stopped by a previous call to this handler.
+  }
+  XML_StopParser(g_parser, g_resumable);
+  XML_SetCharacterDataHandler(g_parser, NULL);
+  if (! g_resumable) {
+    /* Check that aborting an aborted parser is faulted */
+    if (XML_StopParser(g_parser, XML_FALSE) != XML_STATUS_ERROR)
+      fail("Aborting aborted parser not faulted");
+    if (XML_GetErrorCode(g_parser) != XML_ERROR_FINISHED)
+      xml_failure(g_parser);
+  } else if (g_abortable) {
+    /* Check that aborting a suspended parser works */
+    if (XML_StopParser(g_parser, XML_FALSE) == XML_STATUS_ERROR)
+      xml_failure(g_parser);
+  } else {
+    /* Check that suspending a suspended parser works */
+    if (XML_StopParser(g_parser, XML_TRUE) != XML_STATUS_ERROR)
+      fail("Suspending suspended parser not faulted");
+    if (XML_GetErrorCode(g_parser) != XML_ERROR_SUSPENDED)
+      xml_failure(g_parser);
+  }
+}
+
+void XMLCALL
+cr_cdata_handler(void *userData, const XML_Char *s, int len) {
+  int *pfound = (int *)userData;
+
+  /* Internal processing turns the CR into a newline for the
+   * character data handler, but not for the default handler
+   */
+  if (len == 1 && (*s == XCS('\n') || *s == XCS('\r')))
+    *pfound = 1;
+}
+
+void XMLCALL
+rsqb_handler(void *userData, const XML_Char *s, int len) {
+  int *pfound = (int *)userData;
+
+  if (len == 1 && *s == XCS(']'))
+    *pfound = 1;
+}
+
+void XMLCALL
+byte_character_handler(void *userData, const XML_Char *s, int len) {
+#if XML_CONTEXT_BYTES > 0
+  int offset, size;
+  const char *buffer;
+  ByteTestData *data = (ByteTestData *)userData;
+
+  UNUSED_P(s);
+  buffer = XML_GetInputContext(g_parser, &offset, &size);
+  if (buffer == NULL)
+    fail("Failed to get context buffer");
+  if (offset != data->start_element_len)
+    fail("Context offset in unexpected position");
+  if (len != data->cdata_len)
+    fail("CDATA length reported incorrectly");
+  if (size != data->total_string_len)
+    fail("Context size is not full buffer");
+  if (XML_GetCurrentByteIndex(g_parser) != offset)
+    fail("Character byte index incorrect");
+  if (XML_GetCurrentByteCount(g_parser) != len)
+    fail("Character byte count incorrect");
+#else
+  UNUSED_P(s);
+  UNUSED_P(userData);
+  UNUSED_P(len);
+#endif
+}
+
+void XMLCALL
+ext2_accumulate_characters(void *userData, const XML_Char *s, int len) {
+  ExtTest2 *test_data = (ExtTest2 *)userData;
+  accumulate_characters(test_data->storage, s, len);
+}
+
+/* Handlers that record their function name and int arg. */
+
+static void
+record_call(struct handler_record_list *const rec, const char *funcname,
+            const int arg) {
+  const int max_entries = sizeof(rec->entries) / sizeof(rec->entries[0]);
+  assert_true(rec->count < max_entries);
+  struct handler_record_entry *const e = &rec->entries[rec->count++];
+  e->name = funcname;
+  e->arg = arg;
+}
+
+void XMLCALL
+record_default_handler(void *userData, const XML_Char *s, int len) {
+  UNUSED_P(s);
+  record_call((struct handler_record_list *)userData, __func__, len);
+}
+
+void XMLCALL
+record_cdata_handler(void *userData, const XML_Char *s, int len) {
+  UNUSED_P(s);
+  record_call((struct handler_record_list *)userData, __func__, len);
+  XML_DefaultCurrent(g_parser);
+}
+
+void XMLCALL
+record_cdata_nodefault_handler(void *userData, const XML_Char *s, int len) {
+  UNUSED_P(s);
+  record_call((struct handler_record_list *)userData, __func__, len);
+}
+
+void XMLCALL
+record_skip_handler(void *userData, const XML_Char *entityName,
+                    int is_parameter_entity) {
+  UNUSED_P(entityName);
+  record_call((struct handler_record_list *)userData, __func__,
+              is_parameter_entity);
+}
+
+void XMLCALL
+record_element_start_handler(void *userData, const XML_Char *name,
+                             const XML_Char **atts) {
+  UNUSED_P(atts);
+  CharData_AppendXMLChars((CharData *)userData, name, (int)xcstrlen(name));
+}
+
+void XMLCALL
+record_element_end_handler(void *userData, const XML_Char *name) {
+  CharData *storage = (CharData *)userData;
+
+  CharData_AppendXMLChars(storage, XCS("/"), 1);
+  CharData_AppendXMLChars(storage, name, -1);
+}
+
+const struct handler_record_entry *
+_handler_record_get(const struct handler_record_list *storage, int index,
+                    const char *file, int line) {
+  if (storage->count <= index) {
+    _fail(file, line, "too few handler calls");
+  }
+  return &storage->entries[index];
+}
+
+/* Entity Declaration Handlers */
+static const XML_Char *entity_name_to_match = NULL;
+static const XML_Char *entity_value_to_match = NULL;
+static int entity_match_flag = ENTITY_MATCH_NOT_FOUND;
+
+void XMLCALL
+param_entity_match_handler(void *userData, const XML_Char *entityName,
+                           int is_parameter_entity, const XML_Char *value,
+                           int value_length, const XML_Char *base,
+                           const XML_Char *systemId, const XML_Char *publicId,
+                           const XML_Char *notationName) {
+  UNUSED_P(userData);
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  UNUSED_P(notationName);
+  if (! is_parameter_entity || entity_name_to_match == NULL
+      || entity_value_to_match == NULL) {
+    return;
+  }
+  if (! xcstrcmp(entityName, entity_name_to_match)) {
+    /* The cast here is safe because we control the horizontal and
+     * the vertical, and we therefore know our strings are never
+     * going to overflow an int.
+     */
+    if (value_length != (int)xcstrlen(entity_value_to_match)
+        || xcstrncmp(value, entity_value_to_match, value_length)) {
+      entity_match_flag = ENTITY_MATCH_FAIL;
+    } else {
+      entity_match_flag = ENTITY_MATCH_SUCCESS;
+    }
+  }
+  /* Else leave the match flag alone */
+}
+
+void
+param_entity_match_init(const XML_Char *name, const XML_Char *value) {
+  entity_name_to_match = name;
+  entity_value_to_match = value;
+  entity_match_flag = ENTITY_MATCH_NOT_FOUND;
+}
+
+int
+get_param_entity_match_flag(void) {
+  return entity_match_flag;
+}
+
+/* Misc handlers */
+
+void XMLCALL
+xml_decl_handler(void *userData, const XML_Char *version,
+                 const XML_Char *encoding, int standalone) {
+  UNUSED_P(version);
+  UNUSED_P(encoding);
+  if (userData != g_handler_data)
+    fail("User data (xml decl) not correctly set");
+  if (standalone != -1)
+    fail("Standalone not flagged as not present in XML decl");
+  g_xdecl_count++;
+}
+
+void XMLCALL
+param_check_skip_handler(void *userData, const XML_Char *entityName,
+                         int is_parameter_entity) {
+  UNUSED_P(entityName);
+  UNUSED_P(is_parameter_entity);
+  if (userData != g_handler_data)
+    fail("User data (skip) not correctly set");
+  g_skip_count++;
+}
+
+void XMLCALL
+data_check_comment_handler(void *userData, const XML_Char *data) {
+  UNUSED_P(data);
+  /* Check that the userData passed through is what we expect */
+  if (userData != g_handler_data)
+    fail("User data (parser) not correctly set");
+  /* Check that the user data in the parser is appropriate */
+  if (XML_GetUserData(userData) != (void *)1)
+    fail("User data in parser not correctly set");
+  g_comment_count++;
+}
+
+void XMLCALL
+selective_aborting_default_handler(void *userData, const XML_Char *s, int len) {
+  const XML_Char trigger_char = *(const XML_Char *)userData;
+
+  int found = 0;
+  for (int i = 0; i < len; ++i) {
+    if (s[i] == trigger_char) {
+      found = 1;
+      break;
+    }
+  }
+
+  if (found) {
+    XML_StopParser(g_parser, g_resumable);
+    XML_SetDefaultHandler(g_parser, NULL);
+  }
+}
+
+void XMLCALL
+suspending_comment_handler(void *userData, const XML_Char *data) {
+  UNUSED_P(data);
+  XML_Parser parser = (XML_Parser)userData;
+  XML_StopParser(parser, XML_TRUE);
+}
+
+void XMLCALL
+element_decl_suspender(void *userData, const XML_Char *name,
+                       XML_Content *model) {
+  UNUSED_P(userData);
+  UNUSED_P(name);
+  XML_StopParser(g_parser, XML_TRUE);
+  XML_FreeContentModel(g_parser, model);
+}
+
+void XMLCALL
+accumulate_pi_characters(void *userData, const XML_Char *target,
+                         const XML_Char *data) {
+  CharData *storage = (CharData *)userData;
+
+  CharData_AppendXMLChars(storage, target, -1);
+  CharData_AppendXMLChars(storage, XCS(": "), 2);
+  CharData_AppendXMLChars(storage, data, -1);
+  CharData_AppendXMLChars(storage, XCS("\n"), 1);
+}
+
+void XMLCALL
+accumulate_comment(void *userData, const XML_Char *data) {
+  CharData *storage = (CharData *)userData;
+
+  CharData_AppendXMLChars(storage, data, -1);
+}
+
+void XMLCALL
+accumulate_entity_decl(void *userData, const XML_Char *entityName,
+                       int is_parameter_entity, const XML_Char *value,
+                       int value_length, const XML_Char *base,
+                       const XML_Char *systemId, const XML_Char *publicId,
+                       const XML_Char *notationName) {
+  CharData *storage = (CharData *)userData;
+
+  UNUSED_P(is_parameter_entity);
+  UNUSED_P(base);
+  UNUSED_P(systemId);
+  UNUSED_P(publicId);
+  UNUSED_P(notationName);
+  CharData_AppendXMLChars(storage, entityName, -1);
+  CharData_AppendXMLChars(storage, XCS("="), 1);
+  if (value == NULL)
+    CharData_AppendXMLChars(storage, XCS("(null)"), -1);
+  else
+    CharData_AppendXMLChars(storage, value, value_length);
+  CharData_AppendXMLChars(storage, XCS("\n"), 1);
+}
+
+void XMLCALL
+accumulate_char_data(void *userData, const XML_Char *s, int len) {
+  CharData *const storage = (CharData *)userData;
+  CharData_AppendXMLChars(storage, s, len);
+}
+
+void XMLCALL
+accumulate_start_element(void *userData, const XML_Char *name,
+                         const XML_Char **atts) {
+  CharData *const storage = (CharData *)userData;
+  CharData_AppendXMLChars(storage, XCS("("), 1);
+  CharData_AppendXMLChars(storage, name, -1);
+
+  if ((atts != NULL) && (atts[0] != NULL)) {
+    CharData_AppendXMLChars(storage, XCS("("), 1);
+    while (atts[0] != NULL) {
+      CharData_AppendXMLChars(storage, atts[0], -1);
+      CharData_AppendXMLChars(storage, XCS("="), 1);
+      CharData_AppendXMLChars(storage, atts[1], -1);
+      atts += 2;
+      if (atts[0] != NULL) {
+        CharData_AppendXMLChars(storage, XCS(","), 1);
+      }
+    }
+    CharData_AppendXMLChars(storage, XCS(")"), 1);
+  }
+
+  CharData_AppendXMLChars(storage, XCS(")\n"), 2);
+}
+
+void XMLCALL
+checking_default_handler(void *userData, const XML_Char *s, int len) {
+  DefaultCheck *data = (DefaultCheck *)userData;
+  int i;
+
+  for (i = 0; data[i].expected != NULL; i++) {
+    if (data[i].expectedLen == len
+        && ! memcmp(data[i].expected, s, len * sizeof(XML_Char))) {
+      data[i].seen = XML_TRUE;
+      break;
+    }
+  }
+}
+
+void XMLCALL
+accumulate_and_suspend_comment_handler(void *userData, const XML_Char *data) {
+  ParserPlusStorage *const parserPlusStorage = (ParserPlusStorage *)userData;
+  accumulate_comment(parserPlusStorage->storage, data);
+  XML_StopParser(parserPlusStorage->parser, XML_TRUE);
+}
diff --git a/tests/handlers.h b/tests/handlers.h
new file mode 100644 (file)
index 0000000..e1f0995
--- /dev/null
@@ -0,0 +1,595 @@
+/* XML handler functions for the Expat test suite
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
+   Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Joe Orton <jorton@redhat.com>
+   Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2020      Tim Gates <tim.gates@iress.com>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
+   Copyright (c) 2023      Sony Corporation / Snild Dolkow <snild@sony.com>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef XML_HANDLERS_H
+#  define XML_HANDLERS_H
+
+#  include "expat_config.h"
+
+#  include "expat.h"
+
+/* Variable holding the expected handler userData */
+extern const void *g_handler_data;
+/* Count of the number of times the comment handler has been invoked */
+extern int g_comment_count;
+/* Count of the number of skipped entities */
+extern int g_skip_count;
+/* Count of the number of times the XML declaration handler is invoked */
+extern int g_xdecl_count;
+
+/* Start/End Element Handlers */
+
+extern void XMLCALL start_element_event_handler(void *userData,
+                                                const XML_Char *name,
+                                                const XML_Char **atts);
+
+extern void XMLCALL end_element_event_handler(void *userData,
+                                              const XML_Char *name);
+
+#  define STRUCT_START_TAG 0
+#  define STRUCT_END_TAG 1
+
+extern void XMLCALL start_element_event_handler2(void *userData,
+                                                 const XML_Char *name,
+                                                 const XML_Char **attr);
+
+extern void XMLCALL end_element_event_handler2(void *userData,
+                                               const XML_Char *name);
+
+typedef struct attrInfo {
+  const XML_Char *name;
+  const XML_Char *value;
+} AttrInfo;
+
+typedef struct elementInfo {
+  const XML_Char *name;
+  int attr_count;
+  const XML_Char *id_name;
+  AttrInfo *attributes;
+} ElementInfo;
+
+extern void XMLCALL counting_start_element_handler(void *userData,
+                                                   const XML_Char *name,
+                                                   const XML_Char **atts);
+
+extern void XMLCALL suspending_end_handler(void *userData, const XML_Char *s);
+
+extern void XMLCALL start_element_suspender(void *userData,
+                                            const XML_Char *name,
+                                            const XML_Char **atts);
+
+extern int g_triplet_start_flag;
+extern int g_triplet_end_flag;
+
+extern void XMLCALL triplet_start_checker(void *userData, const XML_Char *name,
+                                          const XML_Char **atts);
+
+extern void XMLCALL triplet_end_checker(void *userData, const XML_Char *name);
+
+extern void XMLCALL overwrite_start_checker(void *userData,
+                                            const XML_Char *name,
+                                            const XML_Char **atts);
+
+extern void XMLCALL overwrite_end_checker(void *userData, const XML_Char *name);
+
+extern void XMLCALL start_element_fail(void *userData, const XML_Char *name,
+                                       const XML_Char **atts);
+
+extern void XMLCALL start_ns_clearing_start_element(void *userData,
+                                                    const XML_Char *prefix,
+                                                    const XML_Char *uri);
+
+typedef struct {
+  XML_Parser parser;
+  int deep;
+} DataIssue240;
+
+extern void XMLCALL start_element_issue_240(void *userData,
+                                            const XML_Char *name,
+                                            const XML_Char **atts);
+
+extern void XMLCALL end_element_issue_240(void *userData, const XML_Char *name);
+
+/* Text encoding handlers */
+
+extern int XMLCALL UnknownEncodingHandler(void *data, const XML_Char *encoding,
+                                          XML_Encoding *info);
+
+extern int XMLCALL UnrecognisedEncodingHandler(void *data,
+                                               const XML_Char *encoding,
+                                               XML_Encoding *info);
+
+extern int XMLCALL unknown_released_encoding_handler(void *data,
+                                                     const XML_Char *encoding,
+                                                     XML_Encoding *info);
+
+extern int XMLCALL MiscEncodingHandler(void *data, const XML_Char *encoding,
+                                       XML_Encoding *info);
+
+extern int XMLCALL long_encoding_handler(void *userData,
+                                         const XML_Char *encoding,
+                                         XML_Encoding *info);
+
+/* External Entity Handlers */
+
+typedef struct ExtOption {
+  const XML_Char *system_id;
+  const char *parse_text;
+} ExtOption;
+
+extern int XMLCALL external_entity_optioner(XML_Parser parser,
+                                            const XML_Char *context,
+                                            const XML_Char *base,
+                                            const XML_Char *systemId,
+                                            const XML_Char *publicId);
+
+extern int XMLCALL external_entity_loader(XML_Parser parser,
+                                          const XML_Char *context,
+                                          const XML_Char *base,
+                                          const XML_Char *systemId,
+                                          const XML_Char *publicId);
+
+typedef struct ext_faults {
+  const char *parse_text;
+  const char *fail_text;
+  const XML_Char *encoding;
+  enum XML_Error error;
+} ExtFaults;
+
+extern int XMLCALL external_entity_faulter(XML_Parser parser,
+                                           const XML_Char *context,
+                                           const XML_Char *base,
+                                           const XML_Char *systemId,
+                                           const XML_Char *publicId);
+extern int XMLCALL external_entity_failer__if_not_xml_ge(
+    XML_Parser parser, const XML_Char *context, const XML_Char *base,
+    const XML_Char *systemId, const XML_Char *publicId);
+extern int XMLCALL external_entity_null_loader(XML_Parser parser,
+                                               const XML_Char *context,
+                                               const XML_Char *base,
+                                               const XML_Char *systemId,
+                                               const XML_Char *publicId);
+
+extern int XMLCALL external_entity_resetter(XML_Parser parser,
+                                            const XML_Char *context,
+                                            const XML_Char *base,
+                                            const XML_Char *systemId,
+                                            const XML_Char *publicId);
+
+extern int XMLCALL external_entity_suspender(XML_Parser parser,
+                                             const XML_Char *context,
+                                             const XML_Char *base,
+                                             const XML_Char *systemId,
+                                             const XML_Char *publicId);
+
+extern int XMLCALL external_entity_suspend_xmldecl(XML_Parser parser,
+                                                   const XML_Char *context,
+                                                   const XML_Char *base,
+                                                   const XML_Char *systemId,
+                                                   const XML_Char *publicId);
+
+extern int XMLCALL external_entity_suspending_faulter(XML_Parser parser,
+                                                      const XML_Char *context,
+                                                      const XML_Char *base,
+                                                      const XML_Char *systemId,
+                                                      const XML_Char *publicId);
+
+extern int XMLCALL external_entity_cr_catcher(XML_Parser parser,
+                                              const XML_Char *context,
+                                              const XML_Char *base,
+                                              const XML_Char *systemId,
+                                              const XML_Char *publicId);
+
+extern int XMLCALL external_entity_bad_cr_catcher(XML_Parser parser,
+                                                  const XML_Char *context,
+                                                  const XML_Char *base,
+                                                  const XML_Char *systemId,
+                                                  const XML_Char *publicId);
+
+extern int XMLCALL external_entity_rsqb_catcher(XML_Parser parser,
+                                                const XML_Char *context,
+                                                const XML_Char *base,
+                                                const XML_Char *systemId,
+                                                const XML_Char *publicId);
+
+extern int XMLCALL external_entity_good_cdata_ascii(XML_Parser parser,
+                                                    const XML_Char *context,
+                                                    const XML_Char *base,
+                                                    const XML_Char *systemId,
+                                                    const XML_Char *publicId);
+
+/* Entity declaration handlers */
+
+extern void XMLCALL entity_suspending_decl_handler(void *userData,
+                                                   const XML_Char *name,
+                                                   XML_Content *model);
+
+extern void XMLCALL entity_suspending_xdecl_handler(void *userData,
+                                                    const XML_Char *version,
+                                                    const XML_Char *encoding,
+                                                    int standalone);
+
+extern int XMLCALL external_entity_param_checker(XML_Parser parser,
+                                                 const XML_Char *context,
+                                                 const XML_Char *base,
+                                                 const XML_Char *systemId,
+                                                 const XML_Char *publicId);
+
+extern int XMLCALL external_entity_ref_param_checker(XML_Parser parameter,
+                                                     const XML_Char *context,
+                                                     const XML_Char *base,
+                                                     const XML_Char *systemId,
+                                                     const XML_Char *publicId);
+
+extern int XMLCALL external_entity_param(XML_Parser parser,
+                                         const XML_Char *context,
+                                         const XML_Char *base,
+                                         const XML_Char *systemId,
+                                         const XML_Char *publicId);
+
+extern int XMLCALL external_entity_load_ignore(XML_Parser parser,
+                                               const XML_Char *context,
+                                               const XML_Char *base,
+                                               const XML_Char *systemId,
+                                               const XML_Char *publicId);
+
+extern int XMLCALL external_entity_load_ignore_utf16(XML_Parser parser,
+                                                     const XML_Char *context,
+                                                     const XML_Char *base,
+                                                     const XML_Char *systemId,
+                                                     const XML_Char *publicId);
+
+extern int XMLCALL external_entity_load_ignore_utf16_be(
+    XML_Parser parser, const XML_Char *context, const XML_Char *base,
+    const XML_Char *systemId, const XML_Char *publicId);
+
+extern int XMLCALL external_entity_valuer(XML_Parser parser,
+                                          const XML_Char *context,
+                                          const XML_Char *base,
+                                          const XML_Char *systemId,
+                                          const XML_Char *publicId);
+
+extern int XMLCALL external_entity_not_standalone(XML_Parser parser,
+                                                  const XML_Char *context,
+                                                  const XML_Char *base,
+                                                  const XML_Char *systemId,
+                                                  const XML_Char *publicId);
+
+extern int XMLCALL external_entity_value_aborter(XML_Parser parser,
+                                                 const XML_Char *context,
+                                                 const XML_Char *base,
+                                                 const XML_Char *systemId,
+                                                 const XML_Char *publicId);
+
+extern int XMLCALL external_entity_public(XML_Parser parser,
+                                          const XML_Char *context,
+                                          const XML_Char *base,
+                                          const XML_Char *systemId,
+                                          const XML_Char *publicId);
+
+extern int XMLCALL external_entity_devaluer(XML_Parser parser,
+                                            const XML_Char *context,
+                                            const XML_Char *base,
+                                            const XML_Char *systemId,
+                                            const XML_Char *publicId);
+
+typedef struct ext_hdlr_data {
+  const char *parse_text;
+  XML_ExternalEntityRefHandler handler;
+} ExtHdlrData;
+
+extern int XMLCALL external_entity_oneshot_loader(XML_Parser parser,
+                                                  const XML_Char *context,
+                                                  const XML_Char *base,
+                                                  const XML_Char *systemId,
+                                                  const XML_Char *publicId);
+
+typedef struct ExtTest2 {
+  const char *parse_text;
+  int parse_len;
+  const XML_Char *encoding;
+  CharData *storage;
+} ExtTest2;
+
+extern int XMLCALL external_entity_loader2(XML_Parser parser,
+                                           const XML_Char *context,
+                                           const XML_Char *base,
+                                           const XML_Char *systemId,
+                                           const XML_Char *publicId);
+
+typedef struct ExtFaults2 {
+  const char *parse_text;
+  int parse_len;
+  const char *fail_text;
+  const XML_Char *encoding;
+  enum XML_Error error;
+} ExtFaults2;
+
+extern int XMLCALL external_entity_faulter2(XML_Parser parser,
+                                            const XML_Char *context,
+                                            const XML_Char *base,
+                                            const XML_Char *systemId,
+                                            const XML_Char *publicId);
+
+extern int XMLCALL external_entity_unfinished_attlist(XML_Parser parser,
+                                                      const XML_Char *context,
+                                                      const XML_Char *base,
+                                                      const XML_Char *systemId,
+                                                      const XML_Char *publicId);
+
+extern int XMLCALL external_entity_handler(XML_Parser parser,
+                                           const XML_Char *context,
+                                           const XML_Char *base,
+                                           const XML_Char *systemId,
+                                           const XML_Char *publicId);
+
+extern int XMLCALL external_entity_duff_loader(XML_Parser parser,
+                                               const XML_Char *context,
+                                               const XML_Char *base,
+                                               const XML_Char *systemId,
+                                               const XML_Char *publicId);
+
+extern int XMLCALL external_entity_dbl_handler(XML_Parser parser,
+                                               const XML_Char *context,
+                                               const XML_Char *base,
+                                               const XML_Char *systemId,
+                                               const XML_Char *publicId);
+
+extern int XMLCALL external_entity_dbl_handler_2(XML_Parser parser,
+                                                 const XML_Char *context,
+                                                 const XML_Char *base,
+                                                 const XML_Char *systemId,
+                                                 const XML_Char *publicId);
+
+extern int XMLCALL external_entity_alloc_set_encoding(XML_Parser parser,
+                                                      const XML_Char *context,
+                                                      const XML_Char *base,
+                                                      const XML_Char *systemId,
+                                                      const XML_Char *publicId);
+
+extern int XMLCALL external_entity_reallocator(XML_Parser parser,
+                                               const XML_Char *context,
+                                               const XML_Char *base,
+                                               const XML_Char *systemId,
+                                               const XML_Char *publicId);
+
+extern int XMLCALL external_entity_alloc(XML_Parser parser,
+                                         const XML_Char *context,
+                                         const XML_Char *base,
+                                         const XML_Char *systemId,
+                                         const XML_Char *publicId);
+
+extern int XMLCALL external_entity_parser_create_alloc_fail_handler(
+    XML_Parser parser, const XML_Char *context, const XML_Char *base,
+    const XML_Char *systemId, const XML_Char *publicId);
+
+struct AccountingTestCase {
+  const char *primaryText;
+  const char *firstExternalText;  /* often NULL */
+  const char *secondExternalText; /* often NULL */
+  const unsigned long long expectedCountBytesIndirectExtra;
+};
+
+extern int accounting_external_entity_ref_handler(XML_Parser parser,
+                                                  const XML_Char *context,
+                                                  const XML_Char *base,
+                                                  const XML_Char *systemId,
+                                                  const XML_Char *publicId);
+
+/* NotStandalone handlers */
+
+extern int XMLCALL reject_not_standalone_handler(void *userData);
+
+extern int XMLCALL accept_not_standalone_handler(void *userData);
+
+/* Attribute List handlers */
+
+typedef struct AttTest {
+  const char *definition;
+  const XML_Char *element_name;
+  const XML_Char *attr_name;
+  const XML_Char *attr_type;
+  const XML_Char *default_value;
+  int is_required;
+} AttTest;
+
+extern void XMLCALL verify_attlist_decl_handler(
+    void *userData, const XML_Char *element_name, const XML_Char *attr_name,
+    const XML_Char *attr_type, const XML_Char *default_value, int is_required);
+
+/* Character data handlers */
+
+extern void XMLCALL clearing_aborting_character_handler(void *userData,
+                                                        const XML_Char *s,
+                                                        int len);
+
+extern void XMLCALL parser_stop_character_handler(void *userData,
+                                                  const XML_Char *s, int len);
+
+extern void XMLCALL cr_cdata_handler(void *userData, const XML_Char *s,
+                                     int len);
+
+extern void XMLCALL rsqb_handler(void *userData, const XML_Char *s, int len);
+
+typedef struct ByteTestData {
+  int start_element_len;
+  int cdata_len;
+  int total_string_len;
+} ByteTestData;
+
+extern void XMLCALL byte_character_handler(void *userData, const XML_Char *s,
+                                           int len);
+
+extern void XMLCALL ext2_accumulate_characters(void *userData,
+                                               const XML_Char *s, int len);
+
+/* Handlers that record their `len` arg and a single identifying character */
+
+struct handler_record_entry {
+  const char *name;
+  int arg;
+};
+struct handler_record_list {
+  int count;
+  struct handler_record_entry entries[50]; // arbitrary big-enough max count
+};
+
+extern void XMLCALL record_default_handler(void *userData, const XML_Char *s,
+                                           int len);
+
+extern void XMLCALL record_cdata_handler(void *userData, const XML_Char *s,
+                                         int len);
+
+extern void XMLCALL record_cdata_nodefault_handler(void *userData,
+                                                   const XML_Char *s, int len);
+
+extern void XMLCALL record_skip_handler(void *userData,
+                                        const XML_Char *entityName,
+                                        int is_parameter_entity);
+
+extern void XMLCALL record_element_start_handler(void *userData,
+                                                 const XML_Char *name,
+                                                 const XML_Char **atts);
+
+extern void XMLCALL record_element_end_handler(void *userData,
+                                               const XML_Char *name);
+
+extern const struct handler_record_entry *
+_handler_record_get(const struct handler_record_list *storage, int index,
+                    const char *file, int line);
+
+#  define handler_record_get(storage, index)                                   \
+    _handler_record_get((storage), (index), __FILE__, __LINE__)
+
+#  define assert_record_handler_called(storage, index, expected_name,          \
+                                       expected_arg)                           \
+    do {                                                                       \
+      const struct handler_record_entry *e                                     \
+          = handler_record_get(storage, index);                                \
+      assert_true(strcmp(e->name, expected_name) == 0);                        \
+      assert_true(e->arg == (expected_arg));                                   \
+    } while (0)
+
+/* Entity Declaration Handlers */
+#  define ENTITY_MATCH_FAIL (-1)
+#  define ENTITY_MATCH_NOT_FOUND (0)
+#  define ENTITY_MATCH_SUCCESS (1)
+
+extern void XMLCALL param_entity_match_handler(
+    void *userData, const XML_Char *entityName, int is_parameter_entity,
+    const XML_Char *value, int value_length, const XML_Char *base,
+    const XML_Char *systemId, const XML_Char *publicId,
+    const XML_Char *notationName);
+
+extern void param_entity_match_init(const XML_Char *name,
+                                    const XML_Char *value);
+
+extern int get_param_entity_match_flag(void);
+
+/* Misc handlers */
+
+extern void XMLCALL xml_decl_handler(void *userData, const XML_Char *version,
+                                     const XML_Char *encoding, int standalone);
+
+extern void XMLCALL param_check_skip_handler(void *userData,
+                                             const XML_Char *entityName,
+                                             int is_parameter_entity);
+
+extern void XMLCALL data_check_comment_handler(void *userData,
+                                               const XML_Char *data);
+
+extern void XMLCALL selective_aborting_default_handler(void *userData,
+                                                       const XML_Char *s,
+                                                       int len);
+
+extern void XMLCALL suspending_comment_handler(void *userData,
+                                               const XML_Char *data);
+
+extern void XMLCALL element_decl_suspender(void *userData, const XML_Char *name,
+                                           XML_Content *model);
+
+extern void XMLCALL accumulate_pi_characters(void *userData,
+                                             const XML_Char *target,
+                                             const XML_Char *data);
+
+extern void XMLCALL accumulate_comment(void *userData, const XML_Char *data);
+
+extern void XMLCALL accumulate_entity_decl(
+    void *userData, const XML_Char *entityName, int is_parameter_entity,
+    const XML_Char *value, int value_length, const XML_Char *base,
+    const XML_Char *systemId, const XML_Char *publicId,
+    const XML_Char *notationName);
+
+extern void XMLCALL accumulate_char_data(void *userData, const XML_Char *s,
+                                         int len);
+
+extern void XMLCALL accumulate_start_element(void *userData,
+                                             const XML_Char *name,
+                                             const XML_Char **atts);
+
+typedef struct default_check {
+  const XML_Char *expected;
+  const int expectedLen;
+  XML_Bool seen;
+} DefaultCheck;
+
+void XMLCALL checking_default_handler(void *userData, const XML_Char *s,
+                                      int len);
+
+typedef struct {
+  XML_Parser parser;
+  CharData *storage;
+} ParserPlusStorage;
+
+extern void XMLCALL
+accumulate_and_suspend_comment_handler(void *userData, const XML_Char *data);
+
+#endif /* XML_HANDLERS_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/handlers_cxx.cpp b/tests/handlers_cxx.cpp
new file mode 100644 (file)
index 0000000..86c62b1
--- /dev/null
@@ -0,0 +1,32 @@
+/* C++ compilation harness for the test suite.
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2023 Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "handlers.c"
index 48822e5..de92549 100644 (file)
@@ -6,8 +6,9 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk>
-   Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017-2023 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2022      Sean McBride <sean@rogue-research.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -49,12 +50,13 @@ typedef struct allocation_entry {
 static AllocationEntry *alloc_head = NULL;
 static AllocationEntry *alloc_tail = NULL;
 
-static AllocationEntry *find_allocation(void *ptr);
+static AllocationEntry *find_allocation(const void *ptr);
 
 /* Allocate some memory and keep track of it. */
 void *
 tracking_malloc(size_t size) {
-  AllocationEntry *entry = malloc(sizeof(AllocationEntry));
+  AllocationEntry *const entry
+      = (AllocationEntry *)malloc(sizeof(AllocationEntry));
 
   if (entry == NULL) {
     printf("Allocator failure\n");
@@ -82,7 +84,7 @@ tracking_malloc(size_t size) {
 }
 
 static AllocationEntry *
-find_allocation(void *ptr) {
+find_allocation(const void *ptr) {
   AllocationEntry *entry;
 
   for (entry = alloc_head; entry != NULL; entry = entry->next) {
@@ -140,7 +142,7 @@ tracking_realloc(void *ptr, size_t size) {
   entry = find_allocation(ptr);
   if (entry == NULL) {
     printf("Attempting to realloc unallocated memory at %p\n", ptr);
-    entry = malloc(sizeof(AllocationEntry));
+    entry = (AllocationEntry *)malloc(sizeof(AllocationEntry));
     if (entry == NULL) {
       printf("Reallocator failure\n");
       return NULL;
@@ -162,12 +164,11 @@ tracking_realloc(void *ptr, size_t size) {
       alloc_tail = entry;
     }
   } else {
-    entry->allocation = realloc(ptr, size);
-    if (entry->allocation == NULL) {
-      /* Realloc semantics say the original is still allocated */
-      entry->allocation = ptr;
+    void *const reallocated = realloc(ptr, size);
+    if (reallocated == NULL) {
       return NULL;
     }
+    entry->allocation = reallocated;
   }
 
   entry->num_bytes = size;
diff --git a/tests/memcheck_cxx.cpp b/tests/memcheck_cxx.cpp
new file mode 100644 (file)
index 0000000..e62ac98
--- /dev/null
@@ -0,0 +1,32 @@
+/* C++ compilation harness for the test suite.
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2023 Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "memcheck.c"
index 1c65748..baccd76 100644 (file)
                                  |_| XML parser
 
    Copyright (c) 2004-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
-   Copyright (c) 2016-2020 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016-2023 Sebastian Pipping <sebastian@pipping.org>
    Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
    Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <snild@sony.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
    USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
+#if defined(NDEBUG)
+#  undef NDEBUG /* because test suite relies on assert(...) at the moment */
+#endif
+
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <setjmp.h>
@@ -88,7 +94,8 @@ tcase_add_test(TCase *tc, tcase_test_function test) {
   if (tc->allocated == tc->ntests) {
     int nalloc = tc->allocated + 100;
     size_t new_size = sizeof(tcase_test_function) * nalloc;
-    tcase_test_function *new_tests = realloc(tc->tests, new_size);
+    tcase_test_function *const new_tests
+        = (tcase_test_function *)realloc(tc->tests, new_size);
     assert(new_tests != NULL);
     tc->tests = new_tests;
     tc->allocated = nalloc;
@@ -123,7 +130,7 @@ suite_free(Suite *suite) {
 
 SRunner *
 srunner_create(Suite *suite) {
-  SRunner *runner = calloc(1, sizeof(SRunner));
+  SRunner *const runner = (SRunner *)calloc(1, sizeof(SRunner));
   if (runner != NULL) {
     runner->suite = suite;
   }
@@ -132,17 +139,35 @@ srunner_create(Suite *suite) {
 
 static jmp_buf env;
 
+#define SUBTEST_LEN (50) // informative, but not too long
 static char const *_check_current_function = NULL;
+static char _check_current_subtest[SUBTEST_LEN];
 static int _check_current_lineno = -1;
 static char const *_check_current_filename = NULL;
 
 void
 _check_set_test_info(char const *function, char const *filename, int lineno) {
   _check_current_function = function;
+  set_subtest("%s", "");
   _check_current_lineno = lineno;
   _check_current_filename = filename;
 }
 
+void
+set_subtest(char const *fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  vsnprintf(_check_current_subtest, SUBTEST_LEN, fmt, ap);
+  va_end(ap);
+  // replace line feeds with spaces, for nicer error logs
+  for (size_t i = 0; i < SUBTEST_LEN; ++i) {
+    if (_check_current_subtest[i] == '\n') {
+      _check_current_subtest[i] = ' ';
+    }
+  }
+  _check_current_subtest[SUBTEST_LEN - 1] = '\0'; // ensure termination
+}
+
 static void
 handle_success(int verbosity) {
   if (verbosity >= CK_VERBOSE) {
@@ -151,16 +176,20 @@ handle_success(int verbosity) {
 }
 
 static void
-handle_failure(SRunner *runner, int verbosity, const char *phase_info) {
+handle_failure(SRunner *runner, int verbosity, const char *context,
+               const char *phase_info) {
   runner->nfailures++;
   if (verbosity != CK_SILENT) {
-    printf("FAIL: %s (%s at %s:%d)\n", _check_current_function, phase_info,
-           _check_current_filename, _check_current_lineno);
+    if (strlen(_check_current_subtest) != 0) {
+      phase_info = _check_current_subtest;
+    }
+    printf("FAIL [%s]: %s (%s at %s:%d)\n", context, _check_current_function,
+           phase_info, _check_current_filename, _check_current_lineno);
   }
 }
 
 void
-srunner_run_all(SRunner *runner, int verbosity) {
+srunner_run_all(SRunner *runner, const char *context, int verbosity) {
   Suite *suite;
   TCase *volatile tc;
   assert(runner != NULL);
@@ -170,26 +199,28 @@ srunner_run_all(SRunner *runner, int verbosity) {
     volatile int i;
     for (i = 0; i < tc->ntests; ++i) {
       runner->nchecks++;
+      set_subtest("%s", "");
 
       if (tc->setup != NULL) {
         /* setup */
         if (setjmp(env)) {
-          handle_failure(runner, verbosity, "during setup");
+          handle_failure(runner, verbosity, context, "during setup");
           continue;
         }
         tc->setup();
       }
       /* test */
       if (setjmp(env)) {
-        handle_failure(runner, verbosity, "during actual test");
+        handle_failure(runner, verbosity, context, "during actual test");
         continue;
       }
       (tc->tests[i])();
+      set_subtest("%s", "");
 
       /* teardown */
       if (tc->teardown != NULL) {
         if (setjmp(env)) {
-          handle_failure(runner, verbosity, "during teardown");
+          handle_failure(runner, verbosity, context, "during teardown");
           continue;
         }
         tc->teardown();
@@ -199,6 +230,10 @@ srunner_run_all(SRunner *runner, int verbosity) {
     }
     tc = tc->next_tcase;
   }
+}
+
+void
+srunner_summarize(SRunner *runner, int verbosity) {
   if (verbosity != CK_SILENT) {
     int passed = runner->nchecks - runner->nfailures;
     double percentage = ((double)passed) / runner->nchecks;
@@ -209,12 +244,11 @@ srunner_run_all(SRunner *runner, int verbosity) {
 }
 
 void
-_fail_unless(int condition, const char *file, int line, const char *msg) {
+_fail(const char *file, int line, const char *msg) {
   /* Always print the error message so it isn't lost.  In this case,
      we have a failure, so there's no reason to be quiet about what
      it is.
   */
-  UNUSED_P(condition);
   _check_current_filename = file;
   _check_current_lineno = line;
   if (msg != NULL) {
index cc1f835..3d888f8 100644 (file)
@@ -14,7 +14,9 @@
 
    Copyright (c) 2004-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
    Copyright (c) 2006-2012 Karl Waclawek <karl@waclawek.net>
-   Copyright (c) 2016-2017 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2022      Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <snild@sony.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
 extern "C" {
 #endif
 
-#define CK_NOFORK 0
-#define CK_FORK 1
+#ifndef XML_MINICHECK_H
+#  define XML_MINICHECK_H
 
-#define CK_SILENT 0
-#define CK_NORMAL 1
-#define CK_VERBOSE 2
+#  define CK_NOFORK 0
+#  define CK_FORK 1
+
+#  define CK_SILENT 0
+#  define CK_NORMAL 1
+#  define CK_VERBOSE 2
 
 /* Workaround for Microsoft's compiler and Tru64 Unix systems where the
    C compiler has a working __func__, but the C++ compiler only has a
    working __FUNCTION__.  This could be fixed in configure.in, but it's
    not worth it right now. */
-#if defined(_MSC_VER) || (defined(__osf__) && defined(__cplusplus))
-#  define __func__ __FUNCTION__
-#endif
-
-#define START_TEST(testname)                                                   \
-  static void testname(void) {                                                 \
-    _check_set_test_info(__func__, __FILE__, __LINE__);                        \
-    {
-#define END_TEST                                                               \
-  }                                                                            \
-  }
-
-#define fail(msg) _fail_unless(0, __FILE__, __LINE__, msg)
+#  if defined(_MSC_VER) || (defined(__osf__) && defined(__cplusplus))
+#    define __func__ __FUNCTION__
+#  endif
+
+/* PRINTF_LIKE has two effects:
+    1. Make clang's -Wformat-nonliteral stop warning about non-literal format
+       strings in annotated functions' code.
+    2. Make both clang and gcc's -Wformat-nonliteral warn about *callers* of
+       the annotated function that use a non-literal format string.
+*/
+#  if defined(__GNUC__)
+#    define PRINTF_LIKE(fmtpos, argspos)                                       \
+      __attribute__((format(printf, fmtpos, argspos)))
+#  else
+#    define PRINTF_LIKE(fmtpos, argspos)
+#  endif
+
+#  define START_TEST(testname)                                                 \
+    static void testname(void) {                                               \
+      _check_set_test_info(__func__, __FILE__, __LINE__);                      \
+      {
+#  define END_TEST                                                             \
+    }                                                                          \
+    }
+
+void PRINTF_LIKE(1, 2) set_subtest(char const *fmt, ...);
+
+#  define fail(msg) _fail(__FILE__, __LINE__, msg)
+#  define assert_true(cond)                                                    \
+    do {                                                                       \
+      if (! (cond)) {                                                          \
+        _fail(__FILE__, __LINE__, "check failed: " #cond);                     \
+      }                                                                        \
+    } while (0)
 
 typedef void (*tcase_setup_function)(void);
 typedef void (*tcase_teardown_function)(void);
@@ -103,18 +129,25 @@ void _check_set_test_info(char const *function, char const *filename,
  * Prototypes for the actual implementation.
  */
 
-void _fail_unless(int condition, const char *file, int line, const char *msg);
+#  if defined(__GNUC__)
+__attribute__((noreturn))
+#  endif
+void
+_fail(const char *file, int line, const char *msg);
 Suite *suite_create(const char *name);
 TCase *tcase_create(const char *name);
 void suite_add_tcase(Suite *suite, TCase *tc);
-void tcase_add_checked_fixture(TCase *, tcase_setup_function,
-                               tcase_teardown_function);
+void tcase_add_checked_fixture(TCase *tc, tcase_setup_function setup,
+                               tcase_teardown_function teardown);
 void tcase_add_test(TCase *tc, tcase_test_function test);
 SRunner *srunner_create(Suite *suite);
-void srunner_run_all(SRunner *runner, int verbosity);
+void srunner_run_all(SRunner *runner, const char *context, int verbosity);
+void srunner_summarize(SRunner *runner, int verbosity);
 int srunner_ntests_failed(SRunner *runner);
 void srunner_free(SRunner *runner);
 
+#endif /* XML_MINICHECK_H */
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/tests/minicheck_cxx.cpp b/tests/minicheck_cxx.cpp
new file mode 100644 (file)
index 0000000..58881c6
--- /dev/null
@@ -0,0 +1,32 @@
+/* C++ compilation harness for the test suite.
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2023 Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "minicheck.c"
diff --git a/tests/misc_tests.c b/tests/misc_tests.c
new file mode 100644 (file)
index 0000000..ffde056
--- /dev/null
@@ -0,0 +1,523 @@
+/* Tests in the "miscellaneous" test case for the Expat test suite
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
+   Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Joe Orton <jorton@redhat.com>
+   Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2020      Tim Gates <tim.gates@iress.com>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
+   Copyright (c) 2023      Sony Corporation / Snild Dolkow <snild@sony.com>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#if defined(NDEBUG)
+#  undef NDEBUG /* because test suite relies on assert(...) at the moment */
+#endif
+
+#include <assert.h>
+#include <string.h>
+
+#include "expat_config.h"
+
+#include "expat.h"
+#include "internal.h"
+#include "minicheck.h"
+#include "memcheck.h"
+#include "common.h"
+#include "ascii.h" /* for ASCII_xxx */
+#include "handlers.h"
+#include "misc_tests.h"
+
+/* Test that a failure to allocate the parser structure fails gracefully */
+START_TEST(test_misc_alloc_create_parser) {
+  XML_Memory_Handling_Suite memsuite = {duff_allocator, realloc, free};
+  unsigned int i;
+  const unsigned int max_alloc_count = 10;
+
+  /* Something this simple shouldn't need more than 10 allocations */
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    g_parser = XML_ParserCreate_MM(NULL, &memsuite, NULL);
+    if (g_parser != NULL)
+      break;
+  }
+  if (i == 0)
+    fail("Parser unexpectedly ignored failing allocator");
+  else if (i == max_alloc_count)
+    fail("Parser not created with max allocation count");
+}
+END_TEST
+
+/* Test memory allocation failures for a parser with an encoding */
+START_TEST(test_misc_alloc_create_parser_with_encoding) {
+  XML_Memory_Handling_Suite memsuite = {duff_allocator, realloc, free};
+  unsigned int i;
+  const unsigned int max_alloc_count = 10;
+
+  /* Try several levels of allocation */
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    g_parser = XML_ParserCreate_MM(XCS("us-ascii"), &memsuite, NULL);
+    if (g_parser != NULL)
+      break;
+  }
+  if (i == 0)
+    fail("Parser ignored failing allocator");
+  else if (i == max_alloc_count)
+    fail("Parser not created with max allocation count");
+}
+END_TEST
+
+/* Test that freeing a NULL parser doesn't cause an explosion.
+ * (Not actually tested anywhere else)
+ */
+START_TEST(test_misc_null_parser) {
+  XML_ParserFree(NULL);
+}
+END_TEST
+
+#if defined(__has_feature)
+#  if __has_feature(undefined_behavior_sanitizer)
+#    define EXPAT_TESTS_UBSAN 1
+#  else
+#    define EXPAT_TESTS_UBSAN 0
+#  endif
+#else
+#  define EXPAT_TESTS_UBSAN 0
+#endif
+
+/* Test that XML_ErrorString rejects out-of-range codes */
+START_TEST(test_misc_error_string) {
+#if ! EXPAT_TESTS_UBSAN // because this would trigger UBSan
+  union {
+    enum XML_Error xml_error;
+    int integer;
+  } trickery;
+
+  assert_true(sizeof(enum XML_Error) == sizeof(int)); // self-test
+
+  trickery.integer = -1;
+  if (XML_ErrorString(trickery.xml_error) != NULL)
+    fail("Negative error code not rejected");
+
+  trickery.integer = 100;
+  if (XML_ErrorString(trickery.xml_error) != NULL)
+    fail("Large error code not rejected");
+#endif
+}
+END_TEST
+
+/* Test the version information is consistent */
+
+/* Since we are working in XML_LChars (potentially 16-bits), we
+ * can't use the standard C library functions for character
+ * manipulation and have to roll our own.
+ */
+static int
+parse_version(const XML_LChar *version_text,
+              XML_Expat_Version *version_struct) {
+  if (! version_text)
+    return XML_FALSE;
+
+  while (*version_text != 0x00) {
+    if (*version_text >= ASCII_0 && *version_text <= ASCII_9)
+      break;
+    version_text++;
+  }
+  if (*version_text == 0x00)
+    return XML_FALSE;
+
+  /* version_struct->major = strtoul(version_text, 10, &version_text) */
+  version_struct->major = 0;
+  while (*version_text >= ASCII_0 && *version_text <= ASCII_9) {
+    version_struct->major
+        = 10 * version_struct->major + (*version_text++ - ASCII_0);
+  }
+  if (*version_text++ != ASCII_PERIOD)
+    return XML_FALSE;
+
+  /* Now for the minor version number */
+  version_struct->minor = 0;
+  while (*version_text >= ASCII_0 && *version_text <= ASCII_9) {
+    version_struct->minor
+        = 10 * version_struct->minor + (*version_text++ - ASCII_0);
+  }
+  if (*version_text++ != ASCII_PERIOD)
+    return XML_FALSE;
+
+  /* Finally the micro version number */
+  version_struct->micro = 0;
+  while (*version_text >= ASCII_0 && *version_text <= ASCII_9) {
+    version_struct->micro
+        = 10 * version_struct->micro + (*version_text++ - ASCII_0);
+  }
+  if (*version_text != 0x00)
+    return XML_FALSE;
+  return XML_TRUE;
+}
+
+static int
+versions_equal(const XML_Expat_Version *first,
+               const XML_Expat_Version *second) {
+  return (first->major == second->major && first->minor == second->minor
+          && first->micro == second->micro);
+}
+
+START_TEST(test_misc_version) {
+  XML_Expat_Version read_version = XML_ExpatVersionInfo();
+  /* Silence compiler warning with the following assignment */
+  XML_Expat_Version parsed_version = {0, 0, 0};
+  const XML_LChar *version_text = XML_ExpatVersion();
+
+  if (version_text == NULL)
+    fail("Could not obtain version text");
+  assert(version_text != NULL);
+  if (! parse_version(version_text, &parsed_version))
+    fail("Unable to parse version text");
+  if (! versions_equal(&read_version, &parsed_version))
+    fail("Version mismatch");
+
+  if (xcstrcmp(version_text, XCS("expat_2.6.2"))) /* needs bump on releases */
+    fail("XML_*_VERSION in expat.h out of sync?\n");
+}
+END_TEST
+
+/* Test feature information */
+START_TEST(test_misc_features) {
+  const XML_Feature *features = XML_GetFeatureList();
+
+  /* Prevent problems with double-freeing parsers */
+  g_parser = NULL;
+  if (features == NULL) {
+    fail("Failed to get feature information");
+  } else {
+    /* Loop through the features checking what we can */
+    while (features->feature != XML_FEATURE_END) {
+      switch (features->feature) {
+      case XML_FEATURE_SIZEOF_XML_CHAR:
+        if (features->value != sizeof(XML_Char))
+          fail("Incorrect size of XML_Char");
+        break;
+      case XML_FEATURE_SIZEOF_XML_LCHAR:
+        if (features->value != sizeof(XML_LChar))
+          fail("Incorrect size of XML_LChar");
+        break;
+      default:
+        break;
+      }
+      features++;
+    }
+  }
+}
+END_TEST
+
+/* Regression test for GitHub Issue #17: memory leak parsing attribute
+ * values with mixed bound and unbound namespaces.
+ */
+START_TEST(test_misc_attribute_leak) {
+  const char *text = "<D xmlns:L=\"D\" l:a='' L:a=''/>";
+  XML_Memory_Handling_Suite memsuite
+      = {tracking_malloc, tracking_realloc, tracking_free};
+
+  g_parser = XML_ParserCreate_MM(XCS("UTF-8"), &memsuite, XCS("\n"));
+  expect_failure(text, XML_ERROR_UNBOUND_PREFIX, "Unbound prefixes not found");
+  XML_ParserFree(g_parser);
+  /* Prevent the teardown trying to double free */
+  g_parser = NULL;
+
+  if (! tracking_report())
+    fail("Memory leak found");
+}
+END_TEST
+
+/* Test parser created for UTF-16LE is successful */
+START_TEST(test_misc_utf16le) {
+  const char text[] =
+      /* <?xml version='1.0'?><q>Hi</q> */
+      "<\0?\0x\0m\0l\0 \0"
+      "v\0e\0r\0s\0i\0o\0n\0=\0'\0\x31\0.\0\x30\0'\0?\0>\0"
+      "<\0q\0>\0H\0i\0<\0/\0q\0>\0";
+  const XML_Char *expected = XCS("Hi");
+  CharData storage;
+
+  g_parser = XML_ParserCreate(XCS("UTF-16LE"));
+  if (g_parser == NULL)
+    fail("Parser not created");
+
+  CharData_Init(&storage);
+  XML_SetUserData(g_parser, &storage);
+  XML_SetCharacterDataHandler(g_parser, accumulate_characters);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_misc_stop_during_end_handler_issue_240_1) {
+  XML_Parser parser;
+  DataIssue240 *mydata;
+  enum XML_Status result;
+  const char *const doc1 = "<doc><e1/><e><foo/></e></doc>";
+
+  parser = XML_ParserCreate(NULL);
+  XML_SetElementHandler(parser, start_element_issue_240, end_element_issue_240);
+  mydata = (DataIssue240 *)malloc(sizeof(DataIssue240));
+  mydata->parser = parser;
+  mydata->deep = 0;
+  XML_SetUserData(parser, mydata);
+
+  result = _XML_Parse_SINGLE_BYTES(parser, doc1, (int)strlen(doc1), 1);
+  XML_ParserFree(parser);
+  free(mydata);
+  if (result != XML_STATUS_ERROR)
+    fail("Stopping the parser did not work as expected");
+}
+END_TEST
+
+START_TEST(test_misc_stop_during_end_handler_issue_240_2) {
+  XML_Parser parser;
+  DataIssue240 *mydata;
+  enum XML_Status result;
+  const char *const doc2 = "<doc><elem/></doc>";
+
+  parser = XML_ParserCreate(NULL);
+  XML_SetElementHandler(parser, start_element_issue_240, end_element_issue_240);
+  mydata = (DataIssue240 *)malloc(sizeof(DataIssue240));
+  mydata->parser = parser;
+  mydata->deep = 0;
+  XML_SetUserData(parser, mydata);
+
+  result = _XML_Parse_SINGLE_BYTES(parser, doc2, (int)strlen(doc2), 1);
+  XML_ParserFree(parser);
+  free(mydata);
+  if (result != XML_STATUS_ERROR)
+    fail("Stopping the parser did not work as expected");
+}
+END_TEST
+
+START_TEST(test_misc_deny_internal_entity_closing_doctype_issue_317) {
+  const char *const inputOne = "<!DOCTYPE d [\n"
+                               "<!ENTITY % e ']><d/>'>\n"
+                               "\n"
+                               "%e;";
+  const char *const inputTwo = "<!DOCTYPE d [\n"
+                               "<!ENTITY % e1 ']><d/>'><!ENTITY % e2 '&e1;'>\n"
+                               "\n"
+                               "%e2;";
+  const char *const inputThree = "<!DOCTYPE d [\n"
+                                 "<!ENTITY % e ']><d'>\n"
+                                 "\n"
+                                 "%e;";
+  const char *const inputIssue317 = "<!DOCTYPE doc [\n"
+                                    "<!ENTITY % foo ']>\n"
+                                    "<doc>Hell<oc (#PCDATA)*>'>\n"
+                                    "%foo;\n"
+                                    "]>\n"
+                                    "<doc>Hello, world</dVc>";
+
+  const char *const inputs[] = {inputOne, inputTwo, inputThree, inputIssue317};
+  size_t inputIndex = 0;
+
+  for (; inputIndex < sizeof(inputs) / sizeof(inputs[0]); inputIndex++) {
+    set_subtest("%s", inputs[inputIndex]);
+    XML_Parser parser;
+    enum XML_Status parseResult;
+    int setParamEntityResult;
+    XML_Size lineNumber;
+    XML_Size columnNumber;
+    const char *const input = inputs[inputIndex];
+
+    parser = XML_ParserCreate(NULL);
+    setParamEntityResult
+        = XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    if (setParamEntityResult != 1)
+      fail("Failed to set XML_PARAM_ENTITY_PARSING_ALWAYS.");
+
+    parseResult = _XML_Parse_SINGLE_BYTES(parser, input, (int)strlen(input), 0);
+    if (parseResult != XML_STATUS_ERROR) {
+      parseResult = _XML_Parse_SINGLE_BYTES(parser, "", 0, 1);
+      if (parseResult != XML_STATUS_ERROR) {
+        fail("Parsing was expected to fail but succeeded.");
+      }
+    }
+
+    if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN)
+      fail("Error code does not match XML_ERROR_INVALID_TOKEN");
+
+    lineNumber = XML_GetCurrentLineNumber(parser);
+    if (lineNumber != 4)
+      fail("XML_GetCurrentLineNumber does not work as expected.");
+
+    columnNumber = XML_GetCurrentColumnNumber(parser);
+    if (columnNumber != 0)
+      fail("XML_GetCurrentColumnNumber does not work as expected.");
+
+    XML_ParserFree(parser);
+  }
+}
+END_TEST
+
+START_TEST(test_misc_tag_mismatch_reset_leak) {
+#ifdef XML_NS
+  const char *const text = "<open xmlns='https://namespace1.test'></close>";
+  XML_Parser parser = XML_ParserCreateNS(NULL, XCS('\n'));
+
+  if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail("Call to parse was expected to fail");
+  if (XML_GetErrorCode(parser) != XML_ERROR_TAG_MISMATCH)
+    fail("Call to parse was expected to fail from a closing tag mismatch");
+
+  XML_ParserReset(parser, NULL);
+
+  if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail("Call to parse was expected to fail");
+  if (XML_GetErrorCode(parser) != XML_ERROR_TAG_MISMATCH)
+    fail("Call to parse was expected to fail from a closing tag mismatch");
+
+  XML_ParserFree(parser);
+#endif
+}
+END_TEST
+
+START_TEST(test_misc_create_external_entity_parser_with_null_context) {
+  // With XML_DTD undefined, the only supported case of external entities
+  // is pattern "<!ENTITY entity123 SYSTEM 'filename123'>". A NULL context
+  // was causing a segfault through a null pointer dereference in function
+  // setContext, previously.
+  XML_Parser parser = XML_ParserCreate(NULL);
+  XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL);
+#ifdef XML_DTD
+  assert_true(ext_parser != NULL);
+  XML_ParserFree(ext_parser);
+#else
+  assert_true(ext_parser == NULL);
+#endif /* XML_DTD */
+  XML_ParserFree(parser);
+}
+END_TEST
+
+START_TEST(test_misc_general_entities_support) {
+  const char *const doc
+      = "<!DOCTYPE r [\n"
+        "<!ENTITY e1 'v1'>\n"
+        "<!ENTITY e2 SYSTEM 'v2'>\n"
+        "]>\n"
+        "<r a1='[&e1;]'>[&e1;][&e2;][&amp;&apos;&gt;&lt;&quot;]</r>";
+
+  CharData storage;
+  CharData_Init(&storage);
+
+  XML_Parser parser = XML_ParserCreate(NULL);
+  XML_SetUserData(parser, &storage);
+  XML_SetStartElementHandler(parser, accumulate_start_element);
+  XML_SetExternalEntityRefHandler(parser,
+                                  external_entity_failer__if_not_xml_ge);
+  XML_SetEntityDeclHandler(parser, accumulate_entity_decl);
+  XML_SetCharacterDataHandler(parser, accumulate_char_data);
+
+  if (_XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc), XML_TRUE)
+      != XML_STATUS_OK) {
+    xml_failure(parser);
+  }
+
+  XML_ParserFree(parser);
+
+  CharData_CheckXMLChars(&storage,
+  /* clang-format off */
+#if XML_GE == 1
+                         XCS("e1=v1\n")
+                         XCS("e2=(null)\n")
+                         XCS("(r(a1=[v1]))\n")
+                         XCS("[v1][][&'><\"]")
+#else
+                         XCS("e1=&amp;e1;\n")
+                         XCS("e2=(null)\n")
+                         XCS("(r(a1=[&e1;]))\n")
+                         XCS("[&e1;][&e2;][&'><\"]")
+#endif
+  );
+  /* clang-format on */
+}
+END_TEST
+
+static void XMLCALL
+resumable_stopping_character_handler(void *userData, const XML_Char *s,
+                                     int len) {
+  UNUSED_P(s);
+  UNUSED_P(len);
+  XML_Parser parser = (XML_Parser)userData;
+  XML_StopParser(parser, XML_TRUE);
+}
+
+// NOTE: This test needs active LeakSanitizer to be of actual use
+START_TEST(test_misc_char_handler_stop_without_leak) {
+  const char *const data
+      = "<!DOCTYPE t1[<!ENTITY e1 'angle<'><!ENTITY e2 '&e1;'>]><t1>&e2;";
+  XML_Parser parser = XML_ParserCreate(NULL);
+  assert_true(parser != NULL);
+  XML_SetUserData(parser, parser);
+  XML_SetCharacterDataHandler(parser, resumable_stopping_character_handler);
+  _XML_Parse_SINGLE_BYTES(parser, data, (int)strlen(data), XML_FALSE);
+  XML_ParserFree(parser);
+}
+END_TEST
+
+void
+make_miscellaneous_test_case(Suite *s) {
+  TCase *tc_misc = tcase_create("miscellaneous tests");
+
+  suite_add_tcase(s, tc_misc);
+  tcase_add_checked_fixture(tc_misc, NULL, basic_teardown);
+
+  tcase_add_test(tc_misc, test_misc_alloc_create_parser);
+  tcase_add_test(tc_misc, test_misc_alloc_create_parser_with_encoding);
+  tcase_add_test(tc_misc, test_misc_null_parser);
+  tcase_add_test(tc_misc, test_misc_error_string);
+  tcase_add_test(tc_misc, test_misc_version);
+  tcase_add_test(tc_misc, test_misc_features);
+  tcase_add_test(tc_misc, test_misc_attribute_leak);
+  tcase_add_test(tc_misc, test_misc_utf16le);
+  tcase_add_test(tc_misc, test_misc_stop_during_end_handler_issue_240_1);
+  tcase_add_test(tc_misc, test_misc_stop_during_end_handler_issue_240_2);
+  tcase_add_test__ifdef_xml_dtd(
+      tc_misc, test_misc_deny_internal_entity_closing_doctype_issue_317);
+  tcase_add_test(tc_misc, test_misc_tag_mismatch_reset_leak);
+  tcase_add_test(tc_misc,
+                 test_misc_create_external_entity_parser_with_null_context);
+  tcase_add_test(tc_misc, test_misc_general_entities_support);
+  tcase_add_test(tc_misc, test_misc_char_handler_stop_without_leak);
+}
diff --git a/tests/misc_tests.h b/tests/misc_tests.h
new file mode 100644 (file)
index 0000000..3d9c4b8
--- /dev/null
@@ -0,0 +1,56 @@
+/* Tests in the "miscellaneous" test case for the Expat test suite
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
+   Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Joe Orton <jorton@redhat.com>
+   Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2020      Tim Gates <tim.gates@iress.com>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef XML_MISC_TESTS_H
+#  define XML_MISC_TESTS_H
+
+extern void make_miscellaneous_test_case(Suite *s);
+
+#endif /* XML_MISC_TESTS_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/misc_tests_cxx.cpp b/tests/misc_tests_cxx.cpp
new file mode 100644 (file)
index 0000000..0b84c1b
--- /dev/null
@@ -0,0 +1,32 @@
+/* C++ compilation harness for the test suite.
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2023 Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "misc_tests.c"
diff --git a/tests/ns_tests.c b/tests/ns_tests.c
new file mode 100644 (file)
index 0000000..411e1d3
--- /dev/null
@@ -0,0 +1,754 @@
+/* Tests in the "namespace" test case for the Expat test suite
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
+   Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2023 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Joe Orton <jorton@redhat.com>
+   Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2020      Tim Gates <tim.gates@iress.com>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
+   Copyright (c) 2023      Sony Corporation / Snild Dolkow <snild@sony.com>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "expat_config.h"
+
+#include <string.h>
+
+#include "expat.h"
+#include "internal.h"
+#include "minicheck.h"
+#include "common.h"
+#include "dummy.h"
+#include "handlers.h"
+#include "ns_tests.h"
+
+static void
+namespace_setup(void) {
+  g_parser = XML_ParserCreateNS(NULL, XCS(' '));
+  if (g_parser == NULL)
+    fail("Parser not created.");
+}
+
+static void
+namespace_teardown(void) {
+  basic_teardown();
+}
+
+START_TEST(test_return_ns_triplet) {
+  const char *text = "<foo:e xmlns:foo='http://example.org/' bar:a='12'\n"
+                     "       xmlns:bar='http://example.org/'>";
+  const char *epilog = "</foo:e>";
+  const XML_Char *elemstr[]
+      = {XCS("http://example.org/ e foo"), XCS("http://example.org/ a bar")};
+  XML_SetReturnNSTriplet(g_parser, XML_TRUE);
+  XML_SetUserData(g_parser, (void *)elemstr);
+  XML_SetElementHandler(g_parser, triplet_start_checker, triplet_end_checker);
+  XML_SetNamespaceDeclHandler(g_parser, dummy_start_namespace_decl_handler,
+                              dummy_end_namespace_decl_handler);
+  g_triplet_start_flag = XML_FALSE;
+  g_triplet_end_flag = XML_FALSE;
+  init_dummy_handlers();
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  /* Check that unsetting "return triplets" fails while still parsing */
+  XML_SetReturnNSTriplet(g_parser, XML_FALSE);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, epilog, (int)strlen(epilog), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  if (! g_triplet_start_flag)
+    fail("triplet_start_checker not invoked");
+  if (! g_triplet_end_flag)
+    fail("triplet_end_checker not invoked");
+  if (get_dummy_handler_flags()
+      != (DUMMY_START_NS_DECL_HANDLER_FLAG | DUMMY_END_NS_DECL_HANDLER_FLAG))
+    fail("Namespace handlers not called");
+}
+END_TEST
+
+/* Test that the parsing status is correctly reset by XML_ParserReset().
+ * We use test_return_ns_triplet() for our example parse to improve
+ * coverage of tidying up code executed.
+ */
+START_TEST(test_ns_parser_reset) {
+  XML_ParsingStatus status;
+
+  XML_GetParsingStatus(g_parser, &status);
+  if (status.parsing != XML_INITIALIZED)
+    fail("parsing status doesn't start INITIALIZED");
+  test_return_ns_triplet();
+  XML_GetParsingStatus(g_parser, &status);
+  if (status.parsing != XML_FINISHED)
+    fail("parsing status doesn't end FINISHED");
+  XML_ParserReset(g_parser, NULL);
+  XML_GetParsingStatus(g_parser, &status);
+  if (status.parsing != XML_INITIALIZED)
+    fail("parsing status doesn't reset to INITIALIZED");
+}
+END_TEST
+
+static void
+run_ns_tagname_overwrite_test(const char *text, const XML_Char *result) {
+  CharData storage;
+  CharData_Init(&storage);
+  XML_SetUserData(g_parser, &storage);
+  XML_SetElementHandler(g_parser, overwrite_start_checker,
+                        overwrite_end_checker);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, result);
+}
+
+/* Regression test for SF bug #566334. */
+START_TEST(test_ns_tagname_overwrite) {
+  const char *text = "<n:e xmlns:n='http://example.org/'>\n"
+                     "  <n:f n:attr='foo'/>\n"
+                     "  <n:g n:attr2='bar'/>\n"
+                     "</n:e>";
+  const XML_Char *result = XCS("start http://example.org/ e\n")
+      XCS("start http://example.org/ f\n")
+          XCS("attribute http://example.org/ attr\n")
+              XCS("end http://example.org/ f\n")
+                  XCS("start http://example.org/ g\n")
+                      XCS("attribute http://example.org/ attr2\n")
+                          XCS("end http://example.org/ g\n")
+                              XCS("end http://example.org/ e\n");
+  run_ns_tagname_overwrite_test(text, result);
+}
+END_TEST
+
+/* Regression test for SF bug #566334. */
+START_TEST(test_ns_tagname_overwrite_triplet) {
+  const char *text = "<n:e xmlns:n='http://example.org/'>\n"
+                     "  <n:f n:attr='foo'/>\n"
+                     "  <n:g n:attr2='bar'/>\n"
+                     "</n:e>";
+  const XML_Char *result = XCS("start http://example.org/ e n\n")
+      XCS("start http://example.org/ f n\n")
+          XCS("attribute http://example.org/ attr n\n")
+              XCS("end http://example.org/ f n\n")
+                  XCS("start http://example.org/ g n\n")
+                      XCS("attribute http://example.org/ attr2 n\n")
+                          XCS("end http://example.org/ g n\n")
+                              XCS("end http://example.org/ e n\n");
+  XML_SetReturnNSTriplet(g_parser, XML_TRUE);
+  run_ns_tagname_overwrite_test(text, result);
+}
+END_TEST
+
+/* Regression test for SF bug #620343. */
+START_TEST(test_start_ns_clears_start_element) {
+  /* This needs to use separate start/end tags; using the empty tag
+     syntax doesn't cause the problematic path through Expat to be
+     taken.
+  */
+  const char *text = "<e xmlns='http://example.org/'></e>";
+
+  XML_SetStartElementHandler(g_parser, start_element_fail);
+  XML_SetStartNamespaceDeclHandler(g_parser, start_ns_clearing_start_element);
+  XML_SetEndNamespaceDeclHandler(g_parser, dummy_end_namespace_decl_handler);
+  XML_UseParserAsHandlerArg(g_parser);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Regression test for SF bug #616863. */
+START_TEST(test_default_ns_from_ext_subset_and_ext_ge) {
+  const char *text = "<?xml version='1.0'?>\n"
+                     "<!DOCTYPE doc SYSTEM 'http://example.org/doc.dtd' [\n"
+                     "  <!ENTITY en SYSTEM 'http://example.org/entity.ent'>\n"
+                     "]>\n"
+                     "<doc xmlns='http://example.org/ns1'>\n"
+                     "&en;\n"
+                     "</doc>";
+
+  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  XML_SetExternalEntityRefHandler(g_parser, external_entity_handler);
+  /* We actually need to set this handler to tickle this bug. */
+  XML_SetStartElementHandler(g_parser, dummy_start_element);
+  XML_SetUserData(g_parser, NULL);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Regression test #1 for SF bug #673791. */
+START_TEST(test_ns_prefix_with_empty_uri_1) {
+  const char *text = "<doc xmlns:prefix='http://example.org/'>\n"
+                     "  <e xmlns:prefix=''/>\n"
+                     "</doc>";
+
+  expect_failure(text, XML_ERROR_UNDECLARING_PREFIX,
+                 "Did not report re-setting namespace"
+                 " URI with prefix to ''.");
+}
+END_TEST
+
+/* Regression test #2 for SF bug #673791. */
+START_TEST(test_ns_prefix_with_empty_uri_2) {
+  const char *text = "<?xml version='1.0'?>\n"
+                     "<docelem xmlns:pre=''/>";
+
+  expect_failure(text, XML_ERROR_UNDECLARING_PREFIX,
+                 "Did not report setting namespace URI with prefix to ''.");
+}
+END_TEST
+
+/* Regression test #3 for SF bug #673791. */
+START_TEST(test_ns_prefix_with_empty_uri_3) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ELEMENT doc EMPTY>\n"
+                     "  <!ATTLIST doc\n"
+                     "    xmlns:prefix CDATA ''>\n"
+                     "]>\n"
+                     "<doc/>";
+
+  expect_failure(text, XML_ERROR_UNDECLARING_PREFIX,
+                 "Didn't report attr default setting NS w/ prefix to ''.");
+}
+END_TEST
+
+/* Regression test #4 for SF bug #673791. */
+START_TEST(test_ns_prefix_with_empty_uri_4) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ELEMENT prefix:doc EMPTY>\n"
+                     "  <!ATTLIST prefix:doc\n"
+                     "    xmlns:prefix CDATA 'http://example.org/'>\n"
+                     "]>\n"
+                     "<prefix:doc/>";
+  /* Packaged info expected by the end element handler;
+     the weird structuring lets us reuse the triplet_end_checker()
+     function also used for another test. */
+  const XML_Char *elemstr[] = {XCS("http://example.org/ doc prefix")};
+  XML_SetReturnNSTriplet(g_parser, XML_TRUE);
+  XML_SetUserData(g_parser, (void *)elemstr);
+  XML_SetEndElementHandler(g_parser, triplet_end_checker);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test with non-xmlns prefix */
+START_TEST(test_ns_unbound_prefix) {
+  const char *text = "<!DOCTYPE doc [\n"
+                     "  <!ELEMENT prefix:doc EMPTY>\n"
+                     "  <!ATTLIST prefix:doc\n"
+                     "    notxmlns:prefix CDATA 'http://example.org/'>\n"
+                     "]>\n"
+                     "<prefix:doc/>";
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail("Unbound prefix incorrectly passed");
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_UNBOUND_PREFIX)
+    xml_failure(g_parser);
+}
+END_TEST
+
+START_TEST(test_ns_default_with_empty_uri) {
+  const char *text = "<doc xmlns='http://example.org/'>\n"
+                     "  <e xmlns=''/>\n"
+                     "</doc>";
+  /* Add some handlers to exercise extra code paths */
+  XML_SetStartNamespaceDeclHandler(g_parser,
+                                   dummy_start_namespace_decl_handler);
+  XML_SetEndNamespaceDeclHandler(g_parser, dummy_end_namespace_decl_handler);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Regression test for SF bug #692964: two prefixes for one namespace. */
+START_TEST(test_ns_duplicate_attrs_diff_prefixes) {
+  const char *text = "<doc xmlns:a='http://example.org/a'\n"
+                     "     xmlns:b='http://example.org/a'\n"
+                     "     a:a='v' b:a='v' />";
+  expect_failure(text, XML_ERROR_DUPLICATE_ATTRIBUTE,
+                 "did not report multiple attributes with same URI+name");
+}
+END_TEST
+
+START_TEST(test_ns_duplicate_hashes) {
+  /* The hash of an attribute is calculated as the hash of its URI
+   * concatenated with a space followed by its name (after the
+   * colon).  We wish to generate attributes with the same hash
+   * value modulo the attribute table size so that we can check that
+   * the attribute hash table works correctly.  The attribute hash
+   * table size will be the smallest power of two greater than the
+   * number of attributes, but at least eight.  There is
+   * unfortunately no programmatic way of getting the hash or the
+   * table size at user level, but the test code coverage percentage
+   * will drop if the hashes cease to point to the same row.
+   *
+   * The cunning plan is to have few enough attributes to have a
+   * reliable table size of 8, and have the single letter attribute
+   * names be 8 characters apart, producing a hash which will be the
+   * same modulo 8.
+   */
+  const char *text = "<doc xmlns:a='http://example.org/a'\n"
+                     "     a:a='v' a:i='w' />";
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Regression test for SF bug #695401: unbound prefix. */
+START_TEST(test_ns_unbound_prefix_on_attribute) {
+  const char *text = "<doc a:attr=''/>";
+  expect_failure(text, XML_ERROR_UNBOUND_PREFIX,
+                 "did not report unbound prefix on attribute");
+}
+END_TEST
+
+/* Regression test for SF bug #695401: unbound prefix. */
+START_TEST(test_ns_unbound_prefix_on_element) {
+  const char *text = "<a:doc/>";
+  expect_failure(text, XML_ERROR_UNBOUND_PREFIX,
+                 "did not report unbound prefix on element");
+}
+END_TEST
+
+/* Test that long element names with namespaces are handled correctly */
+START_TEST(test_ns_long_element) {
+  const char *text
+      = "<foo:thisisalongenoughelementnametotriggerareallocation\n"
+        " xmlns:foo='http://example.org/' bar:a='12'\n"
+        " xmlns:bar='http://example.org/'>"
+        "</foo:thisisalongenoughelementnametotriggerareallocation>";
+  const XML_Char *elemstr[]
+      = {XCS("http://example.org/")
+             XCS(" thisisalongenoughelementnametotriggerareallocation foo"),
+         XCS("http://example.org/ a bar")};
+
+  XML_SetReturnNSTriplet(g_parser, XML_TRUE);
+  XML_SetUserData(g_parser, (void *)elemstr);
+  XML_SetElementHandler(g_parser, triplet_start_checker, triplet_end_checker);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test mixed population of prefixed and unprefixed attributes */
+START_TEST(test_ns_mixed_prefix_atts) {
+  const char *text = "<e a='12' bar:b='13'\n"
+                     " xmlns:bar='http://example.org/'>"
+                     "</e>";
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test having a long namespaced element name inside a short one.
+ * This exercises some internal buffer reallocation that is shared
+ * across elements with the same namespace URI.
+ */
+START_TEST(test_ns_extend_uri_buffer) {
+  const char *text = "<foo:e xmlns:foo='http://example.org/'>"
+                     " <foo:thisisalongenoughnametotriggerallocationaction"
+                     "   foo:a='12' />"
+                     "</foo:e>";
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test that xmlns is correctly rejected as an attribute in the xmlns
+ * namespace, but not in other namespaces
+ */
+START_TEST(test_ns_reserved_attributes) {
+  const char *text1
+      = "<foo:e xmlns:foo='http://example.org/' xmlns:xmlns='12' />";
+  const char *text2
+      = "<foo:e xmlns:foo='http://example.org/' foo:xmlns='12' />";
+  expect_failure(text1, XML_ERROR_RESERVED_PREFIX_XMLNS,
+                 "xmlns not rejected as an attribute");
+  XML_ParserReset(g_parser, NULL);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text2, (int)strlen(text2), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test more reserved attributes */
+START_TEST(test_ns_reserved_attributes_2) {
+  const char *text1 = "<foo:e xmlns:foo='http://example.org/'"
+                      "  xmlns:xml='http://example.org/' />";
+  const char *text2
+      = "<foo:e xmlns:foo='http://www.w3.org/XML/1998/namespace' />";
+  const char *text3 = "<foo:e xmlns:foo='http://www.w3.org/2000/xmlns/' />";
+
+  expect_failure(text1, XML_ERROR_RESERVED_PREFIX_XML,
+                 "xml not rejected as an attribute");
+  XML_ParserReset(g_parser, NULL);
+  expect_failure(text2, XML_ERROR_RESERVED_NAMESPACE_URI,
+                 "Use of w3.org URL not faulted");
+  XML_ParserReset(g_parser, NULL);
+  expect_failure(text3, XML_ERROR_RESERVED_NAMESPACE_URI,
+                 "Use of w3.org xmlns URL not faulted");
+}
+END_TEST
+
+/* Test string pool handling of namespace names of 2048 characters */
+/* Exercises a particular string pool growth path */
+START_TEST(test_ns_extremely_long_prefix) {
+  /* C99 compilers are only required to support 4095-character
+   * strings, so the following needs to be split in two to be safe
+   * for all compilers.
+   */
+  const char *text1
+      = "<doc "
+        /* 64 character on each line */
+        /* ...gives a total length of 2048 */
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        ":a='12'";
+  const char *text2
+      = " xmlns:"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "='foo'\n>"
+        "</doc>";
+
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text1, (int)strlen(text1), XML_FALSE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text2, (int)strlen(text2), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+}
+END_TEST
+
+/* Test unknown encoding handlers in namespace setup */
+START_TEST(test_ns_unknown_encoding_success) {
+  const char *text = "<?xml version='1.0' encoding='prefix-conv'?>\n"
+                     "<foo:e xmlns:foo='http://example.org/'>Hi</foo:e>";
+
+  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
+  run_character_check(text, XCS("Hi"));
+}
+END_TEST
+
+/* Test that too many colons are rejected */
+START_TEST(test_ns_double_colon) {
+  const char *text = "<foo:e xmlns:foo='http://example.org/' foo:a:b='bar' />";
+  const enum XML_Status status
+      = _XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE);
+#ifdef XML_NS
+  if ((status == XML_STATUS_OK)
+      || (XML_GetErrorCode(g_parser) != XML_ERROR_INVALID_TOKEN)) {
+    fail("Double colon in attribute name not faulted"
+         " (despite active namespace support)");
+  }
+#else
+  if (status != XML_STATUS_OK) {
+    fail("Double colon in attribute name faulted"
+         " (despite inactive namespace support");
+  }
+#endif
+}
+END_TEST
+
+START_TEST(test_ns_double_colon_element) {
+  const char *text = "<foo:bar:e xmlns:foo='http://example.org/' />";
+  const enum XML_Status status
+      = _XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE);
+#ifdef XML_NS
+  if ((status == XML_STATUS_OK)
+      || (XML_GetErrorCode(g_parser) != XML_ERROR_INVALID_TOKEN)) {
+    fail("Double colon in element name not faulted"
+         " (despite active namespace support)");
+  }
+#else
+  if (status != XML_STATUS_OK) {
+    fail("Double colon in element name faulted"
+         " (despite inactive namespace support");
+  }
+#endif
+}
+END_TEST
+
+/* Test that non-name characters after a colon are rejected */
+START_TEST(test_ns_bad_attr_leafname) {
+  const char *text = "<foo:e xmlns:foo='http://example.org/' foo:?ar='baz' />";
+
+  expect_failure(text, XML_ERROR_INVALID_TOKEN,
+                 "Invalid character in leafname not faulted");
+}
+END_TEST
+
+START_TEST(test_ns_bad_element_leafname) {
+  const char *text = "<foo:?oc xmlns:foo='http://example.org/' />";
+
+  expect_failure(text, XML_ERROR_INVALID_TOKEN,
+                 "Invalid character in element leafname not faulted");
+}
+END_TEST
+
+/* Test high-byte-set UTF-16 characters are valid in a leafname */
+START_TEST(test_ns_utf16_leafname) {
+  const char text[] =
+      /* <n:e xmlns:n='URI' n:{KHO KHWAI}='a' />
+       * where {KHO KHWAI} = U+0E04 = 0xe0 0xb8 0x84 in UTF-8
+       */
+      "<\0n\0:\0e\0 \0x\0m\0l\0n\0s\0:\0n\0=\0'\0U\0R\0I\0'\0 \0"
+      "n\0:\0\x04\x0e=\0'\0a\0'\0 \0/\0>\0";
+  const XML_Char *expected = XCS("a");
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetStartElementHandler(g_parser, accumulate_attribute);
+  XML_SetUserData(g_parser, &storage);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_ns_utf16_element_leafname) {
+  const char text[] =
+      /* <n:{KHO KHWAI} xmlns:n='URI'/>
+       * where {KHO KHWAI} = U+0E04 = 0xe0 0xb8 0x84 in UTF-8
+       */
+      "\0<\0n\0:\x0e\x04\0 \0x\0m\0l\0n\0s\0:\0n\0=\0'\0U\0R\0I\0'\0/\0>";
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("URI \x0e04");
+#else
+  const XML_Char *expected = XCS("URI \xe0\xb8\x84");
+#endif
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetStartElementHandler(g_parser, start_element_event_handler);
+  XML_SetUserData(g_parser, &storage);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_ns_utf16_doctype) {
+  const char text[] =
+      /* <!DOCTYPE foo:{KHO KHWAI} [ <!ENTITY bar 'baz'> ]>\n
+       * where {KHO KHWAI} = U+0E04 = 0xe0 0xb8 0x84 in UTF-8
+       */
+      "\0<\0!\0D\0O\0C\0T\0Y\0P\0E\0 \0f\0o\0o\0:\x0e\x04\0 "
+      "\0[\0 \0<\0!\0E\0N\0T\0I\0T\0Y\0 \0b\0a\0r\0 \0'\0b\0a\0z\0'\0>\0 "
+      "\0]\0>\0\n"
+      /* <foo:{KHO KHWAI} xmlns:foo='URI'>&bar;</foo:{KHO KHWAI}> */
+      "\0<\0f\0o\0o\0:\x0e\x04\0 "
+      "\0x\0m\0l\0n\0s\0:\0f\0o\0o\0=\0'\0U\0R\0I\0'\0>"
+      "\0&\0b\0a\0r\0;"
+      "\0<\0/\0f\0o\0o\0:\x0e\x04\0>";
+#ifdef XML_UNICODE
+  const XML_Char *expected = XCS("URI \x0e04");
+#else
+  const XML_Char *expected = XCS("URI \xe0\xb8\x84");
+#endif
+  CharData storage;
+
+  CharData_Init(&storage);
+  XML_SetUserData(g_parser, &storage);
+  XML_SetStartElementHandler(g_parser, start_element_event_handler);
+  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
+  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+  CharData_CheckXMLChars(&storage, expected);
+}
+END_TEST
+
+START_TEST(test_ns_invalid_doctype) {
+  const char *text = "<!DOCTYPE foo:!bad [ <!ENTITY bar 'baz' ]>\n"
+                     "<foo:!bad>&bar;</foo:!bad>";
+
+  expect_failure(text, XML_ERROR_INVALID_TOKEN,
+                 "Invalid character in document local name not faulted");
+}
+END_TEST
+
+START_TEST(test_ns_double_colon_doctype) {
+  const char *text = "<!DOCTYPE foo:a:doc [ <!ENTITY bar 'baz' ]>\n"
+                     "<foo:a:doc>&bar;</foo:a:doc>";
+
+  expect_failure(text, XML_ERROR_SYNTAX,
+                 "Double colon in document name not faulted");
+}
+END_TEST
+
+START_TEST(test_ns_separator_in_uri) {
+  struct test_case {
+    enum XML_Status expectedStatus;
+    const char *doc;
+    XML_Char namesep;
+  };
+  struct test_case cases[] = {
+      {XML_STATUS_OK, "<doc xmlns='one_two' />", XCS('\n')},
+      {XML_STATUS_ERROR, "<doc xmlns='one&#x0A;two' />", XCS('\n')},
+      {XML_STATUS_OK, "<doc xmlns='one:two' />", XCS(':')},
+  };
+
+  size_t i = 0;
+  size_t failCount = 0;
+  for (; i < sizeof(cases) / sizeof(cases[0]); i++) {
+    set_subtest("%s", cases[i].doc);
+    XML_Parser parser = XML_ParserCreateNS(NULL, cases[i].namesep);
+    XML_SetElementHandler(parser, dummy_start_element, dummy_end_element);
+    if (_XML_Parse_SINGLE_BYTES(parser, cases[i].doc, (int)strlen(cases[i].doc),
+                                /*isFinal*/ XML_TRUE)
+        != cases[i].expectedStatus) {
+      failCount++;
+    }
+    XML_ParserFree(parser);
+  }
+
+  if (failCount) {
+    fail("Namespace separator handling is broken");
+  }
+}
+END_TEST
+
+void
+make_namespace_test_case(Suite *s) {
+  TCase *tc_namespace = tcase_create("XML namespaces");
+
+  suite_add_tcase(s, tc_namespace);
+  tcase_add_checked_fixture(tc_namespace, namespace_setup, namespace_teardown);
+  tcase_add_test(tc_namespace, test_return_ns_triplet);
+  tcase_add_test(tc_namespace, test_ns_parser_reset);
+  tcase_add_test(tc_namespace, test_ns_tagname_overwrite);
+  tcase_add_test(tc_namespace, test_ns_tagname_overwrite_triplet);
+  tcase_add_test(tc_namespace, test_start_ns_clears_start_element);
+  tcase_add_test__ifdef_xml_dtd(tc_namespace,
+                                test_default_ns_from_ext_subset_and_ext_ge);
+  tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_1);
+  tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_2);
+  tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_3);
+  tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_4);
+  tcase_add_test(tc_namespace, test_ns_unbound_prefix);
+  tcase_add_test(tc_namespace, test_ns_default_with_empty_uri);
+  tcase_add_test(tc_namespace, test_ns_duplicate_attrs_diff_prefixes);
+  tcase_add_test(tc_namespace, test_ns_duplicate_hashes);
+  tcase_add_test(tc_namespace, test_ns_unbound_prefix_on_attribute);
+  tcase_add_test(tc_namespace, test_ns_unbound_prefix_on_element);
+  tcase_add_test(tc_namespace, test_ns_long_element);
+  tcase_add_test(tc_namespace, test_ns_mixed_prefix_atts);
+  tcase_add_test(tc_namespace, test_ns_extend_uri_buffer);
+  tcase_add_test(tc_namespace, test_ns_reserved_attributes);
+  tcase_add_test(tc_namespace, test_ns_reserved_attributes_2);
+  tcase_add_test(tc_namespace, test_ns_extremely_long_prefix);
+  tcase_add_test(tc_namespace, test_ns_unknown_encoding_success);
+  tcase_add_test(tc_namespace, test_ns_double_colon);
+  tcase_add_test(tc_namespace, test_ns_double_colon_element);
+  tcase_add_test(tc_namespace, test_ns_bad_attr_leafname);
+  tcase_add_test(tc_namespace, test_ns_bad_element_leafname);
+  tcase_add_test(tc_namespace, test_ns_utf16_leafname);
+  tcase_add_test(tc_namespace, test_ns_utf16_element_leafname);
+  tcase_add_test__if_xml_ge(tc_namespace, test_ns_utf16_doctype);
+  tcase_add_test(tc_namespace, test_ns_invalid_doctype);
+  tcase_add_test(tc_namespace, test_ns_double_colon_doctype);
+  tcase_add_test(tc_namespace, test_ns_separator_in_uri);
+}
diff --git a/tests/ns_tests.h b/tests/ns_tests.h
new file mode 100644 (file)
index 0000000..acb0db7
--- /dev/null
@@ -0,0 +1,56 @@
+/* Tests in the "namespace" test case for the Expat test suite
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
+   Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Joe Orton <jorton@redhat.com>
+   Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2020      Tim Gates <tim.gates@iress.com>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef XML_NS_TESTS_H
+#  define XML_NS_TESTS_H
+
+extern void make_namespace_test_case(Suite *s);
+
+#endif /* XML_NS_TESTS_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/ns_tests_cxx.cpp b/tests/ns_tests_cxx.cpp
new file mode 100644 (file)
index 0000000..b2fe187
--- /dev/null
@@ -0,0 +1,32 @@
+/* C++ compilation harness for the test suite.
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2023 Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "ns_tests.c"
diff --git a/tests/nsalloc_tests.c b/tests/nsalloc_tests.c
new file mode 100644 (file)
index 0000000..ec88586
--- /dev/null
@@ -0,0 +1,1537 @@
+/* Tests in the "namespace allocation" test case for the Expat test suite
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
+   Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2023 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Joe Orton <jorton@redhat.com>
+   Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2020      Tim Gates <tim.gates@iress.com>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#if defined(NDEBUG)
+#  undef NDEBUG /* because test suite relies on assert(...) at the moment */
+#endif
+
+#include <string.h>
+#include <assert.h>
+
+#include "expat.h"
+#include "common.h"
+#include "minicheck.h"
+#include "dummy.h"
+#include "handlers.h"
+#include "nsalloc_tests.h"
+
+static void
+nsalloc_setup(void) {
+  XML_Memory_Handling_Suite memsuite = {duff_allocator, duff_reallocator, free};
+  XML_Char ns_sep[2] = {' ', '\0'};
+
+  /* Ensure the parser creation will go through */
+  g_allocation_count = ALLOC_ALWAYS_SUCCEED;
+  g_reallocation_count = REALLOC_ALWAYS_SUCCEED;
+  g_parser = XML_ParserCreate_MM(NULL, &memsuite, ns_sep);
+  if (g_parser == NULL)
+    fail("Parser not created");
+}
+
+static void
+nsalloc_teardown(void) {
+  basic_teardown();
+}
+
+/* Test the effects of allocation failure in simple namespace parsing.
+ * Based on test_ns_default_with_empty_uri()
+ */
+START_TEST(test_nsalloc_xmlns) {
+  const char *text = "<doc xmlns='http://example.org/'>\n"
+                     "  <e xmlns=''/>\n"
+                     "</doc>";
+  unsigned int i;
+  const unsigned int max_alloc_count = 30;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    /* Exercise more code paths with a default handler */
+    XML_SetDefaultHandler(g_parser, dummy_default_handler);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* Resetting the parser is insufficient, because some memory
+     * allocations are cached within the parser.  Instead we use
+     * the teardown and setup routines to ensure that we have the
+     * right sort of parser back in our hands.
+     */
+    nsalloc_teardown();
+    nsalloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing allocations");
+  else if (i == max_alloc_count)
+    fail("Parsing failed even at maximum allocation count");
+}
+END_TEST
+
+/* Test XML_ParseBuffer interface with namespace and a dicky allocator */
+START_TEST(test_nsalloc_parse_buffer) {
+  const char *text = "<doc>Hello</doc>";
+  void *buffer;
+
+  /* Try a parse before the start of the world */
+  /* (Exercises new code path) */
+  if (XML_ParseBuffer(g_parser, 0, XML_FALSE) != XML_STATUS_ERROR)
+    fail("Pre-init XML_ParseBuffer not faulted");
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_NO_BUFFER)
+    fail("Pre-init XML_ParseBuffer faulted for wrong reason");
+
+  buffer = XML_GetBuffer(g_parser, 1 /* any small number greater than 0 */);
+  if (buffer == NULL)
+    fail("Could not acquire parse buffer");
+
+  g_allocation_count = 0;
+  if (XML_ParseBuffer(g_parser, 0, XML_FALSE) != XML_STATUS_ERROR)
+    fail("Pre-init XML_ParseBuffer not faulted");
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_NO_MEMORY)
+    fail("Pre-init XML_ParseBuffer faulted for wrong reason");
+
+  /* Now with actual memory allocation */
+  g_allocation_count = ALLOC_ALWAYS_SUCCEED;
+  if (XML_ParseBuffer(g_parser, 0, XML_FALSE) != XML_STATUS_OK)
+    xml_failure(g_parser);
+
+  /* Check that resuming an unsuspended parser is faulted */
+  if (XML_ResumeParser(g_parser) != XML_STATUS_ERROR)
+    fail("Resuming unsuspended parser not faulted");
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_NOT_SUSPENDED)
+    xml_failure(g_parser);
+
+  /* Get the parser into suspended state */
+  XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler);
+  g_resumable = XML_TRUE;
+  buffer = XML_GetBuffer(g_parser, (int)strlen(text));
+  if (buffer == NULL)
+    fail("Could not acquire parse buffer");
+  assert(buffer != NULL);
+  memcpy(buffer, text, strlen(text));
+  if (XML_ParseBuffer(g_parser, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_SUSPENDED)
+    xml_failure(g_parser);
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_NONE)
+    xml_failure(g_parser);
+  if (XML_ParseBuffer(g_parser, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail("Suspended XML_ParseBuffer not faulted");
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_SUSPENDED)
+    xml_failure(g_parser);
+  if (XML_GetBuffer(g_parser, (int)strlen(text)) != NULL)
+    fail("Suspended XML_GetBuffer not faulted");
+
+  /* Get it going again and complete the world */
+  XML_SetCharacterDataHandler(g_parser, NULL);
+  if (XML_ResumeParser(g_parser) != XML_STATUS_OK)
+    xml_failure(g_parser);
+  if (XML_ParseBuffer(g_parser, (int)strlen(text), XML_TRUE)
+      != XML_STATUS_ERROR)
+    fail("Post-finishing XML_ParseBuffer not faulted");
+  if (XML_GetErrorCode(g_parser) != XML_ERROR_FINISHED)
+    xml_failure(g_parser);
+  if (XML_GetBuffer(g_parser, (int)strlen(text)) != NULL)
+    fail("Post-finishing XML_GetBuffer not faulted");
+}
+END_TEST
+
+/* Check handling of long prefix names (pool growth) */
+START_TEST(test_nsalloc_long_prefix) {
+  const char *text
+      = "<"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        ":foo xmlns:"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "='http://example.org/'>"
+        "</"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        ":foo>";
+  int i;
+  const int max_alloc_count = 40;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_nsalloc_xmlns() */
+    nsalloc_teardown();
+    nsalloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing allocations");
+  else if (i == max_alloc_count)
+    fail("Parsing failed even at max allocation count");
+}
+END_TEST
+
+/* Check handling of long uri names (pool growth) */
+START_TEST(test_nsalloc_long_uri) {
+  const char *text
+      = "<foo:e xmlns:foo='http://example.org/"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "' bar:a='12'\n"
+        "xmlns:bar='http://example.org/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
+        "'>"
+        "</foo:e>";
+  int i;
+  const int max_alloc_count = 40;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_nsalloc_xmlns() */
+    nsalloc_teardown();
+    nsalloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing allocations");
+  else if (i == max_alloc_count)
+    fail("Parsing failed even at max allocation count");
+}
+END_TEST
+
+/* Test handling of long attribute names with prefixes */
+START_TEST(test_nsalloc_long_attr) {
+  const char *text
+      = "<foo:e xmlns:foo='http://example.org/' bar:"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "='12'\n"
+        "xmlns:bar='http://example.org/'>"
+        "</foo:e>";
+  int i;
+  const int max_alloc_count = 40;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_nsalloc_xmlns() */
+    nsalloc_teardown();
+    nsalloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing allocations");
+  else if (i == max_alloc_count)
+    fail("Parsing failed even at max allocation count");
+}
+END_TEST
+
+/* Test handling of an attribute name with a long namespace prefix */
+START_TEST(test_nsalloc_long_attr_prefix) {
+  const char *text
+      = "<foo:e xmlns:foo='http://example.org/' "
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        ":a='12'\n"
+        "xmlns:"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "='http://example.org/'>"
+        "</foo:e>";
+  const XML_Char *elemstr[] = {
+      /* clang-format off */
+        XCS("http://example.org/ e foo"),
+        XCS("http://example.org/ a ")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
+        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
+      /* clang-format on */
+  };
+  int i;
+  const int max_alloc_count = 40;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetReturnNSTriplet(g_parser, XML_TRUE);
+    XML_SetUserData(g_parser, (void *)elemstr);
+    XML_SetElementHandler(g_parser, triplet_start_checker, triplet_end_checker);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_nsalloc_xmlns() */
+    nsalloc_teardown();
+    nsalloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing allocations");
+  else if (i == max_alloc_count)
+    fail("Parsing failed even at max allocation count");
+}
+END_TEST
+
+/* Test attribute handling in the face of a dodgy reallocator */
+START_TEST(test_nsalloc_realloc_attributes) {
+  const char *text = "<foo:e xmlns:foo='http://example.org/' bar:a='12'\n"
+                     "       xmlns:bar='http://example.org/'>"
+                     "</foo:e>";
+  int i;
+  const int max_realloc_count = 10;
+
+  for (i = 0; i < max_realloc_count; i++) {
+    g_reallocation_count = i;
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_nsalloc_xmlns() */
+    nsalloc_teardown();
+    nsalloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing reallocations");
+  else if (i == max_realloc_count)
+    fail("Parsing failed at max reallocation count");
+}
+END_TEST
+
+/* Test long element names with namespaces under a failing allocator */
+START_TEST(test_nsalloc_long_element) {
+  const char *text
+      = "<foo:thisisalongenoughelementnametotriggerareallocation\n"
+        " xmlns:foo='http://example.org/' bar:a='12'\n"
+        " xmlns:bar='http://example.org/'>"
+        "</foo:thisisalongenoughelementnametotriggerareallocation>";
+  const XML_Char *elemstr[]
+      = {XCS("http://example.org/")
+             XCS(" thisisalongenoughelementnametotriggerareallocation foo"),
+         XCS("http://example.org/ a bar")};
+  int i;
+  const int max_alloc_count = 30;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetReturnNSTriplet(g_parser, XML_TRUE);
+    XML_SetUserData(g_parser, (void *)elemstr);
+    XML_SetElementHandler(g_parser, triplet_start_checker, triplet_end_checker);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_nsalloc_xmlns() */
+    nsalloc_teardown();
+    nsalloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing reallocations");
+  else if (i == max_alloc_count)
+    fail("Parsing failed at max reallocation count");
+}
+END_TEST
+
+/* Test the effects of reallocation failure when reassigning a
+ * binding.
+ *
+ * XML_ParserReset does not free the BINDING structures used by a
+ * parser, but instead adds them to an internal free list to be reused
+ * as necessary.  Likewise the URI buffers allocated for the binding
+ * aren't freed, but kept attached to their existing binding.  If the
+ * new binding has a longer URI, it will need reallocation.  This test
+ * provokes that reallocation, and tests the control path if it fails.
+ */
+START_TEST(test_nsalloc_realloc_binding_uri) {
+  const char *first = "<doc xmlns='http://example.org/'>\n"
+                      "  <e xmlns='' />\n"
+                      "</doc>";
+  const char *second
+      = "<doc xmlns='http://example.org/long/enough/URI/to/reallocate/'>\n"
+        "  <e xmlns='' />\n"
+        "</doc>";
+  unsigned i;
+  const unsigned max_realloc_count = 10;
+
+  /* First, do a full parse that will leave bindings around */
+  if (_XML_Parse_SINGLE_BYTES(g_parser, first, (int)strlen(first), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+
+  /* Now repeat with a longer URI and a duff reallocator */
+  for (i = 0; i < max_realloc_count; i++) {
+    XML_ParserReset(g_parser, NULL);
+    g_reallocation_count = i;
+    if (_XML_Parse_SINGLE_BYTES(g_parser, second, (int)strlen(second), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing reallocation");
+  else if (i == max_realloc_count)
+    fail("Parsing failed at max reallocation count");
+}
+END_TEST
+
+/* Check handling of long prefix names (pool growth) */
+START_TEST(test_nsalloc_realloc_long_prefix) {
+  const char *text
+      = "<"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        ":foo xmlns:"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "='http://example.org/'>"
+        "</"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        ":foo>";
+  int i;
+  const int max_realloc_count = 12;
+
+  for (i = 0; i < max_realloc_count; i++) {
+    g_reallocation_count = i;
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_nsalloc_xmlns() */
+    nsalloc_teardown();
+    nsalloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing reallocations");
+  else if (i == max_realloc_count)
+    fail("Parsing failed even at max reallocation count");
+}
+END_TEST
+
+/* Check handling of even long prefix names (different code path) */
+START_TEST(test_nsalloc_realloc_longer_prefix) {
+  const char *text
+      = "<"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "Q:foo xmlns:"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "Q='http://example.org/'>"
+        "</"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "Q:foo>";
+  int i;
+  const int max_realloc_count = 12;
+
+  for (i = 0; i < max_realloc_count; i++) {
+    g_reallocation_count = i;
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_nsalloc_xmlns() */
+    nsalloc_teardown();
+    nsalloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing reallocations");
+  else if (i == max_realloc_count)
+    fail("Parsing failed even at max reallocation count");
+}
+END_TEST
+
+START_TEST(test_nsalloc_long_namespace) {
+  const char *text1
+      = "<"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        ":e xmlns:"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "='http://example.org/'>\n";
+  const char *text2
+      = "<"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        ":f "
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        ":attr='foo'/>\n"
+        "</"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        ":e>";
+  int i;
+  const int max_alloc_count = 40;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text1, (int)strlen(text1), XML_FALSE)
+            != XML_STATUS_ERROR
+        && _XML_Parse_SINGLE_BYTES(g_parser, text2, (int)strlen(text2),
+                                   XML_TRUE)
+               != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_nsalloc_xmlns() */
+    nsalloc_teardown();
+    nsalloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing allocations");
+  else if (i == max_alloc_count)
+    fail("Parsing failed even at max allocation count");
+}
+END_TEST
+
+/* Using a slightly shorter namespace name provokes allocations in
+ * slightly different places in the code.
+ */
+START_TEST(test_nsalloc_less_long_namespace) {
+  const char *text
+      = "<"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678"
+        ":e xmlns:"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678"
+        "='http://example.org/'>\n"
+        "<"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678"
+        ":f "
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678"
+        ":att='foo'/>\n"
+        "</"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678"
+        ":e>";
+  int i;
+  const int max_alloc_count = 40;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_nsalloc_xmlns() */
+    nsalloc_teardown();
+    nsalloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing allocations");
+  else if (i == max_alloc_count)
+    fail("Parsing failed even at max allocation count");
+}
+END_TEST
+
+START_TEST(test_nsalloc_long_context) {
+  const char *text
+      = "<!DOCTYPE doc SYSTEM 'foo' [\n"
+        "  <!ATTLIST doc baz ID #REQUIRED>\n"
+        "  <!ENTITY en SYSTEM 'bar'>\n"
+        "]>\n"
+        "<doc xmlns='http://example.org/"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKL"
+        "' baz='2'>\n"
+        "&en;"
+        "</doc>";
+  ExtOption options[] = {
+      {XCS("foo"), "<!ELEMENT e EMPTY>"}, {XCS("bar"), "<e/>"}, {NULL, NULL}};
+  int i;
+  const int max_alloc_count = 70;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetUserData(g_parser, options);
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_optioner);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+
+    /* See comment in test_nsalloc_xmlns() */
+    nsalloc_teardown();
+    nsalloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing allocations");
+  else if (i == max_alloc_count)
+    fail("Parsing failed even at max allocation count");
+}
+END_TEST
+
+/* This function is void; it will throw a fail() on error, so if it
+ * returns normally it must have succeeded.
+ */
+static void
+context_realloc_test(const char *text) {
+  ExtOption options[] = {
+      {XCS("foo"), "<!ELEMENT e EMPTY>"}, {XCS("bar"), "<e/>"}, {NULL, NULL}};
+  int i;
+  const int max_realloc_count = 6;
+
+  for (i = 0; i < max_realloc_count; i++) {
+    g_reallocation_count = i;
+    XML_SetUserData(g_parser, options);
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_optioner);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_nsalloc_xmlns() */
+    nsalloc_teardown();
+    nsalloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing reallocations");
+  else if (i == max_realloc_count)
+    fail("Parsing failed even at max reallocation count");
+}
+
+START_TEST(test_nsalloc_realloc_long_context) {
+  const char *text
+      = "<!DOCTYPE doc SYSTEM 'foo' [\n"
+        "  <!ENTITY en SYSTEM 'bar'>\n"
+        "]>\n"
+        "<doc xmlns='http://example.org/"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKL"
+        "'>\n"
+        "&en;"
+        "</doc>";
+
+  context_realloc_test(text);
+}
+END_TEST
+
+START_TEST(test_nsalloc_realloc_long_context_2) {
+  const char *text
+      = "<!DOCTYPE doc SYSTEM 'foo' [\n"
+        "  <!ENTITY en SYSTEM 'bar'>\n"
+        "]>\n"
+        "<doc xmlns='http://example.org/"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJK"
+        "'>\n"
+        "&en;"
+        "</doc>";
+
+  context_realloc_test(text);
+}
+END_TEST
+
+START_TEST(test_nsalloc_realloc_long_context_3) {
+  const char *text
+      = "<!DOCTYPE doc SYSTEM 'foo' [\n"
+        "  <!ENTITY en SYSTEM 'bar'>\n"
+        "]>\n"
+        "<doc xmlns='http://example.org/"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGH"
+        "'>\n"
+        "&en;"
+        "</doc>";
+
+  context_realloc_test(text);
+}
+END_TEST
+
+START_TEST(test_nsalloc_realloc_long_context_4) {
+  const char *text
+      = "<!DOCTYPE doc SYSTEM 'foo' [\n"
+        "  <!ENTITY en SYSTEM 'bar'>\n"
+        "]>\n"
+        "<doc xmlns='http://example.org/"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO"
+        "'>\n"
+        "&en;"
+        "</doc>";
+
+  context_realloc_test(text);
+}
+END_TEST
+
+START_TEST(test_nsalloc_realloc_long_context_5) {
+  const char *text
+      = "<!DOCTYPE doc SYSTEM 'foo' [\n"
+        "  <!ENTITY en SYSTEM 'bar'>\n"
+        "]>\n"
+        "<doc xmlns='http://example.org/"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABC"
+        "'>\n"
+        "&en;"
+        "</doc>";
+
+  context_realloc_test(text);
+}
+END_TEST
+
+START_TEST(test_nsalloc_realloc_long_context_6) {
+  const char *text
+      = "<!DOCTYPE doc SYSTEM 'foo' [\n"
+        "  <!ENTITY en SYSTEM 'bar'>\n"
+        "]>\n"
+        "<doc xmlns='http://example.org/"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
+        "'>\n"
+        "&en;"
+        "</doc>";
+
+  context_realloc_test(text);
+}
+END_TEST
+
+START_TEST(test_nsalloc_realloc_long_context_7) {
+  const char *text
+      = "<!DOCTYPE doc SYSTEM 'foo' [\n"
+        "  <!ENTITY en SYSTEM 'bar'>\n"
+        "]>\n"
+        "<doc xmlns='http://example.org/"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLM"
+        "'>\n"
+        "&en;"
+        "</doc>";
+
+  context_realloc_test(text);
+}
+END_TEST
+
+START_TEST(test_nsalloc_realloc_long_ge_name) {
+  const char *text
+      = "<!DOCTYPE doc SYSTEM 'foo' [\n"
+        "  <!ENTITY "
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        " SYSTEM 'bar'>\n"
+        "]>\n"
+        "<doc xmlns='http://example.org/baz'>\n"
+        "&"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        ";"
+        "</doc>";
+  ExtOption options[] = {
+      {XCS("foo"), "<!ELEMENT el EMPTY>"}, {XCS("bar"), "<el/>"}, {NULL, NULL}};
+  int i;
+  const int max_realloc_count = 10;
+
+  for (i = 0; i < max_realloc_count; i++) {
+    g_reallocation_count = i;
+    XML_SetUserData(g_parser, options);
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_optioner);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_nsalloc_xmlns() */
+    nsalloc_teardown();
+    nsalloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing reallocations");
+  else if (i == max_realloc_count)
+    fail("Parsing failed even at max reallocation count");
+}
+END_TEST
+
+/* Test that when a namespace is passed through the context mechanism
+ * to an external entity parser, the parsers handle reallocation
+ * failures correctly.  The prefix is exactly the right length to
+ * provoke particular uncommon code paths.
+ */
+START_TEST(test_nsalloc_realloc_long_context_in_dtd) {
+  const char *text1
+      = "<!DOCTYPE "
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        ":doc [\n"
+        "  <!ENTITY First SYSTEM 'foo/First'>\n"
+        "]>\n"
+        "<"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        ":doc xmlns:"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "='foo/Second'>&First;";
+  const char *text2
+      = "</"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        ":doc>";
+  ExtOption options[] = {{XCS("foo/First"), "Hello world"}, {NULL, NULL}};
+  int i;
+  const int max_realloc_count = 20;
+
+  for (i = 0; i < max_realloc_count; i++) {
+    g_reallocation_count = i;
+    XML_SetUserData(g_parser, options);
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_optioner);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text1, (int)strlen(text1), XML_FALSE)
+            != XML_STATUS_ERROR
+        && _XML_Parse_SINGLE_BYTES(g_parser, text2, (int)strlen(text2),
+                                   XML_TRUE)
+               != XML_STATUS_ERROR)
+      break;
+    /* See comment in test_nsalloc_xmlns() */
+    nsalloc_teardown();
+    nsalloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing reallocations");
+  else if (i == max_realloc_count)
+    fail("Parsing failed even at max reallocation count");
+}
+END_TEST
+
+START_TEST(test_nsalloc_long_default_in_ext) {
+  const char *text
+      = "<!DOCTYPE doc [\n"
+        "  <!ATTLIST e a1 CDATA '"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
+        "'>\n"
+        "  <!ENTITY x SYSTEM 'foo'>\n"
+        "]>\n"
+        "<doc>&x;</doc>";
+  ExtOption options[] = {{XCS("foo"), "<e/>"}, {NULL, NULL}};
+  int i;
+  const int max_alloc_count = 50;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetUserData(g_parser, options);
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_optioner);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+
+    /* See comment in test_nsalloc_xmlns() */
+    nsalloc_teardown();
+    nsalloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing allocations");
+  else if (i == max_alloc_count)
+    fail("Parsing failed even at max allocation count");
+}
+END_TEST
+
+START_TEST(test_nsalloc_long_systemid_in_ext) {
+  const char *text
+      = "<!DOCTYPE doc SYSTEM 'foo' [\n"
+        "  <!ENTITY en SYSTEM '"
+        /* 64 characters per line */
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
+        "'>\n"
+        "]>\n"
+        "<doc>&en;</doc>";
+  ExtOption options[] = {
+      {XCS("foo"), "<!ELEMENT e EMPTY>"},
+      {/* clang-format off */
+            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
+            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
+            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
+            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
+            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
+            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
+            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
+            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
+            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
+            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
+            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
+            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
+            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
+            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
+            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
+            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"),
+       /* clang-format on */
+       "<e/>"},
+      {NULL, NULL}};
+  int i;
+  const int max_alloc_count = 55;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetUserData(g_parser, options);
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_optioner);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+
+    /* See comment in test_nsalloc_xmlns() */
+    nsalloc_teardown();
+    nsalloc_setup();
+  }
+  if (i == 0)
+    fail("Parsing worked despite failing allocations");
+  else if (i == max_alloc_count)
+    fail("Parsing failed even at max allocation count");
+}
+END_TEST
+
+/* Test the effects of allocation failure on parsing an element in a
+ * namespace.  Based on test_nsalloc_long_context.
+ */
+START_TEST(test_nsalloc_prefixed_element) {
+  const char *text = "<!DOCTYPE pfx:element SYSTEM 'foo' [\n"
+                     "  <!ATTLIST pfx:element baz ID #REQUIRED>\n"
+                     "  <!ENTITY en SYSTEM 'bar'>\n"
+                     "]>\n"
+                     "<pfx:element xmlns:pfx='http://example.org/' baz='2'>\n"
+                     "&en;"
+                     "</pfx:element>";
+  ExtOption options[] = {
+      {XCS("foo"), "<!ELEMENT e EMPTY>"}, {XCS("bar"), "<e/>"}, {NULL, NULL}};
+  int i;
+  const int max_alloc_count = 70;
+
+  for (i = 0; i < max_alloc_count; i++) {
+    g_allocation_count = i;
+    XML_SetUserData(g_parser, options);
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    XML_SetExternalEntityRefHandler(g_parser, external_entity_optioner);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+
+    /* See comment in test_nsalloc_xmlns() */
+    nsalloc_teardown();
+    nsalloc_setup();
+  }
+  if (i == 0)
+    fail("Success despite failing allocator");
+  else if (i == max_alloc_count)
+    fail("Failed even at full allocation count");
+}
+END_TEST
+
+void
+make_nsalloc_test_case(Suite *s) {
+  TCase *tc_nsalloc = tcase_create("namespace allocation tests");
+
+  suite_add_tcase(s, tc_nsalloc);
+  tcase_add_checked_fixture(tc_nsalloc, nsalloc_setup, nsalloc_teardown);
+
+  tcase_add_test(tc_nsalloc, test_nsalloc_xmlns);
+  tcase_add_test(tc_nsalloc, test_nsalloc_parse_buffer);
+  tcase_add_test(tc_nsalloc, test_nsalloc_long_prefix);
+  tcase_add_test(tc_nsalloc, test_nsalloc_long_uri);
+  tcase_add_test(tc_nsalloc, test_nsalloc_long_attr);
+  tcase_add_test(tc_nsalloc, test_nsalloc_long_attr_prefix);
+  tcase_add_test(tc_nsalloc, test_nsalloc_realloc_attributes);
+  tcase_add_test(tc_nsalloc, test_nsalloc_long_element);
+  tcase_add_test(tc_nsalloc, test_nsalloc_realloc_binding_uri);
+  tcase_add_test(tc_nsalloc, test_nsalloc_realloc_long_prefix);
+  tcase_add_test(tc_nsalloc, test_nsalloc_realloc_longer_prefix);
+  tcase_add_test(tc_nsalloc, test_nsalloc_long_namespace);
+  tcase_add_test(tc_nsalloc, test_nsalloc_less_long_namespace);
+  tcase_add_test(tc_nsalloc, test_nsalloc_long_context);
+  tcase_add_test__if_xml_ge(tc_nsalloc, test_nsalloc_realloc_long_context);
+  tcase_add_test__if_xml_ge(tc_nsalloc, test_nsalloc_realloc_long_context_2);
+  tcase_add_test__if_xml_ge(tc_nsalloc, test_nsalloc_realloc_long_context_3);
+  tcase_add_test__if_xml_ge(tc_nsalloc, test_nsalloc_realloc_long_context_4);
+  tcase_add_test__if_xml_ge(tc_nsalloc, test_nsalloc_realloc_long_context_5);
+  tcase_add_test__if_xml_ge(tc_nsalloc, test_nsalloc_realloc_long_context_6);
+  tcase_add_test__if_xml_ge(tc_nsalloc, test_nsalloc_realloc_long_context_7);
+  tcase_add_test(tc_nsalloc, test_nsalloc_realloc_long_ge_name);
+  tcase_add_test__if_xml_ge(tc_nsalloc,
+                            test_nsalloc_realloc_long_context_in_dtd);
+  tcase_add_test__if_xml_ge(tc_nsalloc, test_nsalloc_long_default_in_ext);
+  tcase_add_test(tc_nsalloc, test_nsalloc_long_systemid_in_ext);
+  tcase_add_test(tc_nsalloc, test_nsalloc_prefixed_element);
+}
diff --git a/tests/nsalloc_tests.h b/tests/nsalloc_tests.h
new file mode 100644 (file)
index 0000000..026a2a2
--- /dev/null
@@ -0,0 +1,56 @@
+/* Tests in the "namespace allocation" test case for the Expat test suite
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
+   Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Joe Orton <jorton@redhat.com>
+   Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2020      Tim Gates <tim.gates@iress.com>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef XML_NSALLOC_TESTS_H
+#  define XML_NSALLOC_TESTS_H
+
+extern void make_nsalloc_test_case(Suite *s);
+
+#endif /* XML_NSALLOC_TESTS_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/nsalloc_tests_cxx.cpp b/tests/nsalloc_tests_cxx.cpp
new file mode 100644 (file)
index 0000000..9ba75d1
--- /dev/null
@@ -0,0 +1,32 @@
+/* C++ compilation harness for the test suite.
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2023 Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "nsalloc_tests.c"
index 915fa52..ecb1c36 100644 (file)
    Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
    Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
    Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
-   Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016-2023 Sebastian Pipping <sebastian@pipping.org>
    Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
    Copyright (c) 2017      Joe Orton <jorton@redhat.com>
    Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
    Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
    Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
    Copyright (c) 2020      Tim Gates <tim.gates@iress.com>
-   Copyright (c) 2021      Dong-hee Na <donghee.na@python.org>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
+   Copyright (c) 2022      Sean McBride <sean@rogue-research.com>
+   Copyright (c) 2023      Sony Corporation / Snild Dolkow <snild@sony.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
    USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
-#include <expat_config.h>
+#include "expat_config.h"
 
-#if defined(NDEBUG)
-#  undef NDEBUG /* because test suite relies on assert(...) at the moment */
-#endif
-
-#include <assert.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <stddef.h> /* ptrdiff_t */
-#include <ctype.h>
-#include <limits.h>
-#include <stdint.h> /* intptr_t uint64_t */
-
-#if ! defined(__cplusplus)
-#  include <stdbool.h>
-#endif
-
-#include "expat.h"
-#include "chardata.h"
-#include "structdata.h"
-#include "internal.h"
-#include "minicheck.h"
-#include "memcheck.h"
-#include "siphash.h"
-#include "ascii.h" /* for ASCII_xxx */
-
-#ifdef XML_LARGE_SIZE
-#  define XML_FMT_INT_MOD "ll"
-#else
-#  define XML_FMT_INT_MOD "l"
-#endif
-
-#ifdef XML_UNICODE_WCHAR_T
-#  define XML_FMT_CHAR "lc"
-#  define XML_FMT_STR "ls"
-#  include <wchar.h>
-#  define xcstrlen(s) wcslen(s)
-#  define xcstrcmp(s, t) wcscmp((s), (t))
-#  define xcstrncmp(s, t, n) wcsncmp((s), (t), (n))
-#  define XCS(s) _XCS(s)
-#  define _XCS(s) L##s
-#else
-#  ifdef XML_UNICODE
-#    error "No support for UTF-16 character without wchar_t in tests"
-#  else
-#    define XML_FMT_CHAR "c"
-#    define XML_FMT_STR "s"
-#    define xcstrlen(s) strlen(s)
-#    define xcstrcmp(s, t) strcmp((s), (t))
-#    define xcstrncmp(s, t, n) strncmp((s), (t), (n))
-#    define XCS(s) s
-#  endif /* XML_UNICODE */
-#endif   /* XML_UNICODE_WCHAR_T */
-
-static XML_Parser g_parser = NULL;
-
-static void
-tcase_add_test__ifdef_xml_dtd(TCase *tc, tcase_test_function test) {
-#ifdef XML_DTD
-  tcase_add_test(tc, test);
-#else
-  UNUSED_P(tc);
-  UNUSED_P(test);
-#endif
-}
-
-static void
-basic_setup(void) {
-  g_parser = XML_ParserCreate(NULL);
-  if (g_parser == NULL)
-    fail("Parser not created.");
-}
-
-static void
-basic_teardown(void) {
-  if (g_parser != NULL) {
-    XML_ParserFree(g_parser);
-    g_parser = NULL;
-  }
-}
-
-/* Generate a failure using the parser state to create an error message;
-   this should be used when the parser reports an error we weren't
-   expecting.
-*/
-static void
-_xml_failure(XML_Parser parser, const char *file, int line) {
-  char buffer[1024];
-  enum XML_Error err = XML_GetErrorCode(parser);
-  sprintf(buffer,
-          "    %d: %" XML_FMT_STR " (line %" XML_FMT_INT_MOD
-          "u, offset %" XML_FMT_INT_MOD "u)\n    reported from %s, line %d\n",
-          err, XML_ErrorString(err), XML_GetCurrentLineNumber(parser),
-          XML_GetCurrentColumnNumber(parser), file, line);
-  _fail_unless(0, file, line, buffer);
-}
-
-static enum XML_Status
-_XML_Parse_SINGLE_BYTES(XML_Parser parser, const char *s, int len,
-                        int isFinal) {
-  enum XML_Status res = XML_STATUS_ERROR;
-  int offset = 0;
-
-  if (len == 0) {
-    return XML_Parse(parser, s, len, isFinal);
-  }
-
-  for (; offset < len; offset++) {
-    const int innerIsFinal = (offset == len - 1) && isFinal;
-    const char c = s[offset]; /* to help out-of-bounds detection */
-    res = XML_Parse(parser, &c, sizeof(char), innerIsFinal);
-    if (res != XML_STATUS_OK) {
-      return res;
-    }
-  }
-  return res;
-}
-
-#define xml_failure(parser) _xml_failure((parser), __FILE__, __LINE__)
-
-static void
-_expect_failure(const char *text, enum XML_Error errorCode,
-                const char *errorMessage, const char *file, int lineno) {
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_OK)
-    /* Hackish use of _fail_unless() macro, but let's us report
-       the right filename and line number. */
-    _fail_unless(0, file, lineno, errorMessage);
-  if (XML_GetErrorCode(g_parser) != errorCode)
-    _xml_failure(g_parser, file, lineno);
-}
-
-#define expect_failure(text, errorCode, errorMessage)                          \
-  _expect_failure((text), (errorCode), (errorMessage), __FILE__, __LINE__)
-
-/* Dummy handlers for when we need to set a handler to tickle a bug,
-   but it doesn't need to do anything.
-*/
-static unsigned long dummy_handler_flags = 0;
-
-#define DUMMY_START_DOCTYPE_HANDLER_FLAG (1UL << 0)
-#define DUMMY_END_DOCTYPE_HANDLER_FLAG (1UL << 1)
-#define DUMMY_ENTITY_DECL_HANDLER_FLAG (1UL << 2)
-#define DUMMY_NOTATION_DECL_HANDLER_FLAG (1UL << 3)
-#define DUMMY_ELEMENT_DECL_HANDLER_FLAG (1UL << 4)
-#define DUMMY_ATTLIST_DECL_HANDLER_FLAG (1UL << 5)
-#define DUMMY_COMMENT_HANDLER_FLAG (1UL << 6)
-#define DUMMY_PI_HANDLER_FLAG (1UL << 7)
-#define DUMMY_START_ELEMENT_HANDLER_FLAG (1UL << 8)
-#define DUMMY_START_CDATA_HANDLER_FLAG (1UL << 9)
-#define DUMMY_END_CDATA_HANDLER_FLAG (1UL << 10)
-#define DUMMY_UNPARSED_ENTITY_DECL_HANDLER_FLAG (1UL << 11)
-#define DUMMY_START_NS_DECL_HANDLER_FLAG (1UL << 12)
-#define DUMMY_END_NS_DECL_HANDLER_FLAG (1UL << 13)
-#define DUMMY_START_DOCTYPE_DECL_HANDLER_FLAG (1UL << 14)
-#define DUMMY_END_DOCTYPE_DECL_HANDLER_FLAG (1UL << 15)
-#define DUMMY_SKIP_HANDLER_FLAG (1UL << 16)
-#define DUMMY_DEFAULT_HANDLER_FLAG (1UL << 17)
-
-static void XMLCALL
-dummy_xdecl_handler(void *userData, const XML_Char *version,
-                    const XML_Char *encoding, int standalone) {
-  UNUSED_P(userData);
-  UNUSED_P(version);
-  UNUSED_P(encoding);
-  UNUSED_P(standalone);
-}
-
-static void XMLCALL
-dummy_start_doctype_handler(void *userData, const XML_Char *doctypeName,
-                            const XML_Char *sysid, const XML_Char *pubid,
-                            int has_internal_subset) {
-  UNUSED_P(userData);
-  UNUSED_P(doctypeName);
-  UNUSED_P(sysid);
-  UNUSED_P(pubid);
-  UNUSED_P(has_internal_subset);
-  dummy_handler_flags |= DUMMY_START_DOCTYPE_HANDLER_FLAG;
-}
-
-static void XMLCALL
-dummy_end_doctype_handler(void *userData) {
-  UNUSED_P(userData);
-  dummy_handler_flags |= DUMMY_END_DOCTYPE_HANDLER_FLAG;
-}
-
-static void XMLCALL
-dummy_entity_decl_handler(void *userData, const XML_Char *entityName,
-                          int is_parameter_entity, const XML_Char *value,
-                          int value_length, const XML_Char *base,
-                          const XML_Char *systemId, const XML_Char *publicId,
-                          const XML_Char *notationName) {
-  UNUSED_P(userData);
-  UNUSED_P(entityName);
-  UNUSED_P(is_parameter_entity);
-  UNUSED_P(value);
-  UNUSED_P(value_length);
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  UNUSED_P(notationName);
-  dummy_handler_flags |= DUMMY_ENTITY_DECL_HANDLER_FLAG;
-}
-
-static void XMLCALL
-dummy_notation_decl_handler(void *userData, const XML_Char *notationName,
-                            const XML_Char *base, const XML_Char *systemId,
-                            const XML_Char *publicId) {
-  UNUSED_P(userData);
-  UNUSED_P(notationName);
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  dummy_handler_flags |= DUMMY_NOTATION_DECL_HANDLER_FLAG;
-}
-
-static void XMLCALL
-dummy_element_decl_handler(void *userData, const XML_Char *name,
-                           XML_Content *model) {
-  UNUSED_P(userData);
-  UNUSED_P(name);
-  /* The content model must be freed by the handler.  Unfortunately
-   * we cannot pass the parser as the userData because this is used
-   * with other handlers that require other userData.
-   */
-  XML_FreeContentModel(g_parser, model);
-  dummy_handler_flags |= DUMMY_ELEMENT_DECL_HANDLER_FLAG;
-}
-
-static void XMLCALL
-dummy_attlist_decl_handler(void *userData, const XML_Char *elname,
-                           const XML_Char *attname, const XML_Char *att_type,
-                           const XML_Char *dflt, int isrequired) {
-  UNUSED_P(userData);
-  UNUSED_P(elname);
-  UNUSED_P(attname);
-  UNUSED_P(att_type);
-  UNUSED_P(dflt);
-  UNUSED_P(isrequired);
-  dummy_handler_flags |= DUMMY_ATTLIST_DECL_HANDLER_FLAG;
-}
-
-static void XMLCALL
-dummy_comment_handler(void *userData, const XML_Char *data) {
-  UNUSED_P(userData);
-  UNUSED_P(data);
-  dummy_handler_flags |= DUMMY_COMMENT_HANDLER_FLAG;
-}
-
-static void XMLCALL
-dummy_pi_handler(void *userData, const XML_Char *target, const XML_Char *data) {
-  UNUSED_P(userData);
-  UNUSED_P(target);
-  UNUSED_P(data);
-  dummy_handler_flags |= DUMMY_PI_HANDLER_FLAG;
-}
-
-static void XMLCALL
-dummy_start_element(void *userData, const XML_Char *name,
-                    const XML_Char **atts) {
-  UNUSED_P(userData);
-  UNUSED_P(name);
-  UNUSED_P(atts);
-  dummy_handler_flags |= DUMMY_START_ELEMENT_HANDLER_FLAG;
-}
-
-static void XMLCALL
-dummy_end_element(void *userData, const XML_Char *name) {
-  UNUSED_P(userData);
-  UNUSED_P(name);
-}
-
-static void XMLCALL
-dummy_start_cdata_handler(void *userData) {
-  UNUSED_P(userData);
-  dummy_handler_flags |= DUMMY_START_CDATA_HANDLER_FLAG;
-}
-
-static void XMLCALL
-dummy_end_cdata_handler(void *userData) {
-  UNUSED_P(userData);
-  dummy_handler_flags |= DUMMY_END_CDATA_HANDLER_FLAG;
-}
-
-static void XMLCALL
-dummy_cdata_handler(void *userData, const XML_Char *s, int len) {
-  UNUSED_P(userData);
-  UNUSED_P(s);
-  UNUSED_P(len);
-}
-
-static void XMLCALL
-dummy_start_namespace_decl_handler(void *userData, const XML_Char *prefix,
-                                   const XML_Char *uri) {
-  UNUSED_P(userData);
-  UNUSED_P(prefix);
-  UNUSED_P(uri);
-  dummy_handler_flags |= DUMMY_START_NS_DECL_HANDLER_FLAG;
-}
-
-static void XMLCALL
-dummy_end_namespace_decl_handler(void *userData, const XML_Char *prefix) {
-  UNUSED_P(userData);
-  UNUSED_P(prefix);
-  dummy_handler_flags |= DUMMY_END_NS_DECL_HANDLER_FLAG;
-}
-
-/* This handler is obsolete, but while the code exists we should
- * ensure that dealing with the handler is covered by tests.
- */
-static void XMLCALL
-dummy_unparsed_entity_decl_handler(void *userData, const XML_Char *entityName,
-                                   const XML_Char *base,
-                                   const XML_Char *systemId,
-                                   const XML_Char *publicId,
-                                   const XML_Char *notationName) {
-  UNUSED_P(userData);
-  UNUSED_P(entityName);
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  UNUSED_P(notationName);
-  dummy_handler_flags |= DUMMY_UNPARSED_ENTITY_DECL_HANDLER_FLAG;
-}
-
-static void XMLCALL
-dummy_default_handler(void *userData, const XML_Char *s, int len) {
-  UNUSED_P(userData);
-  UNUSED_P(s);
-  UNUSED_P(len);
-}
-
-static void XMLCALL
-dummy_start_doctype_decl_handler(void *userData, const XML_Char *doctypeName,
-                                 const XML_Char *sysid, const XML_Char *pubid,
-                                 int has_internal_subset) {
-  UNUSED_P(userData);
-  UNUSED_P(doctypeName);
-  UNUSED_P(sysid);
-  UNUSED_P(pubid);
-  UNUSED_P(has_internal_subset);
-  dummy_handler_flags |= DUMMY_START_DOCTYPE_DECL_HANDLER_FLAG;
-}
-
-static void XMLCALL
-dummy_end_doctype_decl_handler(void *userData) {
-  UNUSED_P(userData);
-  dummy_handler_flags |= DUMMY_END_DOCTYPE_DECL_HANDLER_FLAG;
-}
-
-static void XMLCALL
-dummy_skip_handler(void *userData, const XML_Char *entityName,
-                   int is_parameter_entity) {
-  UNUSED_P(userData);
-  UNUSED_P(entityName);
-  UNUSED_P(is_parameter_entity);
-  dummy_handler_flags |= DUMMY_SKIP_HANDLER_FLAG;
-}
-
-/* Useful external entity handler */
-typedef struct ExtOption {
-  const XML_Char *system_id;
-  const char *parse_text;
-} ExtOption;
-
-static int XMLCALL
-external_entity_optioner(XML_Parser parser, const XML_Char *context,
-                         const XML_Char *base, const XML_Char *systemId,
-                         const XML_Char *publicId) {
-  ExtOption *options = (ExtOption *)XML_GetUserData(parser);
-  XML_Parser ext_parser;
-
-  UNUSED_P(base);
-  UNUSED_P(publicId);
-  while (options->parse_text != NULL) {
-    if (! xcstrcmp(systemId, options->system_id)) {
-      enum XML_Status rc;
-      ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-      if (ext_parser == NULL)
-        return XML_STATUS_ERROR;
-      rc = _XML_Parse_SINGLE_BYTES(ext_parser, options->parse_text,
-                                   (int)strlen(options->parse_text), XML_TRUE);
-      XML_ParserFree(ext_parser);
-      return rc;
-    }
-    options++;
-  }
-  fail("No suitable option found");
-  return XML_STATUS_ERROR;
-}
-
-/*
- * Parameter entity evaluation support.
- */
-#define ENTITY_MATCH_FAIL (-1)
-#define ENTITY_MATCH_NOT_FOUND (0)
-#define ENTITY_MATCH_SUCCESS (1)
-static const XML_Char *entity_name_to_match = NULL;
-static const XML_Char *entity_value_to_match = NULL;
-static int entity_match_flag = ENTITY_MATCH_NOT_FOUND;
-
-static void XMLCALL
-param_entity_match_handler(void *userData, const XML_Char *entityName,
-                           int is_parameter_entity, const XML_Char *value,
-                           int value_length, const XML_Char *base,
-                           const XML_Char *systemId, const XML_Char *publicId,
-                           const XML_Char *notationName) {
-  UNUSED_P(userData);
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  UNUSED_P(notationName);
-  if (! is_parameter_entity || entity_name_to_match == NULL
-      || entity_value_to_match == NULL) {
-    return;
-  }
-  if (! xcstrcmp(entityName, entity_name_to_match)) {
-    /* The cast here is safe because we control the horizontal and
-     * the vertical, and we therefore know our strings are never
-     * going to overflow an int.
-     */
-    if (value_length != (int)xcstrlen(entity_value_to_match)
-        || xcstrncmp(value, entity_value_to_match, value_length)) {
-      entity_match_flag = ENTITY_MATCH_FAIL;
-    } else {
-      entity_match_flag = ENTITY_MATCH_SUCCESS;
-    }
-  }
-  /* Else leave the match flag alone */
-}
-
-/*
- * Character & encoding tests.
- */
-
-START_TEST(test_nul_byte) {
-  char text[] = "<doc>\0</doc>";
-
-  /* test that a NUL byte (in US-ASCII data) is an error */
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_OK)
-    fail("Parser did not report error on NUL-byte.");
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_INVALID_TOKEN)
-    xml_failure(g_parser);
-}
-END_TEST
-
-START_TEST(test_u0000_char) {
-  /* test that a NUL byte (in US-ASCII data) is an error */
-  expect_failure("<doc>&#0;</doc>", XML_ERROR_BAD_CHAR_REF,
-                 "Parser did not report error on NUL-byte.");
-}
-END_TEST
-
-START_TEST(test_siphash_self) {
-  if (! sip24_valid())
-    fail("SipHash self-test failed");
-}
-END_TEST
-
-START_TEST(test_siphash_spec) {
-  /* https://131002.net/siphash/siphash.pdf (page 19, "Test values") */
-  const char message[] = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09"
-                         "\x0a\x0b\x0c\x0d\x0e";
-  const size_t len = sizeof(message) - 1;
-  const uint64_t expected = _SIP_ULL(0xa129ca61U, 0x49be45e5U);
-  struct siphash state;
-  struct sipkey key;
-
-  sip_tokey(&key, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09"
-                  "\x0a\x0b\x0c\x0d\x0e\x0f");
-  sip24_init(&state, &key);
-
-  /* Cover spread across calls */
-  sip24_update(&state, message, 4);
-  sip24_update(&state, message + 4, len - 4);
-
-  /* Cover null length */
-  sip24_update(&state, message, 0);
-
-  if (sip24_final(&state) != expected)
-    fail("sip24_final failed spec test\n");
-
-  /* Cover wrapper */
-  if (siphash24(message, len, &key) != expected)
-    fail("siphash24 failed spec test\n");
-}
-END_TEST
-
-START_TEST(test_bom_utf8) {
-  /* This test is really just making sure we don't core on a UTF-8 BOM. */
-  const char *text = "\357\273\277<e/>";
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-START_TEST(test_bom_utf16_be) {
-  char text[] = "\376\377\0<\0e\0/\0>";
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-START_TEST(test_bom_utf16_le) {
-  char text[] = "\377\376<\0e\0/\0>\0";
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Parse whole buffer at once to exercise a different code path */
-START_TEST(test_nobom_utf16_le) {
-  char text[] = " \0<\0e\0/\0>\0";
-
-  if (XML_Parse(g_parser, text, sizeof(text) - 1, XML_TRUE) == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-static void XMLCALL
-accumulate_characters(void *userData, const XML_Char *s, int len) {
-  CharData_AppendXMLChars((CharData *)userData, s, len);
-}
-
-static void XMLCALL
-accumulate_attribute(void *userData, const XML_Char *name,
-                     const XML_Char **atts) {
-  CharData *storage = (CharData *)userData;
-  UNUSED_P(name);
-  /* Check there are attributes to deal with */
-  if (atts == NULL)
-    return;
-
-  while (storage->count < 0 && atts[0] != NULL) {
-    /* "accumulate" the value of the first attribute we see */
-    CharData_AppendXMLChars(storage, atts[1], -1);
-    atts += 2;
-  }
-}
-
-static void
-_run_character_check(const char *text, const XML_Char *expected,
-                     const char *file, int line) {
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  XML_SetCharacterDataHandler(g_parser, accumulate_characters);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    _xml_failure(g_parser, file, line);
-  CharData_CheckXMLChars(&storage, expected);
-}
-
-#define run_character_check(text, expected)                                    \
-  _run_character_check(text, expected, __FILE__, __LINE__)
-
-static void
-_run_attribute_check(const char *text, const XML_Char *expected,
-                     const char *file, int line) {
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  XML_SetStartElementHandler(g_parser, accumulate_attribute);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    _xml_failure(g_parser, file, line);
-  CharData_CheckXMLChars(&storage, expected);
-}
-
-#define run_attribute_check(text, expected)                                    \
-  _run_attribute_check(text, expected, __FILE__, __LINE__)
-
-typedef struct ExtTest {
-  const char *parse_text;
-  const XML_Char *encoding;
-  CharData *storage;
-} ExtTest;
-
-static void XMLCALL
-ext_accumulate_characters(void *userData, const XML_Char *s, int len) {
-  ExtTest *test_data = (ExtTest *)userData;
-  accumulate_characters(test_data->storage, s, len);
-}
-
-static void
-_run_ext_character_check(const char *text, ExtTest *test_data,
-                         const XML_Char *expected, const char *file, int line) {
-  CharData *const storage = (CharData *)malloc(sizeof(CharData));
-
-  CharData_Init(storage);
-  test_data->storage = storage;
-  XML_SetUserData(g_parser, test_data);
-  XML_SetCharacterDataHandler(g_parser, ext_accumulate_characters);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    _xml_failure(g_parser, file, line);
-  CharData_CheckXMLChars(storage, expected);
-
-  free(storage);
-}
-
-#define run_ext_character_check(text, test_data, expected)                     \
-  _run_ext_character_check(text, test_data, expected, __FILE__, __LINE__)
-
-/* Regression test for SF bug #491986. */
-START_TEST(test_danish_latin1) {
-  const char *text = "<?xml version='1.0' encoding='iso-8859-1'?>\n"
-                     "<e>J\xF8rgen \xE6\xF8\xE5\xC6\xD8\xC5</e>";
-#ifdef XML_UNICODE
-  const XML_Char *expected
-      = XCS("J\x00f8rgen \x00e6\x00f8\x00e5\x00c6\x00d8\x00c5");
-#else
-  const XML_Char *expected
-      = XCS("J\xC3\xB8rgen \xC3\xA6\xC3\xB8\xC3\xA5\xC3\x86\xC3\x98\xC3\x85");
-#endif
-  run_character_check(text, expected);
-}
-END_TEST
-
-/* Regression test for SF bug #514281. */
-START_TEST(test_french_charref_hexidecimal) {
-  const char *text = "<?xml version='1.0' encoding='iso-8859-1'?>\n"
-                     "<doc>&#xE9;&#xE8;&#xE0;&#xE7;&#xEA;&#xC8;</doc>";
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("\x00e9\x00e8\x00e0\x00e7\x00ea\x00c8");
-#else
-  const XML_Char *expected
-      = XCS("\xC3\xA9\xC3\xA8\xC3\xA0\xC3\xA7\xC3\xAA\xC3\x88");
-#endif
-  run_character_check(text, expected);
-}
-END_TEST
-
-START_TEST(test_french_charref_decimal) {
-  const char *text = "<?xml version='1.0' encoding='iso-8859-1'?>\n"
-                     "<doc>&#233;&#232;&#224;&#231;&#234;&#200;</doc>";
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("\x00e9\x00e8\x00e0\x00e7\x00ea\x00c8");
-#else
-  const XML_Char *expected
-      = XCS("\xC3\xA9\xC3\xA8\xC3\xA0\xC3\xA7\xC3\xAA\xC3\x88");
-#endif
-  run_character_check(text, expected);
-}
-END_TEST
-
-START_TEST(test_french_latin1) {
-  const char *text = "<?xml version='1.0' encoding='iso-8859-1'?>\n"
-                     "<doc>\xE9\xE8\xE0\xE7\xEa\xC8</doc>";
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("\x00e9\x00e8\x00e0\x00e7\x00ea\x00c8");
-#else
-  const XML_Char *expected
-      = XCS("\xC3\xA9\xC3\xA8\xC3\xA0\xC3\xA7\xC3\xAA\xC3\x88");
-#endif
-  run_character_check(text, expected);
-}
-END_TEST
-
-START_TEST(test_french_utf8) {
-  const char *text = "<?xml version='1.0' encoding='utf-8'?>\n"
-                     "<doc>\xC3\xA9</doc>";
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("\x00e9");
-#else
-  const XML_Char *expected = XCS("\xC3\xA9");
-#endif
-  run_character_check(text, expected);
-}
-END_TEST
-
-/* Regression test for SF bug #600479.
-   XXX There should be a test that exercises all legal XML Unicode
-   characters as PCDATA and attribute value content, and XML Name
-   characters as part of element and attribute names.
-*/
-START_TEST(test_utf8_false_rejection) {
-  const char *text = "<doc>\xEF\xBA\xBF</doc>";
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("\xfebf");
-#else
-  const XML_Char *expected = XCS("\xEF\xBA\xBF");
-#endif
-  run_character_check(text, expected);
-}
-END_TEST
-
-/* Regression test for SF bug #477667.
-   This test assures that any 8-bit character followed by a 7-bit
-   character will not be mistakenly interpreted as a valid UTF-8
-   sequence.
-*/
-START_TEST(test_illegal_utf8) {
-  char text[100];
-  int i;
-
-  for (i = 128; i <= 255; ++i) {
-    sprintf(text, "<e>%ccd</e>", i);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        == XML_STATUS_OK) {
-      sprintf(text, "expected token error for '%c' (ordinal %d) in UTF-8 text",
-              i, i);
-      fail(text);
-    } else if (XML_GetErrorCode(g_parser) != XML_ERROR_INVALID_TOKEN)
-      xml_failure(g_parser);
-    /* Reset the parser since we use the same parser repeatedly. */
-    XML_ParserReset(g_parser, NULL);
-  }
-}
-END_TEST
-
-/* Examples, not masks: */
-#define UTF8_LEAD_1 "\x7f" /* 0b01111111 */
-#define UTF8_LEAD_2 "\xdf" /* 0b11011111 */
-#define UTF8_LEAD_3 "\xef" /* 0b11101111 */
-#define UTF8_LEAD_4 "\xf7" /* 0b11110111 */
-#define UTF8_FOLLOW "\xbf" /* 0b10111111 */
-
-START_TEST(test_utf8_auto_align) {
-  struct TestCase {
-    ptrdiff_t expectedMovementInChars;
-    const char *input;
-  };
-
-  struct TestCase cases[] = {
-      {00, ""},
-
-      {00, UTF8_LEAD_1},
-
-      {-1, UTF8_LEAD_2},
-      {00, UTF8_LEAD_2 UTF8_FOLLOW},
-
-      {-1, UTF8_LEAD_3},
-      {-2, UTF8_LEAD_3 UTF8_FOLLOW},
-      {00, UTF8_LEAD_3 UTF8_FOLLOW UTF8_FOLLOW},
-
-      {-1, UTF8_LEAD_4},
-      {-2, UTF8_LEAD_4 UTF8_FOLLOW},
-      {-3, UTF8_LEAD_4 UTF8_FOLLOW UTF8_FOLLOW},
-      {00, UTF8_LEAD_4 UTF8_FOLLOW UTF8_FOLLOW UTF8_FOLLOW},
-  };
-
-  size_t i = 0;
-  bool success = true;
-  for (; i < sizeof(cases) / sizeof(*cases); i++) {
-    const char *fromLim = cases[i].input + strlen(cases[i].input);
-    const char *const fromLimInitially = fromLim;
-    ptrdiff_t actualMovementInChars;
-
-    _INTERNAL_trim_to_complete_utf8_characters(cases[i].input, &fromLim);
-
-    actualMovementInChars = (fromLim - fromLimInitially);
-    if (actualMovementInChars != cases[i].expectedMovementInChars) {
-      size_t j = 0;
-      success = false;
-      printf("[-] UTF-8 case %2u: Expected movement by %2d chars"
-             ", actually moved by %2d chars: \"",
-             (unsigned)(i + 1), (int)cases[i].expectedMovementInChars,
-             (int)actualMovementInChars);
-      for (; j < strlen(cases[i].input); j++) {
-        printf("\\x%02x", (unsigned char)cases[i].input[j]);
-      }
-      printf("\"\n");
-    }
-  }
-
-  if (! success) {
-    fail("UTF-8 auto-alignment is not bullet-proof\n");
-  }
-}
-END_TEST
-
-START_TEST(test_utf16) {
-  /* <?xml version="1.0" encoding="UTF-16"?>
-   *  <doc a='123'>some {A} text</doc>
-   *
-   * where {A} is U+FF21, FULLWIDTH LATIN CAPITAL LETTER A
-   */
-  char text[]
-      = "\000<\000?\000x\000m\000\154\000 \000v\000e\000r\000s\000i\000o"
-        "\000n\000=\000'\0001\000.\000\060\000'\000 \000e\000n\000c\000o"
-        "\000d\000i\000n\000g\000=\000'\000U\000T\000F\000-\0001\000\066"
-        "\000'\000?\000>\000\n"
-        "\000<\000d\000o\000c\000 \000a\000=\000'\0001\0002\0003\000'\000>"
-        "\000s\000o\000m\000e\000 \xff\x21\000 \000t\000e\000x\000t\000"
-        "<\000/\000d\000o\000c\000>";
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("some \xff21 text");
-#else
-  const XML_Char *expected = XCS("some \357\274\241 text");
-#endif
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  XML_SetCharacterDataHandler(g_parser, accumulate_characters);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-START_TEST(test_utf16_le_epilog_newline) {
-  unsigned int first_chunk_bytes = 17;
-  char text[] = "\xFF\xFE"                  /* BOM */
-                "<\000e\000/\000>\000"      /* document element */
-                "\r\000\n\000\r\000\n\000"; /* epilog */
-
-  if (first_chunk_bytes >= sizeof(text) - 1)
-    fail("bad value of first_chunk_bytes");
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, first_chunk_bytes, XML_FALSE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  else {
-    enum XML_Status rc;
-    rc = _XML_Parse_SINGLE_BYTES(g_parser, text + first_chunk_bytes,
-                                 sizeof(text) - first_chunk_bytes - 1,
-                                 XML_TRUE);
-    if (rc == XML_STATUS_ERROR)
-      xml_failure(g_parser);
-  }
-}
-END_TEST
-
-/* Test that an outright lie in the encoding is faulted */
-START_TEST(test_not_utf16) {
-  const char *text = "<?xml version='1.0' encoding='utf-16'?>"
-                     "<doc>Hi</doc>";
-
-  /* Use a handler to provoke the appropriate code paths */
-  XML_SetXmlDeclHandler(g_parser, dummy_xdecl_handler);
-  expect_failure(text, XML_ERROR_INCORRECT_ENCODING,
-                 "UTF-16 declared in UTF-8 not faulted");
-}
-END_TEST
-
-/* Test that an unknown encoding is rejected */
-START_TEST(test_bad_encoding) {
-  const char *text = "<doc>Hi</doc>";
-
-  if (! XML_SetEncoding(g_parser, XCS("unknown-encoding")))
-    fail("XML_SetEncoding failed");
-  expect_failure(text, XML_ERROR_UNKNOWN_ENCODING,
-                 "Unknown encoding not faulted");
-}
-END_TEST
-
-/* Regression test for SF bug #481609, #774028. */
-START_TEST(test_latin1_umlauts) {
-  const char *text
-      = "<?xml version='1.0' encoding='iso-8859-1'?>\n"
-        "<e a='\xE4 \xF6 \xFC &#228; &#246; &#252; &#x00E4; &#x0F6; &#xFC; >'\n"
-        "  >\xE4 \xF6 \xFC &#228; &#246; &#252; &#x00E4; &#x0F6; &#xFC; ></e>";
-#ifdef XML_UNICODE
-  /* Expected results in UTF-16 */
-  const XML_Char *expected = XCS("\x00e4 \x00f6 \x00fc ")
-      XCS("\x00e4 \x00f6 \x00fc ") XCS("\x00e4 \x00f6 \x00fc >");
-#else
-  /* Expected results in UTF-8 */
-  const XML_Char *expected = XCS("\xC3\xA4 \xC3\xB6 \xC3\xBC ")
-      XCS("\xC3\xA4 \xC3\xB6 \xC3\xBC ") XCS("\xC3\xA4 \xC3\xB6 \xC3\xBC >");
-#endif
-
-  run_character_check(text, expected);
-  XML_ParserReset(g_parser, NULL);
-  run_attribute_check(text, expected);
-  /* Repeat with a default handler */
-  XML_ParserReset(g_parser, NULL);
-  XML_SetDefaultHandler(g_parser, dummy_default_handler);
-  run_character_check(text, expected);
-  XML_ParserReset(g_parser, NULL);
-  XML_SetDefaultHandler(g_parser, dummy_default_handler);
-  run_attribute_check(text, expected);
-}
-END_TEST
-
-/* Test that an element name with a 4-byte UTF-8 character is rejected */
-START_TEST(test_long_utf8_character) {
-  const char *text
-      = "<?xml version='1.0' encoding='utf-8'?>\n"
-        /* 0xf0 0x90 0x80 0x80 = U+10000, the first Linear B character */
-        "<do\xf0\x90\x80\x80/>";
-  expect_failure(text, XML_ERROR_INVALID_TOKEN,
-                 "4-byte UTF-8 character in element name not faulted");
-}
-END_TEST
-
-/* Test that a long latin-1 attribute (too long to convert in one go)
- * is correctly converted
- */
-START_TEST(test_long_latin1_attribute) {
-  const char *text
-      = "<?xml version='1.0' encoding='iso-8859-1'?>\n"
-        "<doc att='"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        /* Last character splits across a buffer boundary */
-        "\xe4'>\n</doc>";
-
-  const XML_Char *expected =
-      /* 64 characters per line */
-      /* clang-format off */
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO")
-  /* clang-format on */
-#ifdef XML_UNICODE
-                                                  XCS("\x00e4");
-#else
-                                                  XCS("\xc3\xa4");
-#endif
-
-  run_attribute_check(text, expected);
-}
-END_TEST
-
-/* Test that a long ASCII attribute (too long to convert in one go)
- * is correctly converted
- */
-START_TEST(test_long_ascii_attribute) {
-  const char *text
-      = "<?xml version='1.0' encoding='us-ascii'?>\n"
-        "<doc att='"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "01234'>\n</doc>";
-  const XML_Char *expected =
-      /* 64 characters per line */
-      /* clang-format off */
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("01234");
-  /* clang-format on */
-
-  run_attribute_check(text, expected);
-}
-END_TEST
-
-/* Regression test #1 for SF bug #653180. */
-START_TEST(test_line_number_after_parse) {
-  const char *text = "<tag>\n"
-                     "\n"
-                     "\n</tag>";
-  XML_Size lineno;
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  lineno = XML_GetCurrentLineNumber(g_parser);
-  if (lineno != 4) {
-    char buffer[100];
-    sprintf(buffer, "expected 4 lines, saw %" XML_FMT_INT_MOD "u", lineno);
-    fail(buffer);
-  }
-}
-END_TEST
-
-/* Regression test #2 for SF bug #653180. */
-START_TEST(test_column_number_after_parse) {
-  const char *text = "<tag></tag>";
-  XML_Size colno;
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  colno = XML_GetCurrentColumnNumber(g_parser);
-  if (colno != 11) {
-    char buffer[100];
-    sprintf(buffer, "expected 11 columns, saw %" XML_FMT_INT_MOD "u", colno);
-    fail(buffer);
-  }
-}
-END_TEST
-
-#define STRUCT_START_TAG 0
-#define STRUCT_END_TAG 1
-static void XMLCALL
-start_element_event_handler2(void *userData, const XML_Char *name,
-                             const XML_Char **attr) {
-  StructData *storage = (StructData *)userData;
-  UNUSED_P(attr);
-  StructData_AddItem(storage, name, XML_GetCurrentColumnNumber(g_parser),
-                     XML_GetCurrentLineNumber(g_parser), STRUCT_START_TAG);
-}
-
-static void XMLCALL
-end_element_event_handler2(void *userData, const XML_Char *name) {
-  StructData *storage = (StructData *)userData;
-  StructData_AddItem(storage, name, XML_GetCurrentColumnNumber(g_parser),
-                     XML_GetCurrentLineNumber(g_parser), STRUCT_END_TAG);
-}
-
-/* Regression test #3 for SF bug #653180. */
-START_TEST(test_line_and_column_numbers_inside_handlers) {
-  const char *text = "<a>\n"      /* Unix end-of-line */
-                     "  <b>\r\n"  /* Windows end-of-line */
-                     "    <c/>\r" /* Mac OS end-of-line */
-                     "  </b>\n"
-                     "  <d>\n"
-                     "    <f/>\n"
-                     "  </d>\n"
-                     "</a>";
-  const StructDataEntry expected[]
-      = {{XCS("a"), 0, 1, STRUCT_START_TAG}, {XCS("b"), 2, 2, STRUCT_START_TAG},
-         {XCS("c"), 4, 3, STRUCT_START_TAG}, {XCS("c"), 8, 3, STRUCT_END_TAG},
-         {XCS("b"), 2, 4, STRUCT_END_TAG},   {XCS("d"), 2, 5, STRUCT_START_TAG},
-         {XCS("f"), 4, 6, STRUCT_START_TAG}, {XCS("f"), 8, 6, STRUCT_END_TAG},
-         {XCS("d"), 2, 7, STRUCT_END_TAG},   {XCS("a"), 0, 8, STRUCT_END_TAG}};
-  const int expected_count = sizeof(expected) / sizeof(StructDataEntry);
-  StructData storage;
-
-  StructData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  XML_SetStartElementHandler(g_parser, start_element_event_handler2);
-  XML_SetEndElementHandler(g_parser, end_element_event_handler2);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-
-  StructData_CheckItems(&storage, expected, expected_count);
-  StructData_Dispose(&storage);
-}
-END_TEST
-
-/* Regression test #4 for SF bug #653180. */
-START_TEST(test_line_number_after_error) {
-  const char *text = "<a>\n"
-                     "  <b>\n"
-                     "  </a>"; /* missing </b> */
-  XML_Size lineno;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
-      != XML_STATUS_ERROR)
-    fail("Expected a parse error");
-
-  lineno = XML_GetCurrentLineNumber(g_parser);
-  if (lineno != 3) {
-    char buffer[100];
-    sprintf(buffer, "expected 3 lines, saw %" XML_FMT_INT_MOD "u", lineno);
-    fail(buffer);
-  }
-}
-END_TEST
-
-/* Regression test #5 for SF bug #653180. */
-START_TEST(test_column_number_after_error) {
-  const char *text = "<a>\n"
-                     "  <b>\n"
-                     "  </a>"; /* missing </b> */
-  XML_Size colno;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
-      != XML_STATUS_ERROR)
-    fail("Expected a parse error");
-
-  colno = XML_GetCurrentColumnNumber(g_parser);
-  if (colno != 4) {
-    char buffer[100];
-    sprintf(buffer, "expected 4 columns, saw %" XML_FMT_INT_MOD "u", colno);
-    fail(buffer);
-  }
-}
-END_TEST
-
-/* Regression test for SF bug #478332. */
-START_TEST(test_really_long_lines) {
-  /* This parses an input line longer than INIT_DATA_BUF_SIZE
-     characters long (defined to be 1024 in xmlparse.c).  We take a
-     really cheesy approach to building the input buffer, because
-     this avoids writing bugs in buffer-filling code.
-  */
-  const char *text
-      = "<e>"
-        /* 64 chars */
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        /* until we have at least 1024 characters on the line: */
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "</e>";
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test cdata processing across a buffer boundary */
-START_TEST(test_really_long_encoded_lines) {
-  /* As above, except that we want to provoke an output buffer
-   * overflow with a non-trivial encoding.  For this we need to pass
-   * the whole cdata in one go, not byte-by-byte.
-   */
-  void *buffer;
-  const char *text
-      = "<?xml version='1.0' encoding='iso-8859-1'?>"
-        "<e>"
-        /* 64 chars */
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        /* until we have at least 1024 characters on the line: */
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+"
-        "</e>";
-  int parse_len = (int)strlen(text);
-
-  /* Need a cdata handler to provoke the code path we want to test */
-  XML_SetCharacterDataHandler(g_parser, dummy_cdata_handler);
-  buffer = XML_GetBuffer(g_parser, parse_len);
-  if (buffer == NULL)
-    fail("Could not allocate parse buffer");
-  assert(buffer != NULL);
-  memcpy(buffer, text, parse_len);
-  if (XML_ParseBuffer(g_parser, parse_len, XML_TRUE) == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/*
- * Element event tests.
- */
-
-static void XMLCALL
-start_element_event_handler(void *userData, const XML_Char *name,
-                            const XML_Char **atts) {
-  UNUSED_P(atts);
-  CharData_AppendXMLChars((CharData *)userData, name, -1);
-}
-
-static void XMLCALL
-end_element_event_handler(void *userData, const XML_Char *name) {
-  CharData *storage = (CharData *)userData;
-  CharData_AppendXMLChars(storage, XCS("/"), 1);
-  CharData_AppendXMLChars(storage, name, -1);
-}
-
-START_TEST(test_end_element_events) {
-  const char *text = "<a><b><c/></b><d><f/></d></a>";
-  const XML_Char *expected = XCS("/c/b/f/d/a");
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  XML_SetEndElementHandler(g_parser, end_element_event_handler);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-/*
- * Attribute tests.
- */
-
-/* Helpers used by the following test; this checks any "attr" and "refs"
-   attributes to make sure whitespace has been normalized.
-
-   Return true if whitespace has been normalized in a string, using
-   the rules for attribute value normalization.  The 'is_cdata' flag
-   is needed since CDATA attributes don't need to have multiple
-   whitespace characters collapsed to a single space, while other
-   attribute data types do.  (Section 3.3.3 of the recommendation.)
-*/
-static int
-is_whitespace_normalized(const XML_Char *s, int is_cdata) {
-  int blanks = 0;
-  int at_start = 1;
-  while (*s) {
-    if (*s == XCS(' '))
-      ++blanks;
-    else if (*s == XCS('\t') || *s == XCS('\n') || *s == XCS('\r'))
-      return 0;
-    else {
-      if (at_start) {
-        at_start = 0;
-        if (blanks && ! is_cdata)
-          /* illegal leading blanks */
-          return 0;
-      } else if (blanks > 1 && ! is_cdata)
-        return 0;
-      blanks = 0;
-    }
-    ++s;
-  }
-  if (blanks && ! is_cdata)
-    return 0;
-  return 1;
-}
-
-/* Check the attribute whitespace checker: */
-static void
-testhelper_is_whitespace_normalized(void) {
-  assert(is_whitespace_normalized(XCS("abc"), 0));
-  assert(is_whitespace_normalized(XCS("abc"), 1));
-  assert(is_whitespace_normalized(XCS("abc def ghi"), 0));
-  assert(is_whitespace_normalized(XCS("abc def ghi"), 1));
-  assert(! is_whitespace_normalized(XCS(" abc def ghi"), 0));
-  assert(is_whitespace_normalized(XCS(" abc def ghi"), 1));
-  assert(! is_whitespace_normalized(XCS("abc  def ghi"), 0));
-  assert(is_whitespace_normalized(XCS("abc  def ghi"), 1));
-  assert(! is_whitespace_normalized(XCS("abc def ghi "), 0));
-  assert(is_whitespace_normalized(XCS("abc def ghi "), 1));
-  assert(! is_whitespace_normalized(XCS(" "), 0));
-  assert(is_whitespace_normalized(XCS(" "), 1));
-  assert(! is_whitespace_normalized(XCS("\t"), 0));
-  assert(! is_whitespace_normalized(XCS("\t"), 1));
-  assert(! is_whitespace_normalized(XCS("\n"), 0));
-  assert(! is_whitespace_normalized(XCS("\n"), 1));
-  assert(! is_whitespace_normalized(XCS("\r"), 0));
-  assert(! is_whitespace_normalized(XCS("\r"), 1));
-  assert(! is_whitespace_normalized(XCS("abc\t def"), 1));
-}
-
-static void XMLCALL
-check_attr_contains_normalized_whitespace(void *userData, const XML_Char *name,
-                                          const XML_Char **atts) {
-  int i;
-  UNUSED_P(userData);
-  UNUSED_P(name);
-  for (i = 0; atts[i] != NULL; i += 2) {
-    const XML_Char *attrname = atts[i];
-    const XML_Char *value = atts[i + 1];
-    if (xcstrcmp(XCS("attr"), attrname) == 0
-        || xcstrcmp(XCS("ents"), attrname) == 0
-        || xcstrcmp(XCS("refs"), attrname) == 0) {
-      if (! is_whitespace_normalized(value, 0)) {
-        char buffer[256];
-        sprintf(buffer,
-                "attribute value not normalized: %" XML_FMT_STR
-                "='%" XML_FMT_STR "'",
-                attrname, value);
-        fail(buffer);
-      }
-    }
-  }
-}
-
-START_TEST(test_attr_whitespace_normalization) {
-  const char *text
-      = "<!DOCTYPE doc [\n"
-        "  <!ATTLIST doc\n"
-        "            attr NMTOKENS #REQUIRED\n"
-        "            ents ENTITIES #REQUIRED\n"
-        "            refs IDREFS   #REQUIRED>\n"
-        "]>\n"
-        "<doc attr='    a  b c\t\td\te\t' refs=' id-1   \t  id-2\t\t'  \n"
-        "     ents=' ent-1   \t\r\n"
-        "            ent-2  ' >\n"
-        "  <e id='id-1'/>\n"
-        "  <e id='id-2'/>\n"
-        "</doc>";
-
-  XML_SetStartElementHandler(g_parser,
-                             check_attr_contains_normalized_whitespace);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/*
- * XML declaration tests.
- */
-
-START_TEST(test_xmldecl_misplaced) {
-  expect_failure("\n"
-                 "<?xml version='1.0'?>\n"
-                 "<a/>",
-                 XML_ERROR_MISPLACED_XML_PI,
-                 "failed to report misplaced XML declaration");
-}
-END_TEST
-
-START_TEST(test_xmldecl_invalid) {
-  expect_failure("<?xml version='1.0' \xc3\xa7?>\n<doc/>", XML_ERROR_XML_DECL,
-                 "Failed to report invalid XML declaration");
-}
-END_TEST
-
-START_TEST(test_xmldecl_missing_attr) {
-  expect_failure("<?xml ='1.0'?>\n<doc/>\n", XML_ERROR_XML_DECL,
-                 "Failed to report missing XML declaration attribute");
-}
-END_TEST
-
-START_TEST(test_xmldecl_missing_value) {
-  expect_failure("<?xml version='1.0' encoding='us-ascii' standalone?>\n"
-                 "<doc/>",
-                 XML_ERROR_XML_DECL,
-                 "Failed to report missing attribute value");
-}
-END_TEST
-
-/* Regression test for SF bug #584832. */
-static int XMLCALL
-UnknownEncodingHandler(void *data, const XML_Char *encoding,
-                       XML_Encoding *info) {
-  UNUSED_P(data);
-  if (xcstrcmp(encoding, XCS("unsupported-encoding")) == 0) {
-    int i;
-    for (i = 0; i < 256; ++i)
-      info->map[i] = i;
-    info->data = NULL;
-    info->convert = NULL;
-    info->release = NULL;
-    return XML_STATUS_OK;
-  }
-  return XML_STATUS_ERROR;
-}
-
-START_TEST(test_unknown_encoding_internal_entity) {
-  const char *text = "<?xml version='1.0' encoding='unsupported-encoding'?>\n"
-                     "<!DOCTYPE test [<!ENTITY foo 'bar'>]>\n"
-                     "<test a='&foo;'/>";
-
-  XML_SetUnknownEncodingHandler(g_parser, UnknownEncodingHandler, NULL);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test unrecognised encoding handler */
-static void
-dummy_release(void *data) {
-  UNUSED_P(data);
-}
-
-static int XMLCALL
-UnrecognisedEncodingHandler(void *data, const XML_Char *encoding,
-                            XML_Encoding *info) {
-  UNUSED_P(data);
-  UNUSED_P(encoding);
-  info->data = NULL;
-  info->convert = NULL;
-  info->release = dummy_release;
-  return XML_STATUS_ERROR;
-}
-
-START_TEST(test_unrecognised_encoding_internal_entity) {
-  const char *text = "<?xml version='1.0' encoding='unsupported-encoding'?>\n"
-                     "<!DOCTYPE test [<!ENTITY foo 'bar'>]>\n"
-                     "<test a='&foo;'/>";
-
-  XML_SetUnknownEncodingHandler(g_parser, UnrecognisedEncodingHandler, NULL);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_ERROR)
-    fail("Unrecognised encoding not rejected");
-}
-END_TEST
-
-/* Regression test for SF bug #620106. */
-static int XMLCALL
-external_entity_loader(XML_Parser parser, const XML_Char *context,
-                       const XML_Char *base, const XML_Char *systemId,
-                       const XML_Char *publicId) {
-  ExtTest *test_data = (ExtTest *)XML_GetUserData(parser);
-  XML_Parser extparser;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  extparser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (extparser == NULL)
-    fail("Could not create external entity parser.");
-  if (test_data->encoding != NULL) {
-    if (! XML_SetEncoding(extparser, test_data->encoding))
-      fail("XML_SetEncoding() ignored for external entity");
-  }
-  if (_XML_Parse_SINGLE_BYTES(extparser, test_data->parse_text,
-                              (int)strlen(test_data->parse_text), XML_TRUE)
-      == XML_STATUS_ERROR) {
-    xml_failure(extparser);
-    return XML_STATUS_ERROR;
-  }
-  XML_ParserFree(extparser);
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_ext_entity_set_encoding) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
-                     "]>\n"
-                     "<doc>&en;</doc>";
-  ExtTest test_data
-      = {/* This text says it's an unsupported encoding, but it's really
-            UTF-8, which we tell Expat using XML_SetEncoding().
-         */
-         "<?xml encoding='iso-8859-3'?>\xC3\xA9", XCS("utf-8"), NULL};
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("\x00e9");
-#else
-  const XML_Char *expected = XCS("\xc3\xa9");
-#endif
-
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
-  run_ext_character_check(text, &test_data, expected);
-}
-END_TEST
-
-/* Test external entities with no handler */
-START_TEST(test_ext_entity_no_handler) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
-                     "]>\n"
-                     "<doc>&en;</doc>";
-
-  XML_SetDefaultHandler(g_parser, dummy_default_handler);
-  run_character_check(text, XCS(""));
-}
-END_TEST
-
-/* Test UTF-8 BOM is accepted */
-START_TEST(test_ext_entity_set_bom) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
-                     "]>\n"
-                     "<doc>&en;</doc>";
-  ExtTest test_data = {"\xEF\xBB\xBF" /* BOM */
-                       "<?xml encoding='iso-8859-3'?>"
-                       "\xC3\xA9",
-                       XCS("utf-8"), NULL};
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("\x00e9");
-#else
-  const XML_Char *expected = XCS("\xc3\xa9");
-#endif
-
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
-  run_ext_character_check(text, &test_data, expected);
-}
-END_TEST
-
-/* Test that bad encodings are faulted */
-typedef struct ext_faults {
-  const char *parse_text;
-  const char *fail_text;
-  const XML_Char *encoding;
-  enum XML_Error error;
-} ExtFaults;
-
-static int XMLCALL
-external_entity_faulter(XML_Parser parser, const XML_Char *context,
-                        const XML_Char *base, const XML_Char *systemId,
-                        const XML_Char *publicId) {
-  XML_Parser ext_parser;
-  ExtFaults *fault = (ExtFaults *)XML_GetUserData(parser);
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    fail("Could not create external entity parser");
-  if (fault->encoding != NULL) {
-    if (! XML_SetEncoding(ext_parser, fault->encoding))
-      fail("XML_SetEncoding failed");
-  }
-  if (_XML_Parse_SINGLE_BYTES(ext_parser, fault->parse_text,
-                              (int)strlen(fault->parse_text), XML_TRUE)
-      != XML_STATUS_ERROR)
-    fail(fault->fail_text);
-  if (XML_GetErrorCode(ext_parser) != fault->error)
-    xml_failure(ext_parser);
-
-  XML_ParserFree(ext_parser);
-  return XML_STATUS_ERROR;
-}
-
-START_TEST(test_ext_entity_bad_encoding) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
-                     "]>\n"
-                     "<doc>&en;</doc>";
-  ExtFaults fault
-      = {"<?xml encoding='iso-8859-3'?>u", "Unsupported encoding not faulted",
-         XCS("unknown"), XML_ERROR_UNKNOWN_ENCODING};
-
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_faulter);
-  XML_SetUserData(g_parser, &fault);
-  expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
-                 "Bad encoding should not have been accepted");
-}
-END_TEST
-
-/* Try handing an invalid encoding to an external entity parser */
-START_TEST(test_ext_entity_bad_encoding_2) {
-  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
-                     "<!DOCTYPE doc SYSTEM 'foo'>\n"
-                     "<doc>&entity;</doc>";
-  ExtFaults fault
-      = {"<!ELEMENT doc (#PCDATA)*>", "Unknown encoding not faulted",
-         XCS("unknown-encoding"), XML_ERROR_UNKNOWN_ENCODING};
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_faulter);
-  XML_SetUserData(g_parser, &fault);
-  expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
-                 "Bad encoding not faulted in external entity handler");
-}
-END_TEST
-
-/* Test that no error is reported for unknown entities if we don't
-   read an external subset.  This was fixed in Expat 1.95.5.
-*/
-START_TEST(test_wfc_undeclared_entity_unread_external_subset) {
-  const char *text = "<!DOCTYPE doc SYSTEM 'foo'>\n"
-                     "<doc>&entity;</doc>";
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test that an error is reported for unknown entities if we don't
-   have an external subset.
-*/
-START_TEST(test_wfc_undeclared_entity_no_external_subset) {
-  expect_failure("<doc>&entity;</doc>", XML_ERROR_UNDEFINED_ENTITY,
-                 "Parser did not report undefined entity w/out a DTD.");
-}
-END_TEST
-
-/* Test that an error is reported for unknown entities if we don't
-   read an external subset, but have been declared standalone.
-*/
-START_TEST(test_wfc_undeclared_entity_standalone) {
-  const char *text
-      = "<?xml version='1.0' encoding='us-ascii' standalone='yes'?>\n"
-        "<!DOCTYPE doc SYSTEM 'foo'>\n"
-        "<doc>&entity;</doc>";
-
-  expect_failure(text, XML_ERROR_UNDEFINED_ENTITY,
-                 "Parser did not report undefined entity (standalone).");
-}
-END_TEST
-
-/* Test that an error is reported for unknown entities if we have read
-   an external subset, and standalone is true.
-*/
-START_TEST(test_wfc_undeclared_entity_with_external_subset_standalone) {
-  const char *text
-      = "<?xml version='1.0' encoding='us-ascii' standalone='yes'?>\n"
-        "<!DOCTYPE doc SYSTEM 'foo'>\n"
-        "<doc>&entity;</doc>";
-  ExtTest test_data = {"<!ELEMENT doc (#PCDATA)*>", NULL, NULL};
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetUserData(g_parser, &test_data);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
-  expect_failure(text, XML_ERROR_UNDEFINED_ENTITY,
-                 "Parser did not report undefined entity (external DTD).");
-}
-END_TEST
-
-/* Test that external entity handling is not done if the parsing flag
- * is set to UNLESS_STANDALONE
- */
-START_TEST(test_entity_with_external_subset_unless_standalone) {
-  const char *text
-      = "<?xml version='1.0' encoding='us-ascii' standalone='yes'?>\n"
-        "<!DOCTYPE doc SYSTEM 'foo'>\n"
-        "<doc>&entity;</doc>";
-  ExtTest test_data = {"<!ENTITY entity 'bar'>", NULL, NULL};
-
-  XML_SetParamEntityParsing(g_parser,
-                            XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE);
-  XML_SetUserData(g_parser, &test_data);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
-  expect_failure(text, XML_ERROR_UNDEFINED_ENTITY,
-                 "Parser did not report undefined entity");
-}
-END_TEST
-
-/* Test that no error is reported for unknown entities if we have read
-   an external subset, and standalone is false.
-*/
-START_TEST(test_wfc_undeclared_entity_with_external_subset) {
-  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
-                     "<!DOCTYPE doc SYSTEM 'foo'>\n"
-                     "<doc>&entity;</doc>";
-  ExtTest test_data = {"<!ELEMENT doc (#PCDATA)*>", NULL, NULL};
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
-  run_ext_character_check(text, &test_data, XCS(""));
-}
-END_TEST
-
-/* Test that an error is reported if our NotStandalone handler fails */
-static int XMLCALL
-reject_not_standalone_handler(void *userData) {
-  UNUSED_P(userData);
-  return XML_STATUS_ERROR;
-}
-
-START_TEST(test_not_standalone_handler_reject) {
-  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
-                     "<!DOCTYPE doc SYSTEM 'foo'>\n"
-                     "<doc>&entity;</doc>";
-  ExtTest test_data = {"<!ELEMENT doc (#PCDATA)*>", NULL, NULL};
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetUserData(g_parser, &test_data);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
-  XML_SetNotStandaloneHandler(g_parser, reject_not_standalone_handler);
-  expect_failure(text, XML_ERROR_NOT_STANDALONE,
-                 "NotStandalone handler failed to reject");
-
-  /* Try again but without external entity handling */
-  XML_ParserReset(g_parser, NULL);
-  XML_SetNotStandaloneHandler(g_parser, reject_not_standalone_handler);
-  expect_failure(text, XML_ERROR_NOT_STANDALONE,
-                 "NotStandalone handler failed to reject");
-}
-END_TEST
-
-/* Test that no error is reported if our NotStandalone handler succeeds */
-static int XMLCALL
-accept_not_standalone_handler(void *userData) {
-  UNUSED_P(userData);
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_not_standalone_handler_accept) {
-  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
-                     "<!DOCTYPE doc SYSTEM 'foo'>\n"
-                     "<doc>&entity;</doc>";
-  ExtTest test_data = {"<!ELEMENT doc (#PCDATA)*>", NULL, NULL};
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
-  XML_SetNotStandaloneHandler(g_parser, accept_not_standalone_handler);
-  run_ext_character_check(text, &test_data, XCS(""));
-
-  /* Repeat without the external entity handler */
-  XML_ParserReset(g_parser, NULL);
-  XML_SetNotStandaloneHandler(g_parser, accept_not_standalone_handler);
-  run_character_check(text, XCS(""));
-}
-END_TEST
-
-START_TEST(test_wfc_no_recursive_entity_refs) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY entity '&#38;entity;'>\n"
-                     "]>\n"
-                     "<doc>&entity;</doc>";
-
-  expect_failure(text, XML_ERROR_RECURSIVE_ENTITY_REF,
-                 "Parser did not report recursive entity reference.");
-}
-END_TEST
-
-/* Test incomplete external entities are faulted */
-START_TEST(test_ext_entity_invalid_parse) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
-                     "]>\n"
-                     "<doc>&en;</doc>";
-  const ExtFaults faults[]
-      = {{"<", "Incomplete element declaration not faulted", NULL,
-          XML_ERROR_UNCLOSED_TOKEN},
-         {"<\xe2\x82", /* First two bytes of a three-byte char */
-          "Incomplete character not faulted", NULL, XML_ERROR_PARTIAL_CHAR},
-         {"<tag>\xe2\x82", "Incomplete character in CDATA not faulted", NULL,
-          XML_ERROR_PARTIAL_CHAR},
-         {NULL, NULL, NULL, XML_ERROR_NONE}};
-  const ExtFaults *fault = faults;
-
-  for (; fault->parse_text != NULL; fault++) {
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_faulter);
-    XML_SetUserData(g_parser, (void *)fault);
-    expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
-                   "Parser did not report external entity error");
-    XML_ParserReset(g_parser, NULL);
-  }
-}
-END_TEST
-
-/* Regression test for SF bug #483514. */
-START_TEST(test_dtd_default_handling) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "<!ENTITY e SYSTEM 'http://example.org/e'>\n"
-                     "<!NOTATION n SYSTEM 'http://example.org/n'>\n"
-                     "<!ELEMENT doc EMPTY>\n"
-                     "<!ATTLIST doc a CDATA #IMPLIED>\n"
-                     "<?pi in dtd?>\n"
-                     "<!--comment in dtd-->\n"
-                     "]><doc/>";
-
-  XML_SetDefaultHandler(g_parser, accumulate_characters);
-  XML_SetStartDoctypeDeclHandler(g_parser, dummy_start_doctype_handler);
-  XML_SetEndDoctypeDeclHandler(g_parser, dummy_end_doctype_handler);
-  XML_SetEntityDeclHandler(g_parser, dummy_entity_decl_handler);
-  XML_SetNotationDeclHandler(g_parser, dummy_notation_decl_handler);
-  XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
-  XML_SetAttlistDeclHandler(g_parser, dummy_attlist_decl_handler);
-  XML_SetProcessingInstructionHandler(g_parser, dummy_pi_handler);
-  XML_SetCommentHandler(g_parser, dummy_comment_handler);
-  XML_SetStartCdataSectionHandler(g_parser, dummy_start_cdata_handler);
-  XML_SetEndCdataSectionHandler(g_parser, dummy_end_cdata_handler);
-  run_character_check(text, XCS("\n\n\n\n\n\n\n<doc/>"));
-}
-END_TEST
-
-/* Test handling of attribute declarations */
-typedef struct AttTest {
-  const char *definition;
-  const XML_Char *element_name;
-  const XML_Char *attr_name;
-  const XML_Char *attr_type;
-  const XML_Char *default_value;
-  int is_required;
-} AttTest;
-
-static void XMLCALL
-verify_attlist_decl_handler(void *userData, const XML_Char *element_name,
-                            const XML_Char *attr_name,
-                            const XML_Char *attr_type,
-                            const XML_Char *default_value, int is_required) {
-  AttTest *at = (AttTest *)userData;
-
-  if (xcstrcmp(element_name, at->element_name))
-    fail("Unexpected element name in attribute declaration");
-  if (xcstrcmp(attr_name, at->attr_name))
-    fail("Unexpected attribute name in attribute declaration");
-  if (xcstrcmp(attr_type, at->attr_type))
-    fail("Unexpected attribute type in attribute declaration");
-  if ((default_value == NULL && at->default_value != NULL)
-      || (default_value != NULL && at->default_value == NULL)
-      || (default_value != NULL && xcstrcmp(default_value, at->default_value)))
-    fail("Unexpected default value in attribute declaration");
-  if (is_required != at->is_required)
-    fail("Requirement mismatch in attribute declaration");
-}
-
-START_TEST(test_dtd_attr_handling) {
-  const char *prolog = "<!DOCTYPE doc [\n"
-                       "<!ELEMENT doc EMPTY>\n";
-  AttTest attr_data[]
-      = {{"<!ATTLIST doc a ( one | two | three ) #REQUIRED>\n"
-          "]>"
-          "<doc a='two'/>",
-          XCS("doc"), XCS("a"),
-          XCS("(one|two|three)"), /* Extraneous spaces will be removed */
-          NULL, XML_TRUE},
-         {"<!NOTATION foo SYSTEM 'http://example.org/foo'>\n"
-          "<!ATTLIST doc a NOTATION (foo) #IMPLIED>\n"
-          "]>"
-          "<doc/>",
-          XCS("doc"), XCS("a"), XCS("NOTATION(foo)"), NULL, XML_FALSE},
-         {"<!ATTLIST doc a NOTATION (foo) 'bar'>\n"
-          "]>"
-          "<doc/>",
-          XCS("doc"), XCS("a"), XCS("NOTATION(foo)"), XCS("bar"), XML_FALSE},
-         {"<!ATTLIST doc a CDATA '\xdb\xb2'>\n"
-          "]>"
-          "<doc/>",
-          XCS("doc"), XCS("a"), XCS("CDATA"),
-#ifdef XML_UNICODE
-          XCS("\x06f2"),
-#else
-          XCS("\xdb\xb2"),
-#endif
-          XML_FALSE},
-         {NULL, NULL, NULL, NULL, NULL, XML_FALSE}};
-  AttTest *test;
-
-  for (test = attr_data; test->definition != NULL; test++) {
-    XML_SetAttlistDeclHandler(g_parser, verify_attlist_decl_handler);
-    XML_SetUserData(g_parser, test);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, prolog, (int)strlen(prolog),
-                                XML_FALSE)
-        == XML_STATUS_ERROR)
-      xml_failure(g_parser);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, test->definition,
-                                (int)strlen(test->definition), XML_TRUE)
-        == XML_STATUS_ERROR)
-      xml_failure(g_parser);
-    XML_ParserReset(g_parser, NULL);
-  }
-}
-END_TEST
-
-/* See related SF bug #673791.
-   When namespace processing is enabled, setting the namespace URI for
-   a prefix is not allowed; this test ensures that it *is* allowed
-   when namespace processing is not enabled.
-   (See Namespaces in XML, section 2.)
-*/
-START_TEST(test_empty_ns_without_namespaces) {
-  const char *text = "<doc xmlns:prefix='http://example.org/'>\n"
-                     "  <e xmlns:prefix=''/>\n"
-                     "</doc>";
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Regression test for SF bug #824420.
-   Checks that an xmlns:prefix attribute set in an attribute's default
-   value isn't misinterpreted.
-*/
-START_TEST(test_ns_in_attribute_default_without_namespaces) {
-  const char *text = "<!DOCTYPE e:element [\n"
-                     "  <!ATTLIST e:element\n"
-                     "    xmlns:e CDATA 'http://example.org/'>\n"
-                     "      ]>\n"
-                     "<e:element/>";
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-static const char *long_character_data_text
-    = "<?xml version='1.0' encoding='iso-8859-1'?><s>"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "</s>";
-
-static XML_Bool resumable = XML_FALSE;
-
-static void
-clearing_aborting_character_handler(void *userData, const XML_Char *s,
-                                    int len) {
-  UNUSED_P(userData);
-  UNUSED_P(s);
-  UNUSED_P(len);
-  XML_StopParser(g_parser, resumable);
-  XML_SetCharacterDataHandler(g_parser, NULL);
-}
-
-/* Regression test for SF bug #1515266: missing check of stopped
-   parser in doContext() 'for' loop. */
-START_TEST(test_stop_parser_between_char_data_calls) {
-  /* The sample data must be big enough that there are two calls to
-     the character data handler from within the inner "for" loop of
-     the XML_TOK_DATA_CHARS case in doContent(), and the character
-     handler must stop the parser and clear the character data
-     handler.
-  */
-  const char *text = long_character_data_text;
-
-  XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler);
-  resumable = XML_FALSE;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_ABORTED)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Regression test for SF bug #1515266: missing check of stopped
-   parser in doContext() 'for' loop. */
-START_TEST(test_suspend_parser_between_char_data_calls) {
-  /* The sample data must be big enough that there are two calls to
-     the character data handler from within the inner "for" loop of
-     the XML_TOK_DATA_CHARS case in doContent(), and the character
-     handler must stop the parser and clear the character data
-     handler.
-  */
-  const char *text = long_character_data_text;
-
-  XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler);
-  resumable = XML_TRUE;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_SUSPENDED)
-    xml_failure(g_parser);
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_NONE)
-    xml_failure(g_parser);
-  /* Try parsing directly */
-  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_ERROR)
-    fail("Attempt to continue parse while suspended not faulted");
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_SUSPENDED)
-    fail("Suspended parse not faulted with correct error");
-}
-END_TEST
-
-static XML_Bool abortable = XML_FALSE;
-
-static void
-parser_stop_character_handler(void *userData, const XML_Char *s, int len) {
-  UNUSED_P(userData);
-  UNUSED_P(s);
-  UNUSED_P(len);
-  XML_StopParser(g_parser, resumable);
-  XML_SetCharacterDataHandler(g_parser, NULL);
-  if (! resumable) {
-    /* Check that aborting an aborted parser is faulted */
-    if (XML_StopParser(g_parser, XML_FALSE) != XML_STATUS_ERROR)
-      fail("Aborting aborted parser not faulted");
-    if (XML_GetErrorCode(g_parser) != XML_ERROR_FINISHED)
-      xml_failure(g_parser);
-  } else if (abortable) {
-    /* Check that aborting a suspended parser works */
-    if (XML_StopParser(g_parser, XML_FALSE) == XML_STATUS_ERROR)
-      xml_failure(g_parser);
-  } else {
-    /* Check that suspending a suspended parser works */
-    if (XML_StopParser(g_parser, XML_TRUE) != XML_STATUS_ERROR)
-      fail("Suspending suspended parser not faulted");
-    if (XML_GetErrorCode(g_parser) != XML_ERROR_SUSPENDED)
-      xml_failure(g_parser);
-  }
-}
-
-/* Test repeated calls to XML_StopParser are handled correctly */
-START_TEST(test_repeated_stop_parser_between_char_data_calls) {
-  const char *text = long_character_data_text;
-
-  XML_SetCharacterDataHandler(g_parser, parser_stop_character_handler);
-  resumable = XML_FALSE;
-  abortable = XML_FALSE;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_ERROR)
-    fail("Failed to double-stop parser");
-
-  XML_ParserReset(g_parser, NULL);
-  XML_SetCharacterDataHandler(g_parser, parser_stop_character_handler);
-  resumable = XML_TRUE;
-  abortable = XML_FALSE;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_SUSPENDED)
-    fail("Failed to double-suspend parser");
-
-  XML_ParserReset(g_parser, NULL);
-  XML_SetCharacterDataHandler(g_parser, parser_stop_character_handler);
-  resumable = XML_TRUE;
-  abortable = XML_TRUE;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_ERROR)
-    fail("Failed to suspend-abort parser");
-}
-END_TEST
-
-START_TEST(test_good_cdata_ascii) {
-  const char *text = "<a><![CDATA[<greeting>Hello, world!</greeting>]]></a>";
-  const XML_Char *expected = XCS("<greeting>Hello, world!</greeting>");
-
-  CharData storage;
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  XML_SetCharacterDataHandler(g_parser, accumulate_characters);
-  /* Add start and end handlers for coverage */
-  XML_SetStartCdataSectionHandler(g_parser, dummy_start_cdata_handler);
-  XML_SetEndCdataSectionHandler(g_parser, dummy_end_cdata_handler);
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-
-  /* Try again, this time with a default handler */
-  XML_ParserReset(g_parser, NULL);
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  XML_SetCharacterDataHandler(g_parser, accumulate_characters);
-  XML_SetDefaultHandler(g_parser, dummy_default_handler);
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-START_TEST(test_good_cdata_utf16) {
-  /* Test data is:
-   *   <?xml version='1.0' encoding='utf-16'?>
-   *   <a><![CDATA[hello]]></a>
-   */
-  const char text[]
-      = "\0<\0?\0x\0m\0l\0"
-        " \0v\0e\0r\0s\0i\0o\0n\0=\0'\0\x31\0.\0\x30\0'\0"
-        " \0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\0"
-        "1\0"
-        "6\0'"
-        "\0?\0>\0\n"
-        "\0<\0a\0>\0<\0!\0[\0C\0D\0A\0T\0A\0[\0h\0e\0l\0l\0o\0]\0]\0>\0<\0/\0a\0>";
-  const XML_Char *expected = XCS("hello");
-
-  CharData storage;
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  XML_SetCharacterDataHandler(g_parser, accumulate_characters);
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-START_TEST(test_good_cdata_utf16_le) {
-  /* Test data is:
-   *   <?xml version='1.0' encoding='utf-16'?>
-   *   <a><![CDATA[hello]]></a>
-   */
-  const char text[]
-      = "<\0?\0x\0m\0l\0"
-        " \0v\0e\0r\0s\0i\0o\0n\0=\0'\0\x31\0.\0\x30\0'\0"
-        " \0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\0"
-        "1\0"
-        "6\0'"
-        "\0?\0>\0\n"
-        "\0<\0a\0>\0<\0!\0[\0C\0D\0A\0T\0A\0[\0h\0e\0l\0l\0o\0]\0]\0>\0<\0/\0a\0>\0";
-  const XML_Char *expected = XCS("hello");
-
-  CharData storage;
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  XML_SetCharacterDataHandler(g_parser, accumulate_characters);
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-/* Test UTF16 conversion of a long cdata string */
-
-/* 16 characters: handy macro to reduce visual clutter */
-#define A_TO_P_IN_UTF16 "\0A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0K\0L\0M\0N\0O\0P"
-
-START_TEST(test_long_cdata_utf16) {
-  /* Test data is:
-   * <?xlm version='1.0' encoding='utf-16'?>
-   * <a><![CDATA[
-   * ABCDEFGHIJKLMNOP
-   * ]]></a>
-   */
-  const char text[]
-      = "\0<\0?\0x\0m\0l\0 "
-        "\0v\0e\0r\0s\0i\0o\0n\0=\0'\0\x31\0.\0\x30\0'\0 "
-        "\0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\0\x31\0\x36\0'\0?\0>"
-        "\0<\0a\0>\0<\0!\0[\0C\0D\0A\0T\0A\0["
-      /* 64 characters per line */
-      /* clang-format off */
-        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
-        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
-        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
-        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
-        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
-        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
-        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
-        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
-        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
-        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
-        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
-        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
-        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
-        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
-        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
-        A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16  A_TO_P_IN_UTF16
-        A_TO_P_IN_UTF16
-        /* clang-format on */
-        "\0]\0]\0>\0<\0/\0a\0>";
-  const XML_Char *expected =
-      /* clang-format off */
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP")
-        XCS("ABCDEFGHIJKLMNOP");
-  /* clang-format on */
-  CharData storage;
-  void *buffer;
-
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  XML_SetCharacterDataHandler(g_parser, accumulate_characters);
-  buffer = XML_GetBuffer(g_parser, sizeof(text) - 1);
-  if (buffer == NULL)
-    fail("Could not allocate parse buffer");
-  assert(buffer != NULL);
-  memcpy(buffer, text, sizeof(text) - 1);
-  if (XML_ParseBuffer(g_parser, sizeof(text) - 1, XML_TRUE) == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-/* Test handling of multiple unit UTF-16 characters */
-START_TEST(test_multichar_cdata_utf16) {
-  /* Test data is:
-   *   <?xml version='1.0' encoding='utf-16'?>
-   *   <a><![CDATA[{MINIM}{CROTCHET}]]></a>
-   *
-   * where {MINIM} is U+1d15e (a minim or half-note)
-   *   UTF-16: 0xd834 0xdd5e
-   *   UTF-8:  0xf0 0x9d 0x85 0x9e
-   * and {CROTCHET} is U+1d15f (a crotchet or quarter-note)
-   *   UTF-16: 0xd834 0xdd5f
-   *   UTF-8:  0xf0 0x9d 0x85 0x9f
-   */
-  const char text[] = "\0<\0?\0x\0m\0l\0"
-                      " \0v\0e\0r\0s\0i\0o\0n\0=\0'\0\x31\0.\0\x30\0'\0"
-                      " \0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\0"
-                      "1\0"
-                      "6\0'"
-                      "\0?\0>\0\n"
-                      "\0<\0a\0>\0<\0!\0[\0C\0D\0A\0T\0A\0["
-                      "\xd8\x34\xdd\x5e\xd8\x34\xdd\x5f"
-                      "\0]\0]\0>\0<\0/\0a\0>";
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("\xd834\xdd5e\xd834\xdd5f");
-#else
-  const XML_Char *expected = XCS("\xf0\x9d\x85\x9e\xf0\x9d\x85\x9f");
-#endif
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  XML_SetCharacterDataHandler(g_parser, accumulate_characters);
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-/* Test that an element name with a UTF-16 surrogate pair is rejected */
-START_TEST(test_utf16_bad_surrogate_pair) {
-  /* Test data is:
-   *   <?xml version='1.0' encoding='utf-16'?>
-   *   <a><![CDATA[{BADLINB}]]></a>
-   *
-   * where {BADLINB} is U+10000 (the first Linear B character)
-   * with the UTF-16 surrogate pair in the wrong order, i.e.
-   *   0xdc00 0xd800
-   */
-  const char text[] = "\0<\0?\0x\0m\0l\0"
-                      " \0v\0e\0r\0s\0i\0o\0n\0=\0'\0\x31\0.\0\x30\0'\0"
-                      " \0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\0"
-                      "1\0"
-                      "6\0'"
-                      "\0?\0>\0\n"
-                      "\0<\0a\0>\0<\0!\0[\0C\0D\0A\0T\0A\0["
-                      "\xdc\x00\xd8\x00"
-                      "\0]\0]\0>\0<\0/\0a\0>";
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      != XML_STATUS_ERROR)
-    fail("Reversed UTF-16 surrogate pair not faulted");
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_INVALID_TOKEN)
-    xml_failure(g_parser);
-}
-END_TEST
-
-START_TEST(test_bad_cdata) {
-  struct CaseData {
-    const char *text;
-    enum XML_Error expectedError;
-  };
-
-  struct CaseData cases[]
-      = {{"<a><", XML_ERROR_UNCLOSED_TOKEN},
-         {"<a><!", XML_ERROR_UNCLOSED_TOKEN},
-         {"<a><![", XML_ERROR_UNCLOSED_TOKEN},
-         {"<a><![C", XML_ERROR_UNCLOSED_TOKEN},
-         {"<a><![CD", XML_ERROR_UNCLOSED_TOKEN},
-         {"<a><![CDA", XML_ERROR_UNCLOSED_TOKEN},
-         {"<a><![CDAT", XML_ERROR_UNCLOSED_TOKEN},
-         {"<a><![CDATA", XML_ERROR_UNCLOSED_TOKEN},
-
-         {"<a><![CDATA[", XML_ERROR_UNCLOSED_CDATA_SECTION},
-         {"<a><![CDATA[]", XML_ERROR_UNCLOSED_CDATA_SECTION},
-         {"<a><![CDATA[]]", XML_ERROR_UNCLOSED_CDATA_SECTION},
-
-         {"<a><!<a/>", XML_ERROR_INVALID_TOKEN},
-         {"<a><![<a/>", XML_ERROR_UNCLOSED_TOKEN},  /* ?! */
-         {"<a><![C<a/>", XML_ERROR_UNCLOSED_TOKEN}, /* ?! */
-         {"<a><![CD<a/>", XML_ERROR_INVALID_TOKEN},
-         {"<a><![CDA<a/>", XML_ERROR_INVALID_TOKEN},
-         {"<a><![CDAT<a/>", XML_ERROR_INVALID_TOKEN},
-         {"<a><![CDATA<a/>", XML_ERROR_INVALID_TOKEN},
-
-         {"<a><![CDATA[<a/>", XML_ERROR_UNCLOSED_CDATA_SECTION},
-         {"<a><![CDATA[]<a/>", XML_ERROR_UNCLOSED_CDATA_SECTION},
-         {"<a><![CDATA[]]<a/>", XML_ERROR_UNCLOSED_CDATA_SECTION}};
-
-  size_t i = 0;
-  for (; i < sizeof(cases) / sizeof(struct CaseData); i++) {
-    const enum XML_Status actualStatus = _XML_Parse_SINGLE_BYTES(
-        g_parser, cases[i].text, (int)strlen(cases[i].text), XML_TRUE);
-    const enum XML_Error actualError = XML_GetErrorCode(g_parser);
-
-    assert(actualStatus == XML_STATUS_ERROR);
-
-    if (actualError != cases[i].expectedError) {
-      char message[100];
-      sprintf(message,
-              "Expected error %d but got error %d for case %u: \"%s\"\n",
-              cases[i].expectedError, actualError, (unsigned int)i + 1,
-              cases[i].text);
-      fail(message);
-    }
-
-    XML_ParserReset(g_parser, NULL);
-  }
-}
-END_TEST
-
-/* Test failures in UTF-16 CDATA */
-START_TEST(test_bad_cdata_utf16) {
-  struct CaseData {
-    size_t text_bytes;
-    const char *text;
-    enum XML_Error expected_error;
-  };
-
-  const char prolog[] = "\0<\0?\0x\0m\0l\0"
-                        " \0v\0e\0r\0s\0i\0o\0n\0=\0'\0\x31\0.\0\x30\0'\0"
-                        " \0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\0"
-                        "1\0"
-                        "6\0'"
-                        "\0?\0>\0\n"
-                        "\0<\0a\0>";
-  struct CaseData cases[] = {
-      {1, "\0", XML_ERROR_UNCLOSED_TOKEN},
-      {2, "\0<", XML_ERROR_UNCLOSED_TOKEN},
-      {3, "\0<\0", XML_ERROR_UNCLOSED_TOKEN},
-      {4, "\0<\0!", XML_ERROR_UNCLOSED_TOKEN},
-      {5, "\0<\0!\0", XML_ERROR_UNCLOSED_TOKEN},
-      {6, "\0<\0!\0[", XML_ERROR_UNCLOSED_TOKEN},
-      {7, "\0<\0!\0[\0", XML_ERROR_UNCLOSED_TOKEN},
-      {8, "\0<\0!\0[\0C", XML_ERROR_UNCLOSED_TOKEN},
-      {9, "\0<\0!\0[\0C\0", XML_ERROR_UNCLOSED_TOKEN},
-      {10, "\0<\0!\0[\0C\0D", XML_ERROR_UNCLOSED_TOKEN},
-      {11, "\0<\0!\0[\0C\0D\0", XML_ERROR_UNCLOSED_TOKEN},
-      {12, "\0<\0!\0[\0C\0D\0A", XML_ERROR_UNCLOSED_TOKEN},
-      {13, "\0<\0!\0[\0C\0D\0A\0", XML_ERROR_UNCLOSED_TOKEN},
-      {14, "\0<\0!\0[\0C\0D\0A\0T", XML_ERROR_UNCLOSED_TOKEN},
-      {15, "\0<\0!\0[\0C\0D\0A\0T\0", XML_ERROR_UNCLOSED_TOKEN},
-      {16, "\0<\0!\0[\0C\0D\0A\0T\0A", XML_ERROR_UNCLOSED_TOKEN},
-      {17, "\0<\0!\0[\0C\0D\0A\0T\0A\0", XML_ERROR_UNCLOSED_TOKEN},
-      {18, "\0<\0!\0[\0C\0D\0A\0T\0A\0[", XML_ERROR_UNCLOSED_CDATA_SECTION},
-      {19, "\0<\0!\0[\0C\0D\0A\0T\0A\0[\0", XML_ERROR_UNCLOSED_CDATA_SECTION},
-      {20, "\0<\0!\0[\0C\0D\0A\0T\0A\0[\0Z", XML_ERROR_UNCLOSED_CDATA_SECTION},
-      /* Now add a four-byte UTF-16 character */
-      {21, "\0<\0!\0[\0C\0D\0A\0T\0A\0[\0Z\xd8",
-       XML_ERROR_UNCLOSED_CDATA_SECTION},
-      {22, "\0<\0!\0[\0C\0D\0A\0T\0A\0[\0Z\xd8\x34", XML_ERROR_PARTIAL_CHAR},
-      {23, "\0<\0!\0[\0C\0D\0A\0T\0A\0[\0Z\xd8\x34\xdd",
-       XML_ERROR_PARTIAL_CHAR},
-      {24, "\0<\0!\0[\0C\0D\0A\0T\0A\0[\0Z\xd8\x34\xdd\x5e",
-       XML_ERROR_UNCLOSED_CDATA_SECTION}};
-  size_t i;
-
-  for (i = 0; i < sizeof(cases) / sizeof(struct CaseData); i++) {
-    enum XML_Status actual_status;
-    enum XML_Error actual_error;
-
-    if (_XML_Parse_SINGLE_BYTES(g_parser, prolog, (int)sizeof(prolog) - 1,
-                                XML_FALSE)
-        == XML_STATUS_ERROR)
-      xml_failure(g_parser);
-    actual_status = _XML_Parse_SINGLE_BYTES(g_parser, cases[i].text,
-                                            (int)cases[i].text_bytes, XML_TRUE);
-    assert(actual_status == XML_STATUS_ERROR);
-    actual_error = XML_GetErrorCode(g_parser);
-    if (actual_error != cases[i].expected_error) {
-      char message[1024];
-
-      sprintf(message,
-              "Expected error %d (%" XML_FMT_STR "), got %d (%" XML_FMT_STR
-              ") for case %lu\n",
-              cases[i].expected_error, XML_ErrorString(cases[i].expected_error),
-              actual_error, XML_ErrorString(actual_error),
-              (long unsigned)(i + 1));
-      fail(message);
-    }
-    XML_ParserReset(g_parser, NULL);
-  }
-}
-END_TEST
-
-static const char *long_cdata_text
-    = "<s><![CDATA["
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "012345678901234567890123456789012345678901234567890123456789"
-      "]]></s>";
-
-/* Test stopping the parser in cdata handler */
-START_TEST(test_stop_parser_between_cdata_calls) {
-  const char *text = long_cdata_text;
-
-  XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler);
-  resumable = XML_FALSE;
-  expect_failure(text, XML_ERROR_ABORTED, "Parse not aborted in CDATA handler");
-}
-END_TEST
-
-/* Test suspending the parser in cdata handler */
-START_TEST(test_suspend_parser_between_cdata_calls) {
-  const char *text = long_cdata_text;
-  enum XML_Status result;
-
-  XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler);
-  resumable = XML_TRUE;
-  result = _XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE);
-  if (result != XML_STATUS_SUSPENDED) {
-    if (result == XML_STATUS_ERROR)
-      xml_failure(g_parser);
-    fail("Parse not suspended in CDATA handler");
-  }
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_NONE)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test memory allocation functions */
-START_TEST(test_memory_allocation) {
-  char *buffer = (char *)XML_MemMalloc(g_parser, 256);
-  char *p;
-
-  if (buffer == NULL) {
-    fail("Allocation failed");
-  } else {
-    /* Try writing to memory; some OSes try to cheat! */
-    buffer[0] = 'T';
-    buffer[1] = 'E';
-    buffer[2] = 'S';
-    buffer[3] = 'T';
-    buffer[4] = '\0';
-    if (strcmp(buffer, "TEST") != 0) {
-      fail("Memory not writable");
-    } else {
-      p = (char *)XML_MemRealloc(g_parser, buffer, 512);
-      if (p == NULL) {
-        fail("Reallocation failed");
-      } else {
-        /* Write again, just to be sure */
-        buffer = p;
-        buffer[0] = 'V';
-        if (strcmp(buffer, "VEST") != 0) {
-          fail("Reallocated memory not writable");
-        }
-      }
-    }
-    XML_MemFree(g_parser, buffer);
-  }
-}
-END_TEST
-
-static void XMLCALL
-record_default_handler(void *userData, const XML_Char *s, int len) {
-  UNUSED_P(s);
-  UNUSED_P(len);
-  CharData_AppendXMLChars((CharData *)userData, XCS("D"), 1);
-}
-
-static void XMLCALL
-record_cdata_handler(void *userData, const XML_Char *s, int len) {
-  UNUSED_P(s);
-  UNUSED_P(len);
-  CharData_AppendXMLChars((CharData *)userData, XCS("C"), 1);
-  XML_DefaultCurrent(g_parser);
-}
-
-static void XMLCALL
-record_cdata_nodefault_handler(void *userData, const XML_Char *s, int len) {
-  UNUSED_P(s);
-  UNUSED_P(len);
-  CharData_AppendXMLChars((CharData *)userData, XCS("c"), 1);
-}
-
-static void XMLCALL
-record_skip_handler(void *userData, const XML_Char *entityName,
-                    int is_parameter_entity) {
-  UNUSED_P(entityName);
-  CharData_AppendXMLChars((CharData *)userData,
-                          is_parameter_entity ? XCS("E") : XCS("e"), 1);
-}
-
-/* Test XML_DefaultCurrent() passes handling on correctly */
-START_TEST(test_default_current) {
-  const char *text = "<doc>hell]</doc>";
-  const char *entity_text = "<!DOCTYPE doc [\n"
-                            "<!ENTITY entity '&#37;'>\n"
-                            "]>\n"
-                            "<doc>&entity;</doc>";
-  CharData storage;
-
-  XML_SetDefaultHandler(g_parser, record_default_handler);
-  XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, XCS("DCDCDCDCDCDD"));
-
-  /* Again, without the defaulting */
-  XML_ParserReset(g_parser, NULL);
-  XML_SetDefaultHandler(g_parser, record_default_handler);
-  XML_SetCharacterDataHandler(g_parser, record_cdata_nodefault_handler);
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, XCS("DcccccD"));
-
-  /* Now with an internal entity to complicate matters */
-  XML_ParserReset(g_parser, NULL);
-  XML_SetDefaultHandler(g_parser, record_default_handler);
-  XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
-                              XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  /* The default handler suppresses the entity */
-  CharData_CheckXMLChars(&storage, XCS("DDDDDDDDDDDDDDDDDDD"));
-
-  /* Again, with a skip handler */
-  XML_ParserReset(g_parser, NULL);
-  XML_SetDefaultHandler(g_parser, record_default_handler);
-  XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
-  XML_SetSkippedEntityHandler(g_parser, record_skip_handler);
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
-                              XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  /* The default handler suppresses the entity */
-  CharData_CheckXMLChars(&storage, XCS("DDDDDDDDDDDDDDDDDeD"));
-
-  /* This time, allow the entity through */
-  XML_ParserReset(g_parser, NULL);
-  XML_SetDefaultHandlerExpand(g_parser, record_default_handler);
-  XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
-                              XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, XCS("DDDDDDDDDDDDDDDDDCDD"));
-
-  /* Finally, without passing the cdata to the default handler */
-  XML_ParserReset(g_parser, NULL);
-  XML_SetDefaultHandlerExpand(g_parser, record_default_handler);
-  XML_SetCharacterDataHandler(g_parser, record_cdata_nodefault_handler);
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
-                              XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, XCS("DDDDDDDDDDDDDDDDDcD"));
-}
-END_TEST
-
-/* Test DTD element parsing code paths */
-START_TEST(test_dtd_elements) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "<!ELEMENT doc (chapter)>\n"
-                     "<!ELEMENT chapter (#PCDATA)>\n"
-                     "]>\n"
-                     "<doc><chapter>Wombats are go</chapter></doc>";
-
-  XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-static void XMLCALL
-element_decl_check_model(void *userData, const XML_Char *name,
-                         XML_Content *model) {
-  UNUSED_P(userData);
-  uint32_t errorFlags = 0;
-
-  /* Expected model array structure is this:
-   * [0] (type 6, quant 0)
-   *   [1] (type 5, quant 0)
-   *     [3] (type 4, quant 0, name "bar")
-   *     [4] (type 4, quant 0, name "foo")
-   *     [5] (type 4, quant 3, name "xyz")
-   *   [2] (type 4, quant 2, name "zebra")
-   */
-  errorFlags |= ((xcstrcmp(name, XCS("junk")) == 0) ? 0 : (1u << 0));
-  errorFlags |= ((model != NULL) ? 0 : (1u << 1));
-
-  errorFlags |= ((model[0].type == XML_CTYPE_SEQ) ? 0 : (1u << 2));
-  errorFlags |= ((model[0].quant == XML_CQUANT_NONE) ? 0 : (1u << 3));
-  errorFlags |= ((model[0].numchildren == 2) ? 0 : (1u << 4));
-  errorFlags |= ((model[0].children == &model[1]) ? 0 : (1u << 5));
-  errorFlags |= ((model[0].name == NULL) ? 0 : (1u << 6));
-
-  errorFlags |= ((model[1].type == XML_CTYPE_CHOICE) ? 0 : (1u << 7));
-  errorFlags |= ((model[1].quant == XML_CQUANT_NONE) ? 0 : (1u << 8));
-  errorFlags |= ((model[1].numchildren == 3) ? 0 : (1u << 9));
-  errorFlags |= ((model[1].children == &model[3]) ? 0 : (1u << 10));
-  errorFlags |= ((model[1].name == NULL) ? 0 : (1u << 11));
-
-  errorFlags |= ((model[2].type == XML_CTYPE_NAME) ? 0 : (1u << 12));
-  errorFlags |= ((model[2].quant == XML_CQUANT_REP) ? 0 : (1u << 13));
-  errorFlags |= ((model[2].numchildren == 0) ? 0 : (1u << 14));
-  errorFlags |= ((model[2].children == NULL) ? 0 : (1u << 15));
-  errorFlags |= ((xcstrcmp(model[2].name, XCS("zebra")) == 0) ? 0 : (1u << 16));
-
-  errorFlags |= ((model[3].type == XML_CTYPE_NAME) ? 0 : (1u << 17));
-  errorFlags |= ((model[3].quant == XML_CQUANT_NONE) ? 0 : (1u << 18));
-  errorFlags |= ((model[3].numchildren == 0) ? 0 : (1u << 19));
-  errorFlags |= ((model[3].children == NULL) ? 0 : (1u << 20));
-  errorFlags |= ((xcstrcmp(model[3].name, XCS("bar")) == 0) ? 0 : (1u << 21));
-
-  errorFlags |= ((model[4].type == XML_CTYPE_NAME) ? 0 : (1u << 22));
-  errorFlags |= ((model[4].quant == XML_CQUANT_NONE) ? 0 : (1u << 23));
-  errorFlags |= ((model[4].numchildren == 0) ? 0 : (1u << 24));
-  errorFlags |= ((model[4].children == NULL) ? 0 : (1u << 25));
-  errorFlags |= ((xcstrcmp(model[4].name, XCS("foo")) == 0) ? 0 : (1u << 26));
-
-  errorFlags |= ((model[5].type == XML_CTYPE_NAME) ? 0 : (1u << 27));
-  errorFlags |= ((model[5].quant == XML_CQUANT_PLUS) ? 0 : (1u << 28));
-  errorFlags |= ((model[5].numchildren == 0) ? 0 : (1u << 29));
-  errorFlags |= ((model[5].children == NULL) ? 0 : (1u << 30));
-  errorFlags |= ((xcstrcmp(model[5].name, XCS("xyz")) == 0) ? 0 : (1u << 31));
-
-  XML_SetUserData(g_parser, (void *)(uintptr_t)errorFlags);
-  XML_FreeContentModel(g_parser, model);
-}
-
-START_TEST(test_dtd_elements_nesting) {
-  // Payload inspired by a test in Perl's XML::Parser
-  const char *text = "<!DOCTYPE foo [\n"
-                     "<!ELEMENT junk ((bar|foo|xyz+), zebra*)>\n"
-                     "]>\n"
-                     "<foo/>";
-
-  XML_SetUserData(g_parser, (void *)(uintptr_t)-1);
-
-  XML_SetElementDeclHandler(g_parser, element_decl_check_model);
-  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-
-  if ((uint32_t)(uintptr_t)XML_GetUserData(g_parser) != 0)
-    fail("Element declaration model regression detected");
-}
-END_TEST
-
-/* Test foreign DTD handling */
-START_TEST(test_set_foreign_dtd) {
-  const char *text1 = "<?xml version='1.0' encoding='us-ascii'?>\n";
-  const char *text2 = "<doc>&entity;</doc>";
-  ExtTest test_data = {"<!ELEMENT doc (#PCDATA)*>", NULL, NULL};
-
-  /* Check hash salt is passed through too */
-  XML_SetHashSalt(g_parser, 0x12345678);
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetUserData(g_parser, &test_data);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
-  /* Add a default handler to exercise more code paths */
-  XML_SetDefaultHandler(g_parser, dummy_default_handler);
-  if (XML_UseForeignDTD(g_parser, XML_TRUE) != XML_ERROR_NONE)
-    fail("Could not set foreign DTD");
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text1, (int)strlen(text1), XML_FALSE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-
-  /* Ensure that trying to set the DTD after parsing has started
-   * is faulted, even if it's the same setting.
-   */
-  if (XML_UseForeignDTD(g_parser, XML_TRUE)
-      != XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING)
-    fail("Failed to reject late foreign DTD setting");
-  /* Ditto for the hash salt */
-  if (XML_SetHashSalt(g_parser, 0x23456789))
-    fail("Failed to reject late hash salt change");
-
-  /* Now finish the parse */
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text2, (int)strlen(text2), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test foreign DTD handling with a failing NotStandalone handler */
-START_TEST(test_foreign_dtd_not_standalone) {
-  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
-                     "<doc>&entity;</doc>";
-  ExtTest test_data = {"<!ELEMENT doc (#PCDATA)*>", NULL, NULL};
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetUserData(g_parser, &test_data);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
-  XML_SetNotStandaloneHandler(g_parser, reject_not_standalone_handler);
-  if (XML_UseForeignDTD(g_parser, XML_TRUE) != XML_ERROR_NONE)
-    fail("Could not set foreign DTD");
-  expect_failure(text, XML_ERROR_NOT_STANDALONE,
-                 "NotStandalonehandler failed to reject");
-}
-END_TEST
-
-/* Test invalid character in a foreign DTD is faulted */
-START_TEST(test_invalid_foreign_dtd) {
-  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
-                     "<doc>&entity;</doc>";
-  ExtFaults test_data
-      = {"$", "Dollar not faulted", NULL, XML_ERROR_INVALID_TOKEN};
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetUserData(g_parser, &test_data);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_faulter);
-  XML_UseForeignDTD(g_parser, XML_TRUE);
-  expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
-                 "Bad DTD should not have been accepted");
-}
-END_TEST
-
-/* Test foreign DTD use with a doctype */
-START_TEST(test_foreign_dtd_with_doctype) {
-  const char *text1 = "<?xml version='1.0' encoding='us-ascii'?>\n"
-                      "<!DOCTYPE doc [<!ENTITY entity 'hello world'>]>\n";
-  const char *text2 = "<doc>&entity;</doc>";
-  ExtTest test_data = {"<!ELEMENT doc (#PCDATA)*>", NULL, NULL};
-
-  /* Check hash salt is passed through too */
-  XML_SetHashSalt(g_parser, 0x12345678);
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetUserData(g_parser, &test_data);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
-  /* Add a default handler to exercise more code paths */
-  XML_SetDefaultHandler(g_parser, dummy_default_handler);
-  if (XML_UseForeignDTD(g_parser, XML_TRUE) != XML_ERROR_NONE)
-    fail("Could not set foreign DTD");
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text1, (int)strlen(text1), XML_FALSE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-
-  /* Ensure that trying to set the DTD after parsing has started
-   * is faulted, even if it's the same setting.
-   */
-  if (XML_UseForeignDTD(g_parser, XML_TRUE)
-      != XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING)
-    fail("Failed to reject late foreign DTD setting");
-  /* Ditto for the hash salt */
-  if (XML_SetHashSalt(g_parser, 0x23456789))
-    fail("Failed to reject late hash salt change");
-
-  /* Now finish the parse */
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text2, (int)strlen(text2), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test XML_UseForeignDTD with no external subset present */
-static int XMLCALL
-external_entity_null_loader(XML_Parser parser, const XML_Char *context,
-                            const XML_Char *base, const XML_Char *systemId,
-                            const XML_Char *publicId) {
-  UNUSED_P(parser);
-  UNUSED_P(context);
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_foreign_dtd_without_external_subset) {
-  const char *text = "<!DOCTYPE doc [<!ENTITY foo 'bar'>]>\n"
-                     "<doc>&foo;</doc>";
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetUserData(g_parser, NULL);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_null_loader);
-  XML_UseForeignDTD(g_parser, XML_TRUE);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-START_TEST(test_empty_foreign_dtd) {
-  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
-                     "<doc>&entity;</doc>";
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_null_loader);
-  XML_UseForeignDTD(g_parser, XML_TRUE);
-  expect_failure(text, XML_ERROR_UNDEFINED_ENTITY,
-                 "Undefined entity not faulted");
-}
-END_TEST
-
-/* Test XML Base is set and unset appropriately */
-START_TEST(test_set_base) {
-  const XML_Char *old_base;
-  const XML_Char *new_base = XCS("/local/file/name.xml");
-
-  old_base = XML_GetBase(g_parser);
-  if (XML_SetBase(g_parser, new_base) != XML_STATUS_OK)
-    fail("Unable to set base");
-  if (xcstrcmp(XML_GetBase(g_parser), new_base) != 0)
-    fail("Base setting not correct");
-  if (XML_SetBase(g_parser, NULL) != XML_STATUS_OK)
-    fail("Unable to NULL base");
-  if (XML_GetBase(g_parser) != NULL)
-    fail("Base setting not nulled");
-  XML_SetBase(g_parser, old_base);
-}
-END_TEST
-
-/* Test attribute counts, indexing, etc */
-typedef struct attrInfo {
-  const XML_Char *name;
-  const XML_Char *value;
-} AttrInfo;
-
-typedef struct elementInfo {
-  const XML_Char *name;
-  int attr_count;
-  const XML_Char *id_name;
-  AttrInfo *attributes;
-} ElementInfo;
-
-static void XMLCALL
-counting_start_element_handler(void *userData, const XML_Char *name,
-                               const XML_Char **atts) {
-  ElementInfo *info = (ElementInfo *)userData;
-  AttrInfo *attr;
-  int count, id, i;
-
-  while (info->name != NULL) {
-    if (! xcstrcmp(name, info->name))
-      break;
-    info++;
-  }
-  if (info->name == NULL)
-    fail("Element not recognised");
-  /* The attribute count is twice what you might expect.  It is a
-   * count of items in atts, an array which contains alternating
-   * attribute names and attribute values.  For the naive user this
-   * is possibly a little unexpected, but it is what the
-   * documentation in expat.h tells us to expect.
-   */
-  count = XML_GetSpecifiedAttributeCount(g_parser);
-  if (info->attr_count * 2 != count) {
-    fail("Not got expected attribute count");
-    return;
-  }
-  id = XML_GetIdAttributeIndex(g_parser);
-  if (id == -1 && info->id_name != NULL) {
-    fail("ID not present");
-    return;
-  }
-  if (id != -1 && xcstrcmp(atts[id], info->id_name)) {
-    fail("ID does not have the correct name");
-    return;
-  }
-  for (i = 0; i < info->attr_count; i++) {
-    attr = info->attributes;
-    while (attr->name != NULL) {
-      if (! xcstrcmp(atts[0], attr->name))
-        break;
-      attr++;
-    }
-    if (attr->name == NULL) {
-      fail("Attribute not recognised");
-      return;
-    }
-    if (xcstrcmp(atts[1], attr->value)) {
-      fail("Attribute has wrong value");
-      return;
-    }
-    /* Remember, two entries in atts per attribute (see above) */
-    atts += 2;
-  }
-}
-
-START_TEST(test_attributes) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "<!ELEMENT doc (tag)>\n"
-                     "<!ATTLIST doc id ID #REQUIRED>\n"
-                     "]>"
-                     "<doc a='1' id='one' b='2'>"
-                     "<tag c='3'/>"
-                     "</doc>";
-  AttrInfo doc_info[] = {{XCS("a"), XCS("1")},
-                         {XCS("b"), XCS("2")},
-                         {XCS("id"), XCS("one")},
-                         {NULL, NULL}};
-  AttrInfo tag_info[] = {{XCS("c"), XCS("3")}, {NULL, NULL}};
-  ElementInfo info[] = {{XCS("doc"), 3, XCS("id"), NULL},
-                        {XCS("tag"), 1, NULL, NULL},
-                        {NULL, 0, NULL, NULL}};
-  info[0].attributes = doc_info;
-  info[1].attributes = tag_info;
-
-  XML_SetStartElementHandler(g_parser, counting_start_element_handler);
-  XML_SetUserData(g_parser, info);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test reset works correctly in the middle of processing an internal
- * entity.  Exercises some obscure code in XML_ParserReset().
- */
-START_TEST(test_reset_in_entity) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "<!ENTITY wombat 'wom'>\n"
-                     "<!ENTITY entity 'hi &wom; there'>\n"
-                     "]>\n"
-                     "<doc>&entity;</doc>";
-  XML_ParsingStatus status;
-
-  resumable = XML_TRUE;
-  XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  XML_GetParsingStatus(g_parser, &status);
-  if (status.parsing != XML_SUSPENDED)
-    fail("Parsing status not SUSPENDED");
-  XML_ParserReset(g_parser, NULL);
-  XML_GetParsingStatus(g_parser, &status);
-  if (status.parsing != XML_INITIALIZED)
-    fail("Parsing status doesn't reset to INITIALIZED");
-}
-END_TEST
-
-/* Test that resume correctly passes through parse errors */
-START_TEST(test_resume_invalid_parse) {
-  const char *text = "<doc>Hello</doc"; /* Missing closing wedge */
-
-  resumable = XML_TRUE;
-  XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler);
-  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  if (XML_ResumeParser(g_parser) == XML_STATUS_OK)
-    fail("Resumed invalid parse not faulted");
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_UNCLOSED_TOKEN)
-    fail("Invalid parse not correctly faulted");
-}
-END_TEST
-
-/* Test that re-suspended parses are correctly passed through */
-START_TEST(test_resume_resuspended) {
-  const char *text = "<doc>Hello<meep/>world</doc>";
-
-  resumable = XML_TRUE;
-  XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler);
-  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  resumable = XML_TRUE;
-  XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler);
-  if (XML_ResumeParser(g_parser) != XML_STATUS_SUSPENDED)
-    fail("Resumption not suspended");
-  /* This one should succeed and finish up */
-  if (XML_ResumeParser(g_parser) != XML_STATUS_OK)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test that CDATA shows up correctly through a default handler */
-START_TEST(test_cdata_default) {
-  const char *text = "<doc><![CDATA[Hello\nworld]]></doc>";
-  const XML_Char *expected = XCS("<doc><![CDATA[Hello\nworld]]></doc>");
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  XML_SetDefaultHandler(g_parser, accumulate_characters);
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-/* Test resetting a subordinate parser does exactly nothing */
-static int XMLCALL
-external_entity_resetter(XML_Parser parser, const XML_Char *context,
-                         const XML_Char *base, const XML_Char *systemId,
-                         const XML_Char *publicId) {
-  const char *text = "<!ELEMENT doc (#PCDATA)*>";
-  XML_Parser ext_parser;
-  XML_ParsingStatus status;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    fail("Could not create external entity parser");
-  XML_GetParsingStatus(ext_parser, &status);
-  if (status.parsing != XML_INITIALIZED) {
-    fail("Parsing status is not INITIALIZED");
-    return XML_STATUS_ERROR;
-  }
-  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR) {
-    xml_failure(parser);
-    return XML_STATUS_ERROR;
-  }
-  XML_GetParsingStatus(ext_parser, &status);
-  if (status.parsing != XML_FINISHED) {
-    fail("Parsing status is not FINISHED");
-    return XML_STATUS_ERROR;
-  }
-  /* Check we can't parse here */
-  if (XML_Parse(ext_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_ERROR)
-    fail("Parsing when finished not faulted");
-  if (XML_GetErrorCode(ext_parser) != XML_ERROR_FINISHED)
-    fail("Parsing when finished faulted with wrong code");
-  XML_ParserReset(ext_parser, NULL);
-  XML_GetParsingStatus(ext_parser, &status);
-  if (status.parsing != XML_FINISHED) {
-    fail("Parsing status not still FINISHED");
-    return XML_STATUS_ERROR;
-  }
-  XML_ParserFree(ext_parser);
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_subordinate_reset) {
-  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
-                     "<!DOCTYPE doc SYSTEM 'foo'>\n"
-                     "<doc>&entity;</doc>";
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_resetter);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test suspending a subordinate parser */
-
-static void XMLCALL
-entity_suspending_decl_handler(void *userData, const XML_Char *name,
-                               XML_Content *model) {
-  XML_Parser ext_parser = (XML_Parser)userData;
-
-  UNUSED_P(name);
-  if (XML_StopParser(ext_parser, XML_TRUE) != XML_STATUS_ERROR)
-    fail("Attempting to suspend a subordinate parser not faulted");
-  if (XML_GetErrorCode(ext_parser) != XML_ERROR_SUSPEND_PE)
-    fail("Suspending subordinate parser get wrong code");
-  XML_SetElementDeclHandler(ext_parser, NULL);
-  XML_FreeContentModel(g_parser, model);
-}
-
-static int XMLCALL
-external_entity_suspender(XML_Parser parser, const XML_Char *context,
-                          const XML_Char *base, const XML_Char *systemId,
-                          const XML_Char *publicId) {
-  const char *text = "<!ELEMENT doc (#PCDATA)*>";
-  XML_Parser ext_parser;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    fail("Could not create external entity parser");
-  XML_SetElementDeclHandler(ext_parser, entity_suspending_decl_handler);
-  XML_SetUserData(ext_parser, ext_parser);
-  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR) {
-    xml_failure(ext_parser);
-    return XML_STATUS_ERROR;
-  }
-  XML_ParserFree(ext_parser);
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_subordinate_suspend) {
-  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
-                     "<!DOCTYPE doc SYSTEM 'foo'>\n"
-                     "<doc>&entity;</doc>";
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_suspender);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test suspending a subordinate parser from an XML declaration */
-/* Increases code coverage of the tests */
-static void XMLCALL
-entity_suspending_xdecl_handler(void *userData, const XML_Char *version,
-                                const XML_Char *encoding, int standalone) {
-  XML_Parser ext_parser = (XML_Parser)userData;
-
-  UNUSED_P(version);
-  UNUSED_P(encoding);
-  UNUSED_P(standalone);
-  XML_StopParser(ext_parser, resumable);
-  XML_SetXmlDeclHandler(ext_parser, NULL);
-}
-
-static int XMLCALL
-external_entity_suspend_xmldecl(XML_Parser parser, const XML_Char *context,
-                                const XML_Char *base, const XML_Char *systemId,
-                                const XML_Char *publicId) {
-  const char *text = "<?xml version='1.0' encoding='us-ascii'?>";
-  XML_Parser ext_parser;
-  XML_ParsingStatus status;
-  enum XML_Status rc;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    fail("Could not create external entity parser");
-  XML_SetXmlDeclHandler(ext_parser, entity_suspending_xdecl_handler);
-  XML_SetUserData(ext_parser, ext_parser);
-  rc = _XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE);
-  XML_GetParsingStatus(ext_parser, &status);
-  if (resumable) {
-    if (rc == XML_STATUS_ERROR)
-      xml_failure(ext_parser);
-    if (status.parsing != XML_SUSPENDED)
-      fail("Ext Parsing status not SUSPENDED");
-  } else {
-    if (rc != XML_STATUS_ERROR)
-      fail("Ext parsing not aborted");
-    if (XML_GetErrorCode(ext_parser) != XML_ERROR_ABORTED)
-      xml_failure(ext_parser);
-    if (status.parsing != XML_FINISHED)
-      fail("Ext Parsing status not FINISHED");
-  }
-
-  XML_ParserFree(ext_parser);
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_subordinate_xdecl_suspend) {
-  const char *text
-      = "<!DOCTYPE doc [\n"
-        "  <!ENTITY entity SYSTEM 'http://example.org/dummy.ent'>\n"
-        "]>\n"
-        "<doc>&entity;</doc>";
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_suspend_xmldecl);
-  resumable = XML_TRUE;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-START_TEST(test_subordinate_xdecl_abort) {
-  const char *text
-      = "<!DOCTYPE doc [\n"
-        "  <!ENTITY entity SYSTEM 'http://example.org/dummy.ent'>\n"
-        "]>\n"
-        "<doc>&entity;</doc>";
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_suspend_xmldecl);
-  resumable = XML_FALSE;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test external entity fault handling with suspension */
-static int XMLCALL
-external_entity_suspending_faulter(XML_Parser parser, const XML_Char *context,
-                                   const XML_Char *base,
-                                   const XML_Char *systemId,
-                                   const XML_Char *publicId) {
-  XML_Parser ext_parser;
-  ExtFaults *fault = (ExtFaults *)XML_GetUserData(parser);
-  void *buffer;
-  int parse_len = (int)strlen(fault->parse_text);
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    fail("Could not create external entity parser");
-  XML_SetXmlDeclHandler(ext_parser, entity_suspending_xdecl_handler);
-  XML_SetUserData(ext_parser, ext_parser);
-  resumable = XML_TRUE;
-  buffer = XML_GetBuffer(ext_parser, parse_len);
-  if (buffer == NULL)
-    fail("Could not allocate parse buffer");
-  assert(buffer != NULL);
-  memcpy(buffer, fault->parse_text, parse_len);
-  if (XML_ParseBuffer(ext_parser, parse_len, XML_FALSE) != XML_STATUS_SUSPENDED)
-    fail("XML declaration did not suspend");
-  if (XML_ResumeParser(ext_parser) != XML_STATUS_OK)
-    xml_failure(ext_parser);
-  if (XML_ParseBuffer(ext_parser, 0, XML_TRUE) != XML_STATUS_ERROR)
-    fail(fault->fail_text);
-  if (XML_GetErrorCode(ext_parser) != fault->error)
-    xml_failure(ext_parser);
-
-  XML_ParserFree(ext_parser);
-  return XML_STATUS_ERROR;
-}
-
-START_TEST(test_ext_entity_invalid_suspended_parse) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
-                     "]>\n"
-                     "<doc>&en;</doc>";
-  ExtFaults faults[]
-      = {{"<?xml version='1.0' encoding='us-ascii'?><",
-          "Incomplete element declaration not faulted", NULL,
-          XML_ERROR_UNCLOSED_TOKEN},
-         {/* First two bytes of a three-byte char */
-          "<?xml version='1.0' encoding='utf-8'?>\xe2\x82",
-          "Incomplete character not faulted", NULL, XML_ERROR_PARTIAL_CHAR},
-         {NULL, NULL, NULL, XML_ERROR_NONE}};
-  ExtFaults *fault;
-
-  for (fault = &faults[0]; fault->parse_text != NULL; fault++) {
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    XML_SetExternalEntityRefHandler(g_parser,
-                                    external_entity_suspending_faulter);
-    XML_SetUserData(g_parser, fault);
-    expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
-                   "Parser did not report external entity error");
-    XML_ParserReset(g_parser, NULL);
-  }
-}
-END_TEST
-
-/* Test setting an explicit encoding */
-START_TEST(test_explicit_encoding) {
-  const char *text1 = "<doc>Hello ";
-  const char *text2 = " World</doc>";
-
-  /* Just check that we can set the encoding to NULL before starting */
-  if (XML_SetEncoding(g_parser, NULL) != XML_STATUS_OK)
-    fail("Failed to initialise encoding to NULL");
-  /* Say we are UTF-8 */
-  if (XML_SetEncoding(g_parser, XCS("utf-8")) != XML_STATUS_OK)
-    fail("Failed to set explicit encoding");
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text1, (int)strlen(text1), XML_FALSE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  /* Try to switch encodings mid-parse */
-  if (XML_SetEncoding(g_parser, XCS("us-ascii")) != XML_STATUS_ERROR)
-    fail("Allowed encoding change");
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text2, (int)strlen(text2), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  /* Try now the parse is over */
-  if (XML_SetEncoding(g_parser, NULL) != XML_STATUS_OK)
-    fail("Failed to unset encoding");
-}
-END_TEST
-
-/* Test handling of trailing CR (rather than newline) */
-static void XMLCALL
-cr_cdata_handler(void *userData, const XML_Char *s, int len) {
-  int *pfound = (int *)userData;
-
-  /* Internal processing turns the CR into a newline for the
-   * character data handler, but not for the default handler
-   */
-  if (len == 1 && (*s == XCS('\n') || *s == XCS('\r')))
-    *pfound = 1;
-}
-
-START_TEST(test_trailing_cr) {
-  const char *text = "<doc>\r";
-  int found_cr;
-
-  /* Try with a character handler, for code coverage */
-  XML_SetCharacterDataHandler(g_parser, cr_cdata_handler);
-  XML_SetUserData(g_parser, &found_cr);
-  found_cr = 0;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_OK)
-    fail("Failed to fault unclosed doc");
-  if (found_cr == 0)
-    fail("Did not catch the carriage return");
-  XML_ParserReset(g_parser, NULL);
-
-  /* Now with a default handler instead */
-  XML_SetDefaultHandler(g_parser, cr_cdata_handler);
-  XML_SetUserData(g_parser, &found_cr);
-  found_cr = 0;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_OK)
-    fail("Failed to fault unclosed doc");
-  if (found_cr == 0)
-    fail("Did not catch default carriage return");
-}
-END_TEST
-
-/* Test trailing CR in an external entity parse */
-static int XMLCALL
-external_entity_cr_catcher(XML_Parser parser, const XML_Char *context,
-                           const XML_Char *base, const XML_Char *systemId,
-                           const XML_Char *publicId) {
-  const char *text = "\r";
-  XML_Parser ext_parser;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    fail("Could not create external entity parser");
-  XML_SetCharacterDataHandler(ext_parser, cr_cdata_handler);
-  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(ext_parser);
-  XML_ParserFree(ext_parser);
-  return XML_STATUS_OK;
-}
-
-static int XMLCALL
-external_entity_bad_cr_catcher(XML_Parser parser, const XML_Char *context,
-                               const XML_Char *base, const XML_Char *systemId,
-                               const XML_Char *publicId) {
-  const char *text = "<tag>\r";
-  XML_Parser ext_parser;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    fail("Could not create external entity parser");
-  XML_SetCharacterDataHandler(ext_parser, cr_cdata_handler);
-  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_OK)
-    fail("Async entity error not caught");
-  if (XML_GetErrorCode(ext_parser) != XML_ERROR_ASYNC_ENTITY)
-    xml_failure(ext_parser);
-  XML_ParserFree(ext_parser);
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_ext_entity_trailing_cr) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
-                     "]>\n"
-                     "<doc>&en;</doc>";
-  int found_cr;
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_cr_catcher);
-  XML_SetUserData(g_parser, &found_cr);
-  found_cr = 0;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_OK)
-    xml_failure(g_parser);
-  if (found_cr == 0)
-    fail("No carriage return found");
-  XML_ParserReset(g_parser, NULL);
-
-  /* Try again with a different trailing CR */
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_bad_cr_catcher);
-  XML_SetUserData(g_parser, &found_cr);
-  found_cr = 0;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_OK)
-    xml_failure(g_parser);
-  if (found_cr == 0)
-    fail("No carriage return found");
-}
-END_TEST
-
-/* Test handling of trailing square bracket */
-static void XMLCALL
-rsqb_handler(void *userData, const XML_Char *s, int len) {
-  int *pfound = (int *)userData;
-
-  if (len == 1 && *s == XCS(']'))
-    *pfound = 1;
-}
-
-START_TEST(test_trailing_rsqb) {
-  const char *text8 = "<doc>]";
-  const char text16[] = "\xFF\xFE<\000d\000o\000c\000>\000]\000";
-  int found_rsqb;
-  int text8_len = (int)strlen(text8);
-
-  XML_SetCharacterDataHandler(g_parser, rsqb_handler);
-  XML_SetUserData(g_parser, &found_rsqb);
-  found_rsqb = 0;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text8, text8_len, XML_TRUE)
-      == XML_STATUS_OK)
-    fail("Failed to fault unclosed doc");
-  if (found_rsqb == 0)
-    fail("Did not catch the right square bracket");
-
-  /* Try again with a different encoding */
-  XML_ParserReset(g_parser, NULL);
-  XML_SetCharacterDataHandler(g_parser, rsqb_handler);
-  XML_SetUserData(g_parser, &found_rsqb);
-  found_rsqb = 0;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text16, (int)sizeof(text16) - 1,
-                              XML_TRUE)
-      == XML_STATUS_OK)
-    fail("Failed to fault unclosed doc");
-  if (found_rsqb == 0)
-    fail("Did not catch the right square bracket");
-
-  /* And finally with a default handler */
-  XML_ParserReset(g_parser, NULL);
-  XML_SetDefaultHandler(g_parser, rsqb_handler);
-  XML_SetUserData(g_parser, &found_rsqb);
-  found_rsqb = 0;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text16, (int)sizeof(text16) - 1,
-                              XML_TRUE)
-      == XML_STATUS_OK)
-    fail("Failed to fault unclosed doc");
-  if (found_rsqb == 0)
-    fail("Did not catch the right square bracket");
-}
-END_TEST
-
-/* Test trailing right square bracket in an external entity parse */
-static int XMLCALL
-external_entity_rsqb_catcher(XML_Parser parser, const XML_Char *context,
-                             const XML_Char *base, const XML_Char *systemId,
-                             const XML_Char *publicId) {
-  const char *text = "<tag>]";
-  XML_Parser ext_parser;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    fail("Could not create external entity parser");
-  XML_SetCharacterDataHandler(ext_parser, rsqb_handler);
-  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_ERROR)
-    fail("Async entity error not caught");
-  if (XML_GetErrorCode(ext_parser) != XML_ERROR_ASYNC_ENTITY)
-    xml_failure(ext_parser);
-  XML_ParserFree(ext_parser);
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_ext_entity_trailing_rsqb) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
-                     "]>\n"
-                     "<doc>&en;</doc>";
-  int found_rsqb;
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_rsqb_catcher);
-  XML_SetUserData(g_parser, &found_rsqb);
-  found_rsqb = 0;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_OK)
-    xml_failure(g_parser);
-  if (found_rsqb == 0)
-    fail("No right square bracket found");
-}
-END_TEST
-
-/* Test CDATA handling in an external entity */
-static int XMLCALL
-external_entity_good_cdata_ascii(XML_Parser parser, const XML_Char *context,
-                                 const XML_Char *base, const XML_Char *systemId,
-                                 const XML_Char *publicId) {
-  const char *text = "<a><![CDATA[<greeting>Hello, world!</greeting>]]></a>";
-  const XML_Char *expected = XCS("<greeting>Hello, world!</greeting>");
-  CharData storage;
-  XML_Parser ext_parser;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  CharData_Init(&storage);
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    fail("Could not create external entity parser");
-  XML_SetUserData(ext_parser, &storage);
-  XML_SetCharacterDataHandler(ext_parser, accumulate_characters);
-
-  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(ext_parser);
-  CharData_CheckXMLChars(&storage, expected);
-
-  XML_ParserFree(ext_parser);
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_ext_entity_good_cdata) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
-                     "]>\n"
-                     "<doc>&en;</doc>";
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_good_cdata_ascii);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_OK)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test user parameter settings */
-/* Variable holding the expected handler userData */
-static void *handler_data = NULL;
-/* Count of the number of times the comment handler has been invoked */
-static int comment_count = 0;
-/* Count of the number of skipped entities */
-static int skip_count = 0;
-/* Count of the number of times the XML declaration handler is invoked */
-static int xdecl_count = 0;
-
-static void XMLCALL
-xml_decl_handler(void *userData, const XML_Char *version,
-                 const XML_Char *encoding, int standalone) {
-  UNUSED_P(version);
-  UNUSED_P(encoding);
-  if (userData != handler_data)
-    fail("User data (xml decl) not correctly set");
-  if (standalone != -1)
-    fail("Standalone not flagged as not present in XML decl");
-  xdecl_count++;
-}
-
-static void XMLCALL
-param_check_skip_handler(void *userData, const XML_Char *entityName,
-                         int is_parameter_entity) {
-  UNUSED_P(entityName);
-  UNUSED_P(is_parameter_entity);
-  if (userData != handler_data)
-    fail("User data (skip) not correctly set");
-  skip_count++;
-}
-
-static void XMLCALL
-data_check_comment_handler(void *userData, const XML_Char *data) {
-  UNUSED_P(data);
-  /* Check that the userData passed through is what we expect */
-  if (userData != handler_data)
-    fail("User data (parser) not correctly set");
-  /* Check that the user data in the parser is appropriate */
-  if (XML_GetUserData(userData) != (void *)1)
-    fail("User data in parser not correctly set");
-  comment_count++;
-}
-
-static int XMLCALL
-external_entity_param_checker(XML_Parser parser, const XML_Char *context,
-                              const XML_Char *base, const XML_Char *systemId,
-                              const XML_Char *publicId) {
-  const char *text = "<!-- Subordinate parser -->\n"
-                     "<!ELEMENT doc (#PCDATA)*>";
-  XML_Parser ext_parser;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    fail("Could not create external entity parser");
-  handler_data = ext_parser;
-  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR) {
-    xml_failure(parser);
-    return XML_STATUS_ERROR;
-  }
-  handler_data = parser;
-  XML_ParserFree(ext_parser);
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_user_parameters) {
-  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
-                     "<!-- Primary parse -->\n"
-                     "<!DOCTYPE doc SYSTEM 'foo'>\n"
-                     "<doc>&entity;";
-  const char *epilog = "<!-- Back to primary parser -->\n"
-                       "</doc>";
-
-  comment_count = 0;
-  skip_count = 0;
-  xdecl_count = 0;
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetXmlDeclHandler(g_parser, xml_decl_handler);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_param_checker);
-  XML_SetCommentHandler(g_parser, data_check_comment_handler);
-  XML_SetSkippedEntityHandler(g_parser, param_check_skip_handler);
-  XML_UseParserAsHandlerArg(g_parser);
-  XML_SetUserData(g_parser, (void *)1);
-  handler_data = g_parser;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  if (comment_count != 2)
-    fail("Comment handler not invoked enough times");
-  /* Ensure we can't change policy mid-parse */
-  if (XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_NEVER))
-    fail("Changed param entity parsing policy while parsing");
-  if (_XML_Parse_SINGLE_BYTES(g_parser, epilog, (int)strlen(epilog), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  if (comment_count != 3)
-    fail("Comment handler not invoked enough times");
-  if (skip_count != 1)
-    fail("Skip handler not invoked enough times");
-  if (xdecl_count != 1)
-    fail("XML declaration handler not invoked");
-}
-END_TEST
-
-/* Test that an explicit external entity handler argument replaces
- * the parser as the first argument.
- *
- * We do not call the first parameter to the external entity handler
- * 'parser' for once, since the first time the handler is called it
- * will actually be a text string.  We need to be able to access the
- * global 'parser' variable to create our external entity parser from,
- * since there are code paths we need to ensure get executed.
- */
-static int XMLCALL
-external_entity_ref_param_checker(XML_Parser parameter, const XML_Char *context,
-                                  const XML_Char *base,
-                                  const XML_Char *systemId,
-                                  const XML_Char *publicId) {
-  const char *text = "<!ELEMENT doc (#PCDATA)*>";
-  XML_Parser ext_parser;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  if ((void *)parameter != handler_data)
-    fail("External entity ref handler parameter not correct");
-
-  /* Here we use the global 'parser' variable */
-  ext_parser = XML_ExternalEntityParserCreate(g_parser, context, NULL);
-  if (ext_parser == NULL)
-    fail("Could not create external entity parser");
-  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(ext_parser);
-
-  XML_ParserFree(ext_parser);
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_ext_entity_ref_parameter) {
-  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
-                     "<!DOCTYPE doc SYSTEM 'foo'>\n"
-                     "<doc>&entity;</doc>";
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_ref_param_checker);
-  /* Set a handler arg that is not NULL and not parser (which is
-   * what NULL would cause to be passed.
-   */
-  XML_SetExternalEntityRefHandlerArg(g_parser, (void *)text);
-  handler_data = (void *)text;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-
-  /* Now try again with unset args */
-  XML_ParserReset(g_parser, NULL);
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_ref_param_checker);
-  XML_SetExternalEntityRefHandlerArg(g_parser, NULL);
-  handler_data = (void *)g_parser;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test the parsing of an empty string */
-START_TEST(test_empty_parse) {
-  const char *text = "<doc></doc>";
-  const char *partial = "<doc>";
-
-  if (XML_Parse(g_parser, NULL, 0, XML_FALSE) == XML_STATUS_ERROR)
-    fail("Parsing empty string faulted");
-  if (XML_Parse(g_parser, NULL, 0, XML_TRUE) != XML_STATUS_ERROR)
-    fail("Parsing final empty string not faulted");
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_NO_ELEMENTS)
-    fail("Parsing final empty string faulted for wrong reason");
-
-  /* Now try with valid text before the empty end */
-  XML_ParserReset(g_parser, NULL);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  if (XML_Parse(g_parser, NULL, 0, XML_TRUE) == XML_STATUS_ERROR)
-    fail("Parsing final empty string faulted");
-
-  /* Now try with invalid text before the empty end */
-  XML_ParserReset(g_parser, NULL);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, partial, (int)strlen(partial),
-                              XML_FALSE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  if (XML_Parse(g_parser, NULL, 0, XML_TRUE) != XML_STATUS_ERROR)
-    fail("Parsing final incomplete empty string not faulted");
-}
-END_TEST
-
-/* Test odd corners of the XML_GetBuffer interface */
-static enum XML_Status
-get_feature(enum XML_FeatureEnum feature_id, long *presult) {
-  const XML_Feature *feature = XML_GetFeatureList();
-
-  if (feature == NULL)
-    return XML_STATUS_ERROR;
-  for (; feature->feature != XML_FEATURE_END; feature++) {
-    if (feature->feature == feature_id) {
-      *presult = feature->value;
-      return XML_STATUS_OK;
-    }
-  }
-  return XML_STATUS_ERROR;
-}
-
-/* Having an element name longer than 1024 characters exercises some
- * of the pool allocation code in the parser that otherwise does not
- * get executed.  The count at the end of the line is the number of
- * characters (bytes) in the element name by that point.x
- */
-static const char *get_buffer_test_text
-    = "<documentwitharidiculouslylongelementnametotease"  /* 0x030 */
-      "aparticularcorneroftheallocationinXML_GetBuffers"  /* 0x060 */
-      "othatwecanimprovethecoverageyetagain012345678901"  /* 0x090 */
-      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x0c0 */
-      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x0f0 */
-      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x120 */
-      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x150 */
-      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x180 */
-      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x1b0 */
-      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x1e0 */
-      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x210 */
-      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x240 */
-      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x270 */
-      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x2a0 */
-      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x2d0 */
-      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x300 */
-      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x330 */
-      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x360 */
-      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x390 */
-      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x3c0 */
-      "123456789abcdef0123456789abcdef0123456789abcdef0"  /* 0x3f0 */
-      "123456789abcdef0123456789abcdef0123456789>\n<ef0"; /* 0x420 */
-
-/* Test odd corners of the XML_GetBuffer interface */
-START_TEST(test_get_buffer_1) {
-  const char *text = get_buffer_test_text;
-  void *buffer;
-  long context_bytes;
-
-  /* Attempt to allocate a negative length buffer */
-  if (XML_GetBuffer(g_parser, -12) != NULL)
-    fail("Negative length buffer not failed");
-
-  /* Now get a small buffer and extend it past valid length */
-  buffer = XML_GetBuffer(g_parser, 1536);
-  if (buffer == NULL)
-    fail("1.5K buffer failed");
-  assert(buffer != NULL);
-  memcpy(buffer, text, strlen(text));
-  if (XML_ParseBuffer(g_parser, (int)strlen(text), XML_FALSE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  if (XML_GetBuffer(g_parser, INT_MAX) != NULL)
-    fail("INT_MAX buffer not failed");
-
-  /* Now try extending it a more reasonable but still too large
-   * amount.  The allocator in XML_GetBuffer() doubles the buffer
-   * size until it exceeds the requested amount or INT_MAX.  If it
-   * exceeds INT_MAX, it rejects the request, so we want a request
-   * between INT_MAX and INT_MAX/2.  A gap of 1K seems comfortable,
-   * with an extra byte just to ensure that the request is off any
-   * boundary.  The request will be inflated internally by
-   * XML_CONTEXT_BYTES (if defined), so we subtract that from our
-   * request.
-   */
-  if (get_feature(XML_FEATURE_CONTEXT_BYTES, &context_bytes) != XML_STATUS_OK)
-    context_bytes = 0;
-  if (XML_GetBuffer(g_parser, INT_MAX - (context_bytes + 1025)) != NULL)
-    fail("INT_MAX- buffer not failed");
-
-  /* Now try extending it a carefully crafted amount */
-  if (XML_GetBuffer(g_parser, 1000) == NULL)
-    fail("1000 buffer failed");
-}
-END_TEST
-
-/* Test more corners of the XML_GetBuffer interface */
-START_TEST(test_get_buffer_2) {
-  const char *text = get_buffer_test_text;
-  void *buffer;
-
-  /* Now get a decent buffer */
-  buffer = XML_GetBuffer(g_parser, 1536);
-  if (buffer == NULL)
-    fail("1.5K buffer failed");
-  assert(buffer != NULL);
-  memcpy(buffer, text, strlen(text));
-  if (XML_ParseBuffer(g_parser, (int)strlen(text), XML_FALSE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-
-  /* Extend it, to catch a different code path */
-  if (XML_GetBuffer(g_parser, 1024) == NULL)
-    fail("1024 buffer failed");
-}
-END_TEST
-
-/* Test for signed integer overflow CVE-2022-23852 */
-#if defined(XML_CONTEXT_BYTES)
-START_TEST(test_get_buffer_3_overflow) {
-  XML_Parser parser = XML_ParserCreate(NULL);
-  assert(parser != NULL);
-
-  const char *const text = "\n";
-  const int expectedKeepValue = (int)strlen(text);
-
-  // After this call, variable "keep" in XML_GetBuffer will
-  // have value expectedKeepValue
-  if (XML_Parse(parser, text, (int)strlen(text), XML_FALSE /* isFinal */)
-      == XML_STATUS_ERROR)
-    xml_failure(parser);
-
-  assert(expectedKeepValue > 0);
-  if (XML_GetBuffer(parser, INT_MAX - expectedKeepValue + 1) != NULL)
-    fail("enlarging buffer not failed");
-
-  XML_ParserFree(parser);
-}
-END_TEST
-#endif // defined(XML_CONTEXT_BYTES)
-
-/* Test position information macros */
-START_TEST(test_byte_info_at_end) {
-  const char *text = "<doc></doc>";
-
-  if (XML_GetCurrentByteIndex(g_parser) != -1
-      || XML_GetCurrentByteCount(g_parser) != 0)
-    fail("Byte index/count incorrect at start of parse");
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  /* At end, the count will be zero and the index the end of string */
-  if (XML_GetCurrentByteCount(g_parser) != 0)
-    fail("Terminal byte count incorrect");
-  if (XML_GetCurrentByteIndex(g_parser) != (XML_Index)strlen(text))
-    fail("Terminal byte index incorrect");
-}
-END_TEST
-
-/* Test position information from errors */
-#define PRE_ERROR_STR "<doc></"
-#define POST_ERROR_STR "wombat></doc>"
-START_TEST(test_byte_info_at_error) {
-  const char *text = PRE_ERROR_STR POST_ERROR_STR;
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_OK)
-    fail("Syntax error not faulted");
-  if (XML_GetCurrentByteCount(g_parser) != 0)
-    fail("Error byte count incorrect");
-  if (XML_GetCurrentByteIndex(g_parser) != strlen(PRE_ERROR_STR))
-    fail("Error byte index incorrect");
-}
-END_TEST
-#undef PRE_ERROR_STR
-#undef POST_ERROR_STR
-
-/* Test position information in handler */
-typedef struct ByteTestData {
-  int start_element_len;
-  int cdata_len;
-  int total_string_len;
-} ByteTestData;
-
-static void
-byte_character_handler(void *userData, const XML_Char *s, int len) {
-#ifdef XML_CONTEXT_BYTES
-  int offset, size;
-  const char *buffer;
-  ByteTestData *data = (ByteTestData *)userData;
-
-  UNUSED_P(s);
-  buffer = XML_GetInputContext(g_parser, &offset, &size);
-  if (buffer == NULL)
-    fail("Failed to get context buffer");
-  if (offset != data->start_element_len)
-    fail("Context offset in unexpected position");
-  if (len != data->cdata_len)
-    fail("CDATA length reported incorrectly");
-  if (size != data->total_string_len)
-    fail("Context size is not full buffer");
-  if (XML_GetCurrentByteIndex(g_parser) != offset)
-    fail("Character byte index incorrect");
-  if (XML_GetCurrentByteCount(g_parser) != len)
-    fail("Character byte count incorrect");
-#else
-  UNUSED_P(s);
-  UNUSED_P(userData);
-  UNUSED_P(len);
-#endif
-}
-
-#define START_ELEMENT "<e>"
-#define CDATA_TEXT "Hello"
-#define END_ELEMENT "</e>"
-START_TEST(test_byte_info_at_cdata) {
-  const char *text = START_ELEMENT CDATA_TEXT END_ELEMENT;
-  int offset, size;
-  ByteTestData data;
-
-  /* Check initial context is empty */
-  if (XML_GetInputContext(g_parser, &offset, &size) != NULL)
-    fail("Unexpected context at start of parse");
-
-  data.start_element_len = (int)strlen(START_ELEMENT);
-  data.cdata_len = (int)strlen(CDATA_TEXT);
-  data.total_string_len = (int)strlen(text);
-  XML_SetCharacterDataHandler(g_parser, byte_character_handler);
-  XML_SetUserData(g_parser, &data);
-  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE) != XML_STATUS_OK)
-    xml_failure(g_parser);
-}
-END_TEST
-#undef START_ELEMENT
-#undef CDATA_TEXT
-#undef END_ELEMENT
-
-/* Test predefined entities are correctly recognised */
-START_TEST(test_predefined_entities) {
-  const char *text = "<doc>&lt;&gt;&amp;&quot;&apos;</doc>";
-  const XML_Char *expected = XCS("<doc>&lt;&gt;&amp;&quot;&apos;</doc>");
-  const XML_Char *result = XCS("<>&\"'");
-  CharData storage;
-
-  XML_SetDefaultHandler(g_parser, accumulate_characters);
-  /* run_character_check uses XML_SetCharacterDataHandler(), which
-   * unfortunately heads off a code path that we need to exercise.
-   */
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  /* The default handler doesn't translate the entities */
-  CharData_CheckXMLChars(&storage, expected);
-
-  /* Now try again and check the translation */
-  XML_ParserReset(g_parser, NULL);
-  run_character_check(text, result);
-}
-END_TEST
-
-/* Regression test that an invalid tag in an external parameter
- * reference in an external DTD is correctly faulted.
- *
- * Only a few specific tags are legal in DTDs ignoring comments and
- * processing instructions, all of which begin with an exclamation
- * mark.  "<el/>" is not one of them, so the parser should raise an
- * error on encountering it.
- */
-static int XMLCALL
-external_entity_param(XML_Parser parser, const XML_Char *context,
-                      const XML_Char *base, const XML_Char *systemId,
-                      const XML_Char *publicId) {
-  const char *text1 = "<!ELEMENT doc EMPTY>\n"
-                      "<!ENTITY % e1 SYSTEM '004-2.ent'>\n"
-                      "<!ENTITY % e2 '%e1;'>\n"
-                      "%e1;\n";
-  const char *text2 = "<!ELEMENT el EMPTY>\n"
-                      "<el/>\n";
-  XML_Parser ext_parser;
-
-  UNUSED_P(base);
-  UNUSED_P(publicId);
-  if (systemId == NULL)
-    return XML_STATUS_OK;
-
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    fail("Could not create external entity parser");
-
-  if (! xcstrcmp(systemId, XCS("004-1.ent"))) {
-    if (_XML_Parse_SINGLE_BYTES(ext_parser, text1, (int)strlen(text1), XML_TRUE)
-        != XML_STATUS_ERROR)
-      fail("Inner DTD with invalid tag not rejected");
-    if (XML_GetErrorCode(ext_parser) != XML_ERROR_EXTERNAL_ENTITY_HANDLING)
-      xml_failure(ext_parser);
-  } else if (! xcstrcmp(systemId, XCS("004-2.ent"))) {
-    if (_XML_Parse_SINGLE_BYTES(ext_parser, text2, (int)strlen(text2), XML_TRUE)
-        != XML_STATUS_ERROR)
-      fail("Invalid tag in external param not rejected");
-    if (XML_GetErrorCode(ext_parser) != XML_ERROR_SYNTAX)
-      xml_failure(ext_parser);
-  } else {
-    fail("Unknown system ID");
-  }
-
-  XML_ParserFree(ext_parser);
-  return XML_STATUS_ERROR;
-}
-
-START_TEST(test_invalid_tag_in_dtd) {
-  const char *text = "<!DOCTYPE doc SYSTEM '004-1.ent'>\n"
-                     "<doc></doc>\n";
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_param);
-  expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
-                 "Invalid tag IN DTD external param not rejected");
-}
-END_TEST
-
-/* Test entities not quite the predefined ones are not mis-recognised */
-START_TEST(test_not_predefined_entities) {
-  const char *text[] = {"<doc>&pt;</doc>", "<doc>&amo;</doc>",
-                        "<doc>&quid;</doc>", "<doc>&apod;</doc>", NULL};
-  int i = 0;
-
-  while (text[i] != NULL) {
-    expect_failure(text[i], XML_ERROR_UNDEFINED_ENTITY,
-                   "Undefined entity not rejected");
-    XML_ParserReset(g_parser, NULL);
-    i++;
-  }
-}
-END_TEST
-
-/* Test conditional inclusion (IGNORE) */
-static int XMLCALL
-external_entity_load_ignore(XML_Parser parser, const XML_Char *context,
-                            const XML_Char *base, const XML_Char *systemId,
-                            const XML_Char *publicId) {
-  const char *text = "<![IGNORE[<!ELEMENT e (#PCDATA)*>]]>";
-  XML_Parser ext_parser;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    fail("Could not create external entity parser");
-  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(parser);
-
-  XML_ParserFree(ext_parser);
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_ignore_section) {
-  const char *text = "<!DOCTYPE doc SYSTEM 'foo'>\n"
-                     "<doc><e>&entity;</e></doc>";
-  const XML_Char *expected
-      = XCS("<![IGNORE[<!ELEMENT e (#PCDATA)*>]]>\n&entity;");
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetUserData(g_parser, &storage);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_load_ignore);
-  XML_SetDefaultHandler(g_parser, accumulate_characters);
-  XML_SetStartDoctypeDeclHandler(g_parser, dummy_start_doctype_handler);
-  XML_SetEndDoctypeDeclHandler(g_parser, dummy_end_doctype_handler);
-  XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
-  XML_SetStartElementHandler(g_parser, dummy_start_element);
-  XML_SetEndElementHandler(g_parser, dummy_end_element);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-static int XMLCALL
-external_entity_load_ignore_utf16(XML_Parser parser, const XML_Char *context,
-                                  const XML_Char *base,
-                                  const XML_Char *systemId,
-                                  const XML_Char *publicId) {
-  const char text[] =
-      /* <![IGNORE[<!ELEMENT e (#PCDATA)*>]]> */
-      "<\0!\0[\0I\0G\0N\0O\0R\0E\0[\0"
-      "<\0!\0E\0L\0E\0M\0E\0N\0T\0 \0e\0 \0"
-      "(\0#\0P\0C\0D\0A\0T\0A\0)\0*\0>\0]\0]\0>\0";
-  XML_Parser ext_parser;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    fail("Could not create external entity parser");
-  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(parser);
-
-  XML_ParserFree(ext_parser);
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_ignore_section_utf16) {
-  const char text[] =
-      /* <!DOCTYPE d SYSTEM 's'> */
-      "<\0!\0D\0O\0C\0T\0Y\0P\0E\0 \0d\0 "
-      "\0S\0Y\0S\0T\0E\0M\0 \0'\0s\0'\0>\0\n\0"
-      /* <d><e>&en;</e></d> */
-      "<\0d\0>\0<\0e\0>\0&\0e\0n\0;\0<\0/\0e\0>\0<\0/\0d\0>\0";
-  const XML_Char *expected = XCS("<![IGNORE[<!ELEMENT e (#PCDATA)*>]]>\n&en;");
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetUserData(g_parser, &storage);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_load_ignore_utf16);
-  XML_SetDefaultHandler(g_parser, accumulate_characters);
-  XML_SetStartDoctypeDeclHandler(g_parser, dummy_start_doctype_handler);
-  XML_SetEndDoctypeDeclHandler(g_parser, dummy_end_doctype_handler);
-  XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
-  XML_SetStartElementHandler(g_parser, dummy_start_element);
-  XML_SetEndElementHandler(g_parser, dummy_end_element);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-static int XMLCALL
-external_entity_load_ignore_utf16_be(XML_Parser parser, const XML_Char *context,
-                                     const XML_Char *base,
-                                     const XML_Char *systemId,
-                                     const XML_Char *publicId) {
-  const char text[] =
-      /* <![IGNORE[<!ELEMENT e (#PCDATA)*>]]> */
-      "\0<\0!\0[\0I\0G\0N\0O\0R\0E\0["
-      "\0<\0!\0E\0L\0E\0M\0E\0N\0T\0 \0e\0 "
-      "\0(\0#\0P\0C\0D\0A\0T\0A\0)\0*\0>\0]\0]\0>";
-  XML_Parser ext_parser;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    fail("Could not create external entity parser");
-  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(parser);
-
-  XML_ParserFree(ext_parser);
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_ignore_section_utf16_be) {
-  const char text[] =
-      /* <!DOCTYPE d SYSTEM 's'> */
-      "\0<\0!\0D\0O\0C\0T\0Y\0P\0E\0 \0d\0 "
-      "\0S\0Y\0S\0T\0E\0M\0 \0'\0s\0'\0>\0\n"
-      /* <d><e>&en;</e></d> */
-      "\0<\0d\0>\0<\0e\0>\0&\0e\0n\0;\0<\0/\0e\0>\0<\0/\0d\0>";
-  const XML_Char *expected = XCS("<![IGNORE[<!ELEMENT e (#PCDATA)*>]]>\n&en;");
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetUserData(g_parser, &storage);
-  XML_SetExternalEntityRefHandler(g_parser,
-                                  external_entity_load_ignore_utf16_be);
-  XML_SetDefaultHandler(g_parser, accumulate_characters);
-  XML_SetStartDoctypeDeclHandler(g_parser, dummy_start_doctype_handler);
-  XML_SetEndDoctypeDeclHandler(g_parser, dummy_end_doctype_handler);
-  XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
-  XML_SetStartElementHandler(g_parser, dummy_start_element);
-  XML_SetEndElementHandler(g_parser, dummy_end_element);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-/* Test mis-formatted conditional exclusion */
-START_TEST(test_bad_ignore_section) {
-  const char *text = "<!DOCTYPE doc SYSTEM 'foo'>\n"
-                     "<doc><e>&entity;</e></doc>";
-  ExtFaults faults[]
-      = {{"<![IGNORE[<!ELEM", "Broken-off declaration not faulted", NULL,
-          XML_ERROR_SYNTAX},
-         {"<![IGNORE[\x01]]>", "Invalid XML character not faulted", NULL,
-          XML_ERROR_INVALID_TOKEN},
-         {/* FIrst two bytes of a three-byte char */
-          "<![IGNORE[\xe2\x82", "Partial XML character not faulted", NULL,
-          XML_ERROR_PARTIAL_CHAR},
-         {NULL, NULL, NULL, XML_ERROR_NONE}};
-  ExtFaults *fault;
-
-  for (fault = &faults[0]; fault->parse_text != NULL; fault++) {
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_faulter);
-    XML_SetUserData(g_parser, fault);
-    expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
-                   "Incomplete IGNORE section not failed");
-    XML_ParserReset(g_parser, NULL);
-  }
-}
-END_TEST
-
-/* Test recursive parsing */
-static int XMLCALL
-external_entity_valuer(XML_Parser parser, const XML_Char *context,
-                       const XML_Char *base, const XML_Char *systemId,
-                       const XML_Char *publicId) {
-  const char *text1 = "<!ELEMENT doc EMPTY>\n"
-                      "<!ENTITY % e1 SYSTEM '004-2.ent'>\n"
-                      "<!ENTITY % e2 '%e1;'>\n"
-                      "%e1;\n";
-  XML_Parser ext_parser;
-
-  UNUSED_P(base);
-  UNUSED_P(publicId);
-  if (systemId == NULL)
-    return XML_STATUS_OK;
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    fail("Could not create external entity parser");
-  if (! xcstrcmp(systemId, XCS("004-1.ent"))) {
-    if (_XML_Parse_SINGLE_BYTES(ext_parser, text1, (int)strlen(text1), XML_TRUE)
-        == XML_STATUS_ERROR)
-      xml_failure(ext_parser);
-  } else if (! xcstrcmp(systemId, XCS("004-2.ent"))) {
-    ExtFaults *fault = (ExtFaults *)XML_GetUserData(parser);
-    enum XML_Status status;
-    enum XML_Error error;
-
-    status = _XML_Parse_SINGLE_BYTES(ext_parser, fault->parse_text,
-                                     (int)strlen(fault->parse_text), XML_TRUE);
-    if (fault->error == XML_ERROR_NONE) {
-      if (status == XML_STATUS_ERROR)
-        xml_failure(ext_parser);
-    } else {
-      if (status != XML_STATUS_ERROR)
-        fail(fault->fail_text);
-      error = XML_GetErrorCode(ext_parser);
-      if (error != fault->error
-          && (fault->error != XML_ERROR_XML_DECL
-              || error != XML_ERROR_TEXT_DECL))
-        xml_failure(ext_parser);
-    }
-  }
-
-  XML_ParserFree(ext_parser);
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_external_entity_values) {
-  const char *text = "<!DOCTYPE doc SYSTEM '004-1.ent'>\n"
-                     "<doc></doc>\n";
-  ExtFaults data_004_2[] = {
-      {"<!ATTLIST doc a1 CDATA 'value'>", NULL, NULL, XML_ERROR_NONE},
-      {"<!ATTLIST $doc a1 CDATA 'value'>", "Invalid token not faulted", NULL,
-       XML_ERROR_INVALID_TOKEN},
-      {"'wombat", "Unterminated string not faulted", NULL,
-       XML_ERROR_UNCLOSED_TOKEN},
-      {"\xe2\x82", "Partial UTF-8 character not faulted", NULL,
-       XML_ERROR_PARTIAL_CHAR},
-      {"<?xml version='1.0' encoding='utf-8'?>\n", NULL, NULL, XML_ERROR_NONE},
-      {"<?xml?>", "Malformed XML declaration not faulted", NULL,
-       XML_ERROR_XML_DECL},
-      {/* UTF-8 BOM */
-       "\xEF\xBB\xBF<!ATTLIST doc a1 CDATA 'value'>", NULL, NULL,
-       XML_ERROR_NONE},
-      {"<?xml version='1.0' encoding='utf-8'?>\n$",
-       "Invalid token after text declaration not faulted", NULL,
-       XML_ERROR_INVALID_TOKEN},
-      {"<?xml version='1.0' encoding='utf-8'?>\n'wombat",
-       "Unterminated string after text decl not faulted", NULL,
-       XML_ERROR_UNCLOSED_TOKEN},
-      {"<?xml version='1.0' encoding='utf-8'?>\n\xe2\x82",
-       "Partial UTF-8 character after text decl not faulted", NULL,
-       XML_ERROR_PARTIAL_CHAR},
-      {"%e1;", "Recursive parameter entity not faulted", NULL,
-       XML_ERROR_RECURSIVE_ENTITY_REF},
-      {NULL, NULL, NULL, XML_ERROR_NONE}};
-  int i;
-
-  for (i = 0; data_004_2[i].parse_text != NULL; i++) {
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_valuer);
-    XML_SetUserData(g_parser, &data_004_2[i]);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        == XML_STATUS_ERROR)
-      xml_failure(g_parser);
-    XML_ParserReset(g_parser, NULL);
-  }
-}
-END_TEST
-
-/* Test the recursive parse interacts with a not standalone handler */
-static int XMLCALL
-external_entity_not_standalone(XML_Parser parser, const XML_Char *context,
-                               const XML_Char *base, const XML_Char *systemId,
-                               const XML_Char *publicId) {
-  const char *text1 = "<!ELEMENT doc EMPTY>\n"
-                      "<!ENTITY % e1 SYSTEM 'bar'>\n"
-                      "%e1;\n";
-  const char *text2 = "<!ATTLIST doc a1 CDATA 'value'>";
-  XML_Parser ext_parser;
-
-  UNUSED_P(base);
-  UNUSED_P(publicId);
-  if (systemId == NULL)
-    return XML_STATUS_OK;
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    fail("Could not create external entity parser");
-  if (! xcstrcmp(systemId, XCS("foo"))) {
-    XML_SetNotStandaloneHandler(ext_parser, reject_not_standalone_handler);
-    if (_XML_Parse_SINGLE_BYTES(ext_parser, text1, (int)strlen(text1), XML_TRUE)
-        != XML_STATUS_ERROR)
-      fail("Expected not standalone rejection");
-    if (XML_GetErrorCode(ext_parser) != XML_ERROR_NOT_STANDALONE)
-      xml_failure(ext_parser);
-    XML_SetNotStandaloneHandler(ext_parser, NULL);
-    XML_ParserFree(ext_parser);
-    return XML_STATUS_ERROR;
-  } else if (! xcstrcmp(systemId, XCS("bar"))) {
-    if (_XML_Parse_SINGLE_BYTES(ext_parser, text2, (int)strlen(text2), XML_TRUE)
-        == XML_STATUS_ERROR)
-      xml_failure(ext_parser);
-  }
-
-  XML_ParserFree(ext_parser);
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_ext_entity_not_standalone) {
-  const char *text = "<!DOCTYPE doc SYSTEM 'foo'>\n"
-                     "<doc></doc>";
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_not_standalone);
-  expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
-                 "Standalone rejection not caught");
-}
-END_TEST
-
-static int XMLCALL
-external_entity_value_aborter(XML_Parser parser, const XML_Char *context,
-                              const XML_Char *base, const XML_Char *systemId,
-                              const XML_Char *publicId) {
-  const char *text1 = "<!ELEMENT doc EMPTY>\n"
-                      "<!ENTITY % e1 SYSTEM '004-2.ent'>\n"
-                      "<!ENTITY % e2 '%e1;'>\n"
-                      "%e1;\n";
-  const char *text2 = "<?xml version='1.0' encoding='utf-8'?>";
-  XML_Parser ext_parser;
-
-  UNUSED_P(base);
-  UNUSED_P(publicId);
-  if (systemId == NULL)
-    return XML_STATUS_OK;
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    fail("Could not create external entity parser");
-  if (! xcstrcmp(systemId, XCS("004-1.ent"))) {
-    if (_XML_Parse_SINGLE_BYTES(ext_parser, text1, (int)strlen(text1), XML_TRUE)
-        == XML_STATUS_ERROR)
-      xml_failure(ext_parser);
-  }
-  if (! xcstrcmp(systemId, XCS("004-2.ent"))) {
-    XML_SetXmlDeclHandler(ext_parser, entity_suspending_xdecl_handler);
-    XML_SetUserData(ext_parser, ext_parser);
-    if (_XML_Parse_SINGLE_BYTES(ext_parser, text2, (int)strlen(text2), XML_TRUE)
-        != XML_STATUS_ERROR)
-      fail("Aborted parse not faulted");
-    if (XML_GetErrorCode(ext_parser) != XML_ERROR_ABORTED)
-      xml_failure(ext_parser);
-  }
-
-  XML_ParserFree(ext_parser);
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_ext_entity_value_abort) {
-  const char *text = "<!DOCTYPE doc SYSTEM '004-1.ent'>\n"
-                     "<doc></doc>\n";
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_value_aborter);
-  resumable = XML_FALSE;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-START_TEST(test_bad_public_doctype) {
-  const char *text = "<?xml version='1.0' encoding='utf-8'?>\n"
-                     "<!DOCTYPE doc PUBLIC '{BadName}' 'test'>\n"
-                     "<doc></doc>";
-
-  /* Setting a handler provokes a particular code path */
-  XML_SetDoctypeDeclHandler(g_parser, dummy_start_doctype_handler,
-                            dummy_end_doctype_handler);
-  expect_failure(text, XML_ERROR_PUBLICID, "Bad Public ID not failed");
-}
-END_TEST
-
-/* Test based on ibm/valid/P32/ibm32v04.xml */
-START_TEST(test_attribute_enum_value) {
-  const char *text = "<?xml version='1.0' standalone='no'?>\n"
-                     "<!DOCTYPE animal SYSTEM 'test.dtd'>\n"
-                     "<animal>This is a \n    <a/>  \n\nyellow tiger</animal>";
-  ExtTest dtd_data
-      = {"<!ELEMENT animal (#PCDATA|a)*>\n"
-         "<!ELEMENT a EMPTY>\n"
-         "<!ATTLIST animal xml:space (default|preserve) 'preserve'>",
-         NULL, NULL};
-  const XML_Char *expected = XCS("This is a \n      \n\nyellow tiger");
-
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
-  XML_SetUserData(g_parser, &dtd_data);
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  /* An attribute list handler provokes a different code path */
-  XML_SetAttlistDeclHandler(g_parser, dummy_attlist_decl_handler);
-  run_ext_character_check(text, &dtd_data, expected);
-}
-END_TEST
-
-/* Slightly bizarrely, the library seems to silently ignore entity
- * definitions for predefined entities, even when they are wrong.  The
- * language of the XML 1.0 spec is somewhat unhelpful as to what ought
- * to happen, so this is currently treated as acceptable.
- */
-START_TEST(test_predefined_entity_redefinition) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "<!ENTITY apos 'foo'>\n"
-                     "]>\n"
-                     "<doc>&apos;</doc>";
-  run_character_check(text, XCS("'"));
-}
-END_TEST
-
-/* Test that the parser stops processing the DTD after an unresolved
- * parameter entity is encountered.
- */
-START_TEST(test_dtd_stop_processing) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "%foo;\n"
-                     "<!ENTITY bar 'bas'>\n"
-                     "]><doc/>";
-
-  XML_SetEntityDeclHandler(g_parser, dummy_entity_decl_handler);
-  dummy_handler_flags = 0;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  if (dummy_handler_flags != 0)
-    fail("DTD processing still going after undefined PE");
-}
-END_TEST
-
-/* Test public notations with no system ID */
-START_TEST(test_public_notation_no_sysid) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "<!NOTATION note PUBLIC 'foo'>\n"
-                     "<!ELEMENT doc EMPTY>\n"
-                     "]>\n<doc/>";
-
-  dummy_handler_flags = 0;
-  XML_SetNotationDeclHandler(g_parser, dummy_notation_decl_handler);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  if (dummy_handler_flags != DUMMY_NOTATION_DECL_HANDLER_FLAG)
-    fail("Notation declaration handler not called");
-}
-END_TEST
-
-static void XMLCALL
-record_element_start_handler(void *userData, const XML_Char *name,
-                             const XML_Char **atts) {
-  UNUSED_P(atts);
-  CharData_AppendXMLChars((CharData *)userData, name, (int)xcstrlen(name));
-}
-
-START_TEST(test_nested_groups) {
-  const char *text
-      = "<!DOCTYPE doc [\n"
-        "<!ELEMENT doc "
-        /* Sixteen elements per line */
-        "(e,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,"
-        "(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?"
-        "))))))))))))))))))))))))))))))))>\n"
-        "<!ELEMENT e EMPTY>"
-        "]>\n"
-        "<doc><e/></doc>";
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
-  XML_SetStartElementHandler(g_parser, record_element_start_handler);
-  XML_SetUserData(g_parser, &storage);
-  dummy_handler_flags = 0;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, XCS("doce"));
-  if (dummy_handler_flags != DUMMY_ELEMENT_DECL_HANDLER_FLAG)
-    fail("Element handler not fired");
-}
-END_TEST
-
-START_TEST(test_group_choice) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "<!ELEMENT doc (a|b|c)+>\n"
-                     "<!ELEMENT a EMPTY>\n"
-                     "<!ELEMENT b (#PCDATA)>\n"
-                     "<!ELEMENT c ANY>\n"
-                     "]>\n"
-                     "<doc>\n"
-                     "<a/>\n"
-                     "<b attr='foo'>This is a foo</b>\n"
-                     "<c></c>\n"
-                     "</doc>\n";
-
-  XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
-  dummy_handler_flags = 0;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  if (dummy_handler_flags != DUMMY_ELEMENT_DECL_HANDLER_FLAG)
-    fail("Element handler flag not raised");
-}
-END_TEST
-
-static int XMLCALL
-external_entity_public(XML_Parser parser, const XML_Char *context,
-                       const XML_Char *base, const XML_Char *systemId,
-                       const XML_Char *publicId) {
-  const char *text1 = (const char *)XML_GetUserData(parser);
-  const char *text2 = "<!ATTLIST doc a CDATA 'value'>";
-  const char *text = NULL;
-  XML_Parser ext_parser;
-  int parse_res;
-
-  UNUSED_P(base);
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    return XML_STATUS_ERROR;
-  if (systemId != NULL && ! xcstrcmp(systemId, XCS("http://example.org/"))) {
-    text = text1;
-  } else if (publicId != NULL && ! xcstrcmp(publicId, XCS("foo"))) {
-    text = text2;
-  } else
-    fail("Unexpected parameters to external entity parser");
-  assert(text != NULL);
-  parse_res
-      = _XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE);
-  XML_ParserFree(ext_parser);
-  return parse_res;
-}
-
-START_TEST(test_standalone_parameter_entity) {
-  const char *text = "<?xml version='1.0' standalone='yes'?>\n"
-                     "<!DOCTYPE doc SYSTEM 'http://example.org/' [\n"
-                     "<!ENTITY % entity '<!ELEMENT doc (#PCDATA)>'>\n"
-                     "%entity;\n"
-                     "]>\n"
-                     "<doc></doc>";
-  char dtd_data[] = "<!ENTITY % e1 'foo'>\n";
-
-  XML_SetUserData(g_parser, dtd_data);
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_public);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test skipping of parameter entity in an external DTD */
-/* Derived from ibm/invalid/P69/ibm69i01.xml */
-START_TEST(test_skipped_parameter_entity) {
-  const char *text = "<?xml version='1.0'?>\n"
-                     "<!DOCTYPE root SYSTEM 'http://example.org/dtd.ent' [\n"
-                     "<!ELEMENT root (#PCDATA|a)* >\n"
-                     "]>\n"
-                     "<root></root>";
-  ExtTest dtd_data = {"%pe2;", NULL, NULL};
-
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
-  XML_SetUserData(g_parser, &dtd_data);
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetSkippedEntityHandler(g_parser, dummy_skip_handler);
-  dummy_handler_flags = 0;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  if (dummy_handler_flags != DUMMY_SKIP_HANDLER_FLAG)
-    fail("Skip handler not executed");
-}
-END_TEST
-
-/* Test recursive parameter entity definition rejected in external DTD */
-START_TEST(test_recursive_external_parameter_entity) {
-  const char *text = "<?xml version='1.0'?>\n"
-                     "<!DOCTYPE root SYSTEM 'http://example.org/dtd.ent' [\n"
-                     "<!ELEMENT root (#PCDATA|a)* >\n"
-                     "]>\n"
-                     "<root></root>";
-  ExtFaults dtd_data = {"<!ENTITY % pe2 '&#37;pe2;'>\n%pe2;",
-                        "Recursive external parameter entity not faulted", NULL,
-                        XML_ERROR_RECURSIVE_ENTITY_REF};
-
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_faulter);
-  XML_SetUserData(g_parser, &dtd_data);
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
-                 "Recursive external parameter not spotted");
-}
-END_TEST
-
-/* Test undefined parameter entity in external entity handler */
-static int XMLCALL
-external_entity_devaluer(XML_Parser parser, const XML_Char *context,
-                         const XML_Char *base, const XML_Char *systemId,
-                         const XML_Char *publicId) {
-  const char *text = "<!ELEMENT doc EMPTY>\n"
-                     "<!ENTITY % e1 SYSTEM 'bar'>\n"
-                     "%e1;\n";
-  XML_Parser ext_parser;
-  intptr_t clear_handler = (intptr_t)XML_GetUserData(parser);
-
-  UNUSED_P(base);
-  UNUSED_P(publicId);
-  if (systemId == NULL || ! xcstrcmp(systemId, XCS("bar")))
-    return XML_STATUS_OK;
-  if (xcstrcmp(systemId, XCS("foo")))
-    fail("Unexpected system ID");
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    fail("Could note create external entity parser");
-  if (clear_handler)
-    XML_SetExternalEntityRefHandler(ext_parser, NULL);
-  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(ext_parser);
-
-  XML_ParserFree(ext_parser);
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_undefined_ext_entity_in_external_dtd) {
-  const char *text = "<!DOCTYPE doc SYSTEM 'foo'>\n"
-                     "<doc></doc>\n";
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_devaluer);
-  XML_SetUserData(g_parser, (void *)(intptr_t)XML_FALSE);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-
-  /* Now repeat without the external entity ref handler invoking
-   * another copy of itself.
-   */
-  XML_ParserReset(g_parser, NULL);
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_devaluer);
-  XML_SetUserData(g_parser, (void *)(intptr_t)XML_TRUE);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-static void XMLCALL
-aborting_xdecl_handler(void *userData, const XML_Char *version,
-                       const XML_Char *encoding, int standalone) {
-  UNUSED_P(userData);
-  UNUSED_P(version);
-  UNUSED_P(encoding);
-  UNUSED_P(standalone);
-  XML_StopParser(g_parser, resumable);
-  XML_SetXmlDeclHandler(g_parser, NULL);
-}
-
-/* Test suspending the parse on receiving an XML declaration works */
-START_TEST(test_suspend_xdecl) {
-  const char *text = long_character_data_text;
-
-  XML_SetXmlDeclHandler(g_parser, aborting_xdecl_handler);
-  resumable = XML_TRUE;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_SUSPENDED)
-    xml_failure(g_parser);
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_NONE)
-    xml_failure(g_parser);
-  /* Attempt to start a new parse while suspended */
-  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_ERROR)
-    fail("Attempt to parse while suspended not faulted");
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_SUSPENDED)
-    fail("Suspended parse not faulted with correct error");
-}
-END_TEST
-
-/* Test aborting the parse in an epilog works */
-static void XMLCALL
-selective_aborting_default_handler(void *userData, const XML_Char *s, int len) {
-  const XML_Char *match = (const XML_Char *)userData;
-
-  if (match == NULL
-      || (xcstrlen(match) == (unsigned)len && ! xcstrncmp(match, s, len))) {
-    XML_StopParser(g_parser, resumable);
-    XML_SetDefaultHandler(g_parser, NULL);
-  }
-}
-
-START_TEST(test_abort_epilog) {
-  const char *text = "<doc></doc>\n\r\n";
-  XML_Char match[] = XCS("\r");
-
-  XML_SetDefaultHandler(g_parser, selective_aborting_default_handler);
-  XML_SetUserData(g_parser, match);
-  resumable = XML_FALSE;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_ERROR)
-    fail("Abort not triggered");
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_ABORTED)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test a different code path for abort in the epilog */
-START_TEST(test_abort_epilog_2) {
-  const char *text = "<doc></doc>\n";
-  XML_Char match[] = XCS("\n");
-
-  XML_SetDefaultHandler(g_parser, selective_aborting_default_handler);
-  XML_SetUserData(g_parser, match);
-  resumable = XML_FALSE;
-  expect_failure(text, XML_ERROR_ABORTED, "Abort not triggered");
-}
-END_TEST
-
-/* Test suspension from the epilog */
-START_TEST(test_suspend_epilog) {
-  const char *text = "<doc></doc>\n";
-  XML_Char match[] = XCS("\n");
-
-  XML_SetDefaultHandler(g_parser, selective_aborting_default_handler);
-  XML_SetUserData(g_parser, match);
-  resumable = XML_TRUE;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_SUSPENDED)
-    xml_failure(g_parser);
-}
-END_TEST
-
-static void XMLCALL
-suspending_end_handler(void *userData, const XML_Char *s) {
-  UNUSED_P(s);
-  XML_StopParser((XML_Parser)userData, 1);
-}
-
-START_TEST(test_suspend_in_sole_empty_tag) {
-  const char *text = "<doc/>";
-  enum XML_Status rc;
-
-  XML_SetEndElementHandler(g_parser, suspending_end_handler);
-  XML_SetUserData(g_parser, g_parser);
-  rc = _XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE);
-  if (rc == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  else if (rc != XML_STATUS_SUSPENDED)
-    fail("Suspend not triggered");
-  rc = XML_ResumeParser(g_parser);
-  if (rc == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  else if (rc != XML_STATUS_OK)
-    fail("Resume failed");
-}
-END_TEST
-
-START_TEST(test_unfinished_epilog) {
-  const char *text = "<doc></doc><";
-
-  expect_failure(text, XML_ERROR_UNCLOSED_TOKEN,
-                 "Incomplete epilog entry not faulted");
-}
-END_TEST
-
-START_TEST(test_partial_char_in_epilog) {
-  const char *text = "<doc></doc>\xe2\x82";
-
-  /* First check that no fault is raised if the parse is not finished */
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  /* Now check that it is faulted once we finish */
-  if (XML_ParseBuffer(g_parser, 0, XML_TRUE) != XML_STATUS_ERROR)
-    fail("Partial character in epilog not faulted");
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_PARTIAL_CHAR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-START_TEST(test_hash_collision) {
-  /* For full coverage of the lookup routine, we need to ensure a
-   * hash collision even though we can only tell that we have one
-   * through breakpoint debugging or coverage statistics.  The
-   * following will cause a hash collision on machines with a 64-bit
-   * long type; others will have to experiment.  The full coverage
-   * tests invoked from qa.sh usually provide a hash collision, but
-   * not always.  This is an attempt to provide insurance.
-   */
-#define COLLIDING_HASH_SALT (unsigned long)_SIP_ULL(0xffffffffU, 0xff99fc90U)
-  const char *text
-      = "<doc>\n"
-        "<a1/><a2/><a3/><a4/><a5/><a6/><a7/><a8/>\n"
-        "<b1></b1><b2 attr='foo'>This is a foo</b2><b3></b3><b4></b4>\n"
-        "<b5></b5><b6></b6><b7></b7><b8></b8>\n"
-        "<c1/><c2/><c3/><c4/><c5/><c6/><c7/><c8/>\n"
-        "<d1/><d2/><d3/><d4/><d5/><d6/><d7/>\n"
-        "<d8>This triggers the table growth and collides with b2</d8>\n"
-        "</doc>\n";
-
-  XML_SetHashSalt(g_parser, COLLIDING_HASH_SALT);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-#undef COLLIDING_HASH_SALT
-
-/* Test resuming a parse suspended in entity substitution */
-static void XMLCALL
-start_element_suspender(void *userData, const XML_Char *name,
-                        const XML_Char **atts) {
-  UNUSED_P(userData);
-  UNUSED_P(atts);
-  if (! xcstrcmp(name, XCS("suspend")))
-    XML_StopParser(g_parser, XML_TRUE);
-  if (! xcstrcmp(name, XCS("abort")))
-    XML_StopParser(g_parser, XML_FALSE);
-}
-
-START_TEST(test_suspend_resume_internal_entity) {
-  const char *text
-      = "<!DOCTYPE doc [\n"
-        "<!ENTITY foo '<suspend>Hi<suspend>Ho</suspend></suspend>'>\n"
-        "]>\n"
-        "<doc>&foo;</doc>\n";
-  const XML_Char *expected1 = XCS("Hi");
-  const XML_Char *expected2 = XCS("HiHo");
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetStartElementHandler(g_parser, start_element_suspender);
-  XML_SetCharacterDataHandler(g_parser, accumulate_characters);
-  XML_SetUserData(g_parser, &storage);
-  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_SUSPENDED)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, XCS(""));
-  if (XML_ResumeParser(g_parser) != XML_STATUS_SUSPENDED)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected1);
-  if (XML_ResumeParser(g_parser) != XML_STATUS_OK)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected2);
-}
-END_TEST
-
-static void XMLCALL
-suspending_comment_handler(void *userData, const XML_Char *data) {
-  UNUSED_P(data);
-  XML_Parser parser = (XML_Parser)userData;
-  XML_StopParser(parser, XML_TRUE);
-}
-
-START_TEST(test_suspend_resume_internal_entity_issue_629) {
-  const char *const text
-      = "<!DOCTYPE a [<!ENTITY e '<!--COMMENT-->a'>]><a>&e;<b>\n"
-        "<"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-        "/>"
-        "</b></a>";
-  const size_t firstChunkSizeBytes = 54;
-
-  XML_Parser parser = XML_ParserCreate(NULL);
-  XML_SetUserData(parser, parser);
-  XML_SetCommentHandler(parser, suspending_comment_handler);
-
-  if (XML_Parse(parser, text, (int)firstChunkSizeBytes, XML_FALSE)
-      != XML_STATUS_SUSPENDED)
-    xml_failure(parser);
-  if (XML_ResumeParser(parser) != XML_STATUS_OK)
-    xml_failure(parser);
-  if (XML_Parse(parser, text + firstChunkSizeBytes,
-                (int)(strlen(text) - firstChunkSizeBytes), XML_TRUE)
-      != XML_STATUS_OK)
-    xml_failure(parser);
-  XML_ParserFree(parser);
-}
-END_TEST
-
-/* Test syntax error is caught at parse resumption */
-START_TEST(test_resume_entity_with_syntax_error) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "<!ENTITY foo '<suspend>Hi</wombat>'>\n"
-                     "]>\n"
-                     "<doc>&foo;</doc>\n";
-
-  XML_SetStartElementHandler(g_parser, start_element_suspender);
-  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_SUSPENDED)
-    xml_failure(g_parser);
-  if (XML_ResumeParser(g_parser) != XML_STATUS_ERROR)
-    fail("Syntax error in entity not faulted");
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_TAG_MISMATCH)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test suspending and resuming in a parameter entity substitution */
-static void XMLCALL
-element_decl_suspender(void *userData, const XML_Char *name,
-                       XML_Content *model) {
-  UNUSED_P(userData);
-  UNUSED_P(name);
-  XML_StopParser(g_parser, XML_TRUE);
-  XML_FreeContentModel(g_parser, model);
-}
-
-START_TEST(test_suspend_resume_parameter_entity) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "<!ENTITY % foo '<!ELEMENT doc (#PCDATA)*>'>\n"
-                     "%foo;\n"
-                     "]>\n"
-                     "<doc>Hello, world</doc>";
-  const XML_Char *expected = XCS("Hello, world");
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetElementDeclHandler(g_parser, element_decl_suspender);
-  XML_SetCharacterDataHandler(g_parser, accumulate_characters);
-  XML_SetUserData(g_parser, &storage);
-  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_SUSPENDED)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, XCS(""));
-  if (XML_ResumeParser(g_parser) != XML_STATUS_OK)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-/* Test attempting to use parser after an error is faulted */
-START_TEST(test_restart_on_error) {
-  const char *text = "<$doc><doc></doc>";
-
-  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_ERROR)
-    fail("Invalid tag name not faulted");
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_INVALID_TOKEN)
-    xml_failure(g_parser);
-  if (XML_Parse(g_parser, NULL, 0, XML_TRUE) != XML_STATUS_ERROR)
-    fail("Restarting invalid parse not faulted");
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_INVALID_TOKEN)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test that angle brackets in an attribute default value are faulted */
-START_TEST(test_reject_lt_in_attribute_value) {
-  const char *text = "<!DOCTYPE doc [<!ATTLIST doc a CDATA '<bar>'>]>\n"
-                     "<doc></doc>";
-
-  expect_failure(text, XML_ERROR_INVALID_TOKEN,
-                 "Bad attribute default not faulted");
-}
-END_TEST
-
-START_TEST(test_reject_unfinished_param_in_att_value) {
-  const char *text = "<!DOCTYPE doc [<!ATTLIST doc a CDATA '&foo'>]>\n"
-                     "<doc></doc>";
-
-  expect_failure(text, XML_ERROR_INVALID_TOKEN,
-                 "Bad attribute default not faulted");
-}
-END_TEST
-
-START_TEST(test_trailing_cr_in_att_value) {
-  const char *text = "<doc a='value\r'/>";
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Try parsing a general entity within a parameter entity in a
- * standalone internal DTD.  Covers a corner case in the parser.
- */
-START_TEST(test_standalone_internal_entity) {
-  const char *text = "<?xml version='1.0' standalone='yes' ?>\n"
-                     "<!DOCTYPE doc [\n"
-                     "  <!ELEMENT doc (#PCDATA)>\n"
-                     "  <!ENTITY % pe '<!ATTLIST doc att2 CDATA \"&ge;\">'>\n"
-                     "  <!ENTITY ge 'AttDefaultValue'>\n"
-                     "  %pe;\n"
-                     "]>\n"
-                     "<doc att2='any'/>";
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test that a reference to an unknown external entity is skipped */
-START_TEST(test_skipped_external_entity) {
-  const char *text = "<!DOCTYPE doc SYSTEM 'http://example.org/'>\n"
-                     "<doc></doc>\n";
-  ExtTest test_data = {"<!ELEMENT doc EMPTY>\n"
-                       "<!ENTITY % e2 '%e1;'>\n",
-                       NULL, NULL};
-
-  XML_SetUserData(g_parser, &test_data);
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test a different form of unknown external entity */
-typedef struct ext_hdlr_data {
-  const char *parse_text;
-  XML_ExternalEntityRefHandler handler;
-} ExtHdlrData;
-
-static int XMLCALL
-external_entity_oneshot_loader(XML_Parser parser, const XML_Char *context,
-                               const XML_Char *base, const XML_Char *systemId,
-                               const XML_Char *publicId) {
-  ExtHdlrData *test_data = (ExtHdlrData *)XML_GetUserData(parser);
-  XML_Parser ext_parser;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    fail("Could not create external entity parser.");
-  /* Use the requested entity parser for further externals */
-  XML_SetExternalEntityRefHandler(ext_parser, test_data->handler);
-  if (_XML_Parse_SINGLE_BYTES(ext_parser, test_data->parse_text,
-                              (int)strlen(test_data->parse_text), XML_TRUE)
-      == XML_STATUS_ERROR) {
-    xml_failure(ext_parser);
-  }
-
-  XML_ParserFree(ext_parser);
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_skipped_null_loaded_ext_entity) {
-  const char *text = "<!DOCTYPE doc SYSTEM 'http://example.org/one.ent'>\n"
-                     "<doc />";
-  ExtHdlrData test_data
-      = {"<!ENTITY % pe1 SYSTEM 'http://example.org/two.ent'>\n"
-         "<!ENTITY % pe2 '%pe1;'>\n"
-         "%pe2;\n",
-         external_entity_null_loader};
-
-  XML_SetUserData(g_parser, &test_data);
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_oneshot_loader);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-START_TEST(test_skipped_unloaded_ext_entity) {
-  const char *text = "<!DOCTYPE doc SYSTEM 'http://example.org/one.ent'>\n"
-                     "<doc />";
-  ExtHdlrData test_data
-      = {"<!ENTITY % pe1 SYSTEM 'http://example.org/two.ent'>\n"
-         "<!ENTITY % pe2 '%pe1;'>\n"
-         "%pe2;\n",
-         NULL};
-
-  XML_SetUserData(g_parser, &test_data);
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_oneshot_loader);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test that a parameter entity value ending with a carriage return
- * has it translated internally into a newline.
- */
-START_TEST(test_param_entity_with_trailing_cr) {
-#define PARAM_ENTITY_NAME "pe"
-#define PARAM_ENTITY_CORE_VALUE "<!ATTLIST doc att CDATA \"default\">"
-  const char *text = "<!DOCTYPE doc SYSTEM 'http://example.org/'>\n"
-                     "<doc/>";
-  ExtTest test_data
-      = {"<!ENTITY % " PARAM_ENTITY_NAME " '" PARAM_ENTITY_CORE_VALUE "\r'>\n"
-         "%" PARAM_ENTITY_NAME ";\n",
-         NULL, NULL};
-
-  XML_SetUserData(g_parser, &test_data);
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader);
-  XML_SetEntityDeclHandler(g_parser, param_entity_match_handler);
-  entity_name_to_match = XCS(PARAM_ENTITY_NAME);
-  entity_value_to_match = XCS(PARAM_ENTITY_CORE_VALUE) XCS("\n");
-  entity_match_flag = ENTITY_MATCH_NOT_FOUND;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  if (entity_match_flag == ENTITY_MATCH_FAIL)
-    fail("Parameter entity CR->NEWLINE conversion failed");
-  else if (entity_match_flag == ENTITY_MATCH_NOT_FOUND)
-    fail("Parameter entity not parsed");
-}
-#undef PARAM_ENTITY_NAME
-#undef PARAM_ENTITY_CORE_VALUE
-END_TEST
-
-START_TEST(test_invalid_character_entity) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY entity '&#x110000;'>\n"
-                     "]>\n"
-                     "<doc>&entity;</doc>";
-
-  expect_failure(text, XML_ERROR_BAD_CHAR_REF,
-                 "Out of range character reference not faulted");
-}
-END_TEST
-
-START_TEST(test_invalid_character_entity_2) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY entity '&#xg0;'>\n"
-                     "]>\n"
-                     "<doc>&entity;</doc>";
-
-  expect_failure(text, XML_ERROR_INVALID_TOKEN,
-                 "Out of range character reference not faulted");
-}
-END_TEST
-
-START_TEST(test_invalid_character_entity_3) {
-  const char text[] =
-      /* <!DOCTYPE doc [\n */
-      "\0<\0!\0D\0O\0C\0T\0Y\0P\0E\0 \0d\0o\0c\0 \0[\0\n"
-      /* U+0E04 = KHO KHWAI
-       * U+0E08 = CHO CHAN */
-      /* <!ENTITY entity '&\u0e04\u0e08;'>\n */
-      "\0<\0!\0E\0N\0T\0I\0T\0Y\0 \0e\0n\0t\0i\0t\0y\0 "
-      "\0'\0&\x0e\x04\x0e\x08\0;\0'\0>\0\n"
-      /* ]>\n */
-      "\0]\0>\0\n"
-      /* <doc>&entity;</doc> */
-      "\0<\0d\0o\0c\0>\0&\0e\0n\0t\0i\0t\0y\0;\0<\0/\0d\0o\0c\0>";
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      != XML_STATUS_ERROR)
-    fail("Invalid start of entity name not faulted");
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_UNDEFINED_ENTITY)
-    xml_failure(g_parser);
-}
-END_TEST
-
-START_TEST(test_invalid_character_entity_4) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY entity '&#1114112;'>\n" /* = &#x110000 */
-                     "]>\n"
-                     "<doc>&entity;</doc>";
-
-  expect_failure(text, XML_ERROR_BAD_CHAR_REF,
-                 "Out of range character reference not faulted");
-}
-END_TEST
-
-/* Test that processing instructions are picked up by a default handler */
-START_TEST(test_pi_handled_in_default) {
-  const char *text = "<?test processing instruction?>\n<doc/>";
-  const XML_Char *expected = XCS("<?test processing instruction?>\n<doc/>");
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetDefaultHandler(g_parser, accumulate_characters);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-/* Test that comments are picked up by a default handler */
-START_TEST(test_comment_handled_in_default) {
-  const char *text = "<!-- This is a comment -->\n<doc/>";
-  const XML_Char *expected = XCS("<!-- This is a comment -->\n<doc/>");
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetDefaultHandler(g_parser, accumulate_characters);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-/* Test PIs that look almost but not quite like XML declarations */
-static void XMLCALL
-accumulate_pi_characters(void *userData, const XML_Char *target,
-                         const XML_Char *data) {
-  CharData *storage = (CharData *)userData;
-
-  CharData_AppendXMLChars(storage, target, -1);
-  CharData_AppendXMLChars(storage, XCS(": "), 2);
-  CharData_AppendXMLChars(storage, data, -1);
-  CharData_AppendXMLChars(storage, XCS("\n"), 1);
-}
-
-START_TEST(test_pi_yml) {
-  const char *text = "<?yml something like data?><doc/>";
-  const XML_Char *expected = XCS("yml: something like data\n");
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetProcessingInstructionHandler(g_parser, accumulate_pi_characters);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-START_TEST(test_pi_xnl) {
-  const char *text = "<?xnl nothing like data?><doc/>";
-  const XML_Char *expected = XCS("xnl: nothing like data\n");
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetProcessingInstructionHandler(g_parser, accumulate_pi_characters);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-START_TEST(test_pi_xmm) {
-  const char *text = "<?xmm everything like data?><doc/>";
-  const XML_Char *expected = XCS("xmm: everything like data\n");
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetProcessingInstructionHandler(g_parser, accumulate_pi_characters);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-START_TEST(test_utf16_pi) {
-  const char text[] =
-      /* <?{KHO KHWAI}{CHO CHAN}?>
-       * where {KHO KHWAI} = U+0E04
-       * and   {CHO CHAN}  = U+0E08
-       */
-      "<\0?\0\x04\x0e\x08\x0e?\0>\0"
-      /* <q/> */
-      "<\0q\0/\0>\0";
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("\x0e04\x0e08: \n");
-#else
-  const XML_Char *expected = XCS("\xe0\xb8\x84\xe0\xb8\x88: \n");
-#endif
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetProcessingInstructionHandler(g_parser, accumulate_pi_characters);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-START_TEST(test_utf16_be_pi) {
-  const char text[] =
-      /* <?{KHO KHWAI}{CHO CHAN}?>
-       * where {KHO KHWAI} = U+0E04
-       * and   {CHO CHAN}  = U+0E08
-       */
-      "\0<\0?\x0e\x04\x0e\x08\0?\0>"
-      /* <q/> */
-      "\0<\0q\0/\0>";
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("\x0e04\x0e08: \n");
-#else
-  const XML_Char *expected = XCS("\xe0\xb8\x84\xe0\xb8\x88: \n");
-#endif
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetProcessingInstructionHandler(g_parser, accumulate_pi_characters);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-/* Test that comments can be picked up and translated */
-static void XMLCALL
-accumulate_comment(void *userData, const XML_Char *data) {
-  CharData *storage = (CharData *)userData;
-
-  CharData_AppendXMLChars(storage, data, -1);
-}
-
-START_TEST(test_utf16_be_comment) {
-  const char text[] =
-      /* <!-- Comment A --> */
-      "\0<\0!\0-\0-\0 \0C\0o\0m\0m\0e\0n\0t\0 \0A\0 \0-\0-\0>\0\n"
-      /* <doc/> */
-      "\0<\0d\0o\0c\0/\0>";
-  const XML_Char *expected = XCS(" Comment A ");
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetCommentHandler(g_parser, accumulate_comment);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-START_TEST(test_utf16_le_comment) {
-  const char text[] =
-      /* <!-- Comment B --> */
-      "<\0!\0-\0-\0 \0C\0o\0m\0m\0e\0n\0t\0 \0B\0 \0-\0-\0>\0\n\0"
-      /* <doc/> */
-      "<\0d\0o\0c\0/\0>\0";
-  const XML_Char *expected = XCS(" Comment B ");
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetCommentHandler(g_parser, accumulate_comment);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-/* Test that the unknown encoding handler with map entries that expect
- * conversion but no conversion function is faulted
- */
-static int XMLCALL
-failing_converter(void *data, const char *s) {
-  UNUSED_P(data);
-  UNUSED_P(s);
-  /* Always claim to have failed */
-  return -1;
-}
-
-static int XMLCALL
-prefix_converter(void *data, const char *s) {
-  UNUSED_P(data);
-  /* If the first byte is 0xff, raise an error */
-  if (s[0] == (char)-1)
-    return -1;
-  /* Just add the low bits of the first byte to the second */
-  return (s[1] + (s[0] & 0x7f)) & 0x01ff;
-}
-
-static int XMLCALL
-MiscEncodingHandler(void *data, const XML_Char *encoding, XML_Encoding *info) {
-  int i;
-  int high_map = -2; /* Assume a 2-byte sequence */
-
-  if (! xcstrcmp(encoding, XCS("invalid-9"))
-      || ! xcstrcmp(encoding, XCS("ascii-like"))
-      || ! xcstrcmp(encoding, XCS("invalid-len"))
-      || ! xcstrcmp(encoding, XCS("invalid-a"))
-      || ! xcstrcmp(encoding, XCS("invalid-surrogate"))
-      || ! xcstrcmp(encoding, XCS("invalid-high")))
-    high_map = -1;
-
-  for (i = 0; i < 128; ++i)
-    info->map[i] = i;
-  for (; i < 256; ++i)
-    info->map[i] = high_map;
-
-  /* If required, put an invalid value in the ASCII entries */
-  if (! xcstrcmp(encoding, XCS("invalid-9")))
-    info->map[9] = 5;
-  /* If required, have a top-bit set character starts a 5-byte sequence */
-  if (! xcstrcmp(encoding, XCS("invalid-len")))
-    info->map[0x81] = -5;
-  /* If required, make a top-bit set character a valid ASCII character */
-  if (! xcstrcmp(encoding, XCS("invalid-a")))
-    info->map[0x82] = 'a';
-  /* If required, give a top-bit set character a forbidden value,
-   * what would otherwise be the first of a surrogate pair.
-   */
-  if (! xcstrcmp(encoding, XCS("invalid-surrogate")))
-    info->map[0x83] = 0xd801;
-  /* If required, give a top-bit set character too high a value */
-  if (! xcstrcmp(encoding, XCS("invalid-high")))
-    info->map[0x84] = 0x010101;
-
-  info->data = data;
-  info->release = NULL;
-  if (! xcstrcmp(encoding, XCS("failing-conv")))
-    info->convert = failing_converter;
-  else if (! xcstrcmp(encoding, XCS("prefix-conv")))
-    info->convert = prefix_converter;
-  else
-    info->convert = NULL;
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_missing_encoding_conversion_fn) {
-  const char *text = "<?xml version='1.0' encoding='no-conv'?>\n"
-                     "<doc>\x81</doc>";
-
-  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
-  /* MiscEncodingHandler sets up an encoding with every top-bit-set
-   * character introducing a two-byte sequence.  For this, it
-   * requires a convert function.  The above function call doesn't
-   * pass one through, so when BadEncodingHandler actually gets
-   * called it should supply an invalid encoding.
-   */
-  expect_failure(text, XML_ERROR_UNKNOWN_ENCODING,
-                 "Encoding with missing convert() not faulted");
-}
-END_TEST
-
-START_TEST(test_failing_encoding_conversion_fn) {
-  const char *text = "<?xml version='1.0' encoding='failing-conv'?>\n"
-                     "<doc>\x81</doc>";
-
-  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
-  /* BadEncodingHandler sets up an encoding with every top-bit-set
-   * character introducing a two-byte sequence.  For this, it
-   * requires a convert function.  The above function call passes
-   * one that insists all possible sequences are invalid anyway.
-   */
-  expect_failure(text, XML_ERROR_INVALID_TOKEN,
-                 "Encoding with failing convert() not faulted");
-}
-END_TEST
-
-/* Test unknown encoding conversions */
-START_TEST(test_unknown_encoding_success) {
-  const char *text = "<?xml version='1.0' encoding='prefix-conv'?>\n"
-                     /* Equivalent to <eoc>Hello, world</eoc> */
-                     "<\x81\x64\x80oc>Hello, world</\x81\x64\x80oc>";
-
-  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
-  run_character_check(text, XCS("Hello, world"));
-}
-END_TEST
-
-/* Test bad name character in unknown encoding */
-START_TEST(test_unknown_encoding_bad_name) {
-  const char *text = "<?xml version='1.0' encoding='prefix-conv'?>\n"
-                     "<\xff\x64oc>Hello, world</\xff\x64oc>";
-
-  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
-  expect_failure(text, XML_ERROR_INVALID_TOKEN,
-                 "Bad name start in unknown encoding not faulted");
-}
-END_TEST
-
-/* Test bad mid-name character in unknown encoding */
-START_TEST(test_unknown_encoding_bad_name_2) {
-  const char *text = "<?xml version='1.0' encoding='prefix-conv'?>\n"
-                     "<d\xffoc>Hello, world</d\xffoc>";
-
-  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
-  expect_failure(text, XML_ERROR_INVALID_TOKEN,
-                 "Bad name in unknown encoding not faulted");
-}
-END_TEST
-
-/* Test element name that is long enough to fill the conversion buffer
- * in an unknown encoding, finishing with an encoded character.
- */
-START_TEST(test_unknown_encoding_long_name_1) {
-  const char *text = "<?xml version='1.0' encoding='prefix-conv'?>\n"
-                     "<abcdefghabcdefghabcdefghijkl\x80m\x80n\x80o\x80p>"
-                     "Hi"
-                     "</abcdefghabcdefghabcdefghijkl\x80m\x80n\x80o\x80p>";
-  const XML_Char *expected = XCS("abcdefghabcdefghabcdefghijklmnop");
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
-  XML_SetStartElementHandler(g_parser, record_element_start_handler);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-/* Test element name that is long enough to fill the conversion buffer
- * in an unknown encoding, finishing with an simple character.
- */
-START_TEST(test_unknown_encoding_long_name_2) {
-  const char *text = "<?xml version='1.0' encoding='prefix-conv'?>\n"
-                     "<abcdefghabcdefghabcdefghijklmnop>"
-                     "Hi"
-                     "</abcdefghabcdefghabcdefghijklmnop>";
-  const XML_Char *expected = XCS("abcdefghabcdefghabcdefghijklmnop");
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
-  XML_SetStartElementHandler(g_parser, record_element_start_handler);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-START_TEST(test_invalid_unknown_encoding) {
-  const char *text = "<?xml version='1.0' encoding='invalid-9'?>\n"
-                     "<doc>Hello world</doc>";
-
-  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
-  expect_failure(text, XML_ERROR_UNKNOWN_ENCODING,
-                 "Invalid unknown encoding not faulted");
-}
-END_TEST
-
-START_TEST(test_unknown_ascii_encoding_ok) {
-  const char *text = "<?xml version='1.0' encoding='ascii-like'?>\n"
-                     "<doc>Hello, world</doc>";
-
-  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
-  run_character_check(text, XCS("Hello, world"));
-}
-END_TEST
-
-START_TEST(test_unknown_ascii_encoding_fail) {
-  const char *text = "<?xml version='1.0' encoding='ascii-like'?>\n"
-                     "<doc>Hello, \x80 world</doc>";
-
-  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
-  expect_failure(text, XML_ERROR_INVALID_TOKEN,
-                 "Invalid character not faulted");
-}
-END_TEST
-
-START_TEST(test_unknown_encoding_invalid_length) {
-  const char *text = "<?xml version='1.0' encoding='invalid-len'?>\n"
-                     "<doc>Hello, world</doc>";
-
-  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
-  expect_failure(text, XML_ERROR_UNKNOWN_ENCODING,
-                 "Invalid unknown encoding not faulted");
-}
-END_TEST
-
-START_TEST(test_unknown_encoding_invalid_topbit) {
-  const char *text = "<?xml version='1.0' encoding='invalid-a'?>\n"
-                     "<doc>Hello, world</doc>";
-
-  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
-  expect_failure(text, XML_ERROR_UNKNOWN_ENCODING,
-                 "Invalid unknown encoding not faulted");
-}
-END_TEST
-
-START_TEST(test_unknown_encoding_invalid_surrogate) {
-  const char *text = "<?xml version='1.0' encoding='invalid-surrogate'?>\n"
-                     "<doc>Hello, \x82 world</doc>";
-
-  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
-  expect_failure(text, XML_ERROR_INVALID_TOKEN,
-                 "Invalid unknown encoding not faulted");
-}
-END_TEST
-
-START_TEST(test_unknown_encoding_invalid_high) {
-  const char *text = "<?xml version='1.0' encoding='invalid-high'?>\n"
-                     "<doc>Hello, world</doc>";
-
-  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
-  expect_failure(text, XML_ERROR_UNKNOWN_ENCODING,
-                 "Invalid unknown encoding not faulted");
-}
-END_TEST
-
-START_TEST(test_unknown_encoding_invalid_attr_value) {
-  const char *text = "<?xml version='1.0' encoding='prefix-conv'?>\n"
-                     "<doc attr='\xff\x30'/>";
-
-  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
-  expect_failure(text, XML_ERROR_INVALID_TOKEN,
-                 "Invalid attribute valid not faulted");
-}
-END_TEST
-
-/* Test an external entity parser set to use latin-1 detects UTF-16
- * BOMs correctly.
- */
-enum ee_parse_flags { EE_PARSE_NONE = 0x00, EE_PARSE_FULL_BUFFER = 0x01 };
-
-typedef struct ExtTest2 {
-  const char *parse_text;
-  int parse_len;
-  const XML_Char *encoding;
-  CharData *storage;
-  enum ee_parse_flags flags;
-} ExtTest2;
-
-static int XMLCALL
-external_entity_loader2(XML_Parser parser, const XML_Char *context,
-                        const XML_Char *base, const XML_Char *systemId,
-                        const XML_Char *publicId) {
-  ExtTest2 *test_data = (ExtTest2 *)XML_GetUserData(parser);
-  XML_Parser extparser;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  extparser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (extparser == NULL)
-    fail("Coulr not create external entity parser");
-  if (test_data->encoding != NULL) {
-    if (! XML_SetEncoding(extparser, test_data->encoding))
-      fail("XML_SetEncoding() ignored for external entity");
-  }
-  if (test_data->flags & EE_PARSE_FULL_BUFFER) {
-    if (XML_Parse(extparser, test_data->parse_text, test_data->parse_len,
-                  XML_TRUE)
-        == XML_STATUS_ERROR) {
-      xml_failure(extparser);
-    }
-  } else if (_XML_Parse_SINGLE_BYTES(extparser, test_data->parse_text,
-                                     test_data->parse_len, XML_TRUE)
-             == XML_STATUS_ERROR) {
-    xml_failure(extparser);
-  }
-
-  XML_ParserFree(extparser);
-  return XML_STATUS_OK;
-}
-
-/* Test that UTF-16 BOM does not select UTF-16 given explicit encoding */
-static void XMLCALL
-ext2_accumulate_characters(void *userData, const XML_Char *s, int len) {
-  ExtTest2 *test_data = (ExtTest2 *)userData;
-  accumulate_characters(test_data->storage, s, len);
-}
-
-START_TEST(test_ext_entity_latin1_utf16le_bom) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
-                     "]>\n"
-                     "<doc>&en;</doc>";
-  ExtTest2 test_data
-      = {/* If UTF-16, 0xfeff is the BOM and 0x204c is black left bullet */
-         /* If Latin-1, 0xff = Y-diaeresis, 0xfe = lowercase thorn,
-          *   0x4c = L and 0x20 is a space
-          */
-         "\xff\xfe\x4c\x20", 4, XCS("iso-8859-1"), NULL, EE_PARSE_NONE};
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("\x00ff\x00feL ");
-#else
-  /* In UTF-8, y-diaeresis is 0xc3 0xbf, lowercase thorn is 0xc3 0xbe */
-  const XML_Char *expected = XCS("\xc3\xbf\xc3\xbeL ");
-#endif
-  CharData storage;
-
-  CharData_Init(&storage);
-  test_data.storage = &storage;
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader2);
-  XML_SetUserData(g_parser, &test_data);
-  XML_SetCharacterDataHandler(g_parser, ext2_accumulate_characters);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-START_TEST(test_ext_entity_latin1_utf16be_bom) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
-                     "]>\n"
-                     "<doc>&en;</doc>";
-  ExtTest2 test_data
-      = {/* If UTF-16, 0xfeff is the BOM and 0x204c is black left bullet */
-         /* If Latin-1, 0xff = Y-diaeresis, 0xfe = lowercase thorn,
-          *   0x4c = L and 0x20 is a space
-          */
-         "\xfe\xff\x20\x4c", 4, XCS("iso-8859-1"), NULL, EE_PARSE_NONE};
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("\x00fe\x00ff L");
-#else
-  /* In UTF-8, y-diaeresis is 0xc3 0xbf, lowercase thorn is 0xc3 0xbe */
-  const XML_Char *expected = XCS("\xc3\xbe\xc3\xbf L");
-#endif
-  CharData storage;
-
-  CharData_Init(&storage);
-  test_data.storage = &storage;
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader2);
-  XML_SetUserData(g_parser, &test_data);
-  XML_SetCharacterDataHandler(g_parser, ext2_accumulate_characters);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-/* Parsing the full buffer rather than a byte at a time makes a
- * difference to the encoding scanning code, so repeat the above tests
- * without breaking them down by byte.
- */
-START_TEST(test_ext_entity_latin1_utf16le_bom2) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
-                     "]>\n"
-                     "<doc>&en;</doc>";
-  ExtTest2 test_data
-      = {/* If UTF-16, 0xfeff is the BOM and 0x204c is black left bullet */
-         /* If Latin-1, 0xff = Y-diaeresis, 0xfe = lowercase thorn,
-          *   0x4c = L and 0x20 is a space
-          */
-         "\xff\xfe\x4c\x20", 4, XCS("iso-8859-1"), NULL, EE_PARSE_FULL_BUFFER};
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("\x00ff\x00feL ");
-#else
-  /* In UTF-8, y-diaeresis is 0xc3 0xbf, lowercase thorn is 0xc3 0xbe */
-  const XML_Char *expected = XCS("\xc3\xbf\xc3\xbeL ");
-#endif
-  CharData storage;
-
-  CharData_Init(&storage);
-  test_data.storage = &storage;
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader2);
-  XML_SetUserData(g_parser, &test_data);
-  XML_SetCharacterDataHandler(g_parser, ext2_accumulate_characters);
-  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-START_TEST(test_ext_entity_latin1_utf16be_bom2) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
-                     "]>\n"
-                     "<doc>&en;</doc>";
-  ExtTest2 test_data
-      = {/* If UTF-16, 0xfeff is the BOM and 0x204c is black left bullet */
-         /* If Latin-1, 0xff = Y-diaeresis, 0xfe = lowercase thorn,
-          *   0x4c = L and 0x20 is a space
-          */
-         "\xfe\xff\x20\x4c", 4, XCS("iso-8859-1"), NULL, EE_PARSE_FULL_BUFFER};
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("\x00fe\x00ff L");
-#else
-  /* In UTF-8, y-diaeresis is 0xc3 0xbf, lowercase thorn is 0xc3 0xbe */
-  const XML_Char *expected = "\xc3\xbe\xc3\xbf L";
-#endif
-  CharData storage;
-
-  CharData_Init(&storage);
-  test_data.storage = &storage;
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader2);
-  XML_SetUserData(g_parser, &test_data);
-  XML_SetCharacterDataHandler(g_parser, ext2_accumulate_characters);
-  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-/* Test little-endian UTF-16 given an explicit big-endian encoding */
-START_TEST(test_ext_entity_utf16_be) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
-                     "]>\n"
-                     "<doc>&en;</doc>";
-  ExtTest2 test_data
-      = {"<\0e\0/\0>\0", 8, XCS("utf-16be"), NULL, EE_PARSE_NONE};
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("\x3c00\x6500\x2f00\x3e00");
-#else
-  const XML_Char *expected = XCS("\xe3\xb0\x80"   /* U+3C00 */
-                                 "\xe6\x94\x80"   /* U+6500 */
-                                 "\xe2\xbc\x80"   /* U+2F00 */
-                                 "\xe3\xb8\x80"); /* U+3E00 */
-#endif
-  CharData storage;
-
-  CharData_Init(&storage);
-  test_data.storage = &storage;
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader2);
-  XML_SetUserData(g_parser, &test_data);
-  XML_SetCharacterDataHandler(g_parser, ext2_accumulate_characters);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-/* Test big-endian UTF-16 given an explicit little-endian encoding */
-START_TEST(test_ext_entity_utf16_le) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
-                     "]>\n"
-                     "<doc>&en;</doc>";
-  ExtTest2 test_data
-      = {"\0<\0e\0/\0>", 8, XCS("utf-16le"), NULL, EE_PARSE_NONE};
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("\x3c00\x6500\x2f00\x3e00");
-#else
-  const XML_Char *expected = XCS("\xe3\xb0\x80"   /* U+3C00 */
-                                 "\xe6\x94\x80"   /* U+6500 */
-                                 "\xe2\xbc\x80"   /* U+2F00 */
-                                 "\xe3\xb8\x80"); /* U+3E00 */
-#endif
-  CharData storage;
-
-  CharData_Init(&storage);
-  test_data.storage = &storage;
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader2);
-  XML_SetUserData(g_parser, &test_data);
-  XML_SetCharacterDataHandler(g_parser, ext2_accumulate_characters);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-/* Test little-endian UTF-16 given no explicit encoding.
- * The existing default encoding (UTF-8) is assumed to hold without a
- * BOM to contradict it, so the entity value will in fact provoke an
- * error because 0x00 is not a valid XML character.  We parse the
- * whole buffer in one go rather than feeding it in byte by byte to
- * exercise different code paths in the initial scanning routines.
- */
-typedef struct ExtFaults2 {
-  const char *parse_text;
-  int parse_len;
-  const char *fail_text;
-  const XML_Char *encoding;
-  enum XML_Error error;
-} ExtFaults2;
-
-static int XMLCALL
-external_entity_faulter2(XML_Parser parser, const XML_Char *context,
-                         const XML_Char *base, const XML_Char *systemId,
-                         const XML_Char *publicId) {
-  ExtFaults2 *test_data = (ExtFaults2 *)XML_GetUserData(parser);
-  XML_Parser extparser;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  extparser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (extparser == NULL)
-    fail("Could not create external entity parser");
-  if (test_data->encoding != NULL) {
-    if (! XML_SetEncoding(extparser, test_data->encoding))
-      fail("XML_SetEncoding() ignored for external entity");
-  }
-  if (XML_Parse(extparser, test_data->parse_text, test_data->parse_len,
-                XML_TRUE)
-      != XML_STATUS_ERROR)
-    fail(test_data->fail_text);
-  if (XML_GetErrorCode(extparser) != test_data->error)
-    xml_failure(extparser);
-
-  XML_ParserFree(extparser);
-  return XML_STATUS_ERROR;
-}
-
-START_TEST(test_ext_entity_utf16_unknown) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
-                     "]>\n"
-                     "<doc>&en;</doc>";
-  ExtFaults2 test_data
-      = {"a\0b\0c\0", 6, "Invalid character in entity not faulted", NULL,
-         XML_ERROR_INVALID_TOKEN};
-
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_faulter2);
-  XML_SetUserData(g_parser, &test_data);
-  expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
-                 "Invalid character should not have been accepted");
-}
-END_TEST
-
-/* Test not-quite-UTF-8 BOM (0xEF 0xBB 0xBF) */
-START_TEST(test_ext_entity_utf8_non_bom) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
-                     "]>\n"
-                     "<doc>&en;</doc>";
-  ExtTest2 test_data
-      = {"\xef\xbb\x80", /* Arabic letter DAD medial form, U+FEC0 */
-         3, NULL, NULL, EE_PARSE_NONE};
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("\xfec0");
-#else
-  const XML_Char *expected = XCS("\xef\xbb\x80");
-#endif
-  CharData storage;
-
-  CharData_Init(&storage);
-  test_data.storage = &storage;
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader2);
-  XML_SetUserData(g_parser, &test_data);
-  XML_SetCharacterDataHandler(g_parser, ext2_accumulate_characters);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-/* Test that UTF-8 in a CDATA section is correctly passed through */
-START_TEST(test_utf8_in_cdata_section) {
-  const char *text = "<doc><![CDATA[one \xc3\xa9 two]]></doc>";
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("one \x00e9 two");
-#else
-  const XML_Char *expected = XCS("one \xc3\xa9 two");
-#endif
-
-  run_character_check(text, expected);
-}
-END_TEST
-
-/* Test that little-endian UTF-16 in a CDATA section is handled */
-START_TEST(test_utf8_in_cdata_section_2) {
-  const char *text = "<doc><![CDATA[\xc3\xa9]\xc3\xa9two]]></doc>";
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("\x00e9]\x00e9two");
-#else
-  const XML_Char *expected = XCS("\xc3\xa9]\xc3\xa9two");
-#endif
-
-  run_character_check(text, expected);
-}
-END_TEST
-
-START_TEST(test_utf8_in_start_tags) {
-  struct test_case {
-    bool goodName;
-    bool goodNameStart;
-    const char *tagName;
-  };
-
-  // The idea with the tests below is this:
-  // We want to cover 1-, 2- and 3-byte sequences, 4-byte sequences
-  // go to isNever and are hence not a concern.
-  //
-  // We start with a character that is a valid name character
-  // (or even name-start character, see XML 1.0r4 spec) and then we flip
-  // single bits at places where (1) the result leaves the UTF-8 encoding space
-  // and (2) we stay in the same n-byte sequence family.
-  //
-  // The flipped bits are highlighted in angle brackets in comments,
-  // e.g. "[<1>011 1001]" means we had [0011 1001] but we now flipped
-  // the most significant bit to 1 to leave UTF-8 encoding space.
-  struct test_case cases[] = {
-      // 1-byte UTF-8: [0xxx xxxx]
-      {true, true, "\x3A"},   // [0011 1010] = ASCII colon ':'
-      {false, false, "\xBA"}, // [<1>011 1010]
-      {true, false, "\x39"},  // [0011 1001] = ASCII nine '9'
-      {false, false, "\xB9"}, // [<1>011 1001]
-
-      // 2-byte UTF-8: [110x xxxx] [10xx xxxx]
-      {true, true, "\xDB\xA5"},   // [1101 1011] [1010 0101] =
-                                  // Arabic small waw U+06E5
-      {false, false, "\x9B\xA5"}, // [1<0>01 1011] [1010 0101]
-      {false, false, "\xDB\x25"}, // [1101 1011] [<0>010 0101]
-      {false, false, "\xDB\xE5"}, // [1101 1011] [1<1>10 0101]
-      {true, false, "\xCC\x81"},  // [1100 1100] [1000 0001] =
-                                  // combining char U+0301
-      {false, false, "\x8C\x81"}, // [1<0>00 1100] [1000 0001]
-      {false, false, "\xCC\x01"}, // [1100 1100] [<0>000 0001]
-      {false, false, "\xCC\xC1"}, // [1100 1100] [1<1>00 0001]
-
-      // 3-byte UTF-8: [1110 xxxx] [10xx xxxx] [10xxxxxx]
-      {true, true, "\xE0\xA4\x85"},   // [1110 0000] [1010 0100] [1000 0101] =
-                                      // Devanagari Letter A U+0905
-      {false, false, "\xA0\xA4\x85"}, // [1<0>10 0000] [1010 0100] [1000 0101]
-      {false, false, "\xE0\x24\x85"}, // [1110 0000] [<0>010 0100] [1000 0101]
-      {false, false, "\xE0\xE4\x85"}, // [1110 0000] [1<1>10 0100] [1000 0101]
-      {false, false, "\xE0\xA4\x05"}, // [1110 0000] [1010 0100] [<0>000 0101]
-      {false, false, "\xE0\xA4\xC5"}, // [1110 0000] [1010 0100] [1<1>00 0101]
-      {true, false, "\xE0\xA4\x81"},  // [1110 0000] [1010 0100] [1000 0001] =
-                                      // combining char U+0901
-      {false, false, "\xA0\xA4\x81"}, // [1<0>10 0000] [1010 0100] [1000 0001]
-      {false, false, "\xE0\x24\x81"}, // [1110 0000] [<0>010 0100] [1000 0001]
-      {false, false, "\xE0\xE4\x81"}, // [1110 0000] [1<1>10 0100] [1000 0001]
-      {false, false, "\xE0\xA4\x01"}, // [1110 0000] [1010 0100] [<0>000 0001]
-      {false, false, "\xE0\xA4\xC1"}, // [1110 0000] [1010 0100] [1<1>00 0001]
-  };
-  const bool atNameStart[] = {true, false};
-
-  size_t i = 0;
-  char doc[1024];
-  size_t failCount = 0;
-
-  for (; i < sizeof(cases) / sizeof(cases[0]); i++) {
-    size_t j = 0;
-    for (; j < sizeof(atNameStart) / sizeof(atNameStart[0]); j++) {
-      const bool expectedSuccess
-          = atNameStart[j] ? cases[i].goodNameStart : cases[i].goodName;
-      sprintf(doc, "<%s%s><!--", atNameStart[j] ? "" : "a", cases[i].tagName);
-      XML_Parser parser = XML_ParserCreate(NULL);
-
-      const enum XML_Status status
-          = XML_Parse(parser, doc, (int)strlen(doc), /*isFinal=*/XML_FALSE);
-
-      bool success = true;
-      if ((status == XML_STATUS_OK) != expectedSuccess) {
-        success = false;
-      }
-      if ((status == XML_STATUS_ERROR)
-          && (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN)) {
-        success = false;
-      }
-
-      if (! success) {
-        fprintf(
-            stderr,
-            "FAIL case %2u (%sat name start, %u-byte sequence, error code %d)\n",
-            (unsigned)i + 1u, atNameStart[j] ? "    " : "not ",
-            (unsigned)strlen(cases[i].tagName), XML_GetErrorCode(parser));
-        failCount++;
-      }
-
-      XML_ParserFree(parser);
-    }
-  }
-
-  if (failCount > 0) {
-    fail("UTF-8 regression detected");
-  }
-}
-END_TEST
-
-/* Test trailing spaces in elements are accepted */
-static void XMLCALL
-record_element_end_handler(void *userData, const XML_Char *name) {
-  CharData *storage = (CharData *)userData;
-
-  CharData_AppendXMLChars(storage, XCS("/"), 1);
-  CharData_AppendXMLChars(storage, name, -1);
-}
-
-START_TEST(test_trailing_spaces_in_elements) {
-  const char *text = "<doc   >Hi</doc >";
-  const XML_Char *expected = XCS("doc/doc");
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetElementHandler(g_parser, record_element_start_handler,
-                        record_element_end_handler);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-START_TEST(test_utf16_attribute) {
-  const char text[] =
-      /* <d {KHO KHWAI}{CHO CHAN}='a'/>
-       * where {KHO KHWAI} = U+0E04 = 0xe0 0xb8 0x84 in UTF-8
-       * and   {CHO CHAN}  = U+0E08 = 0xe0 0xb8 0x88 in UTF-8
-       */
-      "<\0d\0 \0\x04\x0e\x08\x0e=\0'\0a\0'\0/\0>\0";
-  const XML_Char *expected = XCS("a");
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetStartElementHandler(g_parser, accumulate_attribute);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-START_TEST(test_utf16_second_attr) {
-  /* <d a='1' {KHO KHWAI}{CHO CHAN}='2'/>
-   * where {KHO KHWAI} = U+0E04 = 0xe0 0xb8 0x84 in UTF-8
-   * and   {CHO CHAN}  = U+0E08 = 0xe0 0xb8 0x88 in UTF-8
-   */
-  const char text[] = "<\0d\0 \0a\0=\0'\0\x31\0'\0 \0"
-                      "\x04\x0e\x08\x0e=\0'\0\x32\0'\0/\0>\0";
-  const XML_Char *expected = XCS("1");
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetStartElementHandler(g_parser, accumulate_attribute);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-START_TEST(test_attr_after_solidus) {
-  const char *text = "<doc attr1='a' / attr2='b'>";
-
-  expect_failure(text, XML_ERROR_INVALID_TOKEN, "Misplaced / not faulted");
-}
-END_TEST
-
-static void XMLCALL
-accumulate_entity_decl(void *userData, const XML_Char *entityName,
-                       int is_parameter_entity, const XML_Char *value,
-                       int value_length, const XML_Char *base,
-                       const XML_Char *systemId, const XML_Char *publicId,
-                       const XML_Char *notationName) {
-  CharData *storage = (CharData *)userData;
-
-  UNUSED_P(is_parameter_entity);
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  UNUSED_P(notationName);
-  CharData_AppendXMLChars(storage, entityName, -1);
-  CharData_AppendXMLChars(storage, XCS("="), 1);
-  CharData_AppendXMLChars(storage, value, value_length);
-  CharData_AppendXMLChars(storage, XCS("\n"), 1);
-}
-
-START_TEST(test_utf16_pe) {
-  /* <!DOCTYPE doc [
-   * <!ENTITY % {KHO KHWAI}{CHO CHAN} '<!ELEMENT doc (#PCDATA)>'>
-   * %{KHO KHWAI}{CHO CHAN};
-   * ]>
-   * <doc></doc>
-   *
-   * where {KHO KHWAI} = U+0E04 = 0xe0 0xb8 0x84 in UTF-8
-   * and   {CHO CHAN}  = U+0E08 = 0xe0 0xb8 0x88 in UTF-8
-   */
-  const char text[] = "\0<\0!\0D\0O\0C\0T\0Y\0P\0E\0 \0d\0o\0c\0 \0[\0\n"
-                      "\0<\0!\0E\0N\0T\0I\0T\0Y\0 \0%\0 \x0e\x04\x0e\x08\0 "
-                      "\0'\0<\0!\0E\0L\0E\0M\0E\0N\0T\0 "
-                      "\0d\0o\0c\0 \0(\0#\0P\0C\0D\0A\0T\0A\0)\0>\0'\0>\0\n"
-                      "\0%\x0e\x04\x0e\x08\0;\0\n"
-                      "\0]\0>\0\n"
-                      "\0<\0d\0o\0c\0>\0<\0/\0d\0o\0c\0>";
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("\x0e04\x0e08=<!ELEMENT doc (#PCDATA)>\n");
-#else
-  const XML_Char *expected
-      = XCS("\xe0\xb8\x84\xe0\xb8\x88=<!ELEMENT doc (#PCDATA)>\n");
-#endif
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  XML_SetEntityDeclHandler(g_parser, accumulate_entity_decl);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-/* Test that duff attribute description keywords are rejected */
-START_TEST(test_bad_attr_desc_keyword) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ATTLIST doc attr CDATA #!IMPLIED>\n"
-                     "]>\n"
-                     "<doc />";
-
-  expect_failure(text, XML_ERROR_INVALID_TOKEN,
-                 "Bad keyword !IMPLIED not faulted");
-}
-END_TEST
-
-/* Test that an invalid attribute description keyword consisting of
- * UTF-16 characters with their top bytes non-zero are correctly
- * faulted
- */
-START_TEST(test_bad_attr_desc_keyword_utf16) {
-  /* <!DOCTYPE d [
-   * <!ATTLIST d a CDATA #{KHO KHWAI}{CHO CHAN}>
-   * ]><d/>
-   *
-   * where {KHO KHWAI} = U+0E04 = 0xe0 0xb8 0x84 in UTF-8
-   * and   {CHO CHAN}  = U+0E08 = 0xe0 0xb8 0x88 in UTF-8
-   */
-  const char text[]
-      = "\0<\0!\0D\0O\0C\0T\0Y\0P\0E\0 \0d\0 \0[\0\n"
-        "\0<\0!\0A\0T\0T\0L\0I\0S\0T\0 \0d\0 \0a\0 \0C\0D\0A\0T\0A\0 "
-        "\0#\x0e\x04\x0e\x08\0>\0\n"
-        "\0]\0>\0<\0d\0/\0>";
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      != XML_STATUS_ERROR)
-    fail("Invalid UTF16 attribute keyword not faulted");
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_SYNTAX)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test that invalid syntax in a <!DOCTYPE> is rejected.  Do this
- * using prefix-encoding (see above) to trigger specific code paths
- */
-START_TEST(test_bad_doctype) {
-  const char *text = "<?xml version='1.0' encoding='prefix-conv'?>\n"
-                     "<!DOCTYPE doc [ \x80\x44 ]><doc/>";
-
-  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
-  expect_failure(text, XML_ERROR_SYNTAX,
-                 "Invalid bytes in DOCTYPE not faulted");
-}
-END_TEST
-
-START_TEST(test_bad_doctype_utf8) {
-  const char *text = "<!DOCTYPE \xDB\x25"
-                     "doc><doc/>"; // [1101 1011] [<0>010 0101]
-  expect_failure(text, XML_ERROR_INVALID_TOKEN,
-                 "Invalid UTF-8 in DOCTYPE not faulted");
-}
-END_TEST
-
-START_TEST(test_bad_doctype_utf16) {
-  const char text[] =
-      /* <!DOCTYPE doc [ \x06f2 ]><doc/>
-       *
-       * U+06F2 = EXTENDED ARABIC-INDIC DIGIT TWO, a valid number
-       * (name character) but not a valid letter (name start character)
-       */
-      "\0<\0!\0D\0O\0C\0T\0Y\0P\0E\0 \0d\0o\0c\0 \0[\0 "
-      "\x06\xf2"
-      "\0 \0]\0>\0<\0d\0o\0c\0/\0>";
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      != XML_STATUS_ERROR)
-    fail("Invalid bytes in DOCTYPE not faulted");
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_SYNTAX)
-    xml_failure(g_parser);
-}
-END_TEST
-
-START_TEST(test_bad_doctype_plus) {
-  const char *text = "<!DOCTYPE 1+ [ <!ENTITY foo 'bar'> ]>\n"
-                     "<1+>&foo;</1+>";
-
-  expect_failure(text, XML_ERROR_INVALID_TOKEN,
-                 "'+' in document name not faulted");
-}
-END_TEST
-
-START_TEST(test_bad_doctype_star) {
-  const char *text = "<!DOCTYPE 1* [ <!ENTITY foo 'bar'> ]>\n"
-                     "<1*>&foo;</1*>";
-
-  expect_failure(text, XML_ERROR_INVALID_TOKEN,
-                 "'*' in document name not faulted");
-}
-END_TEST
-
-START_TEST(test_bad_doctype_query) {
-  const char *text = "<!DOCTYPE 1? [ <!ENTITY foo 'bar'> ]>\n"
-                     "<1?>&foo;</1?>";
-
-  expect_failure(text, XML_ERROR_INVALID_TOKEN,
-                 "'?' in document name not faulted");
-}
-END_TEST
-
-START_TEST(test_unknown_encoding_bad_ignore) {
-  const char *text = "<?xml version='1.0' encoding='prefix-conv'?>"
-                     "<!DOCTYPE doc SYSTEM 'foo'>"
-                     "<doc><e>&entity;</e></doc>";
-  ExtFaults fault = {"<![IGNORE[<!ELEMENT \xffG (#PCDATA)*>]]>",
-                     "Invalid character not faulted", XCS("prefix-conv"),
-                     XML_ERROR_INVALID_TOKEN};
-
-  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_faulter);
-  XML_SetUserData(g_parser, &fault);
-  expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
-                 "Bad IGNORE section with unknown encoding not failed");
-}
-END_TEST
-
-START_TEST(test_entity_in_utf16_be_attr) {
-  const char text[] =
-      /* <e a='&#228; &#x00E4;'></e> */
-      "\0<\0e\0 \0a\0=\0'\0&\0#\0\x32\0\x32\0\x38\0;\0 "
-      "\0&\0#\0x\0\x30\0\x30\0E\0\x34\0;\0'\0>\0<\0/\0e\0>";
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("\x00e4 \x00e4");
-#else
-  const XML_Char *expected = XCS("\xc3\xa4 \xc3\xa4");
-#endif
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  XML_SetStartElementHandler(g_parser, accumulate_attribute);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-START_TEST(test_entity_in_utf16_le_attr) {
-  const char text[] =
-      /* <e a='&#228; &#x00E4;'></e> */
-      "<\0e\0 \0a\0=\0'\0&\0#\0\x32\0\x32\0\x38\0;\0 \0"
-      "&\0#\0x\0\x30\0\x30\0E\0\x34\0;\0'\0>\0<\0/\0e\0>\0";
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("\x00e4 \x00e4");
-#else
-  const XML_Char *expected = XCS("\xc3\xa4 \xc3\xa4");
-#endif
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  XML_SetStartElementHandler(g_parser, accumulate_attribute);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-START_TEST(test_entity_public_utf16_be) {
-  const char text[] =
-      /* <!DOCTYPE d [ */
-      "\0<\0!\0D\0O\0C\0T\0Y\0P\0E\0 \0d\0 \0[\0\n"
-      /* <!ENTITY % e PUBLIC 'foo' 'bar.ent'> */
-      "\0<\0!\0E\0N\0T\0I\0T\0Y\0 \0%\0 \0e\0 \0P\0U\0B\0L\0I\0C\0 "
-      "\0'\0f\0o\0o\0'\0 \0'\0b\0a\0r\0.\0e\0n\0t\0'\0>\0\n"
-      /* %e; */
-      "\0%\0e\0;\0\n"
-      /* ]> */
-      "\0]\0>\0\n"
-      /* <d>&j;</d> */
-      "\0<\0d\0>\0&\0j\0;\0<\0/\0d\0>";
-  ExtTest2 test_data = {/* <!ENTITY j 'baz'> */
-                        "\0<\0!\0E\0N\0T\0I\0T\0Y\0 \0j\0 \0'\0b\0a\0z\0'\0>",
-                        34, NULL, NULL, EE_PARSE_NONE};
-  const XML_Char *expected = XCS("baz");
-  CharData storage;
-
-  CharData_Init(&storage);
-  test_data.storage = &storage;
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader2);
-  XML_SetUserData(g_parser, &test_data);
-  XML_SetCharacterDataHandler(g_parser, ext2_accumulate_characters);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-START_TEST(test_entity_public_utf16_le) {
-  const char text[] =
-      /* <!DOCTYPE d [ */
-      "<\0!\0D\0O\0C\0T\0Y\0P\0E\0 \0d\0 \0[\0\n\0"
-      /* <!ENTITY % e PUBLIC 'foo' 'bar.ent'> */
-      "<\0!\0E\0N\0T\0I\0T\0Y\0 \0%\0 \0e\0 \0P\0U\0B\0L\0I\0C\0 \0"
-      "'\0f\0o\0o\0'\0 \0'\0b\0a\0r\0.\0e\0n\0t\0'\0>\0\n\0"
-      /* %e; */
-      "%\0e\0;\0\n\0"
-      /* ]> */
-      "]\0>\0\n\0"
-      /* <d>&j;</d> */
-      "<\0d\0>\0&\0j\0;\0<\0/\0d\0>\0";
-  ExtTest2 test_data = {/* <!ENTITY j 'baz'> */
-                        "<\0!\0E\0N\0T\0I\0T\0Y\0 \0j\0 \0'\0b\0a\0z\0'\0>\0",
-                        34, NULL, NULL, EE_PARSE_NONE};
-  const XML_Char *expected = XCS("baz");
-  CharData storage;
-
-  CharData_Init(&storage);
-  test_data.storage = &storage;
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_loader2);
-  XML_SetUserData(g_parser, &test_data);
-  XML_SetCharacterDataHandler(g_parser, ext2_accumulate_characters);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-/* Test that a doctype with neither an internal nor external subset is
- * faulted
- */
-START_TEST(test_short_doctype) {
-  const char *text = "<!DOCTYPE doc></doc>";
-  expect_failure(text, XML_ERROR_INVALID_TOKEN,
-                 "DOCTYPE without subset not rejected");
-}
-END_TEST
-
-START_TEST(test_short_doctype_2) {
-  const char *text = "<!DOCTYPE doc PUBLIC></doc>";
-  expect_failure(text, XML_ERROR_SYNTAX,
-                 "DOCTYPE without Public ID not rejected");
-}
-END_TEST
-
-START_TEST(test_short_doctype_3) {
-  const char *text = "<!DOCTYPE doc SYSTEM></doc>";
-  expect_failure(text, XML_ERROR_SYNTAX,
-                 "DOCTYPE without System ID not rejected");
-}
-END_TEST
-
-START_TEST(test_long_doctype) {
-  const char *text = "<!DOCTYPE doc PUBLIC 'foo' 'bar' 'baz'></doc>";
-  expect_failure(text, XML_ERROR_SYNTAX, "DOCTYPE with extra ID not rejected");
-}
-END_TEST
-
-START_TEST(test_bad_entity) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY foo PUBLIC>\n"
-                     "]>\n"
-                     "<doc/>";
-  expect_failure(text, XML_ERROR_SYNTAX,
-                 "ENTITY without Public ID is not rejected");
-}
-END_TEST
-
-/* Test unquoted value is faulted */
-START_TEST(test_bad_entity_2) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY % foo bar>\n"
-                     "]>\n"
-                     "<doc/>";
-  expect_failure(text, XML_ERROR_SYNTAX,
-                 "ENTITY without Public ID is not rejected");
-}
-END_TEST
-
-START_TEST(test_bad_entity_3) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY % foo PUBLIC>\n"
-                     "]>\n"
-                     "<doc/>";
-  expect_failure(text, XML_ERROR_SYNTAX,
-                 "Parameter ENTITY without Public ID is not rejected");
-}
-END_TEST
-
-START_TEST(test_bad_entity_4) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY % foo SYSTEM>\n"
-                     "]>\n"
-                     "<doc/>";
-  expect_failure(text, XML_ERROR_SYNTAX,
-                 "Parameter ENTITY without Public ID is not rejected");
-}
-END_TEST
-
-START_TEST(test_bad_notation) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!NOTATION n SYSTEM>\n"
-                     "]>\n"
-                     "<doc/>";
-  expect_failure(text, XML_ERROR_SYNTAX,
-                 "Notation without System ID is not rejected");
-}
-END_TEST
-
-/* Test for issue #11, wrongly suppressed default handler */
-typedef struct default_check {
-  const XML_Char *expected;
-  const int expectedLen;
-  XML_Bool seen;
-} DefaultCheck;
-
-static void XMLCALL
-checking_default_handler(void *userData, const XML_Char *s, int len) {
-  DefaultCheck *data = (DefaultCheck *)userData;
-  int i;
-
-  for (i = 0; data[i].expected != NULL; i++) {
-    if (data[i].expectedLen == len
-        && ! memcmp(data[i].expected, s, len * sizeof(XML_Char))) {
-      data[i].seen = XML_TRUE;
-      break;
-    }
-  }
-}
-
-START_TEST(test_default_doctype_handler) {
-  const char *text = "<!DOCTYPE doc PUBLIC 'pubname' 'test.dtd' [\n"
-                     "  <!ENTITY foo 'bar'>\n"
-                     "]>\n"
-                     "<doc>&foo;</doc>";
-  DefaultCheck test_data[] = {{XCS("'pubname'"), 9, XML_FALSE},
-                              {XCS("'test.dtd'"), 10, XML_FALSE},
-                              {NULL, 0, XML_FALSE}};
-  int i;
-
-  XML_SetUserData(g_parser, &test_data);
-  XML_SetDefaultHandler(g_parser, checking_default_handler);
-  XML_SetEntityDeclHandler(g_parser, dummy_entity_decl_handler);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  for (i = 0; test_data[i].expected != NULL; i++)
-    if (! test_data[i].seen)
-      fail("Default handler not run for public !DOCTYPE");
-}
-END_TEST
-
-START_TEST(test_empty_element_abort) {
-  const char *text = "<abort/>";
-
-  XML_SetStartElementHandler(g_parser, start_element_suspender);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_ERROR)
-    fail("Expected to error on abort");
-}
-END_TEST
-
-/* Regression test for GH issue #612: unfinished m_declAttributeType
- * allocation in ->m_tempPool can corrupt following allocation.
- */
-static int XMLCALL
-external_entity_unfinished_attlist(XML_Parser parser, const XML_Char *context,
-                                   const XML_Char *base,
-                                   const XML_Char *systemId,
-                                   const XML_Char *publicId) {
-  const char *text = "<!ELEMENT barf ANY>\n"
-                     "<!ATTLIST barf my_attr (blah|%blah;a|foo) #REQUIRED>\n"
-                     "<!--COMMENT-->\n";
-  XML_Parser ext_parser;
-
-  UNUSED_P(base);
-  UNUSED_P(publicId);
-  if (systemId == NULL)
-    return XML_STATUS_OK;
-
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    fail("Could not create external entity parser");
-
-  if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(ext_parser);
-
-  XML_ParserFree(ext_parser);
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_pool_integrity_with_unfinished_attr) {
-  const char *text = "<?xml version='1.0' encoding='UTF-8'?>\n"
-                     "<!DOCTYPE foo [\n"
-                     "<!ELEMENT foo ANY>\n"
-                     "<!ENTITY % entp SYSTEM \"external.dtd\">\n"
-                     "%entp;\n"
-                     "]>\n"
-                     "<a></a>\n";
-  const XML_Char *expected = XCS("COMMENT");
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_unfinished_attlist);
-  XML_SetAttlistDeclHandler(g_parser, dummy_attlist_decl_handler);
-  XML_SetCommentHandler(g_parser, accumulate_comment);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-typedef struct {
-  XML_Parser parser;
-  CharData *storage;
-} ParserPlusStorage;
-
-static void XMLCALL
-accumulate_and_suspend_comment_handler(void *userData, const XML_Char *data) {
-  ParserPlusStorage *const parserPlusStorage = (ParserPlusStorage *)userData;
-  accumulate_comment(parserPlusStorage->storage, data);
-  XML_StopParser(parserPlusStorage->parser, XML_TRUE);
-}
-
-START_TEST(test_nested_entity_suspend) {
-  const char *const text = "<!DOCTYPE a [\n"
-                           "  <!ENTITY e1 '<!--e1-->'>\n"
-                           "  <!ENTITY e2 '<!--e2 head-->&e1;<!--e2 tail-->'>\n"
-                           "  <!ENTITY e3 '<!--e3 head-->&e2;<!--e3 tail-->'>\n"
-                           "]>\n"
-                           "<a><!--start-->&e3;<!--end--></a>";
-  const XML_Char *const expected = XCS("start") XCS("e3 head") XCS("e2 head")
-      XCS("e1") XCS("e2 tail") XCS("e3 tail") XCS("end");
-  CharData storage;
-  XML_Parser parser = XML_ParserCreate(NULL);
-  ParserPlusStorage parserPlusStorage = {parser, &storage};
-
-  CharData_Init(&storage);
-  XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetCommentHandler(parser, accumulate_and_suspend_comment_handler);
-  XML_SetUserData(parser, &parserPlusStorage);
-
-  enum XML_Status status = XML_Parse(parser, text, (int)strlen(text), XML_TRUE);
-  while (status == XML_STATUS_SUSPENDED) {
-    status = XML_ResumeParser(parser);
-  }
-  if (status != XML_STATUS_OK)
-    xml_failure(parser);
-
-  CharData_CheckXMLChars(&storage, expected);
-  XML_ParserFree(parser);
-}
-END_TEST
-
-/*
- * Namespaces tests.
- */
-
-static void
-namespace_setup(void) {
-  g_parser = XML_ParserCreateNS(NULL, XCS(' '));
-  if (g_parser == NULL)
-    fail("Parser not created.");
-}
-
-static void
-namespace_teardown(void) {
-  basic_teardown();
-}
-
-/* Check that an element name and attribute name match the expected values.
-   The expected values are passed as an array reference of string pointers
-   provided as the userData argument; the first is the expected
-   element name, and the second is the expected attribute name.
-*/
-static int triplet_start_flag = XML_FALSE;
-static int triplet_end_flag = XML_FALSE;
-
-static void XMLCALL
-triplet_start_checker(void *userData, const XML_Char *name,
-                      const XML_Char **atts) {
-  XML_Char **elemstr = (XML_Char **)userData;
-  char buffer[1024];
-  if (xcstrcmp(elemstr[0], name) != 0) {
-    sprintf(buffer, "unexpected start string: '%" XML_FMT_STR "'", name);
-    fail(buffer);
-  }
-  if (xcstrcmp(elemstr[1], atts[0]) != 0) {
-    sprintf(buffer, "unexpected attribute string: '%" XML_FMT_STR "'", atts[0]);
-    fail(buffer);
-  }
-  triplet_start_flag = XML_TRUE;
-}
-
-/* Check that the element name passed to the end-element handler matches
-   the expected value.  The expected value is passed as the first element
-   in an array of strings passed as the userData argument.
-*/
-static void XMLCALL
-triplet_end_checker(void *userData, const XML_Char *name) {
-  XML_Char **elemstr = (XML_Char **)userData;
-  if (xcstrcmp(elemstr[0], name) != 0) {
-    char buffer[1024];
-    sprintf(buffer, "unexpected end string: '%" XML_FMT_STR "'", name);
-    fail(buffer);
-  }
-  triplet_end_flag = XML_TRUE;
-}
-
-START_TEST(test_return_ns_triplet) {
-  const char *text = "<foo:e xmlns:foo='http://example.org/' bar:a='12'\n"
-                     "       xmlns:bar='http://example.org/'>";
-  const char *epilog = "</foo:e>";
-  const XML_Char *elemstr[]
-      = {XCS("http://example.org/ e foo"), XCS("http://example.org/ a bar")};
-  XML_SetReturnNSTriplet(g_parser, XML_TRUE);
-  XML_SetUserData(g_parser, (void *)elemstr);
-  XML_SetElementHandler(g_parser, triplet_start_checker, triplet_end_checker);
-  XML_SetNamespaceDeclHandler(g_parser, dummy_start_namespace_decl_handler,
-                              dummy_end_namespace_decl_handler);
-  triplet_start_flag = XML_FALSE;
-  triplet_end_flag = XML_FALSE;
-  dummy_handler_flags = 0;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  if (! triplet_start_flag)
-    fail("triplet_start_checker not invoked");
-  /* Check that unsetting "return triplets" fails while still parsing */
-  XML_SetReturnNSTriplet(g_parser, XML_FALSE);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, epilog, (int)strlen(epilog), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  if (! triplet_end_flag)
-    fail("triplet_end_checker not invoked");
-  if (dummy_handler_flags
-      != (DUMMY_START_NS_DECL_HANDLER_FLAG | DUMMY_END_NS_DECL_HANDLER_FLAG))
-    fail("Namespace handlers not called");
-}
-END_TEST
-
-static void XMLCALL
-overwrite_start_checker(void *userData, const XML_Char *name,
-                        const XML_Char **atts) {
-  CharData *storage = (CharData *)userData;
-  CharData_AppendXMLChars(storage, XCS("start "), 6);
-  CharData_AppendXMLChars(storage, name, -1);
-  while (*atts != NULL) {
-    CharData_AppendXMLChars(storage, XCS("\nattribute "), 11);
-    CharData_AppendXMLChars(storage, *atts, -1);
-    atts += 2;
-  }
-  CharData_AppendXMLChars(storage, XCS("\n"), 1);
-}
-
-static void XMLCALL
-overwrite_end_checker(void *userData, const XML_Char *name) {
-  CharData *storage = (CharData *)userData;
-  CharData_AppendXMLChars(storage, XCS("end "), 4);
-  CharData_AppendXMLChars(storage, name, -1);
-  CharData_AppendXMLChars(storage, XCS("\n"), 1);
-}
-
-static void
-run_ns_tagname_overwrite_test(const char *text, const XML_Char *result) {
-  CharData storage;
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  XML_SetElementHandler(g_parser, overwrite_start_checker,
-                        overwrite_end_checker);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, result);
-}
-
-/* Regression test for SF bug #566334. */
-START_TEST(test_ns_tagname_overwrite) {
-  const char *text = "<n:e xmlns:n='http://example.org/'>\n"
-                     "  <n:f n:attr='foo'/>\n"
-                     "  <n:g n:attr2='bar'/>\n"
-                     "</n:e>";
-  const XML_Char *result = XCS("start http://example.org/ e\n")
-      XCS("start http://example.org/ f\n")
-          XCS("attribute http://example.org/ attr\n")
-              XCS("end http://example.org/ f\n")
-                  XCS("start http://example.org/ g\n")
-                      XCS("attribute http://example.org/ attr2\n")
-                          XCS("end http://example.org/ g\n")
-                              XCS("end http://example.org/ e\n");
-  run_ns_tagname_overwrite_test(text, result);
-}
-END_TEST
-
-/* Regression test for SF bug #566334. */
-START_TEST(test_ns_tagname_overwrite_triplet) {
-  const char *text = "<n:e xmlns:n='http://example.org/'>\n"
-                     "  <n:f n:attr='foo'/>\n"
-                     "  <n:g n:attr2='bar'/>\n"
-                     "</n:e>";
-  const XML_Char *result = XCS("start http://example.org/ e n\n")
-      XCS("start http://example.org/ f n\n")
-          XCS("attribute http://example.org/ attr n\n")
-              XCS("end http://example.org/ f n\n")
-                  XCS("start http://example.org/ g n\n")
-                      XCS("attribute http://example.org/ attr2 n\n")
-                          XCS("end http://example.org/ g n\n")
-                              XCS("end http://example.org/ e n\n");
-  XML_SetReturnNSTriplet(g_parser, XML_TRUE);
-  run_ns_tagname_overwrite_test(text, result);
-}
-END_TEST
-
-/* Regression test for SF bug #620343. */
-static void XMLCALL
-start_element_fail(void *userData, const XML_Char *name,
-                   const XML_Char **atts) {
-  UNUSED_P(userData);
-  UNUSED_P(name);
-  UNUSED_P(atts);
-
-  /* We should never get here. */
-  fail("should never reach start_element_fail()");
-}
-
-static void XMLCALL
-start_ns_clearing_start_element(void *userData, const XML_Char *prefix,
-                                const XML_Char *uri) {
-  UNUSED_P(prefix);
-  UNUSED_P(uri);
-  XML_SetStartElementHandler((XML_Parser)userData, NULL);
-}
-
-START_TEST(test_start_ns_clears_start_element) {
-  /* This needs to use separate start/end tags; using the empty tag
-     syntax doesn't cause the problematic path through Expat to be
-     taken.
-  */
-  const char *text = "<e xmlns='http://example.org/'></e>";
-
-  XML_SetStartElementHandler(g_parser, start_element_fail);
-  XML_SetStartNamespaceDeclHandler(g_parser, start_ns_clearing_start_element);
-  XML_SetEndNamespaceDeclHandler(g_parser, dummy_end_namespace_decl_handler);
-  XML_UseParserAsHandlerArg(g_parser);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Regression test for SF bug #616863. */
-static int XMLCALL
-external_entity_handler(XML_Parser parser, const XML_Char *context,
-                        const XML_Char *base, const XML_Char *systemId,
-                        const XML_Char *publicId) {
-  intptr_t callno = 1 + (intptr_t)XML_GetUserData(parser);
-  const char *text;
-  XML_Parser p2;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  if (callno == 1)
-    text = ("<!ELEMENT doc (e+)>\n"
-            "<!ATTLIST doc xmlns CDATA #IMPLIED>\n"
-            "<!ELEMENT e EMPTY>\n");
-  else
-    text = ("<?xml version='1.0' encoding='us-ascii'?>"
-            "<e/>");
-
-  XML_SetUserData(parser, (void *)callno);
-  p2 = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (_XML_Parse_SINGLE_BYTES(p2, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR) {
-    xml_failure(p2);
-    return XML_STATUS_ERROR;
-  }
-  XML_ParserFree(p2);
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_default_ns_from_ext_subset_and_ext_ge) {
-  const char *text = "<?xml version='1.0'?>\n"
-                     "<!DOCTYPE doc SYSTEM 'http://example.org/doc.dtd' [\n"
-                     "  <!ENTITY en SYSTEM 'http://example.org/entity.ent'>\n"
-                     "]>\n"
-                     "<doc xmlns='http://example.org/ns1'>\n"
-                     "&en;\n"
-                     "</doc>";
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_handler);
-  /* We actually need to set this handler to tickle this bug. */
-  XML_SetStartElementHandler(g_parser, dummy_start_element);
-  XML_SetUserData(g_parser, NULL);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Regression test #1 for SF bug #673791. */
-START_TEST(test_ns_prefix_with_empty_uri_1) {
-  const char *text = "<doc xmlns:prefix='http://example.org/'>\n"
-                     "  <e xmlns:prefix=''/>\n"
-                     "</doc>";
-
-  expect_failure(text, XML_ERROR_UNDECLARING_PREFIX,
-                 "Did not report re-setting namespace"
-                 " URI with prefix to ''.");
-}
-END_TEST
-
-/* Regression test #2 for SF bug #673791. */
-START_TEST(test_ns_prefix_with_empty_uri_2) {
-  const char *text = "<?xml version='1.0'?>\n"
-                     "<docelem xmlns:pre=''/>";
-
-  expect_failure(text, XML_ERROR_UNDECLARING_PREFIX,
-                 "Did not report setting namespace URI with prefix to ''.");
-}
-END_TEST
-
-/* Regression test #3 for SF bug #673791. */
-START_TEST(test_ns_prefix_with_empty_uri_3) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ELEMENT doc EMPTY>\n"
-                     "  <!ATTLIST doc\n"
-                     "    xmlns:prefix CDATA ''>\n"
-                     "]>\n"
-                     "<doc/>";
-
-  expect_failure(text, XML_ERROR_UNDECLARING_PREFIX,
-                 "Didn't report attr default setting NS w/ prefix to ''.");
-}
-END_TEST
-
-/* Regression test #4 for SF bug #673791. */
-START_TEST(test_ns_prefix_with_empty_uri_4) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ELEMENT prefix:doc EMPTY>\n"
-                     "  <!ATTLIST prefix:doc\n"
-                     "    xmlns:prefix CDATA 'http://example.org/'>\n"
-                     "]>\n"
-                     "<prefix:doc/>";
-  /* Packaged info expected by the end element handler;
-     the weird structuring lets us re-use the triplet_end_checker()
-     function also used for another test. */
-  const XML_Char *elemstr[] = {XCS("http://example.org/ doc prefix")};
-  XML_SetReturnNSTriplet(g_parser, XML_TRUE);
-  XML_SetUserData(g_parser, (void *)elemstr);
-  XML_SetEndElementHandler(g_parser, triplet_end_checker);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test with non-xmlns prefix */
-START_TEST(test_ns_unbound_prefix) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ELEMENT prefix:doc EMPTY>\n"
-                     "  <!ATTLIST prefix:doc\n"
-                     "    notxmlns:prefix CDATA 'http://example.org/'>\n"
-                     "]>\n"
-                     "<prefix:doc/>";
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_ERROR)
-    fail("Unbound prefix incorrectly passed");
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_UNBOUND_PREFIX)
-    xml_failure(g_parser);
-}
-END_TEST
-
-START_TEST(test_ns_default_with_empty_uri) {
-  const char *text = "<doc xmlns='http://example.org/'>\n"
-                     "  <e xmlns=''/>\n"
-                     "</doc>";
-  /* Add some handlers to exercise extra code paths */
-  XML_SetStartNamespaceDeclHandler(g_parser,
-                                   dummy_start_namespace_decl_handler);
-  XML_SetEndNamespaceDeclHandler(g_parser, dummy_end_namespace_decl_handler);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Regression test for SF bug #692964: two prefixes for one namespace. */
-START_TEST(test_ns_duplicate_attrs_diff_prefixes) {
-  const char *text = "<doc xmlns:a='http://example.org/a'\n"
-                     "     xmlns:b='http://example.org/a'\n"
-                     "     a:a='v' b:a='v' />";
-  expect_failure(text, XML_ERROR_DUPLICATE_ATTRIBUTE,
-                 "did not report multiple attributes with same URI+name");
-}
-END_TEST
-
-START_TEST(test_ns_duplicate_hashes) {
-  /* The hash of an attribute is calculated as the hash of its URI
-   * concatenated with a space followed by its name (after the
-   * colon).  We wish to generate attributes with the same hash
-   * value modulo the attribute table size so that we can check that
-   * the attribute hash table works correctly.  The attribute hash
-   * table size will be the smallest power of two greater than the
-   * number of attributes, but at least eight.  There is
-   * unfortunately no programmatic way of getting the hash or the
-   * table size at user level, but the test code coverage percentage
-   * will drop if the hashes cease to point to the same row.
-   *
-   * The cunning plan is to have few enough attributes to have a
-   * reliable table size of 8, and have the single letter attribute
-   * names be 8 characters apart, producing a hash which will be the
-   * same modulo 8.
-   */
-  const char *text = "<doc xmlns:a='http://example.org/a'\n"
-                     "     a:a='v' a:i='w' />";
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Regression test for SF bug #695401: unbound prefix. */
-START_TEST(test_ns_unbound_prefix_on_attribute) {
-  const char *text = "<doc a:attr=''/>";
-  expect_failure(text, XML_ERROR_UNBOUND_PREFIX,
-                 "did not report unbound prefix on attribute");
-}
-END_TEST
-
-/* Regression test for SF bug #695401: unbound prefix. */
-START_TEST(test_ns_unbound_prefix_on_element) {
-  const char *text = "<a:doc/>";
-  expect_failure(text, XML_ERROR_UNBOUND_PREFIX,
-                 "did not report unbound prefix on element");
-}
-END_TEST
-
-/* Test that the parsing status is correctly reset by XML_ParserReset().
- * We usE test_return_ns_triplet() for our example parse to improve
- * coverage of tidying up code executed.
- */
-START_TEST(test_ns_parser_reset) {
-  XML_ParsingStatus status;
-
-  XML_GetParsingStatus(g_parser, &status);
-  if (status.parsing != XML_INITIALIZED)
-    fail("parsing status doesn't start INITIALIZED");
-  test_return_ns_triplet();
-  XML_GetParsingStatus(g_parser, &status);
-  if (status.parsing != XML_FINISHED)
-    fail("parsing status doesn't end FINISHED");
-  XML_ParserReset(g_parser, NULL);
-  XML_GetParsingStatus(g_parser, &status);
-  if (status.parsing != XML_INITIALIZED)
-    fail("parsing status doesn't reset to INITIALIZED");
-}
-END_TEST
-
-/* Test that long element names with namespaces are handled correctly */
-START_TEST(test_ns_long_element) {
-  const char *text
-      = "<foo:thisisalongenoughelementnametotriggerareallocation\n"
-        " xmlns:foo='http://example.org/' bar:a='12'\n"
-        " xmlns:bar='http://example.org/'>"
-        "</foo:thisisalongenoughelementnametotriggerareallocation>";
-  const XML_Char *elemstr[]
-      = {XCS("http://example.org/")
-             XCS(" thisisalongenoughelementnametotriggerareallocation foo"),
-         XCS("http://example.org/ a bar")};
-
-  XML_SetReturnNSTriplet(g_parser, XML_TRUE);
-  XML_SetUserData(g_parser, (void *)elemstr);
-  XML_SetElementHandler(g_parser, triplet_start_checker, triplet_end_checker);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test mixed population of prefixed and unprefixed attributes */
-START_TEST(test_ns_mixed_prefix_atts) {
-  const char *text = "<e a='12' bar:b='13'\n"
-                     " xmlns:bar='http://example.org/'>"
-                     "</e>";
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test having a long namespaced element name inside a short one.
- * This exercises some internal buffer reallocation that is shared
- * across elements with the same namespace URI.
- */
-START_TEST(test_ns_extend_uri_buffer) {
-  const char *text = "<foo:e xmlns:foo='http://example.org/'>"
-                     " <foo:thisisalongenoughnametotriggerallocationaction"
-                     "   foo:a='12' />"
-                     "</foo:e>";
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test that xmlns is correctly rejected as an attribute in the xmlns
- * namespace, but not in other namespaces
- */
-START_TEST(test_ns_reserved_attributes) {
-  const char *text1
-      = "<foo:e xmlns:foo='http://example.org/' xmlns:xmlns='12' />";
-  const char *text2
-      = "<foo:e xmlns:foo='http://example.org/' foo:xmlns='12' />";
-  expect_failure(text1, XML_ERROR_RESERVED_PREFIX_XMLNS,
-                 "xmlns not rejected as an attribute");
-  XML_ParserReset(g_parser, NULL);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text2, (int)strlen(text2), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test more reserved attributes */
-START_TEST(test_ns_reserved_attributes_2) {
-  const char *text1 = "<foo:e xmlns:foo='http://example.org/'"
-                      "  xmlns:xml='http://example.org/' />";
-  const char *text2
-      = "<foo:e xmlns:foo='http://www.w3.org/XML/1998/namespace' />";
-  const char *text3 = "<foo:e xmlns:foo='http://www.w3.org/2000/xmlns/' />";
-
-  expect_failure(text1, XML_ERROR_RESERVED_PREFIX_XML,
-                 "xml not rejected as an attribute");
-  XML_ParserReset(g_parser, NULL);
-  expect_failure(text2, XML_ERROR_RESERVED_NAMESPACE_URI,
-                 "Use of w3.org URL not faulted");
-  XML_ParserReset(g_parser, NULL);
-  expect_failure(text3, XML_ERROR_RESERVED_NAMESPACE_URI,
-                 "Use of w3.org xmlns URL not faulted");
-}
-END_TEST
-
-/* Test string pool handling of namespace names of 2048 characters */
-/* Exercises a particular string pool growth path */
-START_TEST(test_ns_extremely_long_prefix) {
-  /* C99 compilers are only required to support 4095-character
-   * strings, so the following needs to be split in two to be safe
-   * for all compilers.
-   */
-  const char *text1
-      = "<doc "
-        /* 64 character on each line */
-        /* ...gives a total length of 2048 */
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        ":a='12'";
-  const char *text2
-      = " xmlns:"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "='foo'\n>"
-        "</doc>";
-
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text1, (int)strlen(text1), XML_FALSE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text2, (int)strlen(text2), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-/* Test unknown encoding handlers in namespace setup */
-START_TEST(test_ns_unknown_encoding_success) {
-  const char *text = "<?xml version='1.0' encoding='prefix-conv'?>\n"
-                     "<foo:e xmlns:foo='http://example.org/'>Hi</foo:e>";
-
-  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
-  run_character_check(text, XCS("Hi"));
-}
-END_TEST
-
-/* Test that too many colons are rejected */
-START_TEST(test_ns_double_colon) {
-  const char *text = "<foo:e xmlns:foo='http://example.org/' foo:a:b='bar' />";
-  const enum XML_Status status
-      = _XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE);
-#ifdef XML_NS
-  if ((status == XML_STATUS_OK)
-      || (XML_GetErrorCode(g_parser) != XML_ERROR_INVALID_TOKEN)) {
-    fail("Double colon in attribute name not faulted"
-         " (despite active namespace support)");
-  }
-#else
-  if (status != XML_STATUS_OK) {
-    fail("Double colon in attribute name faulted"
-         " (despite inactive namespace support");
-  }
-#endif
-}
-END_TEST
-
-START_TEST(test_ns_double_colon_element) {
-  const char *text = "<foo:bar:e xmlns:foo='http://example.org/' />";
-  const enum XML_Status status
-      = _XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE);
-#ifdef XML_NS
-  if ((status == XML_STATUS_OK)
-      || (XML_GetErrorCode(g_parser) != XML_ERROR_INVALID_TOKEN)) {
-    fail("Double colon in element name not faulted"
-         " (despite active namespace support)");
-  }
-#else
-  if (status != XML_STATUS_OK) {
-    fail("Double colon in element name faulted"
-         " (despite inactive namespace support");
-  }
-#endif
-}
-END_TEST
-
-/* Test that non-name characters after a colon are rejected */
-START_TEST(test_ns_bad_attr_leafname) {
-  const char *text = "<foo:e xmlns:foo='http://example.org/' foo:?ar='baz' />";
-
-  expect_failure(text, XML_ERROR_INVALID_TOKEN,
-                 "Invalid character in leafname not faulted");
-}
-END_TEST
-
-START_TEST(test_ns_bad_element_leafname) {
-  const char *text = "<foo:?oc xmlns:foo='http://example.org/' />";
-
-  expect_failure(text, XML_ERROR_INVALID_TOKEN,
-                 "Invalid character in element leafname not faulted");
-}
-END_TEST
-
-/* Test high-byte-set UTF-16 characters are valid in a leafname */
-START_TEST(test_ns_utf16_leafname) {
-  const char text[] =
-      /* <n:e xmlns:n='URI' n:{KHO KHWAI}='a' />
-       * where {KHO KHWAI} = U+0E04 = 0xe0 0xb8 0x84 in UTF-8
-       */
-      "<\0n\0:\0e\0 \0x\0m\0l\0n\0s\0:\0n\0=\0'\0U\0R\0I\0'\0 \0"
-      "n\0:\0\x04\x0e=\0'\0a\0'\0 \0/\0>\0";
-  const XML_Char *expected = XCS("a");
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetStartElementHandler(g_parser, accumulate_attribute);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-START_TEST(test_ns_utf16_element_leafname) {
-  const char text[] =
-      /* <n:{KHO KHWAI} xmlns:n='URI'/>
-       * where {KHO KHWAI} = U+0E04 = 0xe0 0xb8 0x84 in UTF-8
-       */
-      "\0<\0n\0:\x0e\x04\0 \0x\0m\0l\0n\0s\0:\0n\0=\0'\0U\0R\0I\0'\0/\0>";
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("URI \x0e04");
-#else
-  const XML_Char *expected = XCS("URI \xe0\xb8\x84");
-#endif
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetStartElementHandler(g_parser, start_element_event_handler);
-  XML_SetUserData(g_parser, &storage);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-START_TEST(test_ns_utf16_doctype) {
-  const char text[] =
-      /* <!DOCTYPE foo:{KHO KHWAI} [ <!ENTITY bar 'baz'> ]>\n
-       * where {KHO KHWAI} = U+0E04 = 0xe0 0xb8 0x84 in UTF-8
-       */
-      "\0<\0!\0D\0O\0C\0T\0Y\0P\0E\0 \0f\0o\0o\0:\x0e\x04\0 "
-      "\0[\0 \0<\0!\0E\0N\0T\0I\0T\0Y\0 \0b\0a\0r\0 \0'\0b\0a\0z\0'\0>\0 "
-      "\0]\0>\0\n"
-      /* <foo:{KHO KHWAI} xmlns:foo='URI'>&bar;</foo:{KHO KHWAI}> */
-      "\0<\0f\0o\0o\0:\x0e\x04\0 "
-      "\0x\0m\0l\0n\0s\0:\0f\0o\0o\0=\0'\0U\0R\0I\0'\0>"
-      "\0&\0b\0a\0r\0;"
-      "\0<\0/\0f\0o\0o\0:\x0e\x04\0>";
-#ifdef XML_UNICODE
-  const XML_Char *expected = XCS("URI \x0e04");
-#else
-  const XML_Char *expected = XCS("URI \xe0\xb8\x84");
-#endif
-  CharData storage;
-
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  XML_SetStartElementHandler(g_parser, start_element_event_handler);
-  XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-START_TEST(test_ns_invalid_doctype) {
-  const char *text = "<!DOCTYPE foo:!bad [ <!ENTITY bar 'baz' ]>\n"
-                     "<foo:!bad>&bar;</foo:!bad>";
-
-  expect_failure(text, XML_ERROR_INVALID_TOKEN,
-                 "Invalid character in document local name not faulted");
-}
-END_TEST
-
-START_TEST(test_ns_double_colon_doctype) {
-  const char *text = "<!DOCTYPE foo:a:doc [ <!ENTITY bar 'baz' ]>\n"
-                     "<foo:a:doc>&bar;</foo:a:doc>";
-
-  expect_failure(text, XML_ERROR_SYNTAX,
-                 "Double colon in document name not faulted");
-}
-END_TEST
-
-START_TEST(test_ns_separator_in_uri) {
-  struct test_case {
-    enum XML_Status expectedStatus;
-    const char *doc;
-    XML_Char namesep;
-  };
-  struct test_case cases[] = {
-      {XML_STATUS_OK, "<doc xmlns='one_two' />", XCS('\n')},
-      {XML_STATUS_ERROR, "<doc xmlns='one&#x0A;two' />", XCS('\n')},
-      {XML_STATUS_OK, "<doc xmlns='one:two' />", XCS(':')},
-  };
-
-  size_t i = 0;
-  size_t failCount = 0;
-  for (; i < sizeof(cases) / sizeof(cases[0]); i++) {
-    XML_Parser parser = XML_ParserCreateNS(NULL, cases[i].namesep);
-    XML_SetElementHandler(parser, dummy_start_element, dummy_end_element);
-    if (XML_Parse(parser, cases[i].doc, (int)strlen(cases[i].doc),
-                  /*isFinal*/ XML_TRUE)
-        != cases[i].expectedStatus) {
-      failCount++;
-    }
-    XML_ParserFree(parser);
-  }
-
-  if (failCount) {
-    fail("Namespace separator handling is broken");
-  }
-}
-END_TEST
-
-/* Control variable; the number of times duff_allocator() will successfully
- * allocate */
-#define ALLOC_ALWAYS_SUCCEED (-1)
-#define REALLOC_ALWAYS_SUCCEED (-1)
-
-static intptr_t allocation_count = ALLOC_ALWAYS_SUCCEED;
-static intptr_t reallocation_count = REALLOC_ALWAYS_SUCCEED;
-
-/* Crocked allocator for allocation failure tests */
-static void *
-duff_allocator(size_t size) {
-  if (allocation_count == 0)
-    return NULL;
-  if (allocation_count != ALLOC_ALWAYS_SUCCEED)
-    allocation_count--;
-  return malloc(size);
-}
-
-/* Crocked reallocator for allocation failure tests */
-static void *
-duff_reallocator(void *ptr, size_t size) {
-  if (reallocation_count == 0)
-    return NULL;
-  if (reallocation_count != REALLOC_ALWAYS_SUCCEED)
-    reallocation_count--;
-  return realloc(ptr, size);
-}
-
-/* Test that a failure to allocate the parser structure fails gracefully */
-START_TEST(test_misc_alloc_create_parser) {
-  XML_Memory_Handling_Suite memsuite = {duff_allocator, realloc, free};
-  unsigned int i;
-  const unsigned int max_alloc_count = 10;
-
-  /* Something this simple shouldn't need more than 10 allocations */
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    g_parser = XML_ParserCreate_MM(NULL, &memsuite, NULL);
-    if (g_parser != NULL)
-      break;
-  }
-  if (i == 0)
-    fail("Parser unexpectedly ignored failing allocator");
-  else if (i == max_alloc_count)
-    fail("Parser not created with max allocation count");
-}
-END_TEST
-
-/* Test memory allocation failures for a parser with an encoding */
-START_TEST(test_misc_alloc_create_parser_with_encoding) {
-  XML_Memory_Handling_Suite memsuite = {duff_allocator, realloc, free};
-  unsigned int i;
-  const unsigned int max_alloc_count = 10;
-
-  /* Try several levels of allocation */
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    g_parser = XML_ParserCreate_MM(XCS("us-ascii"), &memsuite, NULL);
-    if (g_parser != NULL)
-      break;
-  }
-  if (i == 0)
-    fail("Parser ignored failing allocator");
-  else if (i == max_alloc_count)
-    fail("Parser not created with max allocation count");
-}
-END_TEST
-
-/* Test that freeing a NULL parser doesn't cause an explosion.
- * (Not actually tested anywhere else)
- */
-START_TEST(test_misc_null_parser) {
-  XML_ParserFree(NULL);
-}
-END_TEST
-
-/* Test that XML_ErrorString rejects out-of-range codes */
-START_TEST(test_misc_error_string) {
-  if (XML_ErrorString((enum XML_Error) - 1) != NULL)
-    fail("Negative error code not rejected");
-  if (XML_ErrorString((enum XML_Error)100) != NULL)
-    fail("Large error code not rejected");
-}
-END_TEST
-
-/* Test the version information is consistent */
-
-/* Since we are working in XML_LChars (potentially 16-bits), we
- * can't use the standard C library functions for character
- * manipulation and have to roll our own.
- */
-static int
-parse_version(const XML_LChar *version_text,
-              XML_Expat_Version *version_struct) {
-  if (! version_text)
-    return XML_FALSE;
-
-  while (*version_text != 0x00) {
-    if (*version_text >= ASCII_0 && *version_text <= ASCII_9)
-      break;
-    version_text++;
-  }
-  if (*version_text == 0x00)
-    return XML_FALSE;
-
-  /* version_struct->major = strtoul(version_text, 10, &version_text) */
-  version_struct->major = 0;
-  while (*version_text >= ASCII_0 && *version_text <= ASCII_9) {
-    version_struct->major
-        = 10 * version_struct->major + (*version_text++ - ASCII_0);
-  }
-  if (*version_text++ != ASCII_PERIOD)
-    return XML_FALSE;
-
-  /* Now for the minor version number */
-  version_struct->minor = 0;
-  while (*version_text >= ASCII_0 && *version_text <= ASCII_9) {
-    version_struct->minor
-        = 10 * version_struct->minor + (*version_text++ - ASCII_0);
-  }
-  if (*version_text++ != ASCII_PERIOD)
-    return XML_FALSE;
-
-  /* Finally the micro version number */
-  version_struct->micro = 0;
-  while (*version_text >= ASCII_0 && *version_text <= ASCII_9) {
-    version_struct->micro
-        = 10 * version_struct->micro + (*version_text++ - ASCII_0);
-  }
-  if (*version_text != 0x00)
-    return XML_FALSE;
-  return XML_TRUE;
-}
-
-static int
-versions_equal(const XML_Expat_Version *first,
-               const XML_Expat_Version *second) {
-  return (first->major == second->major && first->minor == second->minor
-          && first->micro == second->micro);
-}
-
-START_TEST(test_misc_version) {
-  XML_Expat_Version read_version = XML_ExpatVersionInfo();
-  /* Silence compiler warning with the following assignment */
-  XML_Expat_Version parsed_version = {0, 0, 0};
-  const XML_LChar *version_text = XML_ExpatVersion();
-
-  if (version_text == NULL)
-    fail("Could not obtain version text");
-  assert(version_text != NULL);
-  if (! parse_version(version_text, &parsed_version))
-    fail("Unable to parse version text");
-  if (! versions_equal(&read_version, &parsed_version))
-    fail("Version mismatch");
-
-#if ! defined(XML_UNICODE) || defined(XML_UNICODE_WCHAR_T)
-  if (xcstrcmp(version_text, XCS("expat_2.5.0"))) /* needs bump on releases */
-    fail("XML_*_VERSION in expat.h out of sync?\n");
-#else
-  /* If we have XML_UNICODE defined but not XML_UNICODE_WCHAR_T
-   * then XML_LChar is defined as char, for some reason.
-   */
-  if (strcmp(version_text, "expat_2.2.5")) /* needs bump on releases */
-    fail("XML_*_VERSION in expat.h out of sync?\n");
-#endif /* ! defined(XML_UNICODE) || defined(XML_UNICODE_WCHAR_T) */
-}
-END_TEST
-
-/* Test feature information */
-START_TEST(test_misc_features) {
-  const XML_Feature *features = XML_GetFeatureList();
-
-  /* Prevent problems with double-freeing parsers */
-  g_parser = NULL;
-  if (features == NULL) {
-    fail("Failed to get feature information");
-  } else {
-    /* Loop through the features checking what we can */
-    while (features->feature != XML_FEATURE_END) {
-      switch (features->feature) {
-      case XML_FEATURE_SIZEOF_XML_CHAR:
-        if (features->value != sizeof(XML_Char))
-          fail("Incorrect size of XML_Char");
-        break;
-      case XML_FEATURE_SIZEOF_XML_LCHAR:
-        if (features->value != sizeof(XML_LChar))
-          fail("Incorrect size of XML_LChar");
-        break;
-      default:
-        break;
-      }
-      features++;
-    }
-  }
-}
-END_TEST
-
-/* Regression test for GitHub Issue #17: memory leak parsing attribute
- * values with mixed bound and unbound namespaces.
- */
-START_TEST(test_misc_attribute_leak) {
-  const char *text = "<D xmlns:L=\"D\" l:a='' L:a=''/>";
-  XML_Memory_Handling_Suite memsuite
-      = {tracking_malloc, tracking_realloc, tracking_free};
-
-  g_parser = XML_ParserCreate_MM(XCS("UTF-8"), &memsuite, XCS("\n"));
-  expect_failure(text, XML_ERROR_UNBOUND_PREFIX, "Unbound prefixes not found");
-  XML_ParserFree(g_parser);
-  /* Prevent the teardown trying to double free */
-  g_parser = NULL;
-
-  if (! tracking_report())
-    fail("Memory leak found");
-}
-END_TEST
-
-/* Test parser created for UTF-16LE is successful */
-START_TEST(test_misc_utf16le) {
-  const char text[] =
-      /* <?xml version='1.0'?><q>Hi</q> */
-      "<\0?\0x\0m\0l\0 \0"
-      "v\0e\0r\0s\0i\0o\0n\0=\0'\0\x31\0.\0\x30\0'\0?\0>\0"
-      "<\0q\0>\0H\0i\0<\0/\0q\0>\0";
-  const XML_Char *expected = XCS("Hi");
-  CharData storage;
-
-  g_parser = XML_ParserCreate(XCS("UTF-16LE"));
-  if (g_parser == NULL)
-    fail("Parser not created");
-
-  CharData_Init(&storage);
-  XML_SetUserData(g_parser, &storage);
-  XML_SetCharacterDataHandler(g_parser, accumulate_characters);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-  CharData_CheckXMLChars(&storage, expected);
-}
-END_TEST
-
-typedef struct {
-  XML_Parser parser;
-  int deep;
-} DataIssue240;
-
-static void
-start_element_issue_240(void *userData, const XML_Char *name,
-                        const XML_Char **atts) {
-  DataIssue240 *mydata = (DataIssue240 *)userData;
-  UNUSED_P(name);
-  UNUSED_P(atts);
-  mydata->deep++;
-}
-
-static void
-end_element_issue_240(void *userData, const XML_Char *name) {
-  DataIssue240 *mydata = (DataIssue240 *)userData;
-
-  UNUSED_P(name);
-  mydata->deep--;
-  if (mydata->deep == 0) {
-    XML_StopParser(mydata->parser, 0);
-  }
-}
-
-START_TEST(test_misc_stop_during_end_handler_issue_240_1) {
-  XML_Parser parser;
-  DataIssue240 *mydata;
-  enum XML_Status result;
-  const char *const doc1 = "<doc><e1/><e><foo/></e></doc>";
-
-  parser = XML_ParserCreate(NULL);
-  XML_SetElementHandler(parser, start_element_issue_240, end_element_issue_240);
-  mydata = (DataIssue240 *)malloc(sizeof(DataIssue240));
-  mydata->parser = parser;
-  mydata->deep = 0;
-  XML_SetUserData(parser, mydata);
-
-  result = XML_Parse(parser, doc1, (int)strlen(doc1), 1);
-  XML_ParserFree(parser);
-  free(mydata);
-  if (result != XML_STATUS_ERROR)
-    fail("Stopping the parser did not work as expected");
-}
-END_TEST
-
-START_TEST(test_misc_stop_during_end_handler_issue_240_2) {
-  XML_Parser parser;
-  DataIssue240 *mydata;
-  enum XML_Status result;
-  const char *const doc2 = "<doc><elem/></doc>";
-
-  parser = XML_ParserCreate(NULL);
-  XML_SetElementHandler(parser, start_element_issue_240, end_element_issue_240);
-  mydata = (DataIssue240 *)malloc(sizeof(DataIssue240));
-  mydata->parser = parser;
-  mydata->deep = 0;
-  XML_SetUserData(parser, mydata);
-
-  result = XML_Parse(parser, doc2, (int)strlen(doc2), 1);
-  XML_ParserFree(parser);
-  free(mydata);
-  if (result != XML_STATUS_ERROR)
-    fail("Stopping the parser did not work as expected");
-}
-END_TEST
-
-START_TEST(test_misc_deny_internal_entity_closing_doctype_issue_317) {
-  const char *const inputOne = "<!DOCTYPE d [\n"
-                               "<!ENTITY % e ']><d/>'>\n"
-                               "\n"
-                               "%e;";
-  const char *const inputTwo = "<!DOCTYPE d [\n"
-                               "<!ENTITY % e1 ']><d/>'><!ENTITY % e2 '&e1;'>\n"
-                               "\n"
-                               "%e2;";
-  const char *const inputThree = "<!DOCTYPE d [\n"
-                                 "<!ENTITY % e ']><d'>\n"
-                                 "\n"
-                                 "%e;";
-  const char *const inputIssue317 = "<!DOCTYPE doc [\n"
-                                    "<!ENTITY % foo ']>\n"
-                                    "<doc>Hell<oc (#PCDATA)*>'>\n"
-                                    "%foo;\n"
-                                    "]>\n"
-                                    "<doc>Hello, world</dVc>";
-
-  const char *const inputs[] = {inputOne, inputTwo, inputThree, inputIssue317};
-  size_t inputIndex = 0;
-
-  for (; inputIndex < sizeof(inputs) / sizeof(inputs[0]); inputIndex++) {
-    XML_Parser parser;
-    enum XML_Status parseResult;
-    int setParamEntityResult;
-    XML_Size lineNumber;
-    XML_Size columnNumber;
-    const char *const input = inputs[inputIndex];
-
-    parser = XML_ParserCreate(NULL);
-    setParamEntityResult
-        = XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    if (setParamEntityResult != 1)
-      fail("Failed to set XML_PARAM_ENTITY_PARSING_ALWAYS.");
-
-    parseResult = XML_Parse(parser, input, (int)strlen(input), 0);
-    if (parseResult != XML_STATUS_ERROR) {
-      parseResult = XML_Parse(parser, "", 0, 1);
-      if (parseResult != XML_STATUS_ERROR) {
-        fail("Parsing was expected to fail but succeeded.");
-      }
-    }
-
-    if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN)
-      fail("Error code does not match XML_ERROR_INVALID_TOKEN");
-
-    lineNumber = XML_GetCurrentLineNumber(parser);
-    if (lineNumber != 4)
-      fail("XML_GetCurrentLineNumber does not work as expected.");
-
-    columnNumber = XML_GetCurrentColumnNumber(parser);
-    if (columnNumber != 0)
-      fail("XML_GetCurrentColumnNumber does not work as expected.");
-
-    XML_ParserFree(parser);
-  }
-}
-END_TEST
-
-START_TEST(test_misc_tag_mismatch_reset_leak) {
-#ifdef XML_NS
-  const char *const text = "<open xmlns='https://namespace1.test'></close>";
-  XML_Parser parser = XML_ParserCreateNS(NULL, XCS('\n'));
-
-  if (XML_Parse(parser, text, (int)strlen(text), XML_TRUE) != XML_STATUS_ERROR)
-    fail("Call to parse was expected to fail");
-  if (XML_GetErrorCode(parser) != XML_ERROR_TAG_MISMATCH)
-    fail("Call to parse was expected to fail from a closing tag mismatch");
-
-  XML_ParserReset(parser, NULL);
-
-  if (XML_Parse(parser, text, (int)strlen(text), XML_TRUE) != XML_STATUS_ERROR)
-    fail("Call to parse was expected to fail");
-  if (XML_GetErrorCode(parser) != XML_ERROR_TAG_MISMATCH)
-    fail("Call to parse was expected to fail from a closing tag mismatch");
-
-  XML_ParserFree(parser);
-#endif
-}
-END_TEST
-
-static void
-alloc_setup(void) {
-  XML_Memory_Handling_Suite memsuite = {duff_allocator, duff_reallocator, free};
-
-  /* Ensure the parser creation will go through */
-  allocation_count = ALLOC_ALWAYS_SUCCEED;
-  reallocation_count = REALLOC_ALWAYS_SUCCEED;
-  g_parser = XML_ParserCreate_MM(NULL, &memsuite, NULL);
-  if (g_parser == NULL)
-    fail("Parser not created");
-}
-
-static void
-alloc_teardown(void) {
-  basic_teardown();
-}
-
-/* Test the effects of allocation failures on xml declaration processing */
-START_TEST(test_alloc_parse_xdecl) {
-  const char *text = "<?xml version='1.0' encoding='utf-8'?>\n"
-                     "<doc>Hello, world</doc>";
-  int i;
-  const int max_alloc_count = 15;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetXmlDeclHandler(g_parser, dummy_xdecl_handler);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* Resetting the parser is insufficient, because some memory
-     * allocations are cached within the parser.  Instead we use
-     * the teardown and setup routines to ensure that we have the
-     * right sort of parser back in our hands.
-     */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing allocator");
-  if (i == max_alloc_count)
-    fail("Parse failed with max allocations");
-}
-END_TEST
-
-/* As above, but with an encoding big enough to cause storing the
- * version information to expand the string pool being used.
- */
-static int XMLCALL
-long_encoding_handler(void *userData, const XML_Char *encoding,
-                      XML_Encoding *info) {
-  int i;
-
-  UNUSED_P(userData);
-  UNUSED_P(encoding);
-  for (i = 0; i < 256; i++)
-    info->map[i] = i;
-  info->data = NULL;
-  info->convert = NULL;
-  info->release = NULL;
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_alloc_parse_xdecl_2) {
-  const char *text
-      = "<?xml version='1.0' encoding='"
-        /* Each line is 64 characters */
-        "ThisIsAStupidlyLongEncodingNameIntendedToTriggerPoolGrowth123456"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMN"
-        "'?>"
-        "<doc>Hello, world</doc>";
-  int i;
-  const int max_alloc_count = 20;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetXmlDeclHandler(g_parser, dummy_xdecl_handler);
-    XML_SetUnknownEncodingHandler(g_parser, long_encoding_handler, NULL);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing allocator");
-  if (i == max_alloc_count)
-    fail("Parse failed with max allocations");
-}
-END_TEST
-
-/* Test the effects of allocation failures on a straightforward parse */
-START_TEST(test_alloc_parse_pi) {
-  const char *text = "<?xml version='1.0' encoding='utf-8'?>\n"
-                     "<?pi unknown?>\n"
-                     "<doc>"
-                     "Hello, world"
-                     "</doc>";
-  int i;
-  const int max_alloc_count = 15;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetProcessingInstructionHandler(g_parser, dummy_pi_handler);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing allocator");
-  if (i == max_alloc_count)
-    fail("Parse failed with max allocations");
-}
-END_TEST
-
-START_TEST(test_alloc_parse_pi_2) {
-  const char *text = "<?xml version='1.0' encoding='utf-8'?>\n"
-                     "<doc>"
-                     "Hello, world"
-                     "<?pi unknown?>\n"
-                     "</doc>";
-  int i;
-  const int max_alloc_count = 15;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetProcessingInstructionHandler(g_parser, dummy_pi_handler);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing allocator");
-  if (i == max_alloc_count)
-    fail("Parse failed with max allocations");
-}
-END_TEST
-
-START_TEST(test_alloc_parse_pi_3) {
-  const char *text
-      = "<?"
-        /* 64 characters per line */
-        "This processing instruction should be long enough to ensure that"
-        "it triggers the growth of an internal string pool when the      "
-        "allocator fails at a cruicial moment FGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "Q?><doc/>";
-  int i;
-  const int max_alloc_count = 20;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetProcessingInstructionHandler(g_parser, dummy_pi_handler);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing allocator");
-  if (i == max_alloc_count)
-    fail("Parse failed with max allocations");
-}
-END_TEST
-
-START_TEST(test_alloc_parse_comment) {
-  const char *text = "<?xml version='1.0' encoding='utf-8'?>\n"
-                     "<!-- Test parsing this comment -->"
-                     "<doc>Hi</doc>";
-  int i;
-  const int max_alloc_count = 15;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetCommentHandler(g_parser, dummy_comment_handler);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing allocator");
-  if (i == max_alloc_count)
-    fail("Parse failed with max allocations");
-}
-END_TEST
-
-START_TEST(test_alloc_parse_comment_2) {
-  const char *text = "<?xml version='1.0' encoding='utf-8'?>\n"
-                     "<doc>"
-                     "Hello, world"
-                     "<!-- Parse this comment too -->"
-                     "</doc>";
-  int i;
-  const int max_alloc_count = 15;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetCommentHandler(g_parser, dummy_comment_handler);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing allocator");
-  if (i == max_alloc_count)
-    fail("Parse failed with max allocations");
-}
-END_TEST
-
-static int XMLCALL
-external_entity_duff_loader(XML_Parser parser, const XML_Char *context,
-                            const XML_Char *base, const XML_Char *systemId,
-                            const XML_Char *publicId) {
-  XML_Parser new_parser;
-  unsigned int i;
-  const unsigned int max_alloc_count = 10;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  /* Try a few different allocation levels */
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    new_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-    if (new_parser != NULL) {
-      XML_ParserFree(new_parser);
-      break;
-    }
-  }
-  if (i == 0)
-    fail("External parser creation ignored failing allocator");
-  else if (i == max_alloc_count)
-    fail("Extern parser not created with max allocation count");
-
-  /* Make sure other random allocation doesn't now fail */
-  allocation_count = ALLOC_ALWAYS_SUCCEED;
-
-  /* Make sure the failure code path is executed too */
-  return XML_STATUS_ERROR;
-}
-
-/* Test that external parser creation running out of memory is
- * correctly reported.  Based on the external entity test cases.
- */
-START_TEST(test_alloc_create_external_parser) {
-  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
-                     "<!DOCTYPE doc SYSTEM 'foo'>\n"
-                     "<doc>&entity;</doc>";
-  char foo_text[] = "<!ELEMENT doc (#PCDATA)*>";
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetUserData(g_parser, foo_text);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_duff_loader);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_ERROR) {
-    fail("External parser allocator returned success incorrectly");
-  }
-}
-END_TEST
-
-/* More external parser memory allocation testing */
-START_TEST(test_alloc_run_external_parser) {
-  const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
-                     "<!DOCTYPE doc SYSTEM 'foo'>\n"
-                     "<doc>&entity;</doc>";
-  char foo_text[] = "<!ELEMENT doc (#PCDATA)*>";
-  unsigned int i;
-  const unsigned int max_alloc_count = 15;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    XML_SetUserData(g_parser, foo_text);
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_null_loader);
-    allocation_count = i;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing ignored failing allocator");
-  else if (i == max_alloc_count)
-    fail("Parsing failed with allocation count 10");
-}
-END_TEST
-
-static int XMLCALL
-external_entity_dbl_handler(XML_Parser parser, const XML_Char *context,
-                            const XML_Char *base, const XML_Char *systemId,
-                            const XML_Char *publicId) {
-  intptr_t callno = (intptr_t)XML_GetUserData(parser);
-  const char *text;
-  XML_Parser new_parser;
-  int i;
-  const int max_alloc_count = 20;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  if (callno == 0) {
-    /* First time through, check how many calls to malloc occur */
-    text = ("<!ELEMENT doc (e+)>\n"
-            "<!ATTLIST doc xmlns CDATA #IMPLIED>\n"
-            "<!ELEMENT e EMPTY>\n");
-    allocation_count = 10000;
-    new_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-    if (new_parser == NULL) {
-      fail("Unable to allocate first external parser");
-      return XML_STATUS_ERROR;
-    }
-    /* Stash the number of calls in the user data */
-    XML_SetUserData(parser, (void *)(intptr_t)(10000 - allocation_count));
-  } else {
-    text = ("<?xml version='1.0' encoding='us-ascii'?>"
-            "<e/>");
-    /* Try at varying levels to exercise more code paths */
-    for (i = 0; i < max_alloc_count; i++) {
-      allocation_count = callno + i;
-      new_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-      if (new_parser != NULL)
-        break;
-    }
-    if (i == 0) {
-      fail("Second external parser unexpectedly created");
-      XML_ParserFree(new_parser);
-      return XML_STATUS_ERROR;
-    } else if (i == max_alloc_count) {
-      fail("Second external parser not created");
-      return XML_STATUS_ERROR;
-    }
-  }
-
-  allocation_count = ALLOC_ALWAYS_SUCCEED;
-  if (_XML_Parse_SINGLE_BYTES(new_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR) {
-    xml_failure(new_parser);
-    return XML_STATUS_ERROR;
-  }
-  XML_ParserFree(new_parser);
-  return XML_STATUS_OK;
-}
-
-/* Test that running out of memory in dtdCopy is correctly reported.
- * Based on test_default_ns_from_ext_subset_and_ext_ge()
- */
-START_TEST(test_alloc_dtd_copy_default_atts) {
-  const char *text = "<?xml version='1.0'?>\n"
-                     "<!DOCTYPE doc SYSTEM 'http://example.org/doc.dtd' [\n"
-                     "  <!ENTITY en SYSTEM 'http://example.org/entity.ent'>\n"
-                     "]>\n"
-                     "<doc xmlns='http://example.org/ns1'>\n"
-                     "&en;\n"
-                     "</doc>";
-
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_dbl_handler);
-  XML_SetUserData(g_parser, NULL);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-}
-END_TEST
-
-static int XMLCALL
-external_entity_dbl_handler_2(XML_Parser parser, const XML_Char *context,
-                              const XML_Char *base, const XML_Char *systemId,
-                              const XML_Char *publicId) {
-  intptr_t callno = (intptr_t)XML_GetUserData(parser);
-  const char *text;
-  XML_Parser new_parser;
-  enum XML_Status rv;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  if (callno == 0) {
-    /* Try different allocation levels for whole exercise */
-    text = ("<!ELEMENT doc (e+)>\n"
-            "<!ATTLIST doc xmlns CDATA #IMPLIED>\n"
-            "<!ELEMENT e EMPTY>\n");
-    XML_SetUserData(parser, (void *)(intptr_t)1);
-    new_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-    if (new_parser == NULL)
-      return XML_STATUS_ERROR;
-    rv = _XML_Parse_SINGLE_BYTES(new_parser, text, (int)strlen(text), XML_TRUE);
-  } else {
-    /* Just run through once */
-    text = ("<?xml version='1.0' encoding='us-ascii'?>"
-            "<e/>");
-    new_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-    if (new_parser == NULL)
-      return XML_STATUS_ERROR;
-    rv = _XML_Parse_SINGLE_BYTES(new_parser, text, (int)strlen(text), XML_TRUE);
-  }
-  XML_ParserFree(new_parser);
-  if (rv == XML_STATUS_ERROR)
-    return XML_STATUS_ERROR;
-  return XML_STATUS_OK;
-}
-
-/* Test more external entity allocation failure paths */
-START_TEST(test_alloc_external_entity) {
-  const char *text = "<?xml version='1.0'?>\n"
-                     "<!DOCTYPE doc SYSTEM 'http://example.org/doc.dtd' [\n"
-                     "  <!ENTITY en SYSTEM 'http://example.org/entity.ent'>\n"
-                     "]>\n"
-                     "<doc xmlns='http://example.org/ns1'>\n"
-                     "&en;\n"
-                     "</doc>";
-  int i;
-  const int alloc_test_max_repeats = 50;
-
-  for (i = 0; i < alloc_test_max_repeats; i++) {
-    allocation_count = -1;
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_dbl_handler_2);
-    XML_SetUserData(g_parser, NULL);
-    allocation_count = i;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        == XML_STATUS_OK)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  allocation_count = -1;
-  if (i == 0)
-    fail("External entity parsed despite duff allocator");
-  if (i == alloc_test_max_repeats)
-    fail("External entity not parsed at max allocation count");
-}
-END_TEST
-
-/* Test more allocation failure paths */
-static int XMLCALL
-external_entity_alloc_set_encoding(XML_Parser parser, const XML_Char *context,
-                                   const XML_Char *base,
-                                   const XML_Char *systemId,
-                                   const XML_Char *publicId) {
-  /* As for external_entity_loader() */
-  const char *text = "<?xml encoding='iso-8859-3'?>"
-                     "\xC3\xA9";
-  XML_Parser ext_parser;
-  enum XML_Status status;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    return XML_STATUS_ERROR;
-  if (! XML_SetEncoding(ext_parser, XCS("utf-8"))) {
-    XML_ParserFree(ext_parser);
-    return XML_STATUS_ERROR;
-  }
-  status
-      = _XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE);
-  XML_ParserFree(ext_parser);
-  if (status == XML_STATUS_ERROR)
-    return XML_STATUS_ERROR;
-  return XML_STATUS_OK;
-}
-
-START_TEST(test_alloc_ext_entity_set_encoding) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
-                     "]>\n"
-                     "<doc>&en;</doc>";
-  int i;
-  const int max_allocation_count = 30;
-
-  for (i = 0; i < max_allocation_count; i++) {
-    XML_SetExternalEntityRefHandler(g_parser,
-                                    external_entity_alloc_set_encoding);
-    allocation_count = i;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        == XML_STATUS_OK)
-      break;
-    allocation_count = -1;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Encoding check succeeded despite failing allocator");
-  if (i == max_allocation_count)
-    fail("Encoding failed at max allocation count");
-}
-END_TEST
-
-static int XMLCALL
-unknown_released_encoding_handler(void *data, const XML_Char *encoding,
-                                  XML_Encoding *info) {
-  UNUSED_P(data);
-  if (! xcstrcmp(encoding, XCS("unsupported-encoding"))) {
-    int i;
-
-    for (i = 0; i < 256; i++)
-      info->map[i] = i;
-    info->data = NULL;
-    info->convert = NULL;
-    info->release = dummy_release;
-    return XML_STATUS_OK;
-  }
-  return XML_STATUS_ERROR;
-}
-
-/* Test the effects of allocation failure in internal entities.
- * Based on test_unknown_encoding_internal_entity
- */
-START_TEST(test_alloc_internal_entity) {
-  const char *text = "<?xml version='1.0' encoding='unsupported-encoding'?>\n"
-                     "<!DOCTYPE test [<!ENTITY foo 'bar'>]>\n"
-                     "<test a='&foo;'/>";
-  unsigned int i;
-  const unsigned int max_alloc_count = 20;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetUnknownEncodingHandler(g_parser, unknown_released_encoding_handler,
-                                  NULL);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Internal entity worked despite failing allocations");
-  else if (i == max_alloc_count)
-    fail("Internal entity failed at max allocation count");
-}
-END_TEST
-
-/* Test the robustness against allocation failure of element handling
- * Based on test_dtd_default_handling().
- */
-START_TEST(test_alloc_dtd_default_handling) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "<!ENTITY e SYSTEM 'http://example.org/e'>\n"
-                     "<!NOTATION n SYSTEM 'http://example.org/n'>\n"
-                     "<!ENTITY e1 SYSTEM 'http://example.org/e' NDATA n>\n"
-                     "<!ELEMENT doc (#PCDATA)>\n"
-                     "<!ATTLIST doc a CDATA #IMPLIED>\n"
-                     "<?pi in dtd?>\n"
-                     "<!--comment in dtd-->\n"
-                     "]>\n"
-                     "<doc><![CDATA[text in doc]]></doc>";
-  const XML_Char *expected = XCS("\n\n\n\n\n\n\n\n\n<doc>text in doc</doc>");
-  CharData storage;
-  int i;
-  const int max_alloc_count = 25;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    dummy_handler_flags = 0;
-    XML_SetDefaultHandler(g_parser, accumulate_characters);
-    XML_SetDoctypeDeclHandler(g_parser, dummy_start_doctype_handler,
-                              dummy_end_doctype_handler);
-    XML_SetEntityDeclHandler(g_parser, dummy_entity_decl_handler);
-    XML_SetNotationDeclHandler(g_parser, dummy_notation_decl_handler);
-    XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
-    XML_SetAttlistDeclHandler(g_parser, dummy_attlist_decl_handler);
-    XML_SetProcessingInstructionHandler(g_parser, dummy_pi_handler);
-    XML_SetCommentHandler(g_parser, dummy_comment_handler);
-    XML_SetCdataSectionHandler(g_parser, dummy_start_cdata_handler,
-                               dummy_end_cdata_handler);
-    XML_SetUnparsedEntityDeclHandler(g_parser,
-                                     dummy_unparsed_entity_decl_handler);
-    CharData_Init(&storage);
-    XML_SetUserData(g_parser, &storage);
-    XML_SetCharacterDataHandler(g_parser, accumulate_characters);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Default DTD parsed despite allocation failures");
-  if (i == max_alloc_count)
-    fail("Default DTD not parsed with maximum alloc count");
-  CharData_CheckXMLChars(&storage, expected);
-  if (dummy_handler_flags
-      != (DUMMY_START_DOCTYPE_HANDLER_FLAG | DUMMY_END_DOCTYPE_HANDLER_FLAG
-          | DUMMY_ENTITY_DECL_HANDLER_FLAG | DUMMY_NOTATION_DECL_HANDLER_FLAG
-          | DUMMY_ELEMENT_DECL_HANDLER_FLAG | DUMMY_ATTLIST_DECL_HANDLER_FLAG
-          | DUMMY_COMMENT_HANDLER_FLAG | DUMMY_PI_HANDLER_FLAG
-          | DUMMY_START_CDATA_HANDLER_FLAG | DUMMY_END_CDATA_HANDLER_FLAG
-          | DUMMY_UNPARSED_ENTITY_DECL_HANDLER_FLAG))
-    fail("Not all handlers were called");
-}
-END_TEST
-
-/* Test robustness of XML_SetEncoding() with a failing allocator */
-START_TEST(test_alloc_explicit_encoding) {
-  int i;
-  const int max_alloc_count = 5;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    if (XML_SetEncoding(g_parser, XCS("us-ascii")) == XML_STATUS_OK)
-      break;
-  }
-  if (i == 0)
-    fail("Encoding set despite failing allocator");
-  else if (i == max_alloc_count)
-    fail("Encoding not set at max allocation count");
-}
-END_TEST
-
-/* Test robustness of XML_SetBase against a failing allocator */
-START_TEST(test_alloc_set_base) {
-  const XML_Char *new_base = XCS("/local/file/name.xml");
-  int i;
-  const int max_alloc_count = 5;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    if (XML_SetBase(g_parser, new_base) == XML_STATUS_OK)
-      break;
-  }
-  if (i == 0)
-    fail("Base set despite failing allocator");
-  else if (i == max_alloc_count)
-    fail("Base not set with max allocation count");
-}
-END_TEST
-
-/* Test buffer extension in the face of a duff reallocator */
-START_TEST(test_alloc_realloc_buffer) {
-  const char *text = get_buffer_test_text;
-  void *buffer;
-  int i;
-  const int max_realloc_count = 10;
-
-  /* Get a smallish buffer */
-  for (i = 0; i < max_realloc_count; i++) {
-    reallocation_count = i;
-    buffer = XML_GetBuffer(g_parser, 1536);
-    if (buffer == NULL)
-      fail("1.5K buffer reallocation failed");
-    assert(buffer != NULL);
-    memcpy(buffer, text, strlen(text));
-    if (XML_ParseBuffer(g_parser, (int)strlen(text), XML_FALSE)
-        == XML_STATUS_OK)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  reallocation_count = -1;
-  if (i == 0)
-    fail("Parse succeeded with no reallocation");
-  else if (i == max_realloc_count)
-    fail("Parse failed with max reallocation count");
-}
-END_TEST
-
-/* Same test for external entity parsers */
-static int XMLCALL
-external_entity_reallocator(XML_Parser parser, const XML_Char *context,
-                            const XML_Char *base, const XML_Char *systemId,
-                            const XML_Char *publicId) {
-  const char *text = get_buffer_test_text;
-  XML_Parser ext_parser;
-  void *buffer;
-  enum XML_Status status;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    fail("Could not create external entity parser");
-
-  reallocation_count = (intptr_t)XML_GetUserData(parser);
-  buffer = XML_GetBuffer(ext_parser, 1536);
-  if (buffer == NULL)
-    fail("Buffer allocation failed");
-  assert(buffer != NULL);
-  memcpy(buffer, text, strlen(text));
-  status = XML_ParseBuffer(ext_parser, (int)strlen(text), XML_FALSE);
-  reallocation_count = -1;
-  XML_ParserFree(ext_parser);
-  return (status == XML_STATUS_OK) ? XML_STATUS_OK : XML_STATUS_ERROR;
-}
-
-START_TEST(test_alloc_ext_entity_realloc_buffer) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY en SYSTEM 'http://example.org/dummy.ent'>\n"
-                     "]>\n"
-                     "<doc>&en;</doc>";
-  int i;
-  const int max_realloc_count = 10;
-
-  for (i = 0; i < max_realloc_count; i++) {
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_reallocator);
-    XML_SetUserData(g_parser, (void *)(intptr_t)i);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        == XML_STATUS_OK)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Succeeded with no reallocations");
-  if (i == max_realloc_count)
-    fail("Failed with max reallocations");
-}
-END_TEST
-
-/* Test elements with many attributes are handled correctly */
-START_TEST(test_alloc_realloc_many_attributes) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "<!ATTLIST doc za CDATA 'default'>\n"
-                     "<!ATTLIST doc zb CDATA 'def2'>\n"
-                     "<!ATTLIST doc zc CDATA 'def3'>\n"
-                     "]>\n"
-                     "<doc a='1'"
-                     "     b='2'"
-                     "     c='3'"
-                     "     d='4'"
-                     "     e='5'"
-                     "     f='6'"
-                     "     g='7'"
-                     "     h='8'"
-                     "     i='9'"
-                     "     j='10'"
-                     "     k='11'"
-                     "     l='12'"
-                     "     m='13'"
-                     "     n='14'"
-                     "     p='15'"
-                     "     q='16'"
-                     "     r='17'"
-                     "     s='18'>"
-                     "</doc>";
-  int i;
-  const int max_realloc_count = 10;
-
-  for (i = 0; i < max_realloc_count; i++) {
-    reallocation_count = i;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite no reallocations");
-  if (i == max_realloc_count)
-    fail("Parse failed at max reallocations");
-}
-END_TEST
-
-/* Test handling of a public entity with failing allocator */
-START_TEST(test_alloc_public_entity_value) {
-  const char *text = "<!DOCTYPE doc SYSTEM 'http://example.org/'>\n"
-                     "<doc></doc>\n";
-  char dtd_text[]
-      = "<!ELEMENT doc EMPTY>\n"
-        "<!ENTITY % e1 PUBLIC 'foo' 'bar.ent'>\n"
-        "<!ENTITY % "
-        /* Each line is 64 characters */
-        "ThisIsAStupidlyLongParameterNameIntendedToTriggerPoolGrowth12345"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        " '%e1;'>\n"
-        "%e1;\n";
-  int i;
-  const int max_alloc_count = 50;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    dummy_handler_flags = 0;
-    XML_SetUserData(g_parser, dtd_text);
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_public);
-    /* Provoke a particular code path */
-    XML_SetEntityDeclHandler(g_parser, dummy_entity_decl_handler);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing allocation");
-  if (i == max_alloc_count)
-    fail("Parsing failed at max allocation count");
-  if (dummy_handler_flags != DUMMY_ENTITY_DECL_HANDLER_FLAG)
-    fail("Entity declaration handler not called");
-}
-END_TEST
-
-START_TEST(test_alloc_realloc_subst_public_entity_value) {
-  const char *text = "<!DOCTYPE doc SYSTEM 'http://example.org/'>\n"
-                     "<doc></doc>\n";
-  char dtd_text[]
-      = "<!ELEMENT doc EMPTY>\n"
-        "<!ENTITY % "
-        /* Each line is 64 characters */
-        "ThisIsAStupidlyLongParameterNameIntendedToTriggerPoolGrowth12345"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        " PUBLIC 'foo' 'bar.ent'>\n"
-        "%ThisIsAStupidlyLongParameterNameIntendedToTriggerPoolGrowth12345"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP;";
-  int i;
-  const int max_realloc_count = 10;
-
-  for (i = 0; i < max_realloc_count; i++) {
-    reallocation_count = i;
-    XML_SetUserData(g_parser, dtd_text);
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_public);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing reallocation");
-  if (i == max_realloc_count)
-    fail("Parsing failed at max reallocation count");
-}
-END_TEST
-
-START_TEST(test_alloc_parse_public_doctype) {
-  const char *text
-      = "<?xml version='1.0' encoding='utf-8'?>\n"
-        "<!DOCTYPE doc PUBLIC '"
-        /* 64 characters per line */
-        "http://example.com/a/long/enough/name/to/trigger/pool/growth/zz/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "' 'test'>\n"
-        "<doc></doc>";
-  int i;
-  const int max_alloc_count = 25;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    dummy_handler_flags = 0;
-    XML_SetDoctypeDeclHandler(g_parser, dummy_start_doctype_decl_handler,
-                              dummy_end_doctype_decl_handler);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing allocator");
-  if (i == max_alloc_count)
-    fail("Parse failed at maximum allocation count");
-  if (dummy_handler_flags
-      != (DUMMY_START_DOCTYPE_DECL_HANDLER_FLAG
-          | DUMMY_END_DOCTYPE_DECL_HANDLER_FLAG))
-    fail("Doctype handler functions not called");
-}
-END_TEST
-
-START_TEST(test_alloc_parse_public_doctype_long_name) {
-  const char *text
-      = "<?xml version='1.0' encoding='utf-8'?>\n"
-        "<!DOCTYPE doc PUBLIC 'http://example.com/foo' '"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
-        "'>\n"
-        "<doc></doc>";
-  int i;
-  const int max_alloc_count = 25;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetDoctypeDeclHandler(g_parser, dummy_start_doctype_decl_handler,
-                              dummy_end_doctype_decl_handler);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing allocator");
-  if (i == max_alloc_count)
-    fail("Parse failed at maximum allocation count");
-}
-END_TEST
-
-static int XMLCALL
-external_entity_alloc(XML_Parser parser, const XML_Char *context,
-                      const XML_Char *base, const XML_Char *systemId,
-                      const XML_Char *publicId) {
-  const char *text = (const char *)XML_GetUserData(parser);
-  XML_Parser ext_parser;
-  int parse_res;
-
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-  ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
-  if (ext_parser == NULL)
-    return XML_STATUS_ERROR;
-  parse_res
-      = _XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE);
-  XML_ParserFree(ext_parser);
-  return parse_res;
-}
-
-/* Test foreign DTD handling */
-START_TEST(test_alloc_set_foreign_dtd) {
-  const char *text1 = "<?xml version='1.0' encoding='us-ascii'?>\n"
-                      "<doc>&entity;</doc>";
-  char text2[] = "<!ELEMENT doc (#PCDATA)*>";
-  int i;
-  const int max_alloc_count = 25;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    XML_SetUserData(g_parser, &text2);
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_alloc);
-    if (XML_UseForeignDTD(g_parser, XML_TRUE) != XML_ERROR_NONE)
-      fail("Could not set foreign DTD");
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text1, (int)strlen(text1), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing allocator");
-  if (i == max_alloc_count)
-    fail("Parse failed at maximum allocation count");
-}
-END_TEST
-
-/* Test based on ibm/valid/P32/ibm32v04.xml */
-START_TEST(test_alloc_attribute_enum_value) {
-  const char *text = "<?xml version='1.0' standalone='no'?>\n"
-                     "<!DOCTYPE animal SYSTEM 'test.dtd'>\n"
-                     "<animal>This is a \n    <a/>  \n\nyellow tiger</animal>";
-  char dtd_text[] = "<!ELEMENT animal (#PCDATA|a)*>\n"
-                    "<!ELEMENT a EMPTY>\n"
-                    "<!ATTLIST animal xml:space (default|preserve) 'preserve'>";
-  int i;
-  const int max_alloc_count = 30;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_alloc);
-    XML_SetUserData(g_parser, dtd_text);
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    /* An attribute list handler provokes a different code path */
-    XML_SetAttlistDeclHandler(g_parser, dummy_attlist_decl_handler);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing allocator");
-  if (i == max_alloc_count)
-    fail("Parse failed at maximum allocation count");
-}
-END_TEST
-
-/* Test attribute enums sufficient to overflow the string pool */
-START_TEST(test_alloc_realloc_attribute_enum_value) {
-  const char *text = "<?xml version='1.0' standalone='no'?>\n"
-                     "<!DOCTYPE animal SYSTEM 'test.dtd'>\n"
-                     "<animal>This is a yellow tiger</animal>";
-  /* We wish to define a collection of attribute enums that will
-   * cause the string pool storing them to have to expand.  This
-   * means more than 1024 bytes, including the parentheses and
-   * separator bars.
-   */
-  char dtd_text[]
-      = "<!ELEMENT animal (#PCDATA)*>\n"
-        "<!ATTLIST animal thing "
-        "(default"
-        /* Each line is 64 characters */
-        "|ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|BBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|CBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|DBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|EBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|FBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|GBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|HBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|IBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|JBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|KBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|LBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|MBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|NBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|OBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|PBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO)"
-        " 'default'>";
-  int i;
-  const int max_realloc_count = 10;
-
-  for (i = 0; i < max_realloc_count; i++) {
-    reallocation_count = i;
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_alloc);
-    XML_SetUserData(g_parser, dtd_text);
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    /* An attribute list handler provokes a different code path */
-    XML_SetAttlistDeclHandler(g_parser, dummy_attlist_decl_handler);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing reallocator");
-  if (i == max_realloc_count)
-    fail("Parse failed at maximum reallocation count");
-}
-END_TEST
-
-/* Test attribute enums in a #IMPLIED attribute forcing pool growth */
-START_TEST(test_alloc_realloc_implied_attribute) {
-  /* Forcing this particular code path is a balancing act.  The
-   * addition of the closing parenthesis and terminal NUL must be
-   * what pushes the string of enums over the 1024-byte limit,
-   * otherwise a different code path will pick up the realloc.
-   */
-  const char *text
-      = "<!DOCTYPE doc [\n"
-        "<!ELEMENT doc EMPTY>\n"
-        "<!ATTLIST doc a "
-        /* Each line is 64 characters */
-        "(ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|BBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|CBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|DBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|EBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|FBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|GBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|HBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|IBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|JBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|KBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|LBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|MBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|NBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|OBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|PBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMN)"
-        " #IMPLIED>\n"
-        "]><doc/>";
-  int i;
-  const int max_realloc_count = 10;
-
-  for (i = 0; i < max_realloc_count; i++) {
-    reallocation_count = i;
-    XML_SetAttlistDeclHandler(g_parser, dummy_attlist_decl_handler);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing reallocator");
-  if (i == max_realloc_count)
-    fail("Parse failed at maximum reallocation count");
-}
-END_TEST
-
-/* Test attribute enums in a defaulted attribute forcing pool growth */
-START_TEST(test_alloc_realloc_default_attribute) {
-  /* Forcing this particular code path is a balancing act.  The
-   * addition of the closing parenthesis and terminal NUL must be
-   * what pushes the string of enums over the 1024-byte limit,
-   * otherwise a different code path will pick up the realloc.
-   */
-  const char *text
-      = "<!DOCTYPE doc [\n"
-        "<!ELEMENT doc EMPTY>\n"
-        "<!ATTLIST doc a "
-        /* Each line is 64 characters */
-        "(ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|BBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|CBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|DBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|EBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|FBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|GBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|HBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|IBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|JBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|KBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|LBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|MBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|NBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|OBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO"
-        "|PBCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMN)"
-        " 'ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNO'"
-        ">\n]><doc/>";
-  int i;
-  const int max_realloc_count = 10;
-
-  for (i = 0; i < max_realloc_count; i++) {
-    reallocation_count = i;
-    XML_SetAttlistDeclHandler(g_parser, dummy_attlist_decl_handler);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing reallocator");
-  if (i == max_realloc_count)
-    fail("Parse failed at maximum reallocation count");
-}
-END_TEST
-
-/* Test long notation name with dodgy allocator */
-START_TEST(test_alloc_notation) {
-  const char *text
-      = "<!DOCTYPE doc [\n"
-        "<!NOTATION "
-        /* Each line is 64 characters */
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        " SYSTEM 'http://example.org/n'>\n"
-        "<!ENTITY e SYSTEM 'http://example.org/e' NDATA "
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        ">\n"
-        "<!ELEMENT doc EMPTY>\n"
-        "]>\n<doc/>";
-  int i;
-  const int max_alloc_count = 20;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    dummy_handler_flags = 0;
-    XML_SetNotationDeclHandler(g_parser, dummy_notation_decl_handler);
-    XML_SetEntityDeclHandler(g_parser, dummy_entity_decl_handler);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite allocation failures");
-  if (i == max_alloc_count)
-    fail("Parse failed at maximum allocation count");
-  if (dummy_handler_flags
-      != (DUMMY_ENTITY_DECL_HANDLER_FLAG | DUMMY_NOTATION_DECL_HANDLER_FLAG))
-    fail("Entity declaration handler not called");
-}
-END_TEST
-
-/* Test public notation with dodgy allocator */
-START_TEST(test_alloc_public_notation) {
-  const char *text
-      = "<!DOCTYPE doc [\n"
-        "<!NOTATION note PUBLIC '"
-        /* 64 characters per line */
-        "http://example.com/a/long/enough/name/to/trigger/pool/growth/zz/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "' 'foo'>\n"
-        "<!ENTITY e SYSTEM 'http://example.com/e' NDATA note>\n"
-        "<!ELEMENT doc EMPTY>\n"
-        "]>\n<doc/>";
-  int i;
-  const int max_alloc_count = 20;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    dummy_handler_flags = 0;
-    XML_SetNotationDeclHandler(g_parser, dummy_notation_decl_handler);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite allocation failures");
-  if (i == max_alloc_count)
-    fail("Parse failed at maximum allocation count");
-  if (dummy_handler_flags != DUMMY_NOTATION_DECL_HANDLER_FLAG)
-    fail("Notation handler not called");
-}
-END_TEST
-
-/* Test public notation with dodgy allocator */
-START_TEST(test_alloc_system_notation) {
-  const char *text
-      = "<!DOCTYPE doc [\n"
-        "<!NOTATION note SYSTEM '"
-        /* 64 characters per line */
-        "http://example.com/a/long/enough/name/to/trigger/pool/growth/zz/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "'>\n"
-        "<!ENTITY e SYSTEM 'http://example.com/e' NDATA note>\n"
-        "<!ELEMENT doc EMPTY>\n"
-        "]>\n<doc/>";
-  int i;
-  const int max_alloc_count = 20;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    dummy_handler_flags = 0;
-    XML_SetNotationDeclHandler(g_parser, dummy_notation_decl_handler);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite allocation failures");
-  if (i == max_alloc_count)
-    fail("Parse failed at maximum allocation count");
-  if (dummy_handler_flags != DUMMY_NOTATION_DECL_HANDLER_FLAG)
-    fail("Notation handler not called");
-}
-END_TEST
-
-START_TEST(test_alloc_nested_groups) {
-  const char *text
-      = "<!DOCTYPE doc [\n"
-        "<!ELEMENT doc "
-        /* Sixteen elements per line */
-        "(e,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,"
-        "(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?"
-        "))))))))))))))))))))))))))))))))>\n"
-        "<!ELEMENT e EMPTY>"
-        "]>\n"
-        "<doc><e/></doc>";
-  CharData storage;
-  int i;
-  const int max_alloc_count = 20;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    CharData_Init(&storage);
-    XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
-    XML_SetStartElementHandler(g_parser, record_element_start_handler);
-    XML_SetUserData(g_parser, &storage);
-    dummy_handler_flags = 0;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-
-  if (i == 0)
-    fail("Parse succeeded despite failing reallocator");
-  if (i == max_alloc_count)
-    fail("Parse failed at maximum reallocation count");
-  CharData_CheckXMLChars(&storage, XCS("doce"));
-  if (dummy_handler_flags != DUMMY_ELEMENT_DECL_HANDLER_FLAG)
-    fail("Element handler not fired");
-}
-END_TEST
-
-START_TEST(test_alloc_realloc_nested_groups) {
-  const char *text
-      = "<!DOCTYPE doc [\n"
-        "<!ELEMENT doc "
-        /* Sixteen elements per line */
-        "(e,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,"
-        "(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?,(e?"
-        "))))))))))))))))))))))))))))))))>\n"
-        "<!ELEMENT e EMPTY>"
-        "]>\n"
-        "<doc><e/></doc>";
-  CharData storage;
-  int i;
-  const int max_realloc_count = 10;
-
-  for (i = 0; i < max_realloc_count; i++) {
-    reallocation_count = i;
-    CharData_Init(&storage);
-    XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
-    XML_SetStartElementHandler(g_parser, record_element_start_handler);
-    XML_SetUserData(g_parser, &storage);
-    dummy_handler_flags = 0;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-
-  if (i == 0)
-    fail("Parse succeeded despite failing reallocator");
-  if (i == max_realloc_count)
-    fail("Parse failed at maximum reallocation count");
-  CharData_CheckXMLChars(&storage, XCS("doce"));
-  if (dummy_handler_flags != DUMMY_ELEMENT_DECL_HANDLER_FLAG)
-    fail("Element handler not fired");
-}
-END_TEST
-
-START_TEST(test_alloc_large_group) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "<!ELEMENT doc ("
-                     "a1|a2|a3|a4|a5|a6|a7|a8|"
-                     "b1|b2|b3|b4|b5|b6|b7|b8|"
-                     "c1|c2|c3|c4|c5|c6|c7|c8|"
-                     "d1|d2|d3|d4|d5|d6|d7|d8|"
-                     "e1"
-                     ")+>\n"
-                     "]>\n"
-                     "<doc>\n"
-                     "<a1/>\n"
-                     "</doc>\n";
-  int i;
-  const int max_alloc_count = 50;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
-    dummy_handler_flags = 0;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing allocator");
-  if (i == max_alloc_count)
-    fail("Parse failed at maximum allocation count");
-  if (dummy_handler_flags != DUMMY_ELEMENT_DECL_HANDLER_FLAG)
-    fail("Element handler flag not raised");
-}
-END_TEST
-
-START_TEST(test_alloc_realloc_group_choice) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "<!ELEMENT doc ("
-                     "a1|a2|a3|a4|a5|a6|a7|a8|"
-                     "b1|b2|b3|b4|b5|b6|b7|b8|"
-                     "c1|c2|c3|c4|c5|c6|c7|c8|"
-                     "d1|d2|d3|d4|d5|d6|d7|d8|"
-                     "e1"
-                     ")+>\n"
-                     "]>\n"
-                     "<doc>\n"
-                     "<a1/>\n"
-                     "<b2 attr='foo'>This is a foo</b2>\n"
-                     "<c3></c3>\n"
-                     "</doc>\n";
-  int i;
-  const int max_realloc_count = 10;
-
-  for (i = 0; i < max_realloc_count; i++) {
-    reallocation_count = i;
-    XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
-    dummy_handler_flags = 0;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing reallocator");
-  if (i == max_realloc_count)
-    fail("Parse failed at maximum reallocation count");
-  if (dummy_handler_flags != DUMMY_ELEMENT_DECL_HANDLER_FLAG)
-    fail("Element handler flag not raised");
-}
-END_TEST
-
-START_TEST(test_alloc_pi_in_epilog) {
-  const char *text = "<doc></doc>\n"
-                     "<?pi in epilog?>";
-  int i;
-  const int max_alloc_count = 15;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetProcessingInstructionHandler(g_parser, dummy_pi_handler);
-    dummy_handler_flags = 0;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse completed despite failing allocator");
-  if (i == max_alloc_count)
-    fail("Parse failed at maximum allocation count");
-  if (dummy_handler_flags != DUMMY_PI_HANDLER_FLAG)
-    fail("Processing instruction handler not invoked");
-}
-END_TEST
-
-START_TEST(test_alloc_comment_in_epilog) {
-  const char *text = "<doc></doc>\n"
-                     "<!-- comment in epilog -->";
-  int i;
-  const int max_alloc_count = 15;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetCommentHandler(g_parser, dummy_comment_handler);
-    dummy_handler_flags = 0;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse completed despite failing allocator");
-  if (i == max_alloc_count)
-    fail("Parse failed at maximum allocation count");
-  if (dummy_handler_flags != DUMMY_COMMENT_HANDLER_FLAG)
-    fail("Processing instruction handler not invoked");
-}
-END_TEST
-
-START_TEST(test_alloc_realloc_long_attribute_value) {
-  const char *text
-      = "<!DOCTYPE doc [<!ENTITY foo '"
-        /* Each line is 64 characters */
-        "This entity will be substituted as an attribute value, and is   "
-        "calculated to be exactly long enough that the terminating NUL   "
-        "that the library adds internally will trigger the string pool to"
-        "grow. GHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "'>]>\n"
-        "<doc a='&foo;'></doc>";
-  int i;
-  const int max_realloc_count = 10;
-
-  for (i = 0; i < max_realloc_count; i++) {
-    reallocation_count = i;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing reallocator");
-  if (i == max_realloc_count)
-    fail("Parse failed at maximum reallocation count");
-}
-END_TEST
-
-START_TEST(test_alloc_attribute_whitespace) {
-  const char *text = "<doc a=' '></doc>";
-  int i;
-  const int max_alloc_count = 15;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing allocator");
-  if (i == max_alloc_count)
-    fail("Parse failed at maximum allocation count");
-}
-END_TEST
-
-START_TEST(test_alloc_attribute_predefined_entity) {
-  const char *text = "<doc a='&amp;'></doc>";
-  int i;
-  const int max_alloc_count = 15;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing allocator");
-  if (i == max_alloc_count)
-    fail("Parse failed at maximum allocation count");
-}
-END_TEST
-
-/* Test that a character reference at the end of a suitably long
- * default value for an attribute can trigger pool growth, and recovers
- * if the allocator fails on it.
- */
-START_TEST(test_alloc_long_attr_default_with_char_ref) {
-  const char *text
-      = "<!DOCTYPE doc [<!ATTLIST doc a CDATA '"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHI"
-        "&#x31;'>]>\n"
-        "<doc/>";
-  int i;
-  const int max_alloc_count = 20;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing allocator");
-  if (i == max_alloc_count)
-    fail("Parse failed at maximum allocation count");
-}
-END_TEST
-
-/* Test that a long character reference substitution triggers a pool
- * expansion correctly for an attribute value.
- */
-START_TEST(test_alloc_long_attr_value) {
-  const char *text
-      = "<!DOCTYPE test [<!ENTITY foo '\n"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "'>]>\n"
-        "<test a='&foo;'/>";
-  int i;
-  const int max_alloc_count = 25;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing allocator");
-  if (i == max_alloc_count)
-    fail("Parse failed at maximum allocation count");
-}
-END_TEST
-
-/* Test that an error in a nested parameter entity substitution is
- * handled correctly.  It seems unlikely that the code path being
- * exercised can be reached purely by carefully crafted XML, but an
- * allocation error in the right place will definitely do it.
- */
-START_TEST(test_alloc_nested_entities) {
-  const char *text = "<!DOCTYPE doc SYSTEM 'http://example.org/one.ent'>\n"
-                     "<doc />";
-  ExtFaults test_data
-      = {"<!ENTITY % pe1 '"
-         /* 64 characters per line */
-         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-         "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-         "'>\n"
-         "<!ENTITY % pe2 '%pe1;'>\n"
-         "%pe2;",
-         "Memory Fail not faulted", NULL, XML_ERROR_NO_MEMORY};
-
-  /* Causes an allocation error in a nested storeEntityValue() */
-  allocation_count = 12;
-  XML_SetUserData(g_parser, &test_data);
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-  XML_SetExternalEntityRefHandler(g_parser, external_entity_faulter);
-  expect_failure(text, XML_ERROR_EXTERNAL_ENTITY_HANDLING,
-                 "Entity allocation failure not noted");
-}
-END_TEST
-
-START_TEST(test_alloc_realloc_param_entity_newline) {
-  const char *text = "<!DOCTYPE doc SYSTEM 'http://example.org/'>\n"
-                     "<doc/>";
-  char dtd_text[]
-      = "<!ENTITY % pe '<!ATTLIST doc att CDATA \""
-        /* 64 characters per line */
-        "This default value is carefully crafted so that the carriage    "
-        "return right at the end of the entity string causes an internal "
-        "string pool to have to grow.  This allows us to test the alloc  "
-        "failure path from that point. OPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDE"
-        "\">\n'>"
-        "%pe;\n";
-  int i;
-  const int max_realloc_count = 5;
-
-  for (i = 0; i < max_realloc_count; i++) {
-    reallocation_count = i;
-    XML_SetUserData(g_parser, dtd_text);
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_alloc);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing reallocator");
-  if (i == max_realloc_count)
-    fail("Parse failed at maximum reallocation count");
-}
-END_TEST
-
-START_TEST(test_alloc_realloc_ce_extends_pe) {
-  const char *text = "<!DOCTYPE doc SYSTEM 'http://example.org/'>\n"
-                     "<doc/>";
-  char dtd_text[]
-      = "<!ENTITY % pe '<!ATTLIST doc att CDATA \""
-        /* 64 characters per line */
-        "This default value is carefully crafted so that the character   "
-        "entity at the end causes an internal string pool to have to     "
-        "grow.  This allows us to test the allocation failure path from  "
-        "that point onwards. EFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFG&#x51;"
-        "\">\n'>"
-        "%pe;\n";
-  int i;
-  const int max_realloc_count = 5;
-
-  for (i = 0; i < max_realloc_count; i++) {
-    reallocation_count = i;
-    XML_SetUserData(g_parser, dtd_text);
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_alloc);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parse succeeded despite failing reallocator");
-  if (i == max_realloc_count)
-    fail("Parse failed at maximum reallocation count");
-}
-END_TEST
-
-START_TEST(test_alloc_realloc_attributes) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ATTLIST doc\n"
-                     "    a1  (a|b|c)   'a'\n"
-                     "    a2  (foo|bar) #IMPLIED\n"
-                     "    a3  NMTOKEN   #IMPLIED\n"
-                     "    a4  NMTOKENS  #IMPLIED\n"
-                     "    a5  ID        #IMPLIED\n"
-                     "    a6  IDREF     #IMPLIED\n"
-                     "    a7  IDREFS    #IMPLIED\n"
-                     "    a8  ENTITY    #IMPLIED\n"
-                     "    a9  ENTITIES  #IMPLIED\n"
-                     "    a10 CDATA     #IMPLIED\n"
-                     "  >]>\n"
-                     "<doc>wombat</doc>\n";
-  int i;
-  const int max_realloc_count = 5;
-
-  for (i = 0; i < max_realloc_count; i++) {
-    reallocation_count = i;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-
-  if (i == 0)
-    fail("Parse succeeded despite failing reallocator");
-  if (i == max_realloc_count)
-    fail("Parse failed at maximum reallocation count");
-}
-END_TEST
-
-START_TEST(test_alloc_long_doc_name) {
-  const char *text =
-      /* 64 characters per line */
-      "<LongRootElementNameThatWillCauseTheNextAllocationToExpandTheStr"
-      "ingPoolForTheDTDQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-      " a='1'/>";
-  int i;
-  const int max_alloc_count = 20;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing reallocations");
-  else if (i == max_alloc_count)
-    fail("Parsing failed even at max reallocation count");
-}
-END_TEST
-
-START_TEST(test_alloc_long_base) {
-  const char *text = "<!DOCTYPE doc [\n"
-                     "  <!ENTITY e SYSTEM 'foo'>\n"
-                     "]>\n"
-                     "<doc>&e;</doc>";
-  char entity_text[] = "Hello world";
-  const XML_Char *base =
-      /* 64 characters per line */
-      /* clang-format off */
-        XCS("LongBaseURI/that/will/overflow/an/internal/buffer/and/cause/it/t")
-        XCS("o/have/to/grow/PQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/");
-  /* clang-format on */
-  int i;
-  const int max_alloc_count = 25;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetUserData(g_parser, entity_text);
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_alloc);
-    if (XML_SetBase(g_parser, base) == XML_STATUS_ERROR) {
-      XML_ParserReset(g_parser, NULL);
-      continue;
-    }
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing allocations");
-  else if (i == max_alloc_count)
-    fail("Parsing failed even at max allocation count");
-}
-END_TEST
-
-START_TEST(test_alloc_long_public_id) {
-  const char *text
-      = "<!DOCTYPE doc [\n"
-        "  <!ENTITY e PUBLIC '"
-        /* 64 characters per line */
-        "LongPublicIDThatShouldResultInAnInternalStringPoolGrowingAtASpec"
-        "ificMomentKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "' 'bar'>\n"
-        "]>\n"
-        "<doc>&e;</doc>";
-  char entity_text[] = "Hello world";
-  int i;
-  const int max_alloc_count = 40;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetUserData(g_parser, entity_text);
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_alloc);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing allocations");
-  else if (i == max_alloc_count)
-    fail("Parsing failed even at max allocation count");
-}
-END_TEST
-
-START_TEST(test_alloc_long_entity_value) {
-  const char *text
-      = "<!DOCTYPE doc [\n"
-        "  <!ENTITY e1 '"
-        /* 64 characters per line */
-        "Long entity value that should provoke a string pool to grow whil"
-        "e setting up to parse the external entity below. xyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "'>\n"
-        "  <!ENTITY e2 SYSTEM 'bar'>\n"
-        "]>\n"
-        "<doc>&e2;</doc>";
-  char entity_text[] = "Hello world";
-  int i;
-  const int max_alloc_count = 40;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetUserData(g_parser, entity_text);
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_alloc);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing allocations");
-  else if (i == max_alloc_count)
-    fail("Parsing failed even at max allocation count");
-}
-END_TEST
-
-START_TEST(test_alloc_long_notation) {
-  const char *text
-      = "<!DOCTYPE doc [\n"
-        "  <!NOTATION note SYSTEM '"
-        /* 64 characters per line */
-        "ALongNotationNameThatShouldProvokeStringPoolGrowthWhileCallingAn"
-        "ExternalEntityParserUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "'>\n"
-        "  <!ENTITY e1 SYSTEM 'foo' NDATA "
-        /* 64 characters per line */
-        "ALongNotationNameThatShouldProvokeStringPoolGrowthWhileCallingAn"
-        "ExternalEntityParserUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
-        ">\n"
-        "  <!ENTITY e2 SYSTEM 'bar'>\n"
-        "]>\n"
-        "<doc>&e2;</doc>";
-  ExtOption options[]
-      = {{XCS("foo"), "Entity Foo"}, {XCS("bar"), "Entity Bar"}, {NULL, NULL}};
-  int i;
-  const int max_alloc_count = 40;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetUserData(g_parser, options);
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_optioner);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-
-    /* See comment in test_alloc_parse_xdecl() */
-    alloc_teardown();
-    alloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing allocations");
-  else if (i == max_alloc_count)
-    fail("Parsing failed even at max allocation count");
-}
-END_TEST
-
-static int XMLCALL
-external_entity_parser_create_alloc_fail_handler(XML_Parser parser,
-                                                 const XML_Char *context,
-                                                 const XML_Char *base,
-                                                 const XML_Char *systemId,
-                                                 const XML_Char *publicId) {
-  UNUSED_P(base);
-  UNUSED_P(systemId);
-  UNUSED_P(publicId);
-
-  if (context != NULL)
-    fail("Unexpected non-NULL context");
-
-  // The following number intends to fail the upcoming allocation in line
-  // "parser->m_protocolEncodingName = copyString(encodingName,
-  // &(parser->m_mem));" in function parserInit.
-  allocation_count = 3;
-
-  const XML_Char *const encodingName = XCS("UTF-8"); // needs something non-NULL
-  const XML_Parser ext_parser
-      = XML_ExternalEntityParserCreate(parser, context, encodingName);
-  if (ext_parser != NULL)
-    fail(
-        "Call to XML_ExternalEntityParserCreate was expected to fail out-of-memory");
-
-  allocation_count = ALLOC_ALWAYS_SUCCEED;
-  return XML_STATUS_ERROR;
-}
-
-START_TEST(test_alloc_reset_after_external_entity_parser_create_fail) {
-  const char *const text = "<!DOCTYPE doc SYSTEM 'foo'><doc/>";
-
-  XML_SetExternalEntityRefHandler(
-      g_parser, external_entity_parser_create_alloc_fail_handler);
-  XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-
-  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_ERROR)
-    fail("Call to parse was expected to fail");
-
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_EXTERNAL_ENTITY_HANDLING)
-    fail("Call to parse was expected to fail from the external entity handler");
-
-  XML_ParserReset(g_parser, NULL);
-}
-END_TEST
-
-static void
-nsalloc_setup(void) {
-  XML_Memory_Handling_Suite memsuite = {duff_allocator, duff_reallocator, free};
-  XML_Char ns_sep[2] = {' ', '\0'};
-
-  /* Ensure the parser creation will go through */
-  allocation_count = ALLOC_ALWAYS_SUCCEED;
-  reallocation_count = REALLOC_ALWAYS_SUCCEED;
-  g_parser = XML_ParserCreate_MM(NULL, &memsuite, ns_sep);
-  if (g_parser == NULL)
-    fail("Parser not created");
-}
-
-static void
-nsalloc_teardown(void) {
-  basic_teardown();
-}
-
-/* Test the effects of allocation failure in simple namespace parsing.
- * Based on test_ns_default_with_empty_uri()
- */
-START_TEST(test_nsalloc_xmlns) {
-  const char *text = "<doc xmlns='http://example.org/'>\n"
-                     "  <e xmlns=''/>\n"
-                     "</doc>";
-  unsigned int i;
-  const unsigned int max_alloc_count = 30;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    /* Exercise more code paths with a default handler */
-    XML_SetDefaultHandler(g_parser, dummy_default_handler);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* Resetting the parser is insufficient, because some memory
-     * allocations are cached within the parser.  Instead we use
-     * the teardown and setup routines to ensure that we have the
-     * right sort of parser back in our hands.
-     */
-    nsalloc_teardown();
-    nsalloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing allocations");
-  else if (i == max_alloc_count)
-    fail("Parsing failed even at maximum allocation count");
-}
-END_TEST
-
-/* Test XML_ParseBuffer interface with namespace and a dicky allocator */
-START_TEST(test_nsalloc_parse_buffer) {
-  const char *text = "<doc>Hello</doc>";
-  void *buffer;
-
-  /* Try a parse before the start of the world */
-  /* (Exercises new code path) */
-  if (XML_ParseBuffer(g_parser, 0, XML_FALSE) != XML_STATUS_ERROR)
-    fail("Pre-init XML_ParseBuffer not faulted");
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_NO_BUFFER)
-    fail("Pre-init XML_ParseBuffer faulted for wrong reason");
-
-  buffer = XML_GetBuffer(g_parser, 1 /* any small number greater than 0 */);
-  if (buffer == NULL)
-    fail("Could not acquire parse buffer");
-
-  allocation_count = 0;
-  if (XML_ParseBuffer(g_parser, 0, XML_FALSE) != XML_STATUS_ERROR)
-    fail("Pre-init XML_ParseBuffer not faulted");
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_NO_MEMORY)
-    fail("Pre-init XML_ParseBuffer faulted for wrong reason");
-
-  /* Now with actual memory allocation */
-  allocation_count = ALLOC_ALWAYS_SUCCEED;
-  if (XML_ParseBuffer(g_parser, 0, XML_FALSE) != XML_STATUS_OK)
-    xml_failure(g_parser);
-
-  /* Check that resuming an unsuspended parser is faulted */
-  if (XML_ResumeParser(g_parser) != XML_STATUS_ERROR)
-    fail("Resuming unsuspended parser not faulted");
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_NOT_SUSPENDED)
-    xml_failure(g_parser);
-
-  /* Get the parser into suspended state */
-  XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler);
-  resumable = XML_TRUE;
-  buffer = XML_GetBuffer(g_parser, (int)strlen(text));
-  if (buffer == NULL)
-    fail("Could not acquire parse buffer");
-  assert(buffer != NULL);
-  memcpy(buffer, text, strlen(text));
-  if (XML_ParseBuffer(g_parser, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_SUSPENDED)
-    xml_failure(g_parser);
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_NONE)
-    xml_failure(g_parser);
-  if (XML_ParseBuffer(g_parser, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_ERROR)
-    fail("Suspended XML_ParseBuffer not faulted");
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_SUSPENDED)
-    xml_failure(g_parser);
-  if (XML_GetBuffer(g_parser, (int)strlen(text)) != NULL)
-    fail("Suspended XML_GetBuffer not faulted");
-
-  /* Get it going again and complete the world */
-  XML_SetCharacterDataHandler(g_parser, NULL);
-  if (XML_ResumeParser(g_parser) != XML_STATUS_OK)
-    xml_failure(g_parser);
-  if (XML_ParseBuffer(g_parser, (int)strlen(text), XML_TRUE)
-      != XML_STATUS_ERROR)
-    fail("Post-finishing XML_ParseBuffer not faulted");
-  if (XML_GetErrorCode(g_parser) != XML_ERROR_FINISHED)
-    xml_failure(g_parser);
-  if (XML_GetBuffer(g_parser, (int)strlen(text)) != NULL)
-    fail("Post-finishing XML_GetBuffer not faulted");
-}
-END_TEST
-
-/* Check handling of long prefix names (pool growth) */
-START_TEST(test_nsalloc_long_prefix) {
-  const char *text
-      = "<"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        ":foo xmlns:"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "='http://example.org/'>"
-        "</"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        ":foo>";
-  int i;
-  const int max_alloc_count = 40;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_nsalloc_xmlns() */
-    nsalloc_teardown();
-    nsalloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing allocations");
-  else if (i == max_alloc_count)
-    fail("Parsing failed even at max allocation count");
-}
-END_TEST
-
-/* Check handling of long uri names (pool growth) */
-START_TEST(test_nsalloc_long_uri) {
-  const char *text
-      = "<foo:e xmlns:foo='http://example.org/"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "' bar:a='12'\n"
-        "xmlns:bar='http://example.org/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789A/"
-        "'>"
-        "</foo:e>";
-  int i;
-  const int max_alloc_count = 40;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_nsalloc_xmlns() */
-    nsalloc_teardown();
-    nsalloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing allocations");
-  else if (i == max_alloc_count)
-    fail("Parsing failed even at max allocation count");
-}
-END_TEST
-
-/* Test handling of long attribute names with prefixes */
-START_TEST(test_nsalloc_long_attr) {
-  const char *text
-      = "<foo:e xmlns:foo='http://example.org/' bar:"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "='12'\n"
-        "xmlns:bar='http://example.org/'>"
-        "</foo:e>";
-  int i;
-  const int max_alloc_count = 40;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_nsalloc_xmlns() */
-    nsalloc_teardown();
-    nsalloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing allocations");
-  else if (i == max_alloc_count)
-    fail("Parsing failed even at max allocation count");
-}
-END_TEST
-
-/* Test handling of an attribute name with a long namespace prefix */
-START_TEST(test_nsalloc_long_attr_prefix) {
-  const char *text
-      = "<foo:e xmlns:foo='http://example.org/' "
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        ":a='12'\n"
-        "xmlns:"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "='http://example.org/'>"
-        "</foo:e>";
-  const XML_Char *elemstr[] = {
-      /* clang-format off */
-        XCS("http://example.org/ e foo"),
-        XCS("http://example.org/ a ")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
-        XCS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ")
-      /* clang-format on */
-  };
-  int i;
-  const int max_alloc_count = 40;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetReturnNSTriplet(g_parser, XML_TRUE);
-    XML_SetUserData(g_parser, (void *)elemstr);
-    XML_SetElementHandler(g_parser, triplet_start_checker, triplet_end_checker);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_nsalloc_xmlns() */
-    nsalloc_teardown();
-    nsalloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing allocations");
-  else if (i == max_alloc_count)
-    fail("Parsing failed even at max allocation count");
-}
-END_TEST
-
-/* Test attribute handling in the face of a dodgy reallocator */
-START_TEST(test_nsalloc_realloc_attributes) {
-  const char *text = "<foo:e xmlns:foo='http://example.org/' bar:a='12'\n"
-                     "       xmlns:bar='http://example.org/'>"
-                     "</foo:e>";
-  int i;
-  const int max_realloc_count = 10;
-
-  for (i = 0; i < max_realloc_count; i++) {
-    reallocation_count = i;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_nsalloc_xmlns() */
-    nsalloc_teardown();
-    nsalloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing reallocations");
-  else if (i == max_realloc_count)
-    fail("Parsing failed at max reallocation count");
-}
-END_TEST
-
-/* Test long element names with namespaces under a failing allocator */
-START_TEST(test_nsalloc_long_element) {
-  const char *text
-      = "<foo:thisisalongenoughelementnametotriggerareallocation\n"
-        " xmlns:foo='http://example.org/' bar:a='12'\n"
-        " xmlns:bar='http://example.org/'>"
-        "</foo:thisisalongenoughelementnametotriggerareallocation>";
-  const XML_Char *elemstr[]
-      = {XCS("http://example.org/")
-             XCS(" thisisalongenoughelementnametotriggerareallocation foo"),
-         XCS("http://example.org/ a bar")};
-  int i;
-  const int max_alloc_count = 30;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetReturnNSTriplet(g_parser, XML_TRUE);
-    XML_SetUserData(g_parser, (void *)elemstr);
-    XML_SetElementHandler(g_parser, triplet_start_checker, triplet_end_checker);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_nsalloc_xmlns() */
-    nsalloc_teardown();
-    nsalloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing reallocations");
-  else if (i == max_alloc_count)
-    fail("Parsing failed at max reallocation count");
-}
-END_TEST
-
-/* Test the effects of reallocation failure when reassigning a
- * binding.
- *
- * XML_ParserReset does not free the BINDING structures used by a
- * parser, but instead adds them to an internal free list to be reused
- * as necessary.  Likewise the URI buffers allocated for the binding
- * aren't freed, but kept attached to their existing binding.  If the
- * new binding has a longer URI, it will need reallocation.  This test
- * provokes that reallocation, and tests the control path if it fails.
- */
-START_TEST(test_nsalloc_realloc_binding_uri) {
-  const char *first = "<doc xmlns='http://example.org/'>\n"
-                      "  <e xmlns='' />\n"
-                      "</doc>";
-  const char *second
-      = "<doc xmlns='http://example.org/long/enough/URI/to/reallocate/'>\n"
-        "  <e xmlns='' />\n"
-        "</doc>";
-  unsigned i;
-  const unsigned max_realloc_count = 10;
-
-  /* First, do a full parse that will leave bindings around */
-  if (_XML_Parse_SINGLE_BYTES(g_parser, first, (int)strlen(first), XML_TRUE)
-      == XML_STATUS_ERROR)
-    xml_failure(g_parser);
-
-  /* Now repeat with a longer URI and a duff reallocator */
-  for (i = 0; i < max_realloc_count; i++) {
-    XML_ParserReset(g_parser, NULL);
-    reallocation_count = i;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, second, (int)strlen(second), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing reallocation");
-  else if (i == max_realloc_count)
-    fail("Parsing failed at max reallocation count");
-}
-END_TEST
-
-/* Check handling of long prefix names (pool growth) */
-START_TEST(test_nsalloc_realloc_long_prefix) {
-  const char *text
-      = "<"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        ":foo xmlns:"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "='http://example.org/'>"
-        "</"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        ":foo>";
-  int i;
-  const int max_realloc_count = 12;
-
-  for (i = 0; i < max_realloc_count; i++) {
-    reallocation_count = i;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_nsalloc_xmlns() */
-    nsalloc_teardown();
-    nsalloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing reallocations");
-  else if (i == max_realloc_count)
-    fail("Parsing failed even at max reallocation count");
-}
-END_TEST
-
-/* Check handling of even long prefix names (different code path) */
-START_TEST(test_nsalloc_realloc_longer_prefix) {
-  const char *text
-      = "<"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "Q:foo xmlns:"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "Q='http://example.org/'>"
-        "</"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "Q:foo>";
-  int i;
-  const int max_realloc_count = 12;
-
-  for (i = 0; i < max_realloc_count; i++) {
-    reallocation_count = i;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_nsalloc_xmlns() */
-    nsalloc_teardown();
-    nsalloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing reallocations");
-  else if (i == max_realloc_count)
-    fail("Parsing failed even at max reallocation count");
-}
-END_TEST
-
-START_TEST(test_nsalloc_long_namespace) {
-  const char *text1
-      = "<"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        ":e xmlns:"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "='http://example.org/'>\n";
-  const char *text2
-      = "<"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        ":f "
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        ":attr='foo'/>\n"
-        "</"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        ":e>";
-  int i;
-  const int max_alloc_count = 40;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text1, (int)strlen(text1), XML_FALSE)
-            != XML_STATUS_ERROR
-        && _XML_Parse_SINGLE_BYTES(g_parser, text2, (int)strlen(text2),
-                                   XML_TRUE)
-               != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_nsalloc_xmlns() */
-    nsalloc_teardown();
-    nsalloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing allocations");
-  else if (i == max_alloc_count)
-    fail("Parsing failed even at max allocation count");
-}
-END_TEST
-
-/* Using a slightly shorter namespace name provokes allocations in
- * slightly different places in the code.
- */
-START_TEST(test_nsalloc_less_long_namespace) {
-  const char *text
-      = "<"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678"
-        ":e xmlns:"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678"
-        "='http://example.org/'>\n"
-        "<"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678"
-        ":f "
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678"
-        ":att='foo'/>\n"
-        "</"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AZ"
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678"
-        ":e>";
-  int i;
-  const int max_alloc_count = 40;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_nsalloc_xmlns() */
-    nsalloc_teardown();
-    nsalloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing allocations");
-  else if (i == max_alloc_count)
-    fail("Parsing failed even at max allocation count");
-}
-END_TEST
-
-START_TEST(test_nsalloc_long_context) {
-  const char *text
-      = "<!DOCTYPE doc SYSTEM 'foo' [\n"
-        "  <!ATTLIST doc baz ID #REQUIRED>\n"
-        "  <!ENTITY en SYSTEM 'bar'>\n"
-        "]>\n"
-        "<doc xmlns='http://example.org/"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKL"
-        "' baz='2'>\n"
-        "&en;"
-        "</doc>";
-  ExtOption options[] = {
-      {XCS("foo"), "<!ELEMENT e EMPTY>"}, {XCS("bar"), "<e/>"}, {NULL, NULL}};
-  int i;
-  const int max_alloc_count = 70;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetUserData(g_parser, options);
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_optioner);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-
-    /* See comment in test_nsalloc_xmlns() */
-    nsalloc_teardown();
-    nsalloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing allocations");
-  else if (i == max_alloc_count)
-    fail("Parsing failed even at max allocation count");
-}
-END_TEST
-
-/* This function is void; it will throw a fail() on error, so if it
- * returns normally it must have succeeded.
- */
-static void
-context_realloc_test(const char *text) {
-  ExtOption options[] = {
-      {XCS("foo"), "<!ELEMENT e EMPTY>"}, {XCS("bar"), "<e/>"}, {NULL, NULL}};
-  int i;
-  const int max_realloc_count = 6;
-
-  for (i = 0; i < max_realloc_count; i++) {
-    reallocation_count = i;
-    XML_SetUserData(g_parser, options);
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_optioner);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_nsalloc_xmlns() */
-    nsalloc_teardown();
-    nsalloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing reallocations");
-  else if (i == max_realloc_count)
-    fail("Parsing failed even at max reallocation count");
-}
-
-START_TEST(test_nsalloc_realloc_long_context) {
-  const char *text
-      = "<!DOCTYPE doc SYSTEM 'foo' [\n"
-        "  <!ENTITY en SYSTEM 'bar'>\n"
-        "]>\n"
-        "<doc xmlns='http://example.org/"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKL"
-        "'>\n"
-        "&en;"
-        "</doc>";
-
-  context_realloc_test(text);
-}
-END_TEST
-
-START_TEST(test_nsalloc_realloc_long_context_2) {
-  const char *text
-      = "<!DOCTYPE doc SYSTEM 'foo' [\n"
-        "  <!ENTITY en SYSTEM 'bar'>\n"
-        "]>\n"
-        "<doc xmlns='http://example.org/"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJK"
-        "'>\n"
-        "&en;"
-        "</doc>";
-
-  context_realloc_test(text);
-}
-END_TEST
-
-START_TEST(test_nsalloc_realloc_long_context_3) {
-  const char *text
-      = "<!DOCTYPE doc SYSTEM 'foo' [\n"
-        "  <!ENTITY en SYSTEM 'bar'>\n"
-        "]>\n"
-        "<doc xmlns='http://example.org/"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGH"
-        "'>\n"
-        "&en;"
-        "</doc>";
-
-  context_realloc_test(text);
-}
-END_TEST
-
-START_TEST(test_nsalloc_realloc_long_context_4) {
-  const char *text
-      = "<!DOCTYPE doc SYSTEM 'foo' [\n"
-        "  <!ENTITY en SYSTEM 'bar'>\n"
-        "]>\n"
-        "<doc xmlns='http://example.org/"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO"
-        "'>\n"
-        "&en;"
-        "</doc>";
-
-  context_realloc_test(text);
-}
-END_TEST
-
-START_TEST(test_nsalloc_realloc_long_context_5) {
-  const char *text
-      = "<!DOCTYPE doc SYSTEM 'foo' [\n"
-        "  <!ENTITY en SYSTEM 'bar'>\n"
-        "]>\n"
-        "<doc xmlns='http://example.org/"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABC"
-        "'>\n"
-        "&en;"
-        "</doc>";
-
-  context_realloc_test(text);
-}
-END_TEST
-
-START_TEST(test_nsalloc_realloc_long_context_6) {
-  const char *text
-      = "<!DOCTYPE doc SYSTEM 'foo' [\n"
-        "  <!ENTITY en SYSTEM 'bar'>\n"
-        "]>\n"
-        "<doc xmlns='http://example.org/"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNOP"
-        "'>\n"
-        "&en;"
-        "</doc>";
-
-  context_realloc_test(text);
-}
-END_TEST
-
-START_TEST(test_nsalloc_realloc_long_context_7) {
-  const char *text
-      = "<!DOCTYPE doc SYSTEM 'foo' [\n"
-        "  <!ENTITY en SYSTEM 'bar'>\n"
-        "]>\n"
-        "<doc xmlns='http://example.org/"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLM"
-        "'>\n"
-        "&en;"
-        "</doc>";
-
-  context_realloc_test(text);
-}
-END_TEST
-
-START_TEST(test_nsalloc_realloc_long_ge_name) {
-  const char *text
-      = "<!DOCTYPE doc SYSTEM 'foo' [\n"
-        "  <!ENTITY "
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        " SYSTEM 'bar'>\n"
-        "]>\n"
-        "<doc xmlns='http://example.org/baz'>\n"
-        "&"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        ";"
-        "</doc>";
-  ExtOption options[] = {
-      {XCS("foo"), "<!ELEMENT el EMPTY>"}, {XCS("bar"), "<el/>"}, {NULL, NULL}};
-  int i;
-  const int max_realloc_count = 10;
-
-  for (i = 0; i < max_realloc_count; i++) {
-    reallocation_count = i;
-    XML_SetUserData(g_parser, options);
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_optioner);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_nsalloc_xmlns() */
-    nsalloc_teardown();
-    nsalloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing reallocations");
-  else if (i == max_realloc_count)
-    fail("Parsing failed even at max reallocation count");
-}
-END_TEST
-
-/* Test that when a namespace is passed through the context mechanism
- * to an external entity parser, the parsers handle reallocation
- * failures correctly.  The prefix is exactly the right length to
- * provoke particular uncommon code paths.
- */
-START_TEST(test_nsalloc_realloc_long_context_in_dtd) {
-  const char *text1
-      = "<!DOCTYPE "
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        ":doc [\n"
-        "  <!ENTITY First SYSTEM 'foo/First'>\n"
-        "]>\n"
-        "<"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        ":doc xmlns:"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "='foo/Second'>&First;";
-  const char *text2
-      = "</"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        ":doc>";
-  ExtOption options[] = {{XCS("foo/First"), "Hello world"}, {NULL, NULL}};
-  int i;
-  const int max_realloc_count = 20;
-
-  for (i = 0; i < max_realloc_count; i++) {
-    reallocation_count = i;
-    XML_SetUserData(g_parser, options);
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_optioner);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text1, (int)strlen(text1), XML_FALSE)
-            != XML_STATUS_ERROR
-        && _XML_Parse_SINGLE_BYTES(g_parser, text2, (int)strlen(text2),
-                                   XML_TRUE)
-               != XML_STATUS_ERROR)
-      break;
-    /* See comment in test_nsalloc_xmlns() */
-    nsalloc_teardown();
-    nsalloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing reallocations");
-  else if (i == max_realloc_count)
-    fail("Parsing failed even at max reallocation count");
-}
-END_TEST
-
-START_TEST(test_nsalloc_long_default_in_ext) {
-  const char *text
-      = "<!DOCTYPE doc [\n"
-        "  <!ATTLIST e a1 CDATA '"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"
-        "'>\n"
-        "  <!ENTITY x SYSTEM 'foo'>\n"
-        "]>\n"
-        "<doc>&x;</doc>";
-  ExtOption options[] = {{XCS("foo"), "<e/>"}, {NULL, NULL}};
-  int i;
-  const int max_alloc_count = 50;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetUserData(g_parser, options);
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_optioner);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-
-    /* See comment in test_nsalloc_xmlns() */
-    nsalloc_teardown();
-    nsalloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing allocations");
-  else if (i == max_alloc_count)
-    fail("Parsing failed even at max allocation count");
-}
-END_TEST
-
-START_TEST(test_nsalloc_long_systemid_in_ext) {
-  const char *text
-      = "<!DOCTYPE doc SYSTEM 'foo' [\n"
-        "  <!ENTITY en SYSTEM '"
-        /* 64 characters per line */
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"
-        "'>\n"
-        "]>\n"
-        "<doc>&en;</doc>";
-  ExtOption options[] = {
-      {XCS("foo"), "<!ELEMENT e EMPTY>"},
-      {/* clang-format off */
-            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
-            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
-            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
-            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
-            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
-            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
-            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
-            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
-            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
-            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
-            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
-            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
-            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
-            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
-            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/")
-            XCS("ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/ABCDEFGHIJKLMNO/"),
-       /* clang-format on */
-       "<e/>"},
-      {NULL, NULL}};
-  int i;
-  const int max_alloc_count = 55;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetUserData(g_parser, options);
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_optioner);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-
-    /* See comment in test_nsalloc_xmlns() */
-    nsalloc_teardown();
-    nsalloc_setup();
-  }
-  if (i == 0)
-    fail("Parsing worked despite failing allocations");
-  else if (i == max_alloc_count)
-    fail("Parsing failed even at max allocation count");
-}
-END_TEST
-
-/* Test the effects of allocation failure on parsing an element in a
- * namespace.  Based on test_nsalloc_long_context.
- */
-START_TEST(test_nsalloc_prefixed_element) {
-  const char *text = "<!DOCTYPE pfx:element SYSTEM 'foo' [\n"
-                     "  <!ATTLIST pfx:element baz ID #REQUIRED>\n"
-                     "  <!ENTITY en SYSTEM 'bar'>\n"
-                     "]>\n"
-                     "<pfx:element xmlns:pfx='http://example.org/' baz='2'>\n"
-                     "&en;"
-                     "</pfx:element>";
-  ExtOption options[] = {
-      {XCS("foo"), "<!ELEMENT e EMPTY>"}, {XCS("bar"), "<e/>"}, {NULL, NULL}};
-  int i;
-  const int max_alloc_count = 70;
-
-  for (i = 0; i < max_alloc_count; i++) {
-    allocation_count = i;
-    XML_SetUserData(g_parser, options);
-    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    XML_SetExternalEntityRefHandler(g_parser, external_entity_optioner);
-    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
-        != XML_STATUS_ERROR)
-      break;
-
-    /* See comment in test_nsalloc_xmlns() */
-    nsalloc_teardown();
-    nsalloc_setup();
-  }
-  if (i == 0)
-    fail("Success despite failing allocator");
-  else if (i == max_alloc_count)
-    fail("Failed even at full allocation count");
-}
-END_TEST
-
-#if defined(XML_DTD)
-typedef enum XML_Status (*XmlParseFunction)(XML_Parser, const char *, int, int);
-
-struct AccountingTestCase {
-  const char *primaryText;
-  const char *firstExternalText;  /* often NULL */
-  const char *secondExternalText; /* often NULL */
-  const unsigned long long expectedCountBytesIndirectExtra;
-  XML_Bool singleBytesWanted;
-};
-
-static int
-accounting_external_entity_ref_handler(XML_Parser parser,
-                                       const XML_Char *context,
-                                       const XML_Char *base,
-                                       const XML_Char *systemId,
-                                       const XML_Char *publicId) {
-  UNUSED_P(context);
-  UNUSED_P(base);
-  UNUSED_P(publicId);
-
-  const struct AccountingTestCase *const testCase
-      = (const struct AccountingTestCase *)XML_GetUserData(parser);
-
-  const char *externalText = NULL;
-  if (xcstrcmp(systemId, XCS("first.ent")) == 0) {
-    externalText = testCase->firstExternalText;
-  } else if (xcstrcmp(systemId, XCS("second.ent")) == 0) {
-    externalText = testCase->secondExternalText;
-  } else {
-    assert(! "systemId is neither \"first.ent\" nor \"second.ent\"");
-  }
-  assert(externalText);
-
-  XML_Parser entParser = XML_ExternalEntityParserCreate(parser, context, 0);
-  assert(entParser);
-
-  const XmlParseFunction xmlParseFunction
-      = testCase->singleBytesWanted ? _XML_Parse_SINGLE_BYTES : XML_Parse;
-
-  const enum XML_Status status = xmlParseFunction(
-      entParser, externalText, (int)strlen(externalText), XML_TRUE);
-
-  XML_ParserFree(entParser);
-  return status;
-}
-
-START_TEST(test_accounting_precision) {
-  const XML_Bool filled_later = XML_TRUE; /* value is arbitrary */
-  struct AccountingTestCase cases[] = {
-      {"<e/>", NULL, NULL, 0, 0},
-      {"<e></e>", NULL, NULL, 0, 0},
-
-      /* Attributes */
-      {"<e k1=\"v2\" k2=\"v2\"/>", NULL, NULL, 0, filled_later},
-      {"<e k1=\"v2\" k2=\"v2\"></e>", NULL, NULL, 0, 0},
-      {"<p:e xmlns:p=\"https://domain.invalid/\" />", NULL, NULL, 0,
-       filled_later},
-      {"<e k=\"&amp;&apos;&gt;&lt;&quot;\" />", NULL, NULL,
-       sizeof(XML_Char) * 5 /* number of predefined entities */, filled_later},
-      {"<e1 xmlns='https://example.org/'>\n"
-       "  <e2 xmlns=''/>\n"
-       "</e1>",
-       NULL, NULL, 0, filled_later},
-
-      /* Text */
-      {"<e>text</e>", NULL, NULL, 0, filled_later},
-      {"<e1><e2>text1<e3/>text2</e2></e1>", NULL, NULL, 0, filled_later},
-      {"<e>&amp;&apos;&gt;&lt;&quot;</e>", NULL, NULL,
-       sizeof(XML_Char) * 5 /* number of predefined entities */, filled_later},
-      {"<e>&#65;&#41;</e>", NULL, NULL, 0, filled_later},
-
-      /* Prolog */
-      {"<?xml version=\"1.0\"?><root/>", NULL, NULL, 0, filled_later},
-
-      /* Whitespace */
-      {"  <e1>  <e2>  </e2>  </e1>  ", NULL, NULL, 0, filled_later},
-      {"<e1  ><e2  /></e1  >", NULL, NULL, 0, filled_later},
-      {"<e1><e2 k = \"v\"/><e3 k = 'v'/></e1>", NULL, NULL, 0, filled_later},
-
-      /* Comments */
-      {"<!-- Comment --><e><!-- Comment --></e>", NULL, NULL, 0, filled_later},
-
-      /* Processing instructions */
-      {"<?xml-stylesheet type=\"text/xsl\" href=\"https://domain.invalid/\" media=\"all\"?><e/>",
-       NULL, NULL, 0, filled_later},
-      {"<?pi0?><?pi1 ?><?pi2  ?><!DOCTYPE r SYSTEM 'first.ent'><r/>",
-       "<?pi3?><!ENTITY % e1 SYSTEM 'second.ent'><?pi4?>%e1;<?pi5?>", "<?pi6?>",
-       0, filled_later},
-
-      /* CDATA */
-      {"<e><![CDATA[one two three]]></e>", NULL, NULL, 0, filled_later},
-      /* The following is the essence of this OSS-Fuzz finding:
-         https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=34302
-         https://oss-fuzz.com/testcase-detail/4860575394955264
-      */
-      {"<!DOCTYPE r [\n"
-       "<!ENTITY e \"111<![CDATA[2 <= 2]]>333\">\n"
-       "]>\n"
-       "<r>&e;</r>\n",
-       NULL, NULL, sizeof(XML_Char) * strlen("111<![CDATA[2 <= 2]]>333"),
-       filled_later},
-
-      /* Conditional sections */
-      {"<!DOCTYPE r [\n"
-       "<!ENTITY % draft 'INCLUDE'>\n"
-       "<!ENTITY % final 'IGNORE'>\n"
-       "<!ENTITY % import SYSTEM \"first.ent\">\n"
-       "%import;\n"
-       "]>\n"
-       "<r/>\n",
-       "<![%draft;[<!--1-->]]>\n"
-       "<![%final;[<!--22-->]]>",
-       NULL, sizeof(XML_Char) * (strlen("INCLUDE") + strlen("IGNORE")),
-       filled_later},
-
-      /* General entities */
-      {"<!DOCTYPE root [\n"
-       "<!ENTITY nine \"123456789\">\n"
-       "]>\n"
-       "<root>&nine;</root>",
-       NULL, NULL, sizeof(XML_Char) * strlen("123456789"), filled_later},
-      {"<!DOCTYPE root [\n"
-       "<!ENTITY nine \"123456789\">\n"
-       "]>\n"
-       "<root k1=\"&nine;\"/>",
-       NULL, NULL, sizeof(XML_Char) * strlen("123456789"), filled_later},
-      {"<!DOCTYPE root [\n"
-       "<!ENTITY nine \"123456789\">\n"
-       "<!ENTITY nine2 \"&nine;&nine;\">\n"
-       "]>\n"
-       "<root>&nine2;&nine2;&nine2;</root>",
-       NULL, NULL,
-       sizeof(XML_Char) * 3 /* calls to &nine2; */ * 2 /* calls to &nine; */
-           * (strlen("&nine;") + strlen("123456789")),
-       filled_later},
-      {"<!DOCTYPE r [\n"
-       "  <!ENTITY five SYSTEM 'first.ent'>\n"
-       "]>\n"
-       "<r>&five;</r>",
-       "12345", NULL, 0, filled_later},
-
-      /* Parameter entities */
-      {"<!DOCTYPE r [\n"
-       "<!ENTITY % comment \"<!---->\">\n"
-       "%comment;\n"
-       "]>\n"
-       "<r/>",
-       NULL, NULL, sizeof(XML_Char) * strlen("<!---->"), filled_later},
-      {"<!DOCTYPE r [\n"
-       "<!ENTITY % ninedef \"&#60;!ENTITY nine &#34;123456789&#34;&#62;\">\n"
-       "%ninedef;\n"
-       "]>\n"
-       "<r>&nine;</r>",
-       NULL, NULL,
-       sizeof(XML_Char)
-           * (strlen("<!ENTITY nine \"123456789\">") + strlen("123456789")),
-       filled_later},
-      {"<!DOCTYPE r [\n"
-       "<!ENTITY % comment \"<!--1-->\">\n"
-       "<!ENTITY % comment2 \"&#37;comment;<!--22-->&#37;comment;\">\n"
-       "%comment2;\n"
-       "]>\n"
-       "<r/>\n",
-       NULL, NULL,
-       sizeof(XML_Char)
-           * (strlen("%comment;<!--22-->%comment;") + 2 * strlen("<!--1-->")),
-       filled_later},
-      {"<!DOCTYPE r [\n"
-       "  <!ENTITY % five \"12345\">\n"
-       "  <!ENTITY % five2def \"&#60;!ENTITY five2 &#34;[&#37;five;][&#37;five;]]]]&#34;&#62;\">\n"
-       "  %five2def;\n"
-       "]>\n"
-       "<r>&five2;</r>",
-       NULL, NULL, /* from "%five2def;": */
-       sizeof(XML_Char)
-           * (strlen("<!ENTITY five2 \"[%five;][%five;]]]]\">")
-              + 2 /* calls to "%five;" */ * strlen("12345")
-              + /* from "&five2;": */ strlen("[12345][12345]]]]")),
-       filled_later},
-      {"<!DOCTYPE r SYSTEM \"first.ent\">\n"
-       "<r/>",
-       "<!ENTITY % comment '<!--1-->'>\n"
-       "<!ENTITY % comment2 '<!--22-->%comment;<!--22-->%comment;<!--22-->'>\n"
-       "%comment2;",
-       NULL,
-       sizeof(XML_Char)
-           * (strlen("<!--22-->%comment;<!--22-->%comment;<!--22-->")
-              + 2 /* calls to "%comment;" */ * strlen("<!---->")),
-       filled_later},
-      {"<!DOCTYPE r SYSTEM 'first.ent'>\n"
-       "<r/>",
-       "<!ENTITY % e1 PUBLIC 'foo' 'second.ent'>\n"
-       "<!ENTITY % e2 '<!--22-->%e1;<!--22-->'>\n"
-       "%e2;\n",
-       "<!--1-->", sizeof(XML_Char) * strlen("<!--22--><!--1--><!--22-->"),
-       filled_later},
-      {
-          "<!DOCTYPE r SYSTEM 'first.ent'>\n"
-          "<r/>",
-          "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
-          "<!ENTITY % e2 '%e1;'>",
-          "<?xml version='1.0' encoding='utf-8'?>\n"
-          "hello\n"
-          "xml" /* without trailing newline! */,
-          0,
-          filled_later,
-      },
-      {
-          "<!DOCTYPE r SYSTEM 'first.ent'>\n"
-          "<r/>",
-          "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
-          "<!ENTITY % e2 '%e1;'>",
-          "<?xml version='1.0' encoding='utf-8'?>\n"
-          "hello\n"
-          "xml\n" /* with trailing newline! */,
-          0,
-          filled_later,
-      },
-      {"<!DOCTYPE doc SYSTEM 'first.ent'>\n"
-       "<doc></doc>\n",
-       "<!ELEMENT doc EMPTY>\n"
-       "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
-       "<!ENTITY % e2 '%e1;'>\n"
-       "%e1;\n",
-       "\xEF\xBB\xBF<!ATTLIST doc a1 CDATA 'value'>" /* UTF-8 BOM */,
-       strlen("\xEF\xBB\xBF<!ATTLIST doc a1 CDATA 'value'>"), filled_later},
-      {"<!DOCTYPE r [\n"
-       "  <!ENTITY five SYSTEM 'first.ent'>\n"
-       "]>\n"
-       "<r>&five;</r>",
-       "\xEF\xBB\xBF" /* UTF-8 BOM */, NULL, 0, filled_later},
-  };
-
-  const size_t countCases = sizeof(cases) / sizeof(cases[0]);
-  size_t u = 0;
-  for (; u < countCases; u++) {
-    size_t v = 0;
-    for (; v < 2; v++) {
-      const XML_Bool singleBytesWanted = (v == 0) ? XML_FALSE : XML_TRUE;
-      const unsigned long long expectedCountBytesDirect
-          = strlen(cases[u].primaryText);
-      const unsigned long long expectedCountBytesIndirect
-          = (cases[u].firstExternalText ? strlen(cases[u].firstExternalText)
-                                        : 0)
-            + (cases[u].secondExternalText ? strlen(cases[u].secondExternalText)
-                                           : 0)
-            + cases[u].expectedCountBytesIndirectExtra;
-
-      XML_Parser parser = XML_ParserCreate(NULL);
-      XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-      if (cases[u].firstExternalText) {
-        XML_SetExternalEntityRefHandler(parser,
-                                        accounting_external_entity_ref_handler);
-        XML_SetUserData(parser, (void *)&cases[u]);
-        cases[u].singleBytesWanted = singleBytesWanted;
-      }
-
-      const XmlParseFunction xmlParseFunction
-          = singleBytesWanted ? _XML_Parse_SINGLE_BYTES : XML_Parse;
-
-      enum XML_Status status
-          = xmlParseFunction(parser, cases[u].primaryText,
-                             (int)strlen(cases[u].primaryText), XML_TRUE);
-      if (status != XML_STATUS_OK) {
-        _xml_failure(parser, __FILE__, __LINE__);
-      }
-
-      const unsigned long long actualCountBytesDirect
-          = testingAccountingGetCountBytesDirect(parser);
-      const unsigned long long actualCountBytesIndirect
-          = testingAccountingGetCountBytesIndirect(parser);
-
-      XML_ParserFree(parser);
-
-      if (actualCountBytesDirect != expectedCountBytesDirect) {
-        fprintf(
-            stderr,
-            "Document " EXPAT_FMT_SIZE_T("") " of " EXPAT_FMT_SIZE_T("") ", %s: Expected " EXPAT_FMT_ULL(
-                "") " count direct bytes, got " EXPAT_FMT_ULL("") " instead.\n",
-            u + 1, countCases, singleBytesWanted ? "single bytes" : "chunks",
-            expectedCountBytesDirect, actualCountBytesDirect);
-        fail("Count of direct bytes is off");
-      }
-
-      if (actualCountBytesIndirect != expectedCountBytesIndirect) {
-        fprintf(
-            stderr,
-            "Document " EXPAT_FMT_SIZE_T("") " of " EXPAT_FMT_SIZE_T("") ", %s: Expected " EXPAT_FMT_ULL(
-                "") " count indirect bytes, got " EXPAT_FMT_ULL("") " instead.\n",
-            u + 1, countCases, singleBytesWanted ? "single bytes" : "chunks",
-            expectedCountBytesIndirect, actualCountBytesIndirect);
-        fail("Count of indirect bytes is off");
-      }
-    }
-  }
-}
-END_TEST
-
-static float
-portableNAN(void) {
-  return strtof("nan", NULL);
-}
-
-static float
-portableINFINITY(void) {
-  return strtof("infinity", NULL);
-}
-
-START_TEST(test_billion_laughs_attack_protection_api) {
-  XML_Parser parserWithoutParent = XML_ParserCreate(NULL);
-  XML_Parser parserWithParent
-      = XML_ExternalEntityParserCreate(parserWithoutParent, NULL, NULL);
-  if (parserWithoutParent == NULL)
-    fail("parserWithoutParent is NULL");
-  if (parserWithParent == NULL)
-    fail("parserWithParent is NULL");
-
-  // XML_SetBillionLaughsAttackProtectionMaximumAmplification, error cases
-  if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(NULL, 123.0f)
-      == XML_TRUE)
-    fail("Call with NULL parser is NOT supposed to succeed");
-  if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(parserWithParent,
-                                                               123.0f)
-      == XML_TRUE)
-    fail("Call with non-root parser is NOT supposed to succeed");
-  if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
-          parserWithoutParent, portableNAN())
-      == XML_TRUE)
-    fail("Call with NaN limit is NOT supposed to succeed");
-  if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
-          parserWithoutParent, -1.0f)
-      == XML_TRUE)
-    fail("Call with negative limit is NOT supposed to succeed");
-  if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
-          parserWithoutParent, 0.9f)
-      == XML_TRUE)
-    fail("Call with positive limit <1.0 is NOT supposed to succeed");
-
-  // XML_SetBillionLaughsAttackProtectionMaximumAmplification, success cases
-  if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
-          parserWithoutParent, 1.0f)
-      == XML_FALSE)
-    fail("Call with positive limit >=1.0 is supposed to succeed");
-  if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
-          parserWithoutParent, 123456.789f)
-      == XML_FALSE)
-    fail("Call with positive limit >=1.0 is supposed to succeed");
-  if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
-          parserWithoutParent, portableINFINITY())
-      == XML_FALSE)
-    fail("Call with positive limit >=1.0 is supposed to succeed");
-
-  // XML_SetBillionLaughsAttackProtectionActivationThreshold, error cases
-  if (XML_SetBillionLaughsAttackProtectionActivationThreshold(NULL, 123)
-      == XML_TRUE)
-    fail("Call with NULL parser is NOT supposed to succeed");
-  if (XML_SetBillionLaughsAttackProtectionActivationThreshold(parserWithParent,
-                                                              123)
-      == XML_TRUE)
-    fail("Call with non-root parser is NOT supposed to succeed");
-
-  // XML_SetBillionLaughsAttackProtectionActivationThreshold, success cases
-  if (XML_SetBillionLaughsAttackProtectionActivationThreshold(
-          parserWithoutParent, 123)
-      == XML_FALSE)
-    fail("Call with non-NULL parentless parser is supposed to succeed");
+#include <stdio.h>
+#include <string.h>
 
-  XML_ParserFree(parserWithParent);
-  XML_ParserFree(parserWithoutParent);
-}
-END_TEST
+#include "expat.h"
+#include "internal.h"
+#include "minicheck.h"
+#include "common.h"
 
-START_TEST(test_helper_unsigned_char_to_printable) {
-  // Smoke test
-  unsigned char uc = 0;
-  for (; uc < (unsigned char)-1; uc++) {
-    const char *const printable = unsignedCharToPrintable(uc);
-    if (printable == NULL)
-      fail("unsignedCharToPrintable returned NULL");
-    if (strlen(printable) < (size_t)1)
-      fail("unsignedCharToPrintable returned empty string");
-  }
+#include "basic_tests.h"
+#include "ns_tests.h"
+#include "misc_tests.h"
+#include "alloc_tests.h"
+#include "nsalloc_tests.h"
+#include "acc_tests.h"
 
-  // Two concrete samples
-  if (strcmp(unsignedCharToPrintable('A'), "A") != 0)
-    fail("unsignedCharToPrintable result mistaken");
-  if (strcmp(unsignedCharToPrintable('\\'), "\\\\") != 0)
-    fail("unsignedCharToPrintable result mistaken");
-}
-END_TEST
-#endif // defined(XML_DTD)
+XML_Parser g_parser = NULL;
 
 static Suite *
 make_suite(void) {
   Suite *s = suite_create("basic");
-  TCase *tc_basic = tcase_create("basic tests");
-  TCase *tc_namespace = tcase_create("XML namespaces");
-  TCase *tc_misc = tcase_create("miscellaneous tests");
-  TCase *tc_alloc = tcase_create("allocation tests");
-  TCase *tc_nsalloc = tcase_create("namespace allocation tests");
-#if defined(XML_DTD)
-  TCase *tc_accounting = tcase_create("accounting tests");
-#endif
-
-  suite_add_tcase(s, tc_basic);
-  tcase_add_checked_fixture(tc_basic, basic_setup, basic_teardown);
-  tcase_add_test(tc_basic, test_nul_byte);
-  tcase_add_test(tc_basic, test_u0000_char);
-  tcase_add_test(tc_basic, test_siphash_self);
-  tcase_add_test(tc_basic, test_siphash_spec);
-  tcase_add_test(tc_basic, test_bom_utf8);
-  tcase_add_test(tc_basic, test_bom_utf16_be);
-  tcase_add_test(tc_basic, test_bom_utf16_le);
-  tcase_add_test(tc_basic, test_nobom_utf16_le);
-  tcase_add_test(tc_basic, test_illegal_utf8);
-  tcase_add_test(tc_basic, test_utf8_auto_align);
-  tcase_add_test(tc_basic, test_utf16);
-  tcase_add_test(tc_basic, test_utf16_le_epilog_newline);
-  tcase_add_test(tc_basic, test_not_utf16);
-  tcase_add_test(tc_basic, test_bad_encoding);
-  tcase_add_test(tc_basic, test_latin1_umlauts);
-  tcase_add_test(tc_basic, test_long_utf8_character);
-  tcase_add_test(tc_basic, test_long_latin1_attribute);
-  tcase_add_test(tc_basic, test_long_ascii_attribute);
-  /* Regression test for SF bug #491986. */
-  tcase_add_test(tc_basic, test_danish_latin1);
-  /* Regression test for SF bug #514281. */
-  tcase_add_test(tc_basic, test_french_charref_hexidecimal);
-  tcase_add_test(tc_basic, test_french_charref_decimal);
-  tcase_add_test(tc_basic, test_french_latin1);
-  tcase_add_test(tc_basic, test_french_utf8);
-  tcase_add_test(tc_basic, test_utf8_false_rejection);
-  tcase_add_test(tc_basic, test_line_number_after_parse);
-  tcase_add_test(tc_basic, test_column_number_after_parse);
-  tcase_add_test(tc_basic, test_line_and_column_numbers_inside_handlers);
-  tcase_add_test(tc_basic, test_line_number_after_error);
-  tcase_add_test(tc_basic, test_column_number_after_error);
-  tcase_add_test(tc_basic, test_really_long_lines);
-  tcase_add_test(tc_basic, test_really_long_encoded_lines);
-  tcase_add_test(tc_basic, test_end_element_events);
-  tcase_add_test(tc_basic, test_attr_whitespace_normalization);
-  tcase_add_test(tc_basic, test_xmldecl_misplaced);
-  tcase_add_test(tc_basic, test_xmldecl_invalid);
-  tcase_add_test(tc_basic, test_xmldecl_missing_attr);
-  tcase_add_test(tc_basic, test_xmldecl_missing_value);
-  tcase_add_test(tc_basic, test_unknown_encoding_internal_entity);
-  tcase_add_test(tc_basic, test_unrecognised_encoding_internal_entity);
-  tcase_add_test(tc_basic, test_wfc_undeclared_entity_unread_external_subset);
-  tcase_add_test(tc_basic, test_wfc_undeclared_entity_no_external_subset);
-  tcase_add_test(tc_basic, test_wfc_undeclared_entity_standalone);
-  tcase_add_test(tc_basic, test_wfc_undeclared_entity_with_external_subset);
-  tcase_add_test(tc_basic, test_not_standalone_handler_reject);
-  tcase_add_test(tc_basic, test_not_standalone_handler_accept);
-  tcase_add_test(tc_basic,
-                 test_wfc_undeclared_entity_with_external_subset_standalone);
-  tcase_add_test(tc_basic, test_entity_with_external_subset_unless_standalone);
-  tcase_add_test(tc_basic, test_wfc_no_recursive_entity_refs);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_set_encoding);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_no_handler);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_set_bom);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_bad_encoding);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_bad_encoding_2);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_invalid_parse);
-  tcase_add_test__ifdef_xml_dtd(tc_basic,
-                                test_ext_entity_invalid_suspended_parse);
-  tcase_add_test(tc_basic, test_dtd_default_handling);
-  tcase_add_test(tc_basic, test_dtd_attr_handling);
-  tcase_add_test(tc_basic, test_empty_ns_without_namespaces);
-  tcase_add_test(tc_basic, test_ns_in_attribute_default_without_namespaces);
-  tcase_add_test(tc_basic, test_stop_parser_between_char_data_calls);
-  tcase_add_test(tc_basic, test_suspend_parser_between_char_data_calls);
-  tcase_add_test(tc_basic, test_repeated_stop_parser_between_char_data_calls);
-  tcase_add_test(tc_basic, test_good_cdata_ascii);
-  tcase_add_test(tc_basic, test_good_cdata_utf16);
-  tcase_add_test(tc_basic, test_good_cdata_utf16_le);
-  tcase_add_test(tc_basic, test_long_cdata_utf16);
-  tcase_add_test(tc_basic, test_multichar_cdata_utf16);
-  tcase_add_test(tc_basic, test_utf16_bad_surrogate_pair);
-  tcase_add_test(tc_basic, test_bad_cdata);
-  tcase_add_test(tc_basic, test_bad_cdata_utf16);
-  tcase_add_test(tc_basic, test_stop_parser_between_cdata_calls);
-  tcase_add_test(tc_basic, test_suspend_parser_between_cdata_calls);
-  tcase_add_test(tc_basic, test_memory_allocation);
-  tcase_add_test(tc_basic, test_default_current);
-  tcase_add_test(tc_basic, test_dtd_elements);
-  tcase_add_test(tc_basic, test_dtd_elements_nesting);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_set_foreign_dtd);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_foreign_dtd_not_standalone);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_invalid_foreign_dtd);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_foreign_dtd_with_doctype);
-  tcase_add_test__ifdef_xml_dtd(tc_basic,
-                                test_foreign_dtd_without_external_subset);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_empty_foreign_dtd);
-  tcase_add_test(tc_basic, test_set_base);
-  tcase_add_test(tc_basic, test_attributes);
-  tcase_add_test(tc_basic, test_reset_in_entity);
-  tcase_add_test(tc_basic, test_resume_invalid_parse);
-  tcase_add_test(tc_basic, test_resume_resuspended);
-  tcase_add_test(tc_basic, test_cdata_default);
-  tcase_add_test(tc_basic, test_subordinate_reset);
-  tcase_add_test(tc_basic, test_subordinate_suspend);
-  tcase_add_test(tc_basic, test_subordinate_xdecl_suspend);
-  tcase_add_test(tc_basic, test_subordinate_xdecl_abort);
-  tcase_add_test(tc_basic, test_explicit_encoding);
-  tcase_add_test(tc_basic, test_trailing_cr);
-  tcase_add_test(tc_basic, test_ext_entity_trailing_cr);
-  tcase_add_test(tc_basic, test_trailing_rsqb);
-  tcase_add_test(tc_basic, test_ext_entity_trailing_rsqb);
-  tcase_add_test(tc_basic, test_ext_entity_good_cdata);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_user_parameters);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_ref_parameter);
-  tcase_add_test(tc_basic, test_empty_parse);
-  tcase_add_test(tc_basic, test_get_buffer_1);
-  tcase_add_test(tc_basic, test_get_buffer_2);
-#if defined(XML_CONTEXT_BYTES)
-  tcase_add_test(tc_basic, test_get_buffer_3_overflow);
-#endif
-  tcase_add_test(tc_basic, test_byte_info_at_end);
-  tcase_add_test(tc_basic, test_byte_info_at_error);
-  tcase_add_test(tc_basic, test_byte_info_at_cdata);
-  tcase_add_test(tc_basic, test_predefined_entities);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_invalid_tag_in_dtd);
-  tcase_add_test(tc_basic, test_not_predefined_entities);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ignore_section);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ignore_section_utf16);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ignore_section_utf16_be);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_bad_ignore_section);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_external_entity_values);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_not_standalone);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_value_abort);
-  tcase_add_test(tc_basic, test_bad_public_doctype);
-  tcase_add_test(tc_basic, test_attribute_enum_value);
-  tcase_add_test(tc_basic, test_predefined_entity_redefinition);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_dtd_stop_processing);
-  tcase_add_test(tc_basic, test_public_notation_no_sysid);
-  tcase_add_test(tc_basic, test_nested_groups);
-  tcase_add_test(tc_basic, test_group_choice);
-  tcase_add_test(tc_basic, test_standalone_parameter_entity);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_skipped_parameter_entity);
-  tcase_add_test__ifdef_xml_dtd(tc_basic,
-                                test_recursive_external_parameter_entity);
-  tcase_add_test(tc_basic, test_undefined_ext_entity_in_external_dtd);
-  tcase_add_test(tc_basic, test_suspend_xdecl);
-  tcase_add_test(tc_basic, test_abort_epilog);
-  tcase_add_test(tc_basic, test_abort_epilog_2);
-  tcase_add_test(tc_basic, test_suspend_epilog);
-  tcase_add_test(tc_basic, test_suspend_in_sole_empty_tag);
-  tcase_add_test(tc_basic, test_unfinished_epilog);
-  tcase_add_test(tc_basic, test_partial_char_in_epilog);
-  tcase_add_test(tc_basic, test_hash_collision);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_suspend_resume_internal_entity);
-  tcase_add_test__ifdef_xml_dtd(tc_basic,
-                                test_suspend_resume_internal_entity_issue_629);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_resume_entity_with_syntax_error);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_suspend_resume_parameter_entity);
-  tcase_add_test(tc_basic, test_restart_on_error);
-  tcase_add_test(tc_basic, test_reject_lt_in_attribute_value);
-  tcase_add_test(tc_basic, test_reject_unfinished_param_in_att_value);
-  tcase_add_test(tc_basic, test_trailing_cr_in_att_value);
-  tcase_add_test(tc_basic, test_standalone_internal_entity);
-  tcase_add_test(tc_basic, test_skipped_external_entity);
-  tcase_add_test(tc_basic, test_skipped_null_loaded_ext_entity);
-  tcase_add_test(tc_basic, test_skipped_unloaded_ext_entity);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_param_entity_with_trailing_cr);
-  tcase_add_test(tc_basic, test_invalid_character_entity);
-  tcase_add_test(tc_basic, test_invalid_character_entity_2);
-  tcase_add_test(tc_basic, test_invalid_character_entity_3);
-  tcase_add_test(tc_basic, test_invalid_character_entity_4);
-  tcase_add_test(tc_basic, test_pi_handled_in_default);
-  tcase_add_test(tc_basic, test_comment_handled_in_default);
-  tcase_add_test(tc_basic, test_pi_yml);
-  tcase_add_test(tc_basic, test_pi_xnl);
-  tcase_add_test(tc_basic, test_pi_xmm);
-  tcase_add_test(tc_basic, test_utf16_pi);
-  tcase_add_test(tc_basic, test_utf16_be_pi);
-  tcase_add_test(tc_basic, test_utf16_be_comment);
-  tcase_add_test(tc_basic, test_utf16_le_comment);
-  tcase_add_test(tc_basic, test_missing_encoding_conversion_fn);
-  tcase_add_test(tc_basic, test_failing_encoding_conversion_fn);
-  tcase_add_test(tc_basic, test_unknown_encoding_success);
-  tcase_add_test(tc_basic, test_unknown_encoding_bad_name);
-  tcase_add_test(tc_basic, test_unknown_encoding_bad_name_2);
-  tcase_add_test(tc_basic, test_unknown_encoding_long_name_1);
-  tcase_add_test(tc_basic, test_unknown_encoding_long_name_2);
-  tcase_add_test(tc_basic, test_invalid_unknown_encoding);
-  tcase_add_test(tc_basic, test_unknown_ascii_encoding_ok);
-  tcase_add_test(tc_basic, test_unknown_ascii_encoding_fail);
-  tcase_add_test(tc_basic, test_unknown_encoding_invalid_length);
-  tcase_add_test(tc_basic, test_unknown_encoding_invalid_topbit);
-  tcase_add_test(tc_basic, test_unknown_encoding_invalid_surrogate);
-  tcase_add_test(tc_basic, test_unknown_encoding_invalid_high);
-  tcase_add_test(tc_basic, test_unknown_encoding_invalid_attr_value);
-  tcase_add_test(tc_basic, test_ext_entity_latin1_utf16le_bom);
-  tcase_add_test(tc_basic, test_ext_entity_latin1_utf16be_bom);
-  tcase_add_test(tc_basic, test_ext_entity_latin1_utf16le_bom2);
-  tcase_add_test(tc_basic, test_ext_entity_latin1_utf16be_bom2);
-  tcase_add_test(tc_basic, test_ext_entity_utf16_be);
-  tcase_add_test(tc_basic, test_ext_entity_utf16_le);
-  tcase_add_test(tc_basic, test_ext_entity_utf16_unknown);
-  tcase_add_test(tc_basic, test_ext_entity_utf8_non_bom);
-  tcase_add_test(tc_basic, test_utf8_in_cdata_section);
-  tcase_add_test(tc_basic, test_utf8_in_cdata_section_2);
-  tcase_add_test(tc_basic, test_utf8_in_start_tags);
-  tcase_add_test(tc_basic, test_trailing_spaces_in_elements);
-  tcase_add_test(tc_basic, test_utf16_attribute);
-  tcase_add_test(tc_basic, test_utf16_second_attr);
-  tcase_add_test(tc_basic, test_attr_after_solidus);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_utf16_pe);
-  tcase_add_test(tc_basic, test_bad_attr_desc_keyword);
-  tcase_add_test(tc_basic, test_bad_attr_desc_keyword_utf16);
-  tcase_add_test(tc_basic, test_bad_doctype);
-  tcase_add_test(tc_basic, test_bad_doctype_utf8);
-  tcase_add_test(tc_basic, test_bad_doctype_utf16);
-  tcase_add_test(tc_basic, test_bad_doctype_plus);
-  tcase_add_test(tc_basic, test_bad_doctype_star);
-  tcase_add_test(tc_basic, test_bad_doctype_query);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_unknown_encoding_bad_ignore);
-  tcase_add_test(tc_basic, test_entity_in_utf16_be_attr);
-  tcase_add_test(tc_basic, test_entity_in_utf16_le_attr);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_entity_public_utf16_be);
-  tcase_add_test__ifdef_xml_dtd(tc_basic, test_entity_public_utf16_le);
-  tcase_add_test(tc_basic, test_short_doctype);
-  tcase_add_test(tc_basic, test_short_doctype_2);
-  tcase_add_test(tc_basic, test_short_doctype_3);
-  tcase_add_test(tc_basic, test_long_doctype);
-  tcase_add_test(tc_basic, test_bad_entity);
-  tcase_add_test(tc_basic, test_bad_entity_2);
-  tcase_add_test(tc_basic, test_bad_entity_3);
-  tcase_add_test(tc_basic, test_bad_entity_4);
-  tcase_add_test(tc_basic, test_bad_notation);
-  tcase_add_test(tc_basic, test_default_doctype_handler);
-  tcase_add_test(tc_basic, test_empty_element_abort);
-  tcase_add_test__ifdef_xml_dtd(tc_basic,
-                                test_pool_integrity_with_unfinished_attr);
-  tcase_add_test(tc_basic, test_nested_entity_suspend);
 
-  suite_add_tcase(s, tc_namespace);
-  tcase_add_checked_fixture(tc_namespace, namespace_setup, namespace_teardown);
-  tcase_add_test(tc_namespace, test_return_ns_triplet);
-  tcase_add_test(tc_namespace, test_ns_tagname_overwrite);
-  tcase_add_test(tc_namespace, test_ns_tagname_overwrite_triplet);
-  tcase_add_test(tc_namespace, test_start_ns_clears_start_element);
-  tcase_add_test__ifdef_xml_dtd(tc_namespace,
-                                test_default_ns_from_ext_subset_and_ext_ge);
-  tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_1);
-  tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_2);
-  tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_3);
-  tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_4);
-  tcase_add_test(tc_namespace, test_ns_unbound_prefix);
-  tcase_add_test(tc_namespace, test_ns_default_with_empty_uri);
-  tcase_add_test(tc_namespace, test_ns_duplicate_attrs_diff_prefixes);
-  tcase_add_test(tc_namespace, test_ns_duplicate_hashes);
-  tcase_add_test(tc_namespace, test_ns_unbound_prefix_on_attribute);
-  tcase_add_test(tc_namespace, test_ns_unbound_prefix_on_element);
-  tcase_add_test(tc_namespace, test_ns_parser_reset);
-  tcase_add_test(tc_namespace, test_ns_long_element);
-  tcase_add_test(tc_namespace, test_ns_mixed_prefix_atts);
-  tcase_add_test(tc_namespace, test_ns_extend_uri_buffer);
-  tcase_add_test(tc_namespace, test_ns_reserved_attributes);
-  tcase_add_test(tc_namespace, test_ns_reserved_attributes_2);
-  tcase_add_test(tc_namespace, test_ns_extremely_long_prefix);
-  tcase_add_test(tc_namespace, test_ns_unknown_encoding_success);
-  tcase_add_test(tc_namespace, test_ns_double_colon);
-  tcase_add_test(tc_namespace, test_ns_double_colon_element);
-  tcase_add_test(tc_namespace, test_ns_bad_attr_leafname);
-  tcase_add_test(tc_namespace, test_ns_bad_element_leafname);
-  tcase_add_test(tc_namespace, test_ns_utf16_leafname);
-  tcase_add_test(tc_namespace, test_ns_utf16_element_leafname);
-  tcase_add_test(tc_namespace, test_ns_utf16_doctype);
-  tcase_add_test(tc_namespace, test_ns_invalid_doctype);
-  tcase_add_test(tc_namespace, test_ns_double_colon_doctype);
-  tcase_add_test(tc_namespace, test_ns_separator_in_uri);
-
-  suite_add_tcase(s, tc_misc);
-  tcase_add_checked_fixture(tc_misc, NULL, basic_teardown);
-  tcase_add_test(tc_misc, test_misc_alloc_create_parser);
-  tcase_add_test(tc_misc, test_misc_alloc_create_parser_with_encoding);
-  tcase_add_test(tc_misc, test_misc_null_parser);
-  tcase_add_test(tc_misc, test_misc_error_string);
-  tcase_add_test(tc_misc, test_misc_version);
-  tcase_add_test(tc_misc, test_misc_features);
-  tcase_add_test(tc_misc, test_misc_attribute_leak);
-  tcase_add_test(tc_misc, test_misc_utf16le);
-  tcase_add_test(tc_misc, test_misc_stop_during_end_handler_issue_240_1);
-  tcase_add_test(tc_misc, test_misc_stop_during_end_handler_issue_240_2);
-  tcase_add_test__ifdef_xml_dtd(
-      tc_misc, test_misc_deny_internal_entity_closing_doctype_issue_317);
-  tcase_add_test(tc_misc, test_misc_tag_mismatch_reset_leak);
-
-  suite_add_tcase(s, tc_alloc);
-  tcase_add_checked_fixture(tc_alloc, alloc_setup, alloc_teardown);
-  tcase_add_test(tc_alloc, test_alloc_parse_xdecl);
-  tcase_add_test(tc_alloc, test_alloc_parse_xdecl_2);
-  tcase_add_test(tc_alloc, test_alloc_parse_pi);
-  tcase_add_test(tc_alloc, test_alloc_parse_pi_2);
-  tcase_add_test(tc_alloc, test_alloc_parse_pi_3);
-  tcase_add_test(tc_alloc, test_alloc_parse_comment);
-  tcase_add_test(tc_alloc, test_alloc_parse_comment_2);
-  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_create_external_parser);
-  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_run_external_parser);
-  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_dtd_copy_default_atts);
-  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_external_entity);
-  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_ext_entity_set_encoding);
-  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_internal_entity);
-  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_dtd_default_handling);
-  tcase_add_test(tc_alloc, test_alloc_explicit_encoding);
-  tcase_add_test(tc_alloc, test_alloc_set_base);
-  tcase_add_test(tc_alloc, test_alloc_realloc_buffer);
-  tcase_add_test(tc_alloc, test_alloc_ext_entity_realloc_buffer);
-  tcase_add_test(tc_alloc, test_alloc_realloc_many_attributes);
-  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_public_entity_value);
-  tcase_add_test__ifdef_xml_dtd(tc_alloc,
-                                test_alloc_realloc_subst_public_entity_value);
-  tcase_add_test(tc_alloc, test_alloc_parse_public_doctype);
-  tcase_add_test(tc_alloc, test_alloc_parse_public_doctype_long_name);
-  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_set_foreign_dtd);
-  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_attribute_enum_value);
-  tcase_add_test__ifdef_xml_dtd(tc_alloc,
-                                test_alloc_realloc_attribute_enum_value);
-  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_realloc_implied_attribute);
-  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_realloc_default_attribute);
-  tcase_add_test(tc_alloc, test_alloc_notation);
-  tcase_add_test(tc_alloc, test_alloc_public_notation);
-  tcase_add_test(tc_alloc, test_alloc_system_notation);
-  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_nested_groups);
-  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_realloc_nested_groups);
-  tcase_add_test(tc_alloc, test_alloc_large_group);
-  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_realloc_group_choice);
-  tcase_add_test(tc_alloc, test_alloc_pi_in_epilog);
-  tcase_add_test(tc_alloc, test_alloc_comment_in_epilog);
-  tcase_add_test__ifdef_xml_dtd(tc_alloc,
-                                test_alloc_realloc_long_attribute_value);
-  tcase_add_test(tc_alloc, test_alloc_attribute_whitespace);
-  tcase_add_test(tc_alloc, test_alloc_attribute_predefined_entity);
-  tcase_add_test(tc_alloc, test_alloc_long_attr_default_with_char_ref);
-  tcase_add_test(tc_alloc, test_alloc_long_attr_value);
-  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_nested_entities);
-  tcase_add_test__ifdef_xml_dtd(tc_alloc,
-                                test_alloc_realloc_param_entity_newline);
-  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_realloc_ce_extends_pe);
-  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_realloc_attributes);
-  tcase_add_test(tc_alloc, test_alloc_long_doc_name);
-  tcase_add_test(tc_alloc, test_alloc_long_base);
-  tcase_add_test(tc_alloc, test_alloc_long_public_id);
-  tcase_add_test(tc_alloc, test_alloc_long_entity_value);
-  tcase_add_test(tc_alloc, test_alloc_long_notation);
-  tcase_add_test__ifdef_xml_dtd(
-      tc_alloc, test_alloc_reset_after_external_entity_parser_create_fail);
-
-  suite_add_tcase(s, tc_nsalloc);
-  tcase_add_checked_fixture(tc_nsalloc, nsalloc_setup, nsalloc_teardown);
-  tcase_add_test(tc_nsalloc, test_nsalloc_xmlns);
-  tcase_add_test(tc_nsalloc, test_nsalloc_parse_buffer);
-  tcase_add_test(tc_nsalloc, test_nsalloc_long_prefix);
-  tcase_add_test(tc_nsalloc, test_nsalloc_long_uri);
-  tcase_add_test(tc_nsalloc, test_nsalloc_long_attr);
-  tcase_add_test(tc_nsalloc, test_nsalloc_long_attr_prefix);
-  tcase_add_test(tc_nsalloc, test_nsalloc_realloc_attributes);
-  tcase_add_test(tc_nsalloc, test_nsalloc_long_element);
-  tcase_add_test(tc_nsalloc, test_nsalloc_realloc_binding_uri);
-  tcase_add_test(tc_nsalloc, test_nsalloc_realloc_long_prefix);
-  tcase_add_test(tc_nsalloc, test_nsalloc_realloc_longer_prefix);
-  tcase_add_test(tc_nsalloc, test_nsalloc_long_namespace);
-  tcase_add_test(tc_nsalloc, test_nsalloc_less_long_namespace);
-  tcase_add_test(tc_nsalloc, test_nsalloc_long_context);
-  tcase_add_test(tc_nsalloc, test_nsalloc_realloc_long_context);
-  tcase_add_test(tc_nsalloc, test_nsalloc_realloc_long_context_2);
-  tcase_add_test(tc_nsalloc, test_nsalloc_realloc_long_context_3);
-  tcase_add_test(tc_nsalloc, test_nsalloc_realloc_long_context_4);
-  tcase_add_test(tc_nsalloc, test_nsalloc_realloc_long_context_5);
-  tcase_add_test(tc_nsalloc, test_nsalloc_realloc_long_context_6);
-  tcase_add_test(tc_nsalloc, test_nsalloc_realloc_long_context_7);
-  tcase_add_test(tc_nsalloc, test_nsalloc_realloc_long_ge_name);
-  tcase_add_test(tc_nsalloc, test_nsalloc_realloc_long_context_in_dtd);
-  tcase_add_test(tc_nsalloc, test_nsalloc_long_default_in_ext);
-  tcase_add_test(tc_nsalloc, test_nsalloc_long_systemid_in_ext);
-  tcase_add_test(tc_nsalloc, test_nsalloc_prefixed_element);
-
-#if defined(XML_DTD)
-  suite_add_tcase(s, tc_accounting);
-  tcase_add_test(tc_accounting, test_accounting_precision);
-  tcase_add_test(tc_accounting, test_billion_laughs_attack_protection_api);
-  tcase_add_test(tc_accounting, test_helper_unsigned_char_to_printable);
+  make_basic_test_case(s);
+  make_namespace_test_case(s);
+  make_miscellaneous_test_case(s);
+  make_alloc_test_case(s);
+  make_nsalloc_test_case(s);
+#if XML_GE == 1
+  make_accounting_test_case(s);
 #endif
 
   return s;
@@ -12497,9 +84,6 @@ main(int argc, char *argv[]) {
   Suite *s = make_suite();
   SRunner *sr = srunner_create(s);
 
-  /* run the tests for internal helper functions */
-  testhelper_is_whitespace_normalized();
-
   for (i = 1; i < argc; ++i) {
     char *opt = argv[i];
     if (strcmp(opt, "-v") == 0 || strcmp(opt, "--verbose") == 0)
@@ -12513,7 +97,18 @@ main(int argc, char *argv[]) {
   }
   if (verbosity != CK_SILENT)
     printf("Expat version: %" XML_FMT_STR "\n", XML_ExpatVersion());
-  srunner_run_all(sr, verbosity);
+
+  for (g_chunkSize = 0; g_chunkSize <= 5; g_chunkSize++) {
+    for (int enabled = 0; enabled <= 1; ++enabled) {
+      char context[100];
+      g_reparseDeferralEnabledDefault = enabled;
+      snprintf(context, sizeof(context), "chunksize=%d deferral=%d",
+               g_chunkSize, enabled);
+      context[sizeof(context) - 1] = '\0';
+      srunner_run_all(sr, context, verbosity);
+    }
+  }
+  srunner_summarize(sr, verbosity);
   nf = srunner_ntests_failed(sr);
   srunner_free(sr);
 
similarity index 91%
rename from tests/runtestspp.cpp
rename to tests/runtests_cxx.cpp
index 52f5305..3967145 100644 (file)
@@ -9,8 +9,8 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 2005 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
-   Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2005      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2017-2023 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
index d40e6c4..e311f1f 100644 (file)
@@ -7,7 +7,8 @@
                                  |_| XML parser
 
    Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
-   Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2023 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2022      Sean McBride <sean@rogue-research.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
    USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
+#if defined(NDEBUG)
+#  undef NDEBUG /* because test suite relies on assert(...) at the moment */
+#endif
+
 #include "expat_config.h"
 
 #include <assert.h>
@@ -56,7 +61,7 @@
 static XML_Char *
 xmlstrdup(const XML_Char *s) {
   size_t byte_count = (xcstrlen(s) + 1) * sizeof(XML_Char);
-  XML_Char *dup = malloc(byte_count);
+  XML_Char *const dup = (XML_Char *)malloc(byte_count);
 
   assert(dup != NULL);
   memcpy(dup, s, byte_count);
@@ -79,13 +84,13 @@ StructData_AddItem(StructData *storage, const XML_Char *s, int data0, int data1,
   assert(storage != NULL);
   assert(s != NULL);
   if (storage->count == storage->max_count) {
-    StructDataEntry *new;
+    StructDataEntry *new_entries;
 
     storage->max_count += STRUCT_EXTENSION_COUNT;
-    new = realloc(storage->entries,
-                  storage->max_count * sizeof(StructDataEntry));
-    assert(new != NULL);
-    storage->entries = new;
+    new_entries = (StructDataEntry *)realloc(
+        storage->entries, storage->max_count * sizeof(StructDataEntry));
+    assert(new_entries != NULL);
+    storage->entries = new_entries;
   }
 
   entry = &storage->entries[storage->count];
@@ -103,17 +108,17 @@ void
 StructData_CheckItems(StructData *storage, const StructDataEntry *expected,
                       int count) {
   char buffer[1024];
-  int i;
 
   assert(storage != NULL);
   assert(expected != NULL);
   if (count != storage->count) {
-    sprintf(buffer, "wrong number of entries: got %d, expected %d",
-            storage->count, count);
+    snprintf(buffer, sizeof(buffer),
+             "wrong number of entries: got %d, expected %d", storage->count,
+             count);
     StructData_Dispose(storage);
     fail(buffer);
   } else {
-    for (i = 0; i < count; i++) {
+    for (int i = 0; i < count; i++) {
       const StructDataEntry *got = &storage->entries[i];
       const StructDataEntry *want = &expected[i];
 
@@ -126,11 +131,11 @@ StructData_CheckItems(StructData *storage, const StructDataEntry *expected,
       } else {
         if (got->data0 != want->data0 || got->data1 != want->data1
             || got->data2 != want->data2) {
-          sprintf(buffer,
-                  "struct '%" XML_FMT_STR
-                  "' expected (%d,%d,%d), got (%d,%d,%d)",
-                  got->str, want->data0, want->data1, want->data2, got->data0,
-                  got->data1, got->data2);
+          snprintf(buffer, sizeof(buffer),
+                   "struct '%" XML_FMT_STR
+                   "' expected (%d,%d,%d), got (%d,%d,%d)",
+                   got->str, want->data0, want->data1, want->data2, got->data0,
+                   got->data1, got->data2);
           StructData_Dispose(storage);
           fail(buffer);
         }
diff --git a/tests/structdata_cxx.cpp b/tests/structdata_cxx.cpp
new file mode 100644 (file)
index 0000000..43448f6
--- /dev/null
@@ -0,0 +1,32 @@
+/* C++ compilation harness for the test suite.
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2023 Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "structdata.c"
index ce9f3bb..2a4c87e 100644 (file)
@@ -14,7 +14,7 @@
 ; Copyright (c) 2001      Tim Peters <tim.peters@gmail.com>
 ; Copyright (c) 2001-2005 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
 ; Copyright (c) 2006-2017 Karl Waclawek <karl@waclawek.net>
-; Copyright (c) 2007-2022 Sebastian Pipping <sebastian@pipping.org>
+; Copyright (c) 2007-2024 Sebastian Pipping <sebastian@pipping.org>
 ; Copyright (c) 2022      Johnny Jazeix <jazeix@gmail.com>
 ; Licensed under the MIT license:
 ;
@@ -37,7 +37,7 @@
 ; OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 ; USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-#define expatVer "2.5.0"
+#define expatVer "2.6.2"
 
 [Setup]
 AppName=Expat
@@ -77,7 +77,7 @@ Flags: ignoreversion; Source: doc\*.css;                    DestDir: "{app}\Doc"
 Flags: ignoreversion; Source: doc\*.xml;                    DestDir: "{app}\Doc"
 Flags: ignoreversion; Source: win32\bin\Release\*.dll;      DestDir: "{app}\Bin"
 Flags: ignoreversion; Source: win32\bin\Release\*.lib;      DestDir: "{app}\Bin"
-Flags: ignoreversion; Source: win32\version.rc;             DestDir: "{app}\Source\win32"
+Flags: ignoreversion; Source: win32\version.rc.cmake;       DestDir: "{app}\Source\win32"
 Flags: ignoreversion; Source: win32\README.txt;             DestDir: "{app}\Source"
 Flags: ignoreversion; Source: AUTHORS;                      DestDir: "{app}\Source"
 Flags: ignoreversion; Source: Changes;                      DestDir: "{app}\Source"
similarity index 69%
rename from win32/version.rc
rename to win32/version.rc.cmake
index 64a4dad..4b883f3 100644 (file)
@@ -6,8 +6,8 @@ BEGIN
   BEGIN
     BLOCK "040904E4"
     BEGIN
-      VALUE "FileVersion", "VER_FILEVERSION"
-      VALUE "ProductVersion", "VER_FILEVERSION"
+      VALUE "FileVersion", "${PROJECT_VERSION}.0"
+      VALUE "ProductVersion", "${PROJECT_VERSION}.0"
     END
   END
   BLOCK "VarFileInfo"
index 9179e1c..a8a84dc 100644 (file)
@@ -134,6 +134,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
        $(top_srcdir)/conftools/ax-append-compile-flags.m4 \
        $(top_srcdir)/conftools/ax-append-link-flags.m4 \
        $(top_srcdir)/conftools/expatcfg-compiler-supports-visibility.m4 \
+       $(top_srcdir)/conftools/ax-cxx-compile-stdcxx.m4 \
+       $(top_srcdir)/conftools/ax-cxx-compile-stdcxx-11.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
@@ -267,6 +269,7 @@ FGREP = @FGREP@
 FILECMD = @FILECMD@
 FILEMAP = @FILEMAP@
 GREP = @GREP@
+HAVE_CXX11 = @HAVE_CXX11@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
@@ -286,6 +289,7 @@ LIPO = @LIPO@
 LN_S = @LN_S@
 LTLIBOBJS = @LTLIBOBJS@
 LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
 MAKEINFO = @MAKEINFO@
 MANIFEST_TOOL = @MANIFEST_TOOL@
 MKDIR_P = @MKDIR_P@
@@ -392,7 +396,7 @@ all: all-am
 
 .SUFFIXES:
 .SUFFIXES: .c .lo .o .obj
-$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
        @for dep in $?; do \
          case '$(am__configure_deps)' in \
            *$$dep*) \
@@ -416,9 +420,9 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
        cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
 
-$(top_srcdir)/configure:  $(am__configure_deps)
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
        cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
        cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
 $(am__aclocal_m4_deps):
 install-binPROGRAMS: $(bin_PROGRAMS)
index 59121f5..2cb53fe 100644 (file)
@@ -13,6 +13,7 @@
    Copyright (c) 2016-2017 Sebastian Pipping <sebastian@pipping.org>
    Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Copyright (c) 2017      Franek Korta <fkorta@gmail.com>
+   Copyright (c) 2022      Sean McBride <sean@rogue-research.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
 #if defined(_MSC_VER)
 #  include <io.h>
 /* https://msdn.microsoft.com/en-us/library/wyssk1bs(v=vs.100).aspx */
-#  define _EXPAT_read _read
-#  define _EXPAT_read_count_t int
-#  define _EXPAT_read_req_t unsigned int
+#  define EXPAT_read _read
+#  define EXPAT_read_count_t int
+#  define EXPAT_read_req_t unsigned int
 #else /* POSIX */
 /* http://pubs.opengroup.org/onlinepubs/009695399/functions/read.html */
-#  define _EXPAT_read read
-#  define _EXPAT_read_count_t ssize_t
-#  define _EXPAT_read_req_t size_t
+#  define EXPAT_read read
+#  define EXPAT_read_count_t ssize_t
+#  define EXPAT_read_req_t size_t
 #endif
 
 #ifndef S_ISREG
@@ -67,7 +68,7 @@
 #  ifndef S_IFMT
 #    define S_IFMT _S_IFMT
 #  endif
-#  define S_ISREG(m) (((m)&S_IFMT) == S_IFREG)
+#  define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
 #endif /* not S_ISREG */
 
 #ifndef O_BINARY
@@ -87,7 +88,7 @@ filemap(const tchar *name,
         void *arg) {
   size_t nbytes;
   int fd;
-  _EXPAT_read_count_t n;
+  EXPAT_read_count_t n;
   struct stat sb;
   void *p;
 
@@ -125,14 +126,14 @@ filemap(const tchar *name,
     close(fd);
     return 0;
   }
-  n = _EXPAT_read(fd, p, (_EXPAT_read_req_t)nbytes);
+  n = EXPAT_read(fd, p, (EXPAT_read_req_t)nbytes);
   if (n < 0) {
     tperror(name);
     free(p);
     close(fd);
     return 0;
   }
-  if (n != (_EXPAT_read_count_t)nbytes) {
+  if (n != (EXPAT_read_count_t)nbytes) {
     ftprintf(stderr, T("%s: read unexpected number of bytes\n"), name);
     free(p);
     close(fd);
index e352167..0598b86 100644 (file)
    Copyright (c) 2002-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
    Copyright (c) 2004-2006 Karl Waclawek <karl@waclawek.net>
    Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
-   Copyright (c) 2016-2021 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016-2023 Sebastian Pipping <sebastian@pipping.org>
    Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
-   Copyright (c) 2021      Dong-hee Na <donghee.na@python.org>
+   Copyright (c) 2021      Donghee Na <donghee.na@python.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -37,7 +37,7 @@
    USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
-#include <expat_config.h>
+#include "expat_config.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 #  endif
 #endif
 
-#ifdef _DEBUG
-#  define READ_SIZE 16
-#else
-#  define READ_SIZE (1024 * 8)
-#endif
+int g_read_size_bytes = 1024 * 8;
 
 typedef struct {
   XML_Parser parser;
@@ -195,7 +191,7 @@ processStream(const XML_Char *filename, XML_Parser parser) {
   }
   for (;;) {
     int nread;
-    char *buf = (char *)XML_GetBuffer(parser, READ_SIZE);
+    char *buf = (char *)XML_GetBuffer(parser, g_read_size_bytes);
     if (! buf) {
       if (filename != NULL)
         close(fd);
@@ -203,7 +199,7 @@ processStream(const XML_Char *filename, XML_Parser parser) {
                filename != NULL ? filename : T("xmlwf"));
       return 0;
     }
-    nread = read(fd, buf, READ_SIZE);
+    nread = read(fd, buf, g_read_size_bytes);
     if (nread < 0) {
       tperror(filename != NULL ? filename : T("STDIN"));
       if (filename != NULL)
index d75dda2..579201c 100644 (file)
@@ -10,7 +10,7 @@
    Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
    Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
    Copyright (c) 2005      Karl Waclawek <karl@waclawek.net>
-   Copyright (c) 2016-2019 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016-2023 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -42,5 +42,7 @@
 #  define XML_FMT_INT_MOD "l"
 #endif
 
+extern int g_read_size_bytes;
+
 extern int XML_ProcessFile(XML_Parser parser, const XML_Char *filename,
                            unsigned flags);
index 471f2a2..7c0a8cd 100644 (file)
    Copyright (c) 2001-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
    Copyright (c) 2004-2009 Karl Waclawek <karl@waclawek.net>
    Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
-   Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016-2023 Sebastian Pipping <sebastian@pipping.org>
    Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
    Copyright (c) 2020      Joe Orton <jorton@redhat.com>
    Copyright (c) 2020      Kleber Tarcísio <klebertarcisio@yahoo.com.br>
    Copyright (c) 2021      Tim Bray <tbray@textuality.com>
    Copyright (c) 2022      Martin Ettl <ettl.martin78@googlemail.com>
+   Copyright (c) 2022      Sean McBride <sean@rogue-research.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -40,7 +41,7 @@
    USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
-#include <expat_config.h>
+#include "expat_config.h"
 
 #include <assert.h>
 #include <stdio.h>
@@ -178,7 +179,7 @@ is equivalent to lexicographically comparing based on the character number. */
 
 static int
 attcmp(const void *att1, const void *att2) {
-  return tcscmp(*(const XML_Char **)att1, *(const XML_Char **)att2);
+  return tcscmp(*(const XML_Char *const *)att1, *(const XML_Char *const *)att2);
 }
 
 static void XMLCALL
@@ -215,8 +216,8 @@ endElement(void *userData, const XML_Char *name) {
 
 static int
 nsattcmp(const void *p1, const void *p2) {
-  const XML_Char *att1 = *(const XML_Char **)p1;
-  const XML_Char *att2 = *(const XML_Char **)p2;
+  const XML_Char *att1 = *(const XML_Char *const *)p1;
+  const XML_Char *att2 = *(const XML_Char *const *)p2;
   int sep1 = (tcsrchr(att1, NSSEP) != 0);
   int sep2 = (tcsrchr(att2, NSSEP) != 0);
   if (sep1 != sep2)
@@ -370,8 +371,8 @@ xcscmp(const XML_Char *xs, const XML_Char *xt) {
 
 static int
 notationCmp(const void *a, const void *b) {
-  const NotationList *const n1 = *(NotationList **)a;
-  const NotationList *const n2 = *(NotationList **)b;
+  const NotationList *const n1 = *(const NotationList *const *)a;
+  const NotationList *const n2 = *(const NotationList *const *)b;
 
   return xcscmp(n1->notationName, n2->notationName);
 }
@@ -871,6 +872,9 @@ showVersion(XML_Char *prog) {
   }
 }
 
+#if defined(__GNUC__)
+__attribute__((noreturn))
+#endif
 static void
 usage(const XML_Char *prog, int rc) {
   ftprintf(
@@ -883,50 +887,54 @@ usage(const XML_Char *prog, int rc) {
       /* clang-format off */
       T("usage:\n")
       T("  %s [OPTIONS] [FILE ...]\n")
-      T("  %s -h\n")
-      T("  %s -v\n")
+      T("  %s -h|--help\n")
+      T("  %s -v|--version\n")
       T("\n")
       T("xmlwf - Determines if an XML document is well-formed\n")
       T("\n")
       T("positional arguments:\n")
-      T("  FILE          file to process (default: STDIN)\n")
+      T("  FILE           file to process (default: STDIN)\n")
       T("\n")
       T("input control arguments:\n")
-      T("  -s            print an error if the document is not [s]tandalone\n")
-      T("  -n            enable [n]amespace processing\n")
-      T("  -p            enable processing external DTDs and [p]arameter entities\n")
-      T("  -x            enable processing of e[x]ternal entities\n")
-      T("  -e ENCODING   override any in-document [e]ncoding declaration\n")
-      T("  -w            enable support for [W]indows code pages\n")
-      T("  -r            disable memory-mapping and use normal file [r]ead IO calls instead\n")
-      T("  -k            when processing multiple files, [k]eep processing after first file with error\n")
+      T("  -s             print an error if the document is not [s]tandalone\n")
+      T("  -n             enable [n]amespace processing\n")
+      T("  -p             enable processing of external DTDs and [p]arameter entities\n")
+      T("  -x             enable processing of e[x]ternal entities\n")
+      T("  -e ENCODING    override any in-document [e]ncoding declaration\n")
+      T("  -w             enable support for [W]indows code pages\n")
+      T("  -r             disable memory-mapping and use [r]ead calls instead\n")
+      T("  -g BYTES       buffer size to request per call pair to XML_[G]etBuffer and read (default: 8 KiB)\n")
+      T("  -k             when processing multiple files, [k]eep processing after first file with error\n")
       T("\n")
       T("output control arguments:\n")
-      T("  -d DIRECTORY  output [d]estination directory\n")
-      T("  -c            write a [c]opy of input XML, not canonical XML\n")
-      T("  -m            write [m]eta XML, not canonical XML\n")
-      T("  -t            write no XML output for [t]iming of plain parsing\n")
-      T("  -N            enable adding doctype and [n]otation declarations\n")
+      T("  -d DIRECTORY   output [d]estination directory\n")
+      T("  -c             write a [c]opy of input XML, not canonical XML\n")
+      T("  -m             write [m]eta XML, not canonical XML\n")
+      T("  -t             write no XML output for [t]iming of plain parsing\n")
+      T("  -N             enable adding doctype and [n]otation declarations\n")
       T("\n")
       T("billion laughs attack protection:\n")
       T("  NOTE: If you ever need to increase these values for non-attack payload, please file a bug report.\n")
       T("\n")
-      T("  -a FACTOR     set maximum tolerated [a]mplification factor (default: 100.0)\n")
-      T("  -b BYTES      set number of output [b]ytes needed to activate (default: 8 MiB)\n")
+      T("  -a FACTOR      set maximum tolerated [a]mplification factor (default: 100.0)\n")
+      T("  -b BYTES       set number of output [b]ytes needed to activate (default: 8 MiB)\n")
+      T("\n")
+      T("reparse deferral:\n")
+      T("  -q             disable reparse deferral, and allow [q]uadratic parse runtime with large tokens\n")
       T("\n")
       T("info arguments:\n")
-      T("  -h            show this [h]elp message and exit\n")
-      T("  -v            show program's [v]ersion number and exit\n")
+      T("  -h, --help     show this [h]elp message and exit\n")
+      T("  -v, --version  show program's [v]ersion number and exit\n")
       T("\n")
       T("exit status:\n")
-      T("  0             the input files are well-formed and the output (if requested) was written successfully\n")
-      T("  1             could not allocate data structures, signals a serious problem with execution environment\n")
-      T("  2             one or more input files were not well-formed\n")
-      T("  3             could not create an output file\n")
-      T("  4             command-line argument error\n")
+      T("  0              the input files are well-formed and the output (if requested) was written successfully\n")
+      T("  1              could not allocate data structures, signals a serious problem with execution environment\n")
+      T("  2              one or more input files were not well-formed\n")
+      T("  3              could not create an output file\n")
+      T("  4              command-line argument error\n")
       T("\n")
       T("xmlwf of libexpat is software libre, licensed under the MIT license.\n")
-      T("Please report bugs at https://github.com/libexpat/libexpat/issues.  Thank you!\n")
+      T("Please report bugs at https://github.com/libexpat/libexpat/issues -- thank you!\n")
       , /* clang-format on */
       prog, prog, prog);
   exit(rc);
@@ -940,8 +948,10 @@ int wmain(int argc, XML_Char **argv);
 #define XMLWF_SHIFT_ARG_INTO(constCharStarTarget, argc, argv, i, j)            \
   {                                                                            \
     if (argv[i][j + 1] == T('\0')) {                                           \
-      if (++i == argc)                                                         \
+      if (++i == argc) {                                                       \
         usage(argv[0], XMLWF_EXIT_USAGE_ERROR);                                \
+        /* usage called exit(..), never gets here */                           \
+      }                                                                        \
       constCharStarTarget = argv[i];                                           \
     } else {                                                                   \
       constCharStarTarget = argv[i] + j + 1;                                   \
@@ -964,9 +974,11 @@ tmain(int argc, XML_Char **argv) {
   int continueOnError = 0;
 
   float attackMaximumAmplification = -1.0f; /* signaling "not set" */
-  unsigned long long attackThresholdBytes;
+  unsigned long long attackThresholdBytes = 0;
   XML_Bool attackThresholdGiven = XML_FALSE;
 
+  XML_Bool disableDeferral = XML_FALSE;
+
   int exitCode = XMLWF_EXIT_SUCCESS;
   enum XML_ParamEntityParsing paramEntityParsing
       = XML_PARAM_ENTITY_PARSING_NEVER;
@@ -983,9 +995,17 @@ tmain(int argc, XML_Char **argv) {
     if (j == 0) {
       if (argv[i][0] != T('-'))
         break;
-      if (argv[i][1] == T('-') && argv[i][2] == T('\0')) {
-        i++;
-        break;
+      if (argv[i][1] == T('-')) {
+        if (argv[i][2] == T('\0')) {
+          i++;
+          break;
+        } else if (tcscmp(argv[i] + 2, T("help")) == 0) {
+          usage(argv[0], XMLWF_EXIT_SUCCESS);
+          // usage called exit(..), never gets here
+        } else if (tcscmp(argv[i] + 2, T("version")) == 0) {
+          showVersion(argv[0]);
+          return XMLWF_EXIT_SUCCESS;
+        }
       }
       j++;
     }
@@ -1038,10 +1058,30 @@ tmain(int argc, XML_Char **argv) {
       break;
     case T('h'):
       usage(argv[0], XMLWF_EXIT_SUCCESS);
-      return 0;
+      // usage called exit(..), never gets here
     case T('v'):
       showVersion(argv[0]);
-      return 0;
+      return XMLWF_EXIT_SUCCESS;
+    case T('g'): {
+      const XML_Char *valueText = NULL;
+      XMLWF_SHIFT_ARG_INTO(valueText, argc, argv, i, j);
+
+      errno = 0;
+      XML_Char *afterValueText = (XML_Char *)valueText;
+      const long long read_size_bytes_candidate
+          = tcstoull(valueText, &afterValueText, 10);
+      if ((errno != 0) || (afterValueText[0] != T('\0'))
+          || (read_size_bytes_candidate < 1)
+          || (read_size_bytes_candidate > (INT_MAX / 2 + 1))) {
+        // This prevents tperror(..) from reporting misleading "[..]: Success"
+        errno = ERANGE;
+        tperror(T("invalid buffer size") T(
+            " (needs an integer from 1 to INT_MAX/2+1 i.e. 1,073,741,824 on most platforms)"));
+        exit(XMLWF_EXIT_USAGE_ERROR);
+      }
+      g_read_size_bytes = (int)read_size_bytes_candidate;
+      break;
+    }
     case T('k'):
       continueOnError = 1;
       j++;
@@ -1051,7 +1091,7 @@ tmain(int argc, XML_Char **argv) {
       XMLWF_SHIFT_ARG_INTO(valueText, argc, argv, i, j);
 
       errno = 0;
-      XML_Char *afterValueText = (XML_Char *)valueText;
+      XML_Char *afterValueText = NULL;
       attackMaximumAmplification = tcstof(valueText, &afterValueText);
       if ((errno != 0) || (afterValueText[0] != T('\0'))
           || isnan(attackMaximumAmplification)
@@ -1062,9 +1102,10 @@ tmain(int argc, XML_Char **argv) {
             " (needs a floating point number greater or equal than 1.0)"));
         exit(XMLWF_EXIT_USAGE_ERROR);
       }
-#ifndef XML_DTD
-      ftprintf(stderr, T("Warning: Given amplification limit ignored") T(
-                           ", xmlwf has been compiled without DTD support.\n"));
+#if XML_GE == 0
+      ftprintf(stderr,
+               T("Warning: Given amplification limit ignored")
+                   T(", xmlwf has been compiled without DTD/GE support.\n"));
 #endif
       break;
     }
@@ -1083,12 +1124,18 @@ tmain(int argc, XML_Char **argv) {
         exit(XMLWF_EXIT_USAGE_ERROR);
       }
       attackThresholdGiven = XML_TRUE;
-#ifndef XML_DTD
-      ftprintf(stderr, T("Warning: Given attack threshold ignored") T(
-                           ", xmlwf has been compiled without DTD support.\n"));
+#if XML_GE == 0
+      ftprintf(stderr,
+               T("Warning: Given attack threshold ignored")
+                   T(", xmlwf has been compiled without DTD/GE support.\n"));
 #endif
       break;
     }
+    case T('q'): {
+      disableDeferral = XML_TRUE;
+      j++;
+      break;
+    }
     case T('\0'):
       if (j > 1) {
         i++;
@@ -1098,6 +1145,7 @@ tmain(int argc, XML_Char **argv) {
       /* fall through */
     default:
       usage(argv[0], XMLWF_EXIT_USAGE_ERROR);
+      // usage called exit(..), never gets here
     }
   }
   if (i == argc) {
@@ -1120,13 +1168,13 @@ tmain(int argc, XML_Char **argv) {
     }
 
     if (attackMaximumAmplification != -1.0f) {
-#ifdef XML_DTD
+#if XML_GE == 1
       XML_SetBillionLaughsAttackProtectionMaximumAmplification(
           parser, attackMaximumAmplification);
 #endif
     }
     if (attackThresholdGiven) {
-#ifdef XML_DTD
+#if XML_GE == 1
       XML_SetBillionLaughsAttackProtectionActivationThreshold(
           parser, attackThresholdBytes);
 #else
@@ -1134,6 +1182,16 @@ tmain(int argc, XML_Char **argv) {
 #endif
     }
 
+    if (disableDeferral) {
+      const XML_Bool success = XML_SetReparseDeferralEnabled(parser, XML_FALSE);
+      if (! success) {
+        // This prevents tperror(..) from reporting misleading "[..]: Success"
+        errno = EINVAL;
+        tperror(T("Failed to disable reparse deferral"));
+        exit(XMLWF_EXIT_INTERNAL_ERROR);
+      }
+    }
+
     if (requireStandalone)
       XML_SetNotStandaloneHandler(parser, notStandalone);
     XML_SetParamEntityParsing(parser, paramEntityParsing);
index c2a527f..3d32f5d 100755 (executable)
@@ -6,7 +6,7 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2019-2021 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2019-2023 Sebastian Pipping <sebastian@pipping.org>
 # Copyright (c) 2021      Tim Bray <tbray@textuality.com>
 # Licensed under the MIT license:
 #
@@ -33,20 +33,20 @@ import argparse
 
 epilog = """
 exit status:
-  0             the input files are well-formed and the output (if requested) was written successfully
-  1             could not allocate data structures, signals a serious problem with execution environment
-  2             one or more input files were not well-formed
-  3             could not create an output file
-  4             command-line argument error
+  0              the input files are well-formed and the output (if requested) was written successfully
+  1              could not allocate data structures, signals a serious problem with execution environment
+  2              one or more input files were not well-formed
+  3              could not create an output file
+  4              command-line argument error
 
 xmlwf of libexpat is software libre, licensed under the MIT license.
-Please report bugs at https://github.com/libexpat/libexpat/issues.  Thank you!
+Please report bugs at https://github.com/libexpat/libexpat/issues -- thank you!
 """
 
 usage = """
   %(prog)s [OPTIONS] [FILE ...]
-  %(prog)s -h
-  %(prog)s -v
+  %(prog)s -h|--help
+  %(prog)s -v|--version
 """
 
 parser = argparse.ArgumentParser(prog='xmlwf', add_help=False,
@@ -58,11 +58,12 @@ parser = argparse.ArgumentParser(prog='xmlwf', add_help=False,
 input_related = parser.add_argument_group('input control arguments')
 input_related.add_argument('-s', action='store_true', help='print an error if the document is not [s]tandalone')
 input_related.add_argument('-n', action='store_true', help='enable [n]amespace processing')
-input_related.add_argument('-p', action='store_true', help='enable processing external DTDs and [p]arameter entities')
+input_related.add_argument('-p', action='store_true', help='enable processing of external DTDs and [p]arameter entities')
 input_related.add_argument('-x', action='store_true', help='enable processing of e[x]ternal entities')
 input_related.add_argument('-e', action='store', metavar='ENCODING', help='override any in-document [e]ncoding declaration')
 input_related.add_argument('-w', action='store_true', help='enable support for [W]indows code pages')
-input_related.add_argument('-r', action='store_true', help='disable memory-mapping and use normal file [r]ead IO calls instead')
+input_related.add_argument('-r', action='store_true', help='disable memory-mapping and use [r]ead calls instead')
+input_related.add_argument('-g', metavar='BYTES', help='buffer size to request per call pair to XML_[G]etBuffer and read (default: 8 KiB)')
 input_related.add_argument('-k', action='store_true', help='when processing multiple files, [k]eep processing after first file with error')
 
 output_related = parser.add_argument_group('output control arguments')
@@ -81,12 +82,16 @@ billion_laughs.add_argument('-a', metavar='FACTOR',
                             help='set maximum tolerated [a]mplification factor (default: 100.0)')
 billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB)')
 
+reparse_deferral = parser.add_argument_group('reparse deferral')
+reparse_deferral.add_argument('-q', metavar='FACTOR',
+                            help='disable reparse deferral, and allow [q]uadratic parse runtime with large tokens')
+
 parser.add_argument('files', metavar='FILE', nargs='*', help='file to process (default: STDIN)')
 
 info = parser.add_argument_group('info arguments')
 info = info.add_mutually_exclusive_group()
-info.add_argument('-h', action='store_true', help='show this [h]elp message and exit')
-info.add_argument('-v', action='store_true', help='show program\'s [v]ersion number and exit')
+info.add_argument('-h', '--help', action='store_true', help='show this [h]elp message and exit')
+info.add_argument('-v', '--version', action='store_true', help='show program\'s [v]ersion number and exit')
 
 
 if __name__ == '__main__':