Merge "Prevent XML_GetBuffer signed integer overflow" into rvc-dev am: 87560075e3 am: cf32085d8a am: 7c2195c76b am: 6b90eba170

Original change: https://googleplex-android-review.googlesource.com/c/platform/external/expat/+/18724849

Change-Id: Ifa43eb054ad693f78c21ebec0688188e98f81772
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.bp b/Android.bp
index a7fd6b0..5e7c149 100644
--- a/Android.bp
+++ b/Android.bp
@@ -64,13 +64,17 @@
         "-Wno-missing-field-initializers",
         "-DHAVE_EXPAT_CONFIG_H",
         "-UWIN32_LEAN_AND_MEAN",
+        // As of 2.4.6, xmlparse.c includes expat_config.h *before* including
+        // any system header file, meaning that __BIONIC__ and __GLIBC__ and so
+        // on aren't set, but our modifications to have one expat_config.h for
+        // all targets rely on those defines.
+        "-include stdio.h",
     ],
 
     target: {
         darwin: {
             cflags: ["-fno-common"],
         },
-
         windows: {
             enabled: true,
         },
diff --git a/CMake.README b/CMake.README
index a512a7b..37f7c53 100644
--- a/CMake.README
+++ b/CMake.README
@@ -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.3.0 is the source directory of expat, add a subdirectory
+Assuming ~/expat-2.4.6 is the source directory of expat, add a subdirectory
 build and change into that directory:
-~/expat-2.3.0$ mkdir build && cd build
-~/expat-2.3.0/build$
+~/expat-2.4.6$ mkdir build && cd build
+~/expat-2.4.6/build$
 
 From that directory, call cmake first, then call make, make test and
 make install in the usual way:
-~/expat-2.3.0/build$ cmake ..
+~/expat-2.4.6/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.3.0/build
+-- Build files have been written to: /home/patrick/expat-2.4.6/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.3.0/build$ make && make test && make install
+~/expat-2.4.6/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
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e356469..a36b1d0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,36 @@
-# This file is copyrighted under the BSD-license for buildsystem files of KDE
-# copyright 2010, Patrick Spendrin <ps_ml@gmx.de>
+#                          __  __            _
+#                       ___\ \/ /_ __   __ _| |_
+#                      / _ \\  /| '_ \ / _` | __|
+#                     |  __//  \| |_) | (_| | |_
+#                      \___/_/\_\ .__/ \__,_|\__|
+#                               |_| XML parser
+#
+# 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      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      Rolf Eike Beer <eike@sf-mail.de>
+# Copyright (c) 2017      Stephen Groat <stephen@groat.us>
+# Copyright (c) 2017      Franek Korta <fkorta@gmail.com>
+# Copyright (c) 2018      pedro-vicente <pedro.vicente@space-research.org>
+# Copyright (c) 2018      Frank Rast <frank.rast@gefeg.com>
+# Copyright (c) 2018      userwithuid <userwithuid@gmail.com>
+# Copyright (c) 2018      Yury Gribov <tetra2005@gmail.com>
+# Copyright (c) 2019      Kishore Kunche <kishore.kunche@intel.com>
+# Copyright (c) 2019      xantares <xantares09@hotmail.com>
+# Copyright (c) 2019      Mohammed Khajapasha <mohammed.khajapasha@intel.com>
+# Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+# Copyright (c) 2019      Bhargava Shastry <bhargava.shastry@ethereum.org>
+# Copyright (c) 2020      Maciej SroczyƄski <macieksroczynski@poczta.fm>
+# Copyright (c) 2020      Gulliver <gulliver@traumkristalle.net>
+# Copyright (c) 2020      Thomas Beutlich <tc@tbeu.de>
+# Copyright (c) 2021      Alex Richardson <Alexander.Richardson@cl.cam.ac.uk>
+# Unlike most of Expat,
+# this file is copyrighted under the BSD-license for buildsystem files of KDE.
 
 cmake_minimum_required(VERSION 3.1.3)
 
@@ -33,7 +64,7 @@
 
 project(expat
     VERSION
-        2.3.0
+        2.4.6
     LANGUAGES
         C
 )
@@ -70,6 +101,15 @@
 else()
     set(_EXPAT_BUILD_PKGCONFIG_DEFAULT ON)
 endif()
+if(DEFINED BUILD_SHARED_LIBS)
+    set(_EXPAT_SHARED_LIBS_DEFAULT ${BUILD_SHARED_LIBS})
+else()
+    set(_EXPAT_SHARED_LIBS_DEFAULT ON)
+endif()
+if(NOT CMAKE_BUILD_TYPE)
+    set(CMAKE_BUILD_TYPE NoConfig)  # so that accessing CMAKE_*_POSTFIX will be waterproof
+endif()
+string(TOUPPER "${CMAKE_BUILD_TYPE}" _EXPAT_BUILD_TYPE_UPPER)
 
 #
 # Configuration
@@ -77,7 +117,7 @@
 option(EXPAT_BUILD_TOOLS "build the xmlwf tool for expat library" ${_EXPAT_BUILD_TOOLS_DEFAULT})
 option(EXPAT_BUILD_EXAMPLES "build the examples for expat library" ON)
 option(EXPAT_BUILD_TESTS "build the tests for expat library" ON)
-option(EXPAT_SHARED_LIBS "build a shared expat library" ON)
+option(EXPAT_SHARED_LIBS "build a shared expat library" ${_EXPAT_SHARED_LIBS_DEFAULT})
 option(EXPAT_BUILD_DOCS "build man page for xmlwf" ${_EXPAT_BUILD_DOCS_DEFAULT})
 option(EXPAT_BUILD_FUZZERS "build fuzzers for the expat library" OFF)
 option(EXPAT_BUILD_PKGCONFIG "build pkg-config file" ${_EXPAT_BUILD_PKGCONFIG_DEFAULT})
@@ -111,6 +151,9 @@
 if(MSVC OR _EXPAT_HELP)
     set(EXPAT_MSVC_STATIC_CRT OFF CACHE BOOL "Use /MT flag (static CRT) when compiling in MSVC")
 endif()
+if(NOT _EXPAT_HELP)
+    set(_EXPAT_M32 OFF CACHE BOOL "(Unofficial!) Produce 32bit code with -m32")
+endif()
 
 if(EXPAT_BUILD_TESTS)
     # We have to call enable_language() before modifying any CMAKE_CXX_* variables
@@ -130,10 +173,22 @@
 endif()
 
 if(MSVC)
-    # Minimum supported MSVC version is 1910 = Visual Studio 15.0/2017
-    # See also https://cmake.org/cmake/help/latest/variable/MSVC_VERSION.html
-    if(MSVC_VERSION VERSION_LESS 1910)
-        message(SEND_ERROR "MSVC_VERSION ${MSVC_VERSION} is not a supported Visual Studio compiler version. Please use Visual Studio 15.0/2017 or any later version.")
+    # For the three types of MSVC version values, please see:
+    # - https://cmake.org/cmake/help/latest/variable/MSVC_VERSION.html
+    # - https://sourceforge.net/p/predef/wiki/Compilers/
+    # - https://en.wikipedia.org/wiki/Microsoft_Visual_Studio#History
+    set(_EXPAT_MSVC_REQUIRED_INT 1800)  # i.e. 12.0/2013/1800; see PR #426
+    set(_EXPAT_MSVC_SUPPORTED_INT 1910)
+    set(_EXPAT_MSVC_SUPPORTED_DISPLAY "Visual Studio 15.0/2017/${_EXPAT_MSVC_SUPPORTED_INT}")
+
+    if(MSVC_VERSION VERSION_LESS ${_EXPAT_MSVC_SUPPORTED_INT})
+        if(MSVC_VERSION VERSION_LESS ${_EXPAT_MSVC_REQUIRED_INT})
+            message(SEND_ERROR "MSVC_VERSION ${MSVC_VERSION} is TOO OLD to compile Expat without errors.")
+            message(SEND_ERROR "Please use officially supported ${_EXPAT_MSVC_SUPPORTED_DISPLAY} or later.  Thank you!")
+        else()
+            message(WARNING "MSVC_VERSION ${MSVC_VERSION} is NOT OFFICIALLY SUPPORTED by Expat.")
+            message(WARNING "Please use ${_EXPAT_MSVC_SUPPORTED_DISPLAY} or later.  Thank you!")
+        endif()
     endif()
 endif()
 
@@ -225,7 +280,6 @@
 endmacro()
 
 configure_file(expat_config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/expat_config.h")
-add_definitions(-DHAVE_EXPAT_CONFIG_H)
 expat_install(FILES "${CMAKE_CURRENT_BINARY_DIR}/expat_config.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
 
 
@@ -237,6 +291,10 @@
   add_definitions(-DXML_ENABLE_VISIBILITY=1)
   set(EXTRA_COMPILE_FLAGS "${EXTRA_COMPILE_FLAGS} -fvisibility=hidden")
 endif()
+if(MINGW)
+    # Without __USE_MINGW_ANSI_STDIO the compiler produces a false positive
+    set(EXTRA_COMPILE_FLAGS "${EXTRA_COMPILE_FLAGS} -Wno-pedantic-ms-format")
+endif()
 if (EXPAT_WARNINGS_AS_ERRORS)
     if(MSVC)
         add_definitions(/WX)
@@ -251,10 +309,12 @@
     if (EXPAT_MSVC_STATIC_CRT)
         message("-- Using static CRT ${EXPAT_MSVC_STATIC_CRT}")
         foreach(flag_var
+                CMAKE_CXX_FLAGS_${_EXPAT_BUILD_TYPE_UPPER}
                 CMAKE_CXX_FLAGS_DEBUG
                 CMAKE_CXX_FLAGS_RELEASE
                 CMAKE_CXX_FLAGS_MINSIZEREL
                 CMAKE_CXX_FLAGS_RELWITHDEBINFO
+                CMAKE_C_FLAGS_${_EXPAT_BUILD_TYPE_UPPER}
                 CMAKE_C_FLAGS_DEBUG
                 CMAKE_C_FLAGS_RELEASE
                 CMAKE_C_FLAGS_MINSIZEREL
@@ -265,39 +325,59 @@
     endif()
 endif()
 
+if(_EXPAT_M32 AND NOT MSVC)
+    foreach(flag_var
+            CMAKE_CXX_FLAGS_${_EXPAT_BUILD_TYPE_UPPER}
+            CMAKE_CXX_FLAGS_DEBUG
+            CMAKE_CXX_FLAGS_RELEASE
+            CMAKE_CXX_FLAGS_MINSIZEREL
+            CMAKE_CXX_FLAGS_RELWITHDEBINFO
+            CMAKE_C_FLAGS_${_EXPAT_BUILD_TYPE_UPPER}
+            CMAKE_C_FLAGS_DEBUG
+            CMAKE_C_FLAGS_RELEASE
+            CMAKE_C_FLAGS_MINSIZEREL
+            CMAKE_C_FLAGS_RELWITHDEBINFO
+            )
+        set(${flag_var} "${${flag_var}} -m32")
+    endforeach()
+endif()
+
 include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/lib)
 if(MSVC)
     add_definitions(-D_CRT_SECURE_NO_WARNINGS -wd4996)
 endif()
-if(WIN32)
-    if(_EXPAT_UNICODE_WCHAR_T)
-        set(_POSTFIX_WIDE "w")
-    endif()
 
-    if(MSVC AND NOT EXPAT_SHARED_LIBS)
-        if(EXPAT_MSVC_STATIC_CRT)
-            set(_POSTFIX_CRT "MT")
-        else()
-            set(_POSTFIX_CRT "MD")
-        endif()
-    endif()
-
-    foreach(postfix_var
-            CMAKE_DEBUG_POSTFIX
-            CMAKE_RELEASE_POSTFIX
-            CMAKE_MINSIZEREL_POSTFIX
-            CMAKE_RELWITHDEBINFO_POSTFIX
-            )
-        if(postfix_var STREQUAL "CMAKE_DEBUG_POSTFIX")
-            set(_POSTFIX_DEBUG "d")
-        else()
-            set(_POSTFIX_DEBUG "")
-        endif()
-
-        set(${postfix_var} "${_POSTFIX_WIDE}${_POSTFIX_DEBUG}${_POSTFIX_CRT}" CACHE STRING "Windows binary postfix, e.g. libexpat<postfix=[w][d][MD|MT]>.lib")
-    endforeach()
+#
+# Library filename postfix
+#
+if(_EXPAT_UNICODE)
+    set(_POSTFIX_WIDE "w")
 endif()
 
+if(MSVC AND NOT EXPAT_SHARED_LIBS)
+    if(EXPAT_MSVC_STATIC_CRT)
+        set(_POSTFIX_CRT "MT")
+    else()
+        set(_POSTFIX_CRT "MD")
+    endif()
+endif()
+
+foreach(postfix_var
+        CMAKE_${_EXPAT_BUILD_TYPE_UPPER}_POSTFIX
+        CMAKE_DEBUG_POSTFIX
+        CMAKE_RELEASE_POSTFIX
+        CMAKE_MINSIZEREL_POSTFIX
+        CMAKE_RELWITHDEBINFO_POSTFIX
+        )
+    if(WIN32 AND postfix_var STREQUAL "CMAKE_DEBUG_POSTFIX")
+        set(_POSTFIX_DEBUG "d")
+    else()
+        set(_POSTFIX_DEBUG "")  # needs a reset because of being looped
+    endif()
+
+    set(${postfix_var} "${_POSTFIX_WIDE}${_POSTFIX_DEBUG}${_POSTFIX_CRT}" CACHE STRING "Library filename postfix, e.g. libexpat<postfix=[w][d][MD|MT]>.lib")
+endforeach()
+
 #
 # C library
 #
@@ -319,33 +399,33 @@
     set(_SHARED STATIC)
 endif()
 
-# Avoid colliding with Expat.dll of Perl's XML::Parser::Expat
-if(WIN32 AND NOT MINGW)
-    set(_EXPAT_OUTPUT_NAME libexpat)  # CMAKE_*_POSTFIX applies, see above
-else()
-    if(_EXPAT_UNICODE)
-        set(_EXPAT_OUTPUT_NAME expatw)
-    else()
-        set(_EXPAT_OUTPUT_NAME expat)
-    endif()
-endif()
-
 add_library(expat ${_SHARED} ${expat_SRCS})
+if(_EXPAT_LIBM_FOUND)
+    target_link_libraries(expat m)
+endif()
 if(EXPAT_WITH_LIBBSD)
     target_link_libraries(expat ${LIB_BSD})
 endif()
 
-set(LIBCURRENT 8)   # sync
-set(LIBREVISION 0)  # with
-set(LIBAGE 7)       # configure.ac!
+set(LIBCURRENT 9)   # sync
+set(LIBREVISION 6)  # with
+set(LIBAGE 8)       # configure.ac!
 math(EXPR LIBCURRENT_MINUS_AGE "${LIBCURRENT} - ${LIBAGE}")
 
-set_property(TARGET expat PROPERTY OUTPUT_NAME "${_EXPAT_OUTPUT_NAME}")
 if(NOT WIN32)
     set_property(TARGET expat PROPERTY VERSION ${LIBCURRENT_MINUS_AGE}.${LIBAGE}.${LIBREVISION})
     set_property(TARGET expat PROPERTY SOVERSION ${LIBCURRENT_MINUS_AGE})
     set_property(TARGET expat PROPERTY NO_SONAME ${NO_SONAME})
 endif()
+if(WIN32 AND NOT MINGW)
+    # NOTE: This avoids a name collision with Expat.dll of Perl's XML::Parser::Expat
+    #       on Windows by resorting to filename libexpat.dll since Expat 1.95.3.
+    #       Everything but MSVC is already adding prefix "lib", automatically.
+    # NOTE: "set_property(TARGET expat PROPERTY PREFIX lib)" would only affect *.dll
+    #       files but not *.lib files, so we have to rely on property OUTPUT_NAME, instead.
+    #       Property CMAKE_*_POSTFIX still applies.
+    set_property(TARGET expat PROPERTY OUTPUT_NAME libexpat)
+endif()
 
 target_include_directories(expat
     INTERFACE
@@ -369,12 +449,41 @@
 # pkg-config file
 #
 if(EXPAT_BUILD_PKGCONFIG)
-    set(prefix ${CMAKE_INSTALL_PREFIX})
-    set(exec_prefix "\${prefix}")
-    set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
-    set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
-    configure_file(expat.pc.in ${CMAKE_CURRENT_BINARY_DIR}/expat.pc @ONLY)
-    expat_install(FILES ${CMAKE_CURRENT_BINARY_DIR}/expat.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
+    if(CMAKE_INSTALL_LIBDIR MATCHES "^/")
+        set(_expat_pkgconfig_libdir "${CMAKE_INSTALL_LIBDIR}")
+    else()
+        set(_expat_pkgconfig_libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
+    endif()
+
+    if(CMAKE_INSTALL_INCLUDEDIR MATCHES "^/")
+        set(_expat_pkgconfig_includedir "${CMAKE_INSTALL_INCLUDEDIR}")
+    else()
+        set(_expat_pkgconfig_includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
+    endif()
+
+    set_target_properties(expat PROPERTIES
+        pkgconfig_prefix "${CMAKE_INSTALL_PREFIX}"
+        pkgconfig_exec_prefix "\${prefix}"
+        pkgconfig_libdir "${_expat_pkgconfig_libdir}"
+        pkgconfig_includedir "${_expat_pkgconfig_includedir}"
+        pkgconfig_version "${PACKAGE_VERSION}")
+
+    foreach(_build_type ${CMAKE_BUILD_TYPE} Debug Release RelWithDebInfo MinSizeRel)
+        string(TOLOWER "${_build_type}" _build_type_lower)
+        string(TOUPPER "${_build_type}" _build_type_upper)
+        set_property(TARGET expat PROPERTY "pkgconfig_${_build_type_lower}_name" "expat${CMAKE_${_build_type_upper}_POSTFIX}")
+        if(_EXPAT_LIBM_FOUND)
+            set_property(TARGET expat PROPERTY "pkgconfig_libm" "-lm")
+        else()
+            set_property(TARGET expat PROPERTY "pkgconfig_libm" "")
+        endif()
+    endforeach()
+
+    file(GENERATE
+        OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/expat.pc
+        INPUT ${PROJECT_SOURCE_DIR}/expat.pc.cmake)
+
+    expat_install(FILES ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/expat.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
 endif()
 
 #
@@ -391,6 +500,9 @@
     add_executable(xmlwf ${xmlwf_SRCS})
     set_property(TARGET xmlwf PROPERTY RUNTIME_OUTPUT_DIRECTORY xmlwf)
     target_link_libraries(xmlwf expat)
+    if(_EXPAT_LIBM_FOUND)
+        target_link_libraries(xmlwf m)
+    endif()
     expat_install(TARGETS xmlwf DESTINATION ${CMAKE_INSTALL_BINDIR})
 
     if(MINGW AND _EXPAT_UNICODE_WCHAR_T)
@@ -438,6 +550,7 @@
         tests/memcheck.c
         tests/minicheck.c
         tests/structdata.c
+        ${expat_SRCS}
     )
 
     if(NOT MSVC)
@@ -457,13 +570,21 @@
 
     add_executable(runtests tests/runtests.c ${test_SRCS})
     set_property(TARGET runtests PROPERTY RUNTIME_OUTPUT_DIRECTORY tests)
-    target_link_libraries(runtests expat)
     expat_add_test(runtests $<TARGET_FILE:runtests>)
 
     add_executable(runtestspp tests/runtestspp.cpp ${test_SRCS})
     set_property(TARGET runtestspp PROPERTY RUNTIME_OUTPUT_DIRECTORY tests)
-    target_link_libraries(runtestspp expat)
     expat_add_test(runtestspp $<TARGET_FILE:runtestspp>)
+
+    if(_EXPAT_LIBM_FOUND)
+        target_link_libraries(runtests m)
+        target_link_libraries(runtestspp m)
+    endif()
+
+    if(EXPAT_WITH_LIBBSD)
+        target_link_libraries(runtests ${LIB_BSD})
+        target_link_libraries(runtestspp ${LIB_BSD})
+    endif()
 endif()
 
 if(EXPAT_BUILD_FUZZERS)
@@ -663,20 +784,31 @@
 else()
     set(_EXPAT_CHAR_TYPE_SUMMARY "ERROR")
 endif()
-string(TOUPPER "${CMAKE_BUILD_TYPE}" _EXPAT_BUILD_TYPE_UPPER)
+# NOTE: We're not accessing global property GENERATOR_IS_MULTI_CONFIG
+#       because that would require CMake >=3.9
+if(CMAKE_CONFIGURATION_TYPES)
+    set(_EXPAT_GENERATOR_IS_MULTI_CONFIG TRUE)
+else()
+    set(_EXPAT_GENERATOR_IS_MULTI_CONFIG FALSE)
+endif()
 
 message(STATUS "===========================================================================")
 message(STATUS "")
 message(STATUS "Configuration")
+message(STATUS "  Generator .................. ${CMAKE_GENERATOR}")
+if(_EXPAT_GENERATOR_IS_MULTI_CONFIG)
+    message(STATUS "  Build types ................ ${CMAKE_CONFIGURATION_TYPES}")
+else()
+    message(STATUS "  Build type ................. ${CMAKE_BUILD_TYPE}")
+endif()
 message(STATUS "  Prefix ..................... ${CMAKE_INSTALL_PREFIX}")
-message(STATUS "  Build type ................. ${CMAKE_BUILD_TYPE}")
 message(STATUS "  Shared libraries ........... ${EXPAT_SHARED_LIBS}")
 if(MSVC)
     message(STATUS "  Static CRT ................. ${EXPAT_MSVC_STATIC_CRT}")
 endif()
 message(STATUS "  Character type ............. ${_EXPAT_CHAR_TYPE_SUMMARY}")
-if(WIN32)
-    message(STATUS "  Binary postfix ............. ${CMAKE_${_EXPAT_BUILD_TYPE_UPPER}_POSTFIX}")
+if(NOT _EXPAT_GENERATOR_IS_MULTI_CONFIG)
+    message(STATUS "  Library name postfix ....... ${CMAKE_${_EXPAT_BUILD_TYPE_UPPER}_POSTFIX}")
 endif()
 message(STATUS "")
 message(STATUS "  Build documentation ........ ${EXPAT_BUILD_DOCS}")
diff --git a/Changes b/Changes
index edd485c..40127e1 100644
--- a/Changes
+++ b/Changes
@@ -2,6 +2,267 @@
       https://github.com/libexpat/libexpat/labels/help%20wanted
       If you can help, please get in touch.  Thanks!
 
+Release 2.4.6 Sun February 20 2022
+        Bug fixes:
+            #566  Fix a regression introduced by the fix for CVE-2022-25313
+                    in release 2.4.5 that affects applications that (1)
+                    call function XML_SetElementDeclHandler and (2) are
+                    parsing XML that contains nested element declarations
+                    (e.g. "<!ELEMENT junk ((bar|foo|xyz+), zebra*)>").
+
+        Other changes:
+       #567 #568  Version info bumped from 9:5:8 to 9:6:8;
+                    see https://verbump.de/ for what these numbers do
+
+        Special thanks to:
+            Matt Sergeant
+            Samanta Navarro
+            Sergei Trofimovich
+                 and
+            NixOS
+            Perl XML::Parser
+
+Release 2.4.5 Fri February 18 2022
+        Security fixes:
+            #562  CVE-2022-25235 -- Passing malformed 2- and 3-byte UTF-8
+                    sequences (e.g. from start tag names) to the XML
+                    processing application on top of Expat can cause
+                    arbitrary damage (e.g. code execution) depending
+                    on how invalid UTF-8 is handled inside the XML
+                    processor; validation was not their job but Expat's.
+                    Exploits with code execution are known to exist.
+            #561  CVE-2022-25236 -- Passing (one or more) namespace separator
+                    characters in "xmlns[:prefix]" attribute values
+                    made Expat send malformed tag names to the XML
+                    processor on top of Expat which can cause
+                    arbitrary damage (e.g. code execution) depending
+                    on such unexpectable cases are handled inside the XML
+                    processor; validation was not their job but Expat's.
+                    Exploits with code execution are known to exist.
+            #558  CVE-2022-25313 -- Fix stack exhaustion in doctype parsing
+                    that could be triggered by e.g. a 2 megabytes
+                    file with a large number of opening braces.
+                    Expected impact is denial of service or potentially
+                    arbitrary code execution.
+            #560  CVE-2022-25314 -- Fix integer overflow in function copyString;
+                    only affects the encoding name parameter at parser creation
+                    time which is often hardcoded (rather than user input),
+                    takes a value in the gigabytes to trigger, and a 64-bit
+                    machine.  Expected impact is denial of service.
+            #559  CVE-2022-25315 -- Fix integer overflow in function storeRawNames;
+                    needs input in the gigabytes and a 64-bit machine.
+                    Expected impact is denial of service or potentially
+                    arbitrary code execution.
+
+        Other changes:
+       #557 #564  Version info bumped from 9:4:8 to 9:5:8;
+                    see https://verbump.de/ for what these numbers do
+
+        Special thanks to:
+            Ivan Fratric
+            Samanta Navarro
+                 and
+            Google Project Zero
+            JetBrains
+
+Release 2.4.4 Sun January 30 2022
+        Security fixes:
+            #550  CVE-2022-23852 -- Fix signed integer overflow
+                    (undefined behavior) in function XML_GetBuffer
+                    (that is also called by function XML_Parse internally)
+                    for when XML_CONTEXT_BYTES is defined to >0 (which is both
+                    common and default).
+                    Impact is denial of service or more.
+            #551  CVE-2022-23990 -- Fix unsigned integer overflow in function
+                    doProlog triggered by large content in element type
+                    declarations when there is an element declaration handler
+                    present (from a prior call to XML_SetElementDeclHandler).
+                    Impact is denial of service or more.
+
+        Bug fixes:
+       #544 #545  xmlwf: Fix a memory leak on output file opening error
+
+        Other changes:
+            #546  Autotools: Fix broken CMake support under Cygwin
+            #554  Windows: Add missing files to the installer to fix
+                    compilation with CMake from installed sources
+       #552 #554  Version info bumped from 9:3:8 to 9:4:8;
+                    see https://verbump.de/ for what these numbers do
+
+        Special thanks to:
+            Carlo Bramini
+            hwt0415
+            Roland Illig
+            Samanta Navarro
+                 and
+            Clang LeakSan and the Clang team
+
+Release 2.4.3 Sun January 16 2022
+        Security fixes:
+       #531 #534  CVE-2021-45960 -- Fix issues with left shifts by >=29 places
+                    resulting in
+                      a) realloc acting as free
+                      b) realloc allocating too few bytes
+                      c) undefined behavior
+                    depending on architecture and precise value
+                    for XML documents with >=2^27+1 prefixed attributes
+                    on a single XML tag a la
+                    "<r xmlns:a='[..]' a:a123='[..]' [..] />"
+                    where XML_ParserCreateNS is used to create the parser
+                    (which needs argument "-n" when running xmlwf).
+                    Impact is denial of service, or more.
+       #532 #538  CVE-2021-46143 (ZDI-CAN-16157) -- Fix integer overflow
+                    on variable m_groupSize in function doProlog leading
+                    to realloc acting as free.
+                    Impact is denial of service or more.
+            #539  CVE-2022-22822 to CVE-2022-22827 -- Prevent integer overflows
+                    near memory allocation at multiple places.  Mitre assigned
+                    a dedicated CVE for each involved internal C function:
+                    - CVE-2022-22822 for function addBinding
+                    - CVE-2022-22823 for function build_model
+                    - CVE-2022-22824 for function defineAttribute
+                    - CVE-2022-22825 for function lookup
+                    - CVE-2022-22826 for function nextScaffoldPart
+                    - CVE-2022-22827 for function storeAtts
+                    Impact is denial of service or more.
+
+        Other changes:
+            #535  CMake: Make call to file(GENERATE [..]) work for CMake <3.19
+            #541  Autotools|CMake: MinGW: Make run.sh(.in) work for Cygwin
+                    and MSYS2 by not going through Wine on these platforms
+       #527 #528  Address compiler warnings
+       #533 #543  Version info bumped from 9:2:8 to 9:3:8;
+                    see https://verbump.de/ for what these numbers do
+
+        Infrastructure:
+            #536  CI: Check for realistic minimum CMake version
+       #529 #539  CI: Cover compilation with -m32
+            #529  CI: Store coverage reports as artifacts for download
+            #528  CI: Upgrade Clang from 11 to 13
+
+        Special thanks to:
+            An anonymous whitehat
+            Christopher Degawa
+            J. Peter Mugaas
+            Tyson Smith
+                 and
+            GCC Farm Project
+            Trend Micro Zero Day Initiative
+
+Release 2.4.2 Sun December 19 2021
+        Other changes:
+       #509 #510  Link againgst libm for function "isnan"
+       #513 #514  Include expat_config.h as early as possible
+            #498  Autotools: Include files with release archives:
+                    - buildconf.sh
+                    - fuzz/*.c
+       #507 #519  Autotools: Sync CMake templates
+       #495 #524  CMake: MinGW: Fix pkg-config section "Libs" for
+                    - non-release build types (e.g. -DCMAKE_BUILD_TYPE=Debug)
+                    - multi-config CMake generators (e.g. Ninja Multi-Config)
+       #502 #503  docs: Document that function XML_GetBuffer may return NULL
+                    when asking for a buffer of 0 (zero) bytes size
+       #522 #523  docs: Fix return value docs for both
+                    XML_SetBillionLaughsAttackProtection* functions
+       #525 #526  Version info bumped from 9:1:8 to 9:2:8;
+                    see https://verbump.de/ for what these numbers do
+
+        Special thanks to:
+            Dong-hee Na
+            Joergen Ibsen
+            Kai Pastor
+
+Release 2.4.1 Sun May 23 2021
+        Bug fixes:
+       #488 #490  Autotools: Fix installed header expat_config.h for multilib
+                    systems; regression introduced in 2.4.0 by pull request #486
+
+        Other changes:
+       #491 #492  Version info bumped from 9:0:8 to 9:1:8;
+                    see https://verbump.de/ for what these numbers do
+
+        Special thanks to:
+            Gentoo's QA check "multilib_check_headers"
+
+Release 2.4.0 Sun May 23 2021
+        Security fixes:
+   #34 #466 #484  CVE-2013-0340/CWE-776 -- Protect against billion laughs attacks
+                    (denial-of-service; flavors targeting CPU time or RAM or both,
+                    leveraging general entities or parameter entities or both)
+                    by tracking and limiting the input amplification factor
+                    (<amplification> := (<direct> + <indirect>) / <direct>).
+                    By conservative default, amplification up to a factor of 100.0
+                    is tolerated and rejection only starts after 8 MiB of output bytes
+                    (=<direct> + <indirect>) have been processed.
+                    The fix adds the following to the API:
+                    - A new error code XML_ERROR_AMPLIFICATION_LIMIT_BREACH to
+                      signals this specific condition.
+                    - Two new API functions ..
+                      - XML_SetBillionLaughsAttackProtectionMaximumAmplification and
+                      - XML_SetBillionLaughsAttackProtectionActivationThreshold
+                      .. to further tighten billion laughs protection parameters
+                      when desired.  Please see file "doc/reference.html" for details.
+                      If you ever need to increase the defaults for non-attack XML
+                      payload, please file a bug report with libexpat.
+                    - Two new XML_FEATURE_* constants ..
+                      - that can be queried using the XML_GetFeatureList function, and
+                      - that are shown in "xmlwf -v" output.
+                    - Two new environment variable switches ..
+                      - EXPAT_ACCOUNTING_DEBUG=(0|1|2|3) and
+                      - EXPAT_ENTITY_DEBUG=(0|1)
+                      .. for runtime debugging of accounting and entity processing.
+                      Specific behavior of these values may change in the future.
+                    - Two new command line arguments "-a FACTOR" and "-b BYTES"
+                      for xmlwf to further tighten billion laughs protection
+                      parameters when desired.
+                      If you ever need to increase the defaults for non-attack XML
+                      payload, please file a bug report with libexpat.
+
+        Bug fixes:
+       #332 #470  For (non-default) compilation with -DEXPAT_MIN_SIZE=ON (CMake)
+                    or CPPFLAGS=-DXML_MIN_SIZE (GNU Autotools): Fix segfault
+                    for UTF-16 payloads containing CDATA sections.
+       #485 #486  Autotools: Fix generated CMake files for non-64bit and
+                    non-Linux platforms (e.g. macOS and MinGW in particular)
+                    that were introduced with release 2.3.0
+
+        Other changes:
+       #468 #469  xmlwf: Improve help output and the xmlwf man page
+            #463  xmlwf: Improve maintainability through some refactoring
+            #477  xmlwf: Fix man page DocBook validity
+       #458 #459  CMake: Support absolute paths for both CMAKE_INSTALL_LIBDIR
+                    and CMAKE_INSTALL_INCLUDEDIR
+       #471 #481  CMake: Add support for standard variable BUILD_SHARED_LIBS
+            #457  Unexpose symbol _INTERNAL_trim_to_complete_utf8_characters
+            #467  Resolve macro HAVE_EXPAT_CONFIG_H
+            #472  Delete unused legacy helper file "conftools/PrintPath"
+       #473 #483  Improve attribution
+  #464 #465 #477  doc/reference.html: Fix XHTML validity
+       #475 #478  doc/reference.html: Replace the 90s look by OK.css
+            #479  Version info bumped from 8:0:7 to 9:0:8
+                    due to addition of new symbols and error codes;
+                    see https://verbump.de/ for what these numbers do
+
+        Infrastructure:
+            #456  CI: Enable periodic runs
+            #457  CI: Start covering the list of exported symbols
+            #474  CI: Isolate coverage task
+       #476 #482  CI: Adapt to breaking changes in image "ubuntu-18.04"
+            #477  CI: Cover well-formedness and DocBook/XHTML validity
+                    of doc/reference.html and doc/xmlwf.xml
+
+        Special thanks to:
+            Dimitry Andric
+            Eero Helenius
+            Nick Wellnhofer
+            Rhodri James
+            Tomas Korbar
+            Yury Gribov
+                 and
+            Clang LeakSan
+            JetBrains
+            OSS-Fuzz
+
 Release 2.3.0 Thu March 25 2021
         Bug fixes:
             #438  When calling XML_ParseBuffer without a prior successful call to
diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake
index d85e48c..638f0aa 100644
--- a/ConfigureChecks.cmake
+++ b/ConfigureChecks.cmake
@@ -2,6 +2,7 @@
 include(CheckCSourceCompiles)
 include(CheckIncludeFile)
 include(CheckIncludeFiles)
+include(CheckLibraryExists)
 include(CheckSymbolExists)
 include(TestBigEndian)
 
@@ -64,3 +65,5 @@
 
 check_c_compiler_flag("-fno-strict-aliasing" FLAG_NO_STRICT_ALIASING)
 check_c_compiler_flag("-fvisibility=hidden" FLAG_VISIBILITY)
+
+check_library_exists(m cos "" _EXPAT_LIBM_FOUND)
diff --git a/METADATA b/METADATA
index c3e35c9..53ee48c 100644
--- a/METADATA
+++ b/METADATA
@@ -10,13 +10,17 @@
   }
   url {
     type: ARCHIVE
-    value: "https://github.com/libexpat/libexpat/releases/download/R_2_3_0/expat-2.3.0.tar.bz2"
+    value: "https://github.com/libexpat/libexpat/releases/download/R_2_4_6/expat-2.4.6.tar.bz2"
   }
-  version: "R_2_3_0"
+  version: "R_2_4_6"
+  license_note: "contains GFDL documentation"
   license_type: BY_EXCEPTION_ONLY
+  security {
+    tag: "NVD-CPE2.3:cpe:/a:libexpat_project:libexpat:2.4.1"
+  }
   last_upgrade_date {
-    year: 2021
-    month: 4
-    day: 1
+    year: 2022
+    month: 2
+    day: 23
   }
 }
diff --git a/Makefile.am b/Makefile.am
index 4f3b660..e6e7971 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -6,7 +6,8 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017 Expat development team
+# Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2018      KangLin <kl222@126.com>
 # Licensed under the MIT license:
 #
 # Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -65,12 +66,16 @@
 
 
 _EXTRA_DIST_CMAKE = \
+    cmake/autotools/expat-noconfig__linux.cmake.in \
+    cmake/autotools/expat-noconfig__macos.cmake.in \
+    cmake/autotools/expat-noconfig__windows.cmake.in \
     cmake/autotools/expat-package-init.cmake \
     cmake/mingw-toolchain.cmake \
     \
     CMakeLists.txt \
     CMake.README \
     ConfigureChecks.cmake \
+    expat.pc.cmake \
     expat_config.h.cmake
 
 _EXTRA_DIST_WINDOWS = \
@@ -85,11 +90,14 @@
     \
     conftools/expat.m4 \
     conftools/get-version.sh \
-    conftools/PrintPath \
+    \
+    fuzz/xml_parsebuffer_fuzzer.c \
+    fuzz/xml_parse_fuzzer.c \
     \
     xmlwf/xmlwf_helpgen.py \
     xmlwf/xmlwf_helpgen.sh \
     \
+    buildconf.sh \
     Changes \
     README.md \
     \
diff --git a/Makefile.in b/Makefile.in
index 57f9c0c..7c6551f 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -22,7 +22,8 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017 Expat development team
+# Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2018      KangLin <kl222@126.com>
 # Licensed under the MIT license:
 #
 # Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -230,21 +231,17 @@
   unique=`for i in $$list; do \
     if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
   done | $(am__uniquify_input)`
-ETAGS = etags
-CTAGS = ctags
-CSCOPE = cscope
 DIST_SUBDIRS = lib examples tests xmlwf doc
 am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/expat.pc.in \
 	$(srcdir)/expat_config.h.in $(srcdir)/run.sh.in \
 	$(top_srcdir)/cmake/autotools/expat-config-version.cmake.in \
-	$(top_srcdir)/cmake/autotools/expat-noconfig.cmake.in \
 	$(top_srcdir)/cmake/expat-config.cmake.in \
 	$(top_srcdir)/conftools/ar-lib $(top_srcdir)/conftools/compile \
 	$(top_srcdir)/conftools/config.guess \
 	$(top_srcdir)/conftools/config.sub \
 	$(top_srcdir)/conftools/install-sh \
 	$(top_srcdir)/conftools/ltmain.sh \
-	$(top_srcdir)/conftools/missing AUTHORS COPYING \
+	$(top_srcdir)/conftools/missing AUTHORS COPYING README.md \
 	conftools/ar-lib conftools/compile conftools/config.guess \
 	conftools/config.sub conftools/depcomp conftools/install-sh \
 	conftools/ltmain.sh conftools/missing
@@ -309,7 +306,10 @@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+CMAKE_SHARED_LIBRARY_PREFIX = @CMAKE_SHARED_LIBRARY_PREFIX@
 CPPFLAGS = @CPPFLAGS@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
 CXX = @CXX@
 CXXCPP = @CXXCPP@
 CXXDEPMODE = @CXXDEPMODE@
@@ -325,6 +325,7 @@
 ECHO_N = @ECHO_N@
 ECHO_T = @ECHO_T@
 EGREP = @EGREP@
+ETAGS = @ETAGS@
 EXEEXT = @EXEEXT@
 EXPAT_ATTR_INFO = @EXPAT_ATTR_INFO@
 EXPAT_CHAR_TYPE = @EXPAT_CHAR_TYPE@
@@ -346,6 +347,7 @@
 LIBAGE = @LIBAGE@
 LIBCURRENT = @LIBCURRENT@
 LIBDIR_BASENAME = @LIBDIR_BASENAME@
+LIBM = @LIBM@
 LIBOBJS = @LIBOBJS@
 LIBREVISION = @LIBREVISION@
 LIBS = @LIBS@
@@ -380,7 +382,6 @@
 SO_PATCH = @SO_PATCH@
 STRIP = @STRIP@
 VERSION = @VERSION@
-_EXPAT_OUTPUT_NAME = @_EXPAT_OUTPUT_NAME@
 abs_builddir = @abs_builddir@
 abs_srcdir = @abs_srcdir@
 abs_top_builddir = @abs_top_builddir@
@@ -389,6 +390,7 @@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_cv_sizeof_void_p = @ac_cv_sizeof_void_p@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -457,12 +459,16 @@
 
 cmakedir = $(libdir)/cmake/expat-@PACKAGE_VERSION@
 _EXTRA_DIST_CMAKE = \
+    cmake/autotools/expat-noconfig__linux.cmake.in \
+    cmake/autotools/expat-noconfig__macos.cmake.in \
+    cmake/autotools/expat-noconfig__windows.cmake.in \
     cmake/autotools/expat-package-init.cmake \
     cmake/mingw-toolchain.cmake \
     \
     CMakeLists.txt \
     CMake.README \
     ConfigureChecks.cmake \
+    expat.pc.cmake \
     expat_config.h.cmake
 
 _EXTRA_DIST_WINDOWS = \
@@ -477,11 +483,14 @@
     \
     conftools/expat.m4 \
     conftools/get-version.sh \
-    conftools/PrintPath \
+    \
+    fuzz/xml_parsebuffer_fuzzer.c \
+    fuzz/xml_parse_fuzzer.c \
     \
     xmlwf/xmlwf_helpgen.py \
     xmlwf/xmlwf_helpgen.sh \
     \
+    buildconf.sh \
     Changes \
     README.md \
     \
@@ -546,7 +555,7 @@
 	cd $(top_builddir) && $(SHELL) ./config.status $@
 cmake/autotools/expat-config-version.cmake: $(top_builddir)/config.status $(top_srcdir)/cmake/autotools/expat-config-version.cmake.in
 	cd $(top_builddir) && $(SHELL) ./config.status $@
-cmake/autotools/expat-noconfig.cmake: $(top_builddir)/config.status $(top_srcdir)/cmake/autotools/expat-noconfig.cmake.in
+cmake/autotools/expat-noconfig.cmake: $(top_builddir)/config.status 
 	cd $(top_builddir) && $(SHELL) ./config.status $@
 run.sh: $(top_builddir)/config.status $(srcdir)/run.sh.in
 	cd $(top_builddir) && $(SHELL) ./config.status $@
@@ -728,7 +737,6 @@
 distclean-tags:
 	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
 	-rm -f cscope.out cscope.in.out cscope.po.out cscope.files
-
 distdir: $(BUILT_SOURCES)
 	$(MAKE) $(AM_MAKEFLAGS) distdir-am
 
diff --git a/README.md b/README.md
index 6e4b422..959c4a6 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +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)
 [![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.3.0
+# Expat, Release 2.4.6
 
 This is Expat, a C library for parsing XML, started by
-[James Clark](https://en.wikipedia.org/wiki/James_Clark_(programmer)) in 1997.
+[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
 are called when the parser discovers the associated structures in the
@@ -20,7 +22,7 @@
 - Microsoft Visual Studio >=15.0/2017 (rolling `${today} minus 5 years`)
 
 Windows users can use the
-[`expat-win32bin-*.*.*.exe` installer download](https://github.com/libexpat/libexpat/releases),
+[`expat-win32bin-*.*.*.{exe,zip}` download](https://github.com/libexpat/libexpat/releases),
 which includes both pre-compiled libraries and executables, and source code for
 developers.
 
@@ -40,10 +42,10 @@
 
 This approach leverages CMake's own [module `FindEXPAT`](https://cmake.org/cmake/help/latest/module/FindEXPAT.html).
 
-Notice the uppercase `EXPAT` in the following example:
+Notice the *uppercase* `EXPAT` in the following example:
 
 ```cmake
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.0)  # or 3.10, see below
 
 project(hello VERSION 1.0.0)
 
@@ -53,22 +55,27 @@
     hello.c
 )
 
-if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.10")
-    target_link_libraries(hello PUBLIC EXPAT::EXPAT)
-else()
-    target_include_directories(hello PRIVATE ${EXPAT_INCLUDE_DIRS})
-    target_link_libraries(hello PUBLIC ${EXPAT_LIBRARIES})
-endif()
+# a) for CMake >=3.10 (see CMake's FindEXPAT docs)
+target_link_libraries(hello PUBLIC EXPAT::EXPAT)
+
+# b) for CMake >=3.0
+target_include_directories(hello PRIVATE ${EXPAT_INCLUDE_DIRS})
+target_link_libraries(hello PUBLIC ${EXPAT_LIBRARIES})
 ```
 
 ### b) Config Mode
 
-This approach requires files from
-libexpat >=2.2.8 where packaging uses the CMake build system
-or
-libexpat >=2.3.0 where packaging uses the GNU Autotools build system.
+This approach requires files from…
 
-Notice the lowercase `expat` in the following example:
+- libexpat >=2.2.8 where packaging uses the CMake build system
+or
+- libexpat >=2.3.0 where packaging uses the GNU Autotools build system
+  on Linux
+or
+- libexpat >=2.4.0 where packaging uses the GNU Autotools build system
+  on macOS or MinGW.
+
+Notice the *lowercase* `expat` in the following example:
 
 ```cmake
 cmake_minimum_required(VERSION 3.0)
@@ -85,7 +92,7 @@
 ```
 
 
-## Buildung from a Git Clone
+## Building from a Git Clone
 
 If you are building Expat from a check-out from the
 [Git repository](https://github.com/libexpat/libexpat/),
@@ -101,7 +108,7 @@
 from a source distribution.
 
 
-## Buildung from a Source Distribution
+## Building from a Source Distribution
 
 ### a) Building with the configure script (i.e. GNU Autotools)
 
diff --git a/aclocal.m4 b/aclocal.m4
index 9eee1b5..8f7e74f 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1,6 +1,6 @@
-# generated automatically by aclocal 1.16.3 -*- Autoconf -*-
+# generated automatically by aclocal 1.16.5 -*- Autoconf -*-
 
-# Copyright (C) 1996-2020 Free Software Foundation, Inc.
+# 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,
@@ -20,7 +20,7 @@
 If you have problems, you may need to regenerate the build system entirely.
 To do so, use the procedure documented by the package, typically 'autoreconf'.])])
 
-# Copyright (C) 2002-2020 Free Software Foundation, Inc.
+# Copyright (C) 2002-2021 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -35,7 +35,7 @@
 [am__api_version='1.16'
 dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
 dnl require some minimum version.  Point them to the right macro.
-m4_if([$1], [1.16.3], [],
+m4_if([$1], [1.16.5], [],
       [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
 ])
 
@@ -51,12 +51,12 @@
 # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
 # This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
 AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
-[AM_AUTOMAKE_VERSION([1.16.3])dnl
+[AM_AUTOMAKE_VERSION([1.16.5])dnl
 m4_ifndef([AC_AUTOCONF_VERSION],
   [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
 _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
 
-# Copyright (C) 2011-2020 Free Software Foundation, Inc.
+# Copyright (C) 2011-2021 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -118,7 +118,7 @@
 
 # AM_AUX_DIR_EXPAND                                         -*- Autoconf -*-
 
-# Copyright (C) 2001-2020 Free Software Foundation, Inc.
+# Copyright (C) 2001-2021 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -170,7 +170,7 @@
 
 # AM_CONDITIONAL                                            -*- Autoconf -*-
 
-# Copyright (C) 1997-2020 Free Software Foundation, Inc.
+# Copyright (C) 1997-2021 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -201,7 +201,7 @@
 Usually this means the macro was only invoked conditionally.]])
 fi])])
 
-# Copyright (C) 1999-2020 Free Software Foundation, Inc.
+# Copyright (C) 1999-2021 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -392,7 +392,7 @@
 
 # Generate code to set up dependency tracking.              -*- Autoconf -*-
 
-# Copyright (C) 1999-2020 Free Software Foundation, Inc.
+# Copyright (C) 1999-2021 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -460,7 +460,7 @@
 
 # Do all the work for Automake.                             -*- Autoconf -*-
 
-# Copyright (C) 1996-2020 Free Software Foundation, Inc.
+# 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,
@@ -488,6 +488,10 @@
 # release and drop the old call support.
 AC_DEFUN([AM_INIT_AUTOMAKE],
 [AC_PREREQ([2.65])dnl
+m4_ifdef([_$0_ALREADY_INIT],
+  [m4_fatal([$0 expanded multiple times
+]m4_defn([_$0_ALREADY_INIT]))],
+  [m4_define([_$0_ALREADY_INIT], m4_expansion_stack)])dnl
 dnl Autoconf wants to disallow AM_ names.  We explicitly allow
 dnl the ones we care about.
 m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
@@ -524,7 +528,7 @@
 [_AM_SET_OPTIONS([$1])dnl
 dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
 m4_if(
-  m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]),
+  m4_ifset([AC_PACKAGE_NAME], [ok]):m4_ifset([AC_PACKAGE_VERSION], [ok]),
   [ok:ok],,
   [m4_fatal([AC_INIT should be called with package and version arguments])])dnl
  AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
@@ -576,6 +580,20 @@
 		  [m4_define([AC_PROG_OBJCXX],
 			     m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl
 ])
+# Variables for tags utilities; see am/tags.am
+if test -z "$CTAGS"; then
+  CTAGS=ctags
+fi
+AC_SUBST([CTAGS])
+if test -z "$ETAGS"; then
+  ETAGS=etags
+fi
+AC_SUBST([ETAGS])
+if test -z "$CSCOPE"; then
+  CSCOPE=cscope
+fi
+AC_SUBST([CSCOPE])
+
 AC_REQUIRE([AM_SILENT_RULES])dnl
 dnl The testsuite driver may need to know about EXEEXT, so add the
 dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen.  This
@@ -657,7 +675,7 @@
 done
 echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
 
-# Copyright (C) 2001-2020 Free Software Foundation, Inc.
+# Copyright (C) 2001-2021 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -678,7 +696,7 @@
 fi
 AC_SUBST([install_sh])])
 
-# Copyright (C) 2003-2020 Free Software Foundation, Inc.
+# Copyright (C) 2003-2021 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -699,7 +717,7 @@
 
 # Check to see how 'make' treats includes.	            -*- Autoconf -*-
 
-# Copyright (C) 2001-2020 Free Software Foundation, Inc.
+# Copyright (C) 2001-2021 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -742,7 +760,7 @@
 
 # Fake the existence of programs that GNU maintainers use.  -*- Autoconf -*-
 
-# Copyright (C) 1997-2020 Free Software Foundation, Inc.
+# Copyright (C) 1997-2021 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -776,7 +794,7 @@
 
 # Helper functions for option handling.                     -*- Autoconf -*-
 
-# Copyright (C) 2001-2020 Free Software Foundation, Inc.
+# Copyright (C) 2001-2021 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -805,7 +823,7 @@
 AC_DEFUN([_AM_IF_OPTION],
 [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
 
-# Copyright (C) 1999-2020 Free Software Foundation, Inc.
+# Copyright (C) 1999-2021 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -852,7 +870,7 @@
 # For backward compatibility.
 AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
 
-# Copyright (C) 2001-2020 Free Software Foundation, Inc.
+# Copyright (C) 2001-2021 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -871,7 +889,7 @@
 
 # Check to make sure that the build environment is sane.    -*- Autoconf -*-
 
-# Copyright (C) 1996-2020 Free Software Foundation, Inc.
+# 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,
@@ -952,7 +970,7 @@
 rm -f conftest.file
 ])
 
-# Copyright (C) 2009-2020 Free Software Foundation, Inc.
+# Copyright (C) 2009-2021 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1012,7 +1030,7 @@
 _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
 ])
 
-# Copyright (C) 2001-2020 Free Software Foundation, Inc.
+# Copyright (C) 2001-2021 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1040,7 +1058,7 @@
 INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
 AC_SUBST([INSTALL_STRIP_PROGRAM])])
 
-# Copyright (C) 2006-2020 Free Software Foundation, Inc.
+# Copyright (C) 2006-2021 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1059,7 +1077,7 @@
 
 # Check how to create a tarball.                            -*- Autoconf -*-
 
-# Copyright (C) 2004-2020 Free Software Foundation, Inc.
+# Copyright (C) 2004-2021 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
diff --git a/buildconf.sh b/buildconf.sh
new file mode 100755
index 0000000..5edbc56
--- /dev/null
+++ b/buildconf.sh
@@ -0,0 +1,55 @@
+#! /usr/bin/env bash
+#                          __  __            _
+#                       ___\ \/ /_ __   __ _| |_
+#                      / _ \\  /| '_ \ / _` | __|
+#                     |  __//  \| |_) | (_| | |_
+#                      \___/_/\_\ .__/ \__,_|\__|
+#                               |_| XML parser
+#
+# Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+# 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.
+
+set -e
+
+# File expat_config.h.in (as generated by autoheader by autoreconf) contains
+# macro SIZEOF_VOID_P which is (1) not really needed by Expat as of today and
+# (2) a problem to "multilib" systems with one shared installed
+# /usr/include/expat_config.h for two Expats with different "void *" sizes
+# installed in e.g. /usr/lib32 and /usr/lib64.  Hence we patch macro
+# SIZEOF_VOID_P out of template expat_config.h.in so that configure will
+# not put SIZEOF_VOID_P in the eventual expat_config.h.
+patch_expat_config_h_in() {
+    local filename="$1"
+    local sizeof_void_p_line_number="$(fgrep -n SIZEOF_VOID_P "${filename}" | awk -F: '{print $1}')"
+    [[ ${sizeof_void_p_line_number} =~ ^[0-9]+$ ]]  # cheap assert
+    local first_line_to_delete=$(( sizeof_void_p_line_number - 1 ))
+    local last_line_to_delete=$(( sizeof_void_p_line_number + 1 ))
+    # Note: Avoiding "sed -i" only for macOS portability.
+    local tempfile="$(mktemp)"
+    sed "${first_line_to_delete},${last_line_to_delete}d" "${filename}" > "${tempfile}"
+    mv "${tempfile}" "${filename}"
+}
+
+autoreconf --warnings=all --install --verbose "$@"
+
+patch_expat_config_h_in expat_config.h.in
diff --git a/cmake/autotools/expat-config-version.cmake.in b/cmake/autotools/expat-config-version.cmake.in
index 798d861..abdda6e 100644
--- a/cmake/autotools/expat-config-version.cmake.in
+++ b/cmake/autotools/expat-config-version.cmake.in
@@ -17,6 +17,9 @@
 
   if("@PACKAGE_VERSION@" MATCHES "^([0-9]+)\\.")
     set(CVF_VERSION_MAJOR "${CMAKE_MATCH_1}")
+    if(NOT CVF_VERSION_MAJOR VERSION_EQUAL 0)
+      string(REGEX REPLACE "^0+" "" CVF_VERSION_MAJOR "${CVF_VERSION_MAJOR}")
+    endif()
   else()
     set(CVF_VERSION_MAJOR "@PACKAGE_VERSION@")
   endif()
@@ -55,13 +58,13 @@
 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 "8" STREQUAL "")
+if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "@ac_cv_sizeof_void_p@" STREQUAL "")
   return()
 endif()
 
 # check that the installed version has the same 32/64bit-ness as the one which is currently searching:
-if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "8")
-  math(EXPR installedBits "8 * 8")
+if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "@ac_cv_sizeof_void_p@")
+  math(EXPR installedBits "@ac_cv_sizeof_void_p@ * 8")
   set(PACKAGE_VERSION "${PACKAGE_VERSION} (${installedBits}bit)")
   set(PACKAGE_VERSION_UNSUITABLE TRUE)
 endif()
diff --git a/cmake/autotools/expat-noconfig.cmake.in b/cmake/autotools/expat-noconfig__linux.cmake.in
similarity index 85%
rename from cmake/autotools/expat-noconfig.cmake.in
rename to cmake/autotools/expat-noconfig__linux.cmake.in
index 3959504..2726e48 100644
--- a/cmake/autotools/expat-noconfig.cmake.in
+++ b/cmake/autotools/expat-noconfig__linux.cmake.in
@@ -1,11 +1,11 @@
 #----------------------------------------------------------------
-# Generated CMake target import file.
+# Generated CMake target import file for configuration "NoConfig".
 #----------------------------------------------------------------
 
 # Commands may need to know the format version.
 set(CMAKE_IMPORT_FILE_VERSION 1)
 
-# Import target "expat::expat" for configuration ""
+# Import target "expat::expat" for configuration "NoConfig"
 set_property(TARGET expat::expat APPEND PROPERTY IMPORTED_CONFIGURATIONS NOCONFIG)
 set_target_properties(expat::expat PROPERTIES
   IMPORTED_LOCATION_NOCONFIG "${_IMPORT_PREFIX}/@LIBDIR_BASENAME@/libexpat.so.@SO_MAJOR@.@SO_MINOR@.@SO_PATCH@"
diff --git a/cmake/autotools/expat-noconfig.cmake.in b/cmake/autotools/expat-noconfig__macos.cmake.in
similarity index 66%
copy from cmake/autotools/expat-noconfig.cmake.in
copy to cmake/autotools/expat-noconfig__macos.cmake.in
index 3959504..0485cc6 100644
--- a/cmake/autotools/expat-noconfig.cmake.in
+++ b/cmake/autotools/expat-noconfig__macos.cmake.in
@@ -1,19 +1,19 @@
 #----------------------------------------------------------------
-# Generated CMake target import file.
+# Generated CMake target import file for configuration "NoConfig".
 #----------------------------------------------------------------
 
 # Commands may need to know the format version.
 set(CMAKE_IMPORT_FILE_VERSION 1)
 
-# Import target "expat::expat" for configuration ""
+# Import target "expat::expat" for configuration "NoConfig"
 set_property(TARGET expat::expat APPEND PROPERTY IMPORTED_CONFIGURATIONS NOCONFIG)
 set_target_properties(expat::expat PROPERTIES
-  IMPORTED_LOCATION_NOCONFIG "${_IMPORT_PREFIX}/@LIBDIR_BASENAME@/libexpat.so.@SO_MAJOR@.@SO_MINOR@.@SO_PATCH@"
-  IMPORTED_SONAME_NOCONFIG "libexpat.so.@SO_MAJOR@"
+  IMPORTED_LOCATION_NOCONFIG "${_IMPORT_PREFIX}/@LIBDIR_BASENAME@/libexpat.@SO_MAJOR@.@SO_MINOR@.@SO_PATCH@.dylib"
+  IMPORTED_SONAME_NOCONFIG "@rpath/libexpat.@SO_MAJOR@.dylib"
   )
 
 list(APPEND _IMPORT_CHECK_TARGETS expat::expat )
-list(APPEND _IMPORT_CHECK_FILES_FOR_expat::expat "${_IMPORT_PREFIX}/@LIBDIR_BASENAME@/libexpat.so.@SO_MAJOR@.@SO_MINOR@.@SO_PATCH@" )
+list(APPEND _IMPORT_CHECK_FILES_FOR_expat::expat "${_IMPORT_PREFIX}/@LIBDIR_BASENAME@/libexpat.@SO_MAJOR@.@SO_MINOR@.@SO_PATCH@.dylib" )
 
 # Commands beyond this point should not need to know the version.
 set(CMAKE_IMPORT_FILE_VERSION)
diff --git a/cmake/autotools/expat-noconfig__windows.cmake.in b/cmake/autotools/expat-noconfig__windows.cmake.in
new file mode 100644
index 0000000..cbed0ec
--- /dev/null
+++ b/cmake/autotools/expat-noconfig__windows.cmake.in
@@ -0,0 +1,19 @@
+#----------------------------------------------------------------
+# Generated CMake target import file for configuration "NoConfig".
+#----------------------------------------------------------------
+
+# Commands may need to know the format version.
+set(CMAKE_IMPORT_FILE_VERSION 1)
+
+# Import target "expat::expat" for configuration "NoConfig"
+set_property(TARGET expat::expat APPEND PROPERTY IMPORTED_CONFIGURATIONS NOCONFIG)
+set_target_properties(expat::expat PROPERTIES
+  IMPORTED_IMPLIB_NOCONFIG "${_IMPORT_PREFIX}/@LIBDIR_BASENAME@/libexpat.dll.a"
+  IMPORTED_LOCATION_NOCONFIG "${_IMPORT_PREFIX}/bin/@CMAKE_SHARED_LIBRARY_PREFIX@expat-@SO_MAJOR@.dll"
+  )
+
+list(APPEND _IMPORT_CHECK_TARGETS expat::expat )
+list(APPEND _IMPORT_CHECK_FILES_FOR_expat::expat "${_IMPORT_PREFIX}/@LIBDIR_BASENAME@/libexpat.dll.a" "${_IMPORT_PREFIX}/bin/@CMAKE_SHARED_LIBRARY_PREFIX@expat-@SO_MAJOR@.dll" )
+
+# Commands beyond this point should not need to know the version.
+set(CMAKE_IMPORT_FILE_VERSION)
diff --git a/cmake/autotools/expat.cmake b/cmake/autotools/expat.cmake
index 7861d41..24b5d92 100644
--- a/cmake/autotools/expat.cmake
+++ b/cmake/autotools/expat.cmake
@@ -1,10 +1,10 @@
 # Generated by CMake
 
-if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.5)
+if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.6)
    message(FATAL_ERROR "CMake >= 2.6.0 required")
 endif()
 cmake_policy(PUSH)
-cmake_policy(VERSION 2.6...3.17)
+cmake_policy(VERSION 2.6...3.20)
 #----------------------------------------------------------------
 # Generated CMake target import file.
 #----------------------------------------------------------------
@@ -55,8 +55,13 @@
 
 set_target_properties(expat::expat PROPERTIES
   INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
+  INTERFACE_LINK_LIBRARIES "m"
 )
 
+if(CMAKE_VERSION VERSION_LESS 2.8.12)
+  message(FATAL_ERROR "This file relies on consumers using CMake 2.8.12 or greater.")
+endif()
+
 # Load information for each installed configuration.
 get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
 file(GLOB CONFIG_FILES "${_DIR}/expat-*.cmake")
diff --git a/configure b/configure
index 31fa133..a13b801 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.71 for expat 2.3.0.
+# Generated by GNU Autoconf 2.71 for expat 2.4.6.
 #
 # Report bugs to <expat-bugs@libexpat.org>.
 #
@@ -621,8 +621,8 @@
 # Identity of this package.
 PACKAGE_NAME='expat'
 PACKAGE_TARNAME='expat'
-PACKAGE_VERSION='2.3.0'
-PACKAGE_STRING='expat 2.3.0'
+PACKAGE_VERSION='2.4.6'
+PACKAGE_STRING='expat 2.4.6'
 PACKAGE_BUGREPORT='expat-bugs@libexpat.org'
 PACKAGE_URL=''
 
@@ -664,11 +664,12 @@
 am__EXEEXT_TRUE
 LTLIBOBJS
 LIBOBJS
-_EXPAT_OUTPUT_NAME
+CMAKE_SHARED_LIBRARY_PREFIX
 AM_LDFLAGS
 AM_CXXFLAGS
 AM_CFLAGS
 AM_CPPFLAGS
+ac_cv_sizeof_void_p
 SO_PATCH
 SO_MINOR
 SO_MAJOR
@@ -684,6 +685,7 @@
 WITH_DOCBOOK_TRUE
 DOCBOOK_TO_MAN
 FILEMAP
+LIBM
 _INTERNAL_LARGE_SIZE_FALSE
 _INTERNAL_LARGE_SIZE_TRUE
 _INTERNAL_MIN_SIZE_FALSE
@@ -753,6 +755,9 @@
 AM_DEFAULT_VERBOSITY
 AM_DEFAULT_V
 AM_V
+CSCOPE
+ETAGS
+CTAGS
 am__untar
 am__tar
 AMTAR
@@ -1409,7 +1414,7 @@
   # 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.3.0 to adapt to many kinds of systems.
+\`configure' configures expat 2.4.6 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1480,7 +1485,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of expat 2.3.0:";;
+     short | recursive ) echo "Configuration of expat 2.4.6:";;
    esac
   cat <<\_ACEOF
 
@@ -1614,7 +1619,7 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-expat configure 2.3.0
+expat configure 2.4.6
 generated by GNU Autoconf 2.71
 
 Copyright (C) 2021 Free Software Foundation, Inc.
@@ -2032,6 +2037,195 @@
   eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
 
 } # ac_fn_c_check_type
+
+# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES
+# --------------------------------------------
+# Tries to find the compile-time value of EXPR in a program that includes
+# INCLUDES, setting VAR accordingly. Returns whether the value could be
+# computed
+ac_fn_c_compute_int ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if test "$cross_compiling" = yes; then
+    # Depending upon the size, compute the lo and hi bounds.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main (void)
+{
+static int test_array [1 - 2 * !(($2) >= 0)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_lo=0 ac_mid=0
+  while :; do
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main (void)
+{
+static int test_array [1 - 2 * !(($2) <= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_hi=$ac_mid; break
+else $as_nop
+  as_fn_arith $ac_mid + 1 && ac_lo=$as_val
+			if test $ac_lo -le $ac_mid; then
+			  ac_lo= ac_hi=
+			  break
+			fi
+			as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+  done
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main (void)
+{
+static int test_array [1 - 2 * !(($2) < 0)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_hi=-1 ac_mid=-1
+  while :; do
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main (void)
+{
+static int test_array [1 - 2 * !(($2) >= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_lo=$ac_mid; break
+else $as_nop
+  as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val
+			if test $ac_mid -le $ac_hi; then
+			  ac_lo= ac_hi=
+			  break
+			fi
+			as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+  done
+else $as_nop
+  ac_lo= ac_hi=
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+# Binary search between lo and hi bounds.
+while test "x$ac_lo" != "x$ac_hi"; do
+  as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main (void)
+{
+static int test_array [1 - 2 * !(($2) <= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_hi=$ac_mid
+else $as_nop
+  as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+done
+case $ac_lo in #((
+?*) eval "$3=\$ac_lo"; ac_retval=0 ;;
+'') ac_retval=1 ;;
+esac
+  else
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+static long int longval (void) { return $2; }
+static unsigned long int ulongval (void) { return $2; }
+#include <stdio.h>
+#include <stdlib.h>
+int
+main (void)
+{
+
+  FILE *f = fopen ("conftest.val", "w");
+  if (! f)
+    return 1;
+  if (($2) < 0)
+    {
+      long int i = longval ();
+      if (i != ($2))
+	return 1;
+      fprintf (f, "%ld", i);
+    }
+  else
+    {
+      unsigned long int i = ulongval ();
+      if (i != ($2))
+	return 1;
+      fprintf (f, "%lu", i);
+    }
+  /* Do not output a trailing newline, as this causes \r\n confusion
+     on some platforms.  */
+  return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+  echo >>conftest.val; read $3 <conftest.val; ac_retval=0
+else $as_nop
+  ac_retval=1
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f conftest.val
+
+  fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_compute_int
 ac_configure_args_raw=
 for ac_arg
 do
@@ -2056,7 +2250,7 @@
 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.3.0, which was
+It was created by expat $as_me 2.4.6, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -3623,7 +3817,7 @@
 
 # Define the identity of the package.
  PACKAGE='expat'
- VERSION='2.3.0'
+ VERSION='2.4.6'
 
 
 printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -3669,6 +3863,20 @@
 
 
 
+# Variables for tags utilities; see am/tags.am
+if test -z "$CTAGS"; then
+  CTAGS=ctags
+fi
+
+if test -z "$ETAGS"; then
+  ETAGS=etags
+fi
+
+if test -z "$CSCOPE"; then
+  CSCOPE=cscope
+fi
+
+
 
 # POSIX will say in a future version that running "rm -f" with no argument
 # is OK; and we want to be able to make that assumption in our Makefile
@@ -3715,42 +3923,9 @@
 
 
 
-LIBCURRENT=8   # sync
-LIBREVISION=0  # with
-LIBAGE=7       # CMakeLists.txt!
-
-
-if test ${AM_CPPFLAGS+y}
-then :
-
-  case " $AM_CPPFLAGS " in #(
-  *" -DHAVE_EXPAT_CONFIG_H "*) :
-    { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: : AM_CPPFLAGS already contains -DHAVE_EXPAT_CONFIG_H"; } >&5
-  (: AM_CPPFLAGS already contains -DHAVE_EXPAT_CONFIG_H) 2>&5
-  ac_status=$?
-  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
-  test $ac_status = 0; } ;; #(
-  *) :
-
-     as_fn_append AM_CPPFLAGS " -DHAVE_EXPAT_CONFIG_H"
-     { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: : AM_CPPFLAGS=\"\$AM_CPPFLAGS\""; } >&5
-  (: AM_CPPFLAGS="$AM_CPPFLAGS") 2>&5
-  ac_status=$?
-  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
-  test $ac_status = 0; }
-     ;;
-esac
-
-else $as_nop
-
-  AM_CPPFLAGS=-DHAVE_EXPAT_CONFIG_H
-  { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: : AM_CPPFLAGS=\"\$AM_CPPFLAGS\""; } >&5
-  (: AM_CPPFLAGS="$AM_CPPFLAGS") 2>&5
-  ac_status=$?
-  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
-  test $ac_status = 0; }
-
-fi
+LIBCURRENT=9   # sync
+LIBREVISION=6  # with
+LIBAGE=8       # CMakeLists.txt!
 
 ac_config_headers="$ac_config_headers expat_config.h"
 
@@ -13900,7 +14075,7 @@
 
 
 
-for flag in -Wshadow -Wformat=2 -Wmisleading-indentation; do
+for flag in -Wshadow -Wformat=2 -Wno-pedantic-ms-format -Wmisleading-indentation; do
   as_CACHEVAR=`printf "%s\n" "ax_cv_check_cflags__$flag" | $as_tr_sh`
 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts $flag" >&5
 printf %s "checking whether C compiler accepts $flag... " >&6; }
@@ -18677,6 +18852,138 @@
 fi
 
 
+LIBM=
+case $host in
+*-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*)
+  # These system don't have libm, or don't need it
+  ;;
+*-ncr-sysv4.3*)
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _mwvalidcheckl in -lmw" >&5
+printf %s "checking for _mwvalidcheckl in -lmw... " >&6; }
+if test ${ac_cv_lib_mw__mwvalidcheckl+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lmw  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+char _mwvalidcheckl ();
+int
+main (void)
+{
+return _mwvalidcheckl ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  ac_cv_lib_mw__mwvalidcheckl=yes
+else $as_nop
+  ac_cv_lib_mw__mwvalidcheckl=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_mw__mwvalidcheckl" >&5
+printf "%s\n" "$ac_cv_lib_mw__mwvalidcheckl" >&6; }
+if test "x$ac_cv_lib_mw__mwvalidcheckl" = xyes
+then :
+  LIBM=-lmw
+fi
+
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for cos in -lm" >&5
+printf %s "checking for cos in -lm... " >&6; }
+if test ${ac_cv_lib_m_cos+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+char cos ();
+int
+main (void)
+{
+return cos ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  ac_cv_lib_m_cos=yes
+else $as_nop
+  ac_cv_lib_m_cos=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_cos" >&5
+printf "%s\n" "$ac_cv_lib_m_cos" >&6; }
+if test "x$ac_cv_lib_m_cos" = xyes
+then :
+  LIBM="$LIBM -lm"
+fi
+
+  ;;
+*)
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for cos in -lm" >&5
+printf %s "checking for cos in -lm... " >&6; }
+if test ${ac_cv_lib_m_cos+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+char cos ();
+int
+main (void)
+{
+return cos ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  ac_cv_lib_m_cos=yes
+else $as_nop
+  ac_cv_lib_m_cos=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_cos" >&5
+printf "%s\n" "$ac_cv_lib_m_cos" >&6; }
+if test "x$ac_cv_lib_m_cos" = xyes
+then :
+  LIBM=-lm
+fi
+
+  ;;
+esac
+
+
 
 
 # Check whether --with-libbsd was given.
@@ -19279,6 +19586,39 @@
 SO_MAJOR="$(expr "${LIBCURRENT}" - "${LIBAGE}")"
 SO_MINOR="${LIBAGE}"
 SO_PATCH="${LIBREVISION}"
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of void *" >&5
+printf %s "checking size of void *... " >&6; }
+if test ${ac_cv_sizeof_void_p+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (void *))" "ac_cv_sizeof_void_p"        "$ac_includes_default"
+then :
+
+else $as_nop
+  if test "$ac_cv_type_void_p" = yes; then
+     { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (void *)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_void_p=0
+   fi
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_void_p" >&5
+printf "%s\n" "$ac_cv_sizeof_void_p" >&6; }
+
+
+
+printf "%s\n" "#define SIZEOF_VOID_P $ac_cv_sizeof_void_p" >>confdefs.h
+
+  # sets ac_cv_sizeof_void_p
 
 
 
@@ -19298,10 +19638,28 @@
 
 
 
-_EXPAT_OUTPUT_NAME="$PACKAGE_NAME"
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for shared library name prefix" >&5
+printf %s "checking for shared library name prefix... " >&6; }
+case "${host_os}" in #(
+  cygwin*) :
+    CMAKE_SHARED_LIBRARY_PREFIX=cyg ;; #(
+  *) :
+    CMAKE_SHARED_LIBRARY_PREFIX=lib ;;
+esac
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${CMAKE_SHARED_LIBRARY_PREFIX}" >&5
+printf "%s\n" "${CMAKE_SHARED_LIBRARY_PREFIX}" >&6; }
 
 
-ac_config_files="$ac_config_files Makefile expat.pc cmake/expat-config.cmake cmake/autotools/expat-config-version.cmake cmake/autotools/expat-noconfig.cmake doc/Makefile examples/Makefile lib/Makefile tests/Makefile tests/benchmark/Makefile xmlwf/Makefile"
+case "${host_os}" in #(
+  darwin*) :
+    CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__macos.cmake.in ;; #(
+  mingw*|cygwin*) :
+    CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__windows.cmake.in ;; #(
+  *) :
+    CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__linux.cmake.in ;;
+esac
+ac_config_files="$ac_config_files Makefile expat.pc cmake/expat-config.cmake cmake/autotools/expat-config-version.cmake cmake/autotools/expat-noconfig.cmake:${CMAKE_NOCONFIG_SOURCE} doc/Makefile examples/Makefile lib/Makefile tests/Makefile tests/benchmark/Makefile xmlwf/Makefile"
 
 ac_config_files="$ac_config_files run.sh"
 
@@ -19869,7 +20227,7 @@
 # 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.3.0, which was
+This file was extended by expat $as_me 2.4.6, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -19937,7 +20295,7 @@
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config='$ac_cs_config_escaped'
 ac_cs_version="\\
-expat config.status 2.3.0
+expat config.status 2.4.6
 configured by $0, generated by GNU Autoconf 2.71,
   with options \\"\$ac_cs_config\\"
 
@@ -20456,7 +20814,7 @@
     "expat.pc") CONFIG_FILES="$CONFIG_FILES expat.pc" ;;
     "cmake/expat-config.cmake") CONFIG_FILES="$CONFIG_FILES cmake/expat-config.cmake" ;;
     "cmake/autotools/expat-config-version.cmake") CONFIG_FILES="$CONFIG_FILES cmake/autotools/expat-config-version.cmake" ;;
-    "cmake/autotools/expat-noconfig.cmake") CONFIG_FILES="$CONFIG_FILES cmake/autotools/expat-noconfig.cmake" ;;
+    "cmake/autotools/expat-noconfig.cmake") CONFIG_FILES="$CONFIG_FILES cmake/autotools/expat-noconfig.cmake:${CMAKE_NOCONFIG_SOURCE}" ;;
     "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
     "examples/Makefile") CONFIG_FILES="$CONFIG_FILES examples/Makefile" ;;
     "lib/Makefile") CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;;
diff --git a/configure.ac b/configure.ac
index 9a6858e..5175487 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,14 +1,47 @@
 dnl   configuration script for expat
 dnl   Process this file with autoconf to produce a configure script.
+dnl                            __  __            _
+dnl                         ___\ \/ /_ __   __ _| |_
+dnl                        / _ \\  /| '_ \ / _` | __|
+dnl                       |  __//  \| |_) | (_| | |_
+dnl                        \___/_/\_\ .__/ \__,_|\__|
+dnl                                 |_| XML parser
 dnl
-dnl   Copyright 2000 Clark Cooper
+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) 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>
+dnl   Copyright (c) 2018      Yury Gribov <tetra2005@gmail.com>
+dnl   Copyright (c) 2018      Benjamin Peterson <benjamin@python.org>
+dnl   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+dnl   Copyright (c) 2018      KangLin <kl222@126.com>
+dnl   Copyright (c) 2019      Mohammed Khajapasha <mohammed.khajapasha@intel.com>
+dnl   Copyright (c) 2019      Kishore Kunche <kishore.kunche@intel.com>
+dnl   Copyright (c) 2020      Jeffrey Walton <noloader@gmail.com>
+dnl   Licensed under the MIT license:
 dnl
-dnl   This file is part of EXPAT.
+dnl   Permission is  hereby granted,  free of charge,  to any  person obtaining
+dnl   a  copy  of  this  software   and  associated  documentation  files  (the
+dnl   "Software"),  to  deal in  the  Software  without restriction,  including
+dnl   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+dnl   distribute, sublicense, and/or sell copies of the Software, and to permit
+dnl   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+dnl   following conditions:
 dnl
-dnl   EXPAT is free software; you can redistribute it and/or modify it
-dnl   under the terms of the License (based on the MIT/X license) contained
-dnl   in the file COPYING that comes with this distribution.
+dnl   The above copyright  notice and this permission notice  shall be included
+dnl   in all copies or substantial portions of the Software.
 dnl
+dnl   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+dnl   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+dnl   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+dnl   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+dnl   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+dnl   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+dnl   USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 dnl Ensure that Expat is configured with autoconf 2.69 or newer.
 AC_PREREQ([2.69])
@@ -48,11 +81,10 @@
 dnl If the API changes incompatibly set LIBAGE back to 0
 dnl
 
-LIBCURRENT=8   # sync
-LIBREVISION=0  # with
-LIBAGE=7       # CMakeLists.txt!
+LIBCURRENT=9   # sync
+LIBREVISION=6  # with
+LIBAGE=8       # CMakeLists.txt!
 
-AX_APPEND_FLAG([-DHAVE_EXPAT_CONFIG_H], [AM_CPPFLAGS])
 AC_CONFIG_HEADERS([expat_config.h])
 
 AM_PROG_AR
@@ -79,7 +111,7 @@
    AX_APPEND_COMPILE_FLAGS([-fno-strict-aliasing -Wmissing-prototypes -Wstrict-prototypes], [AM_CFLAGS])
    AX_APPEND_COMPILE_FLAGS([-pedantic -Wduplicated-cond -Wduplicated-branches -Wlogical-op], [AM_CFLAGS])
    AX_APPEND_COMPILE_FLAGS([-Wrestrict -Wnull-dereference -Wjump-misses-init -Wdouble-promotion], [AM_CFLAGS])
-   AX_APPEND_COMPILE_FLAGS([-Wshadow -Wformat=2 -Wmisleading-indentation], [AM_CFLAGS])])
+   AX_APPEND_COMPILE_FLAGS([-Wshadow -Wformat=2 -Wno-pedantic-ms-format -Wmisleading-indentation], [AM_CFLAGS])])
 
 AC_LANG_PUSH([C++])
 AC_PROG_CXX
@@ -154,6 +186,7 @@
 AM_CONDITIONAL([_INTERNAL_MIN_SIZE], [echo -- "${CPPFLAGS}${CFLAGS}" | ${FGREP} XML_MIN_SIZE >/dev/null])
 AM_CONDITIONAL([_INTERNAL_LARGE_SIZE], [echo -- "${CPPFLAGS}${CFLAGS}" | ${FGREP} XML_LARGE_SIZE >/dev/null])
 
+LT_LIB_M
 
 AC_ARG_WITH([libbsd],
   [AS_HELP_STRING([--with-libbsd], [utilize libbsd (for arc4random_buf)])],
@@ -340,6 +373,7 @@
 SO_MAJOR="$(expr "${LIBCURRENT}" - "${LIBAGE}")"
 SO_MINOR="${LIBAGE}"
 SO_PATCH="${LIBREVISION}"
+AC_CHECK_SIZEOF([void *])  # sets ac_cv_sizeof_void_p
 AC_SUBST([EXPAT_ATTR_INFO])
 AC_SUBST([EXPAT_DTD])
 AC_SUBST([EXPAT_LARGE_SIZE])
@@ -352,6 +386,7 @@
 AC_SUBST([SO_MAJOR])
 AC_SUBST([SO_MINOR])
 AC_SUBST([SO_PATCH])
+AC_SUBST([ac_cv_sizeof_void_p])
 
 
 dnl write the Automake flags we set
@@ -360,14 +395,23 @@
 AC_SUBST([AM_CXXFLAGS])
 AC_SUBST([AM_LDFLAGS])
 
-dnl updating _EXPAT_OUTPUT_NAME variable to effect the package name in expat.pc file (issue #361)
-AC_SUBST(_EXPAT_OUTPUT_NAME, ["$PACKAGE_NAME"])
+dnl Emulate the use of CMAKE_SHARED_LIBRARY_PREFIX under CMake
+AC_MSG_CHECKING([for shared library name prefix])
+AS_CASE("${host_os}",
+  [cygwin*], [CMAKE_SHARED_LIBRARY_PREFIX=cyg],
+  [CMAKE_SHARED_LIBRARY_PREFIX=lib])
+AC_MSG_RESULT([${CMAKE_SHARED_LIBRARY_PREFIX}])
+AC_SUBST([CMAKE_SHARED_LIBRARY_PREFIX])
 
+AS_CASE("${host_os}",
+  [darwin*], [CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__macos.cmake.in],
+  [mingw*|cygwin*], [CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__windows.cmake.in],
+  [CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__linux.cmake.in])
 AC_CONFIG_FILES([Makefile]
   [expat.pc]
   [cmake/expat-config.cmake]
   [cmake/autotools/expat-config-version.cmake]
-  [cmake/autotools/expat-noconfig.cmake]
+  [cmake/autotools/expat-noconfig.cmake:${CMAKE_NOCONFIG_SOURCE}]
   [doc/Makefile]
   [examples/Makefile]
   [lib/Makefile]
diff --git a/conftools/PrintPath b/conftools/PrintPath
deleted file mode 100755
index e8559a3..0000000
--- a/conftools/PrintPath
+++ /dev/null
@@ -1,116 +0,0 @@
-#!/bin/sh
-# Look for program[s] somewhere in $PATH.
-#
-# Options:
-#  -s
-#    Do not print out full pathname. (silent)
-#  -pPATHNAME
-#    Look in PATHNAME instead of $PATH
-#
-# Usage:
-#  PrintPath [-s] [-pPATHNAME] program [program ...]
-#
-# Initially written by Jim Jagielski for the Apache configuration mechanism
-#  (with kudos to Kernighan/Pike)
-#
-# This script falls under the Apache License.
-# See http://www.apache.org/licenses/LICENSE
-
-##
-# Some "constants"
-##
-pathname=$PATH
-echo="yes"
-
-##
-# Find out what OS we are running for later on
-##
-os=`(uname) 2>/dev/null`
-
-##
-# Parse command line
-##
-for args in $*
-do
-    case $args in
-	-s  ) echo="no" ;;
-	-p* ) pathname="`echo $args | sed 's/^..//'`" ;;
-	*   ) programs="$programs $args" ;;
-    esac
-done
-
-##
-# Now we make the adjustments required for OS/2 and everyone
-# else :)
-#
-# First of all, all OS/2 programs have the '.exe' extension.
-# Next, we adjust PATH (or what was given to us as PATH) to
-# be whitespace separated directories.
-# Finally, we try to determine the best flag to use for
-# test/[] to look for an executable file. OS/2 just has '-r'
-# but with other OSs, we do some funny stuff to check to see
-# if test/[] knows about -x, which is the preferred flag.
-##
-
-if [ "x$os" = "xOS/2" ]
-then
-    ext=".exe"
-    pathname=`echo -E $pathname |
-     sed 's/^;/.;/
-	  s/;;/;.;/g
-	  s/;$/;./
-	  s/;/ /g
-	  s/\\\\/\\//g' `
-    test_exec_flag="-r"
-else
-    ext=""	# No default extensions
-    pathname=`echo $pathname |
-     sed 's/^:/.:/
-	  s/::/:.:/g
-	  s/:$/:./
-	  s/:/ /g' `
-    # Here is how we test to see if test/[] can handle -x
-    testfile="pp.t.$$"
-
-    cat > $testfile <<ENDTEST
-#!/bin/sh
-if [ -x / ] || [ -x /bin ] || [ -x /bin/ls ]; then
-    exit 0
-fi
-exit 1
-ENDTEST
-
-    if `/bin/sh $testfile 2>/dev/null`; then
-	test_exec_flag="-x"
-    else
-	test_exec_flag="-r"
-    fi
-    rm -f $testfile
-fi
-
-for program in $programs
-do
-    for path in $pathname
-    do
-	if [ $test_exec_flag $path/${program}${ext} ] && \
-	   [ ! -d $path/${program}${ext} ]; then
-	    if [ "x$echo" = "xyes" ]; then
-		echo $path/${program}${ext}
-	    fi
-	    exit 0
-	fi
-
-# Next try without extension (if one was used above)
-	if [ "x$ext" != "x" ]; then
-            if [ $test_exec_flag $path/${program} ] && \
-               [ ! -d $path/${program} ]; then
-                if [ "x$echo" = "xyes" ]; then
-                    echo $path/${program}
-                fi
-                exit 0
-            fi
-        fi
-    done
-done
-exit 1
-
diff --git a/conftools/get-version.sh b/conftools/get-version.sh
index 2592f10..273ed3a 100755
--- a/conftools/get-version.sh
+++ b/conftools/get-version.sh
@@ -1,13 +1,39 @@
 #!/bin/sh
-#
 # USAGE: get-version.sh path/to/expat.h
 #
 # This script will print Expat's version number on stdout. For example:
 #
 #   $ ./conftools/get-version.sh ./lib/expat.h
 #   1.95.3
-#   $
+#                          __  __            _
+#                       ___\ \/ /_ __   __ _| |_
+#                      / _ \\  /| '_ \ / _` | __|
+#                     |  __//  \| |_) | (_| | |_
+#                      \___/_/\_\ .__/ \__,_|\__|
+#                               |_| XML parser
 #
+# Copyright (c) 2002 Greg Stein <gstein@users.sourceforge.net>
+# Copyright (c) 2017 Kerin Millar <kfm@plushkava.net>
+# 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 test $# = 0; then
   echo "ERROR: pathname for expat.h was not provided."
diff --git a/doc/Makefile.am b/doc/Makefile.am
index e2f0298..c3a3ce5 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -6,7 +6,9 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017 Expat development team
+# Copyright (c) 2017-2022 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:
 #
 # Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -52,8 +54,7 @@
 	$(RM) xmlwf.1
 
 EXTRA_DIST = \
-    expat.png \
+    ok.min.css \
     reference.html \
     style.css \
-    valid-xhtml10.png \
     xmlwf.xml
diff --git a/doc/Makefile.in b/doc/Makefile.in
index 6fac738..9c7d76d 100644
--- a/doc/Makefile.in
+++ b/doc/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -22,7 +22,9 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017 Expat development team
+# Copyright (c) 2017-2022 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:
 #
 # Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -207,7 +209,10 @@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+CMAKE_SHARED_LIBRARY_PREFIX = @CMAKE_SHARED_LIBRARY_PREFIX@
 CPPFLAGS = @CPPFLAGS@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
 CXX = @CXX@
 CXXCPP = @CXXCPP@
 CXXDEPMODE = @CXXDEPMODE@
@@ -223,6 +228,7 @@
 ECHO_N = @ECHO_N@
 ECHO_T = @ECHO_T@
 EGREP = @EGREP@
+ETAGS = @ETAGS@
 EXEEXT = @EXEEXT@
 EXPAT_ATTR_INFO = @EXPAT_ATTR_INFO@
 EXPAT_CHAR_TYPE = @EXPAT_CHAR_TYPE@
@@ -244,6 +250,7 @@
 LIBAGE = @LIBAGE@
 LIBCURRENT = @LIBCURRENT@
 LIBDIR_BASENAME = @LIBDIR_BASENAME@
+LIBM = @LIBM@
 LIBOBJS = @LIBOBJS@
 LIBREVISION = @LIBREVISION@
 LIBS = @LIBS@
@@ -278,7 +285,6 @@
 SO_PATCH = @SO_PATCH@
 STRIP = @STRIP@
 VERSION = @VERSION@
-_EXPAT_OUTPUT_NAME = @_EXPAT_OUTPUT_NAME@
 abs_builddir = @abs_builddir@
 abs_srcdir = @abs_srcdir@
 abs_top_builddir = @abs_top_builddir@
@@ -287,6 +293,7 @@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_cv_sizeof_void_p = @ac_cv_sizeof_void_p@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -335,10 +342,9 @@
 top_srcdir = @top_srcdir@
 @WITH_DOCBOOK_TRUE@dist_man_MANS = xmlwf.1
 EXTRA_DIST = \
-    expat.png \
+    ok.min.css \
     reference.html \
     style.css \
-    valid-xhtml10.png \
     xmlwf.xml
 
 all: all-am
@@ -429,7 +435,6 @@
 cscope cscopelist:
 
 @WITH_DOCBOOK_TRUE@dist-hook:
-
 distdir: $(BUILT_SOURCES)
 	$(MAKE) $(AM_MAKEFLAGS) distdir-am
 
diff --git a/doc/expat.png b/doc/expat.png
deleted file mode 100644
index 7d7c3db..0000000
--- a/doc/expat.png
+++ /dev/null
Binary files differ
diff --git a/doc/ok.min.css b/doc/ok.min.css
new file mode 100644
index 0000000..8b5f86e
--- /dev/null
+++ b/doc/ok.min.css
@@ -0,0 +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
diff --git a/doc/reference.html b/doc/reference.html
index 1e4780e..26db5a6 100644
--- a/doc/reference.html
+++ b/doc/reference.html
@@ -3,26 +3,55 @@
                       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html>
 <head>
-<!-- Copyright 1999,2000 Clark Cooper <coopercc@netheaven.com>
-     All rights reserved.
-     This is free software. You may distribute or modify according to
-     the terms of the MIT/X License -->
+<!--
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   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      Jakub Wilk <jwilk@jwilk.net>
+   Copyright (c) 2021      Tomas Korbar <tkorbar@redhat.com>
+   Copyright (c) 2021      Nicolas Cavallari <nicolas.cavallari@green-communications.fr>
+   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.
+-->
   <title>Expat XML Parser</title>
   <meta name="author" content="Clark Cooper, coopercc@netheaven.com" />
   <meta http-equiv="Content-Style-Type" content="text/css" />
+  <link href="ok.min.css" rel="stylesheet" type="text/css" />
   <link href="style.css" rel="stylesheet" type="text/css" />
 </head>
 <body>
-  <table cellspacing="0" cellpadding="0" width="100%">
-    <tr>
-      <td class="corner"><img src="expat.png" alt="(Expat logo)" /></td>
-      <td class="banner"><h1>The Expat XML Parser</h1></td>
-    </tr>
-    <tr>
-      <td class="releaseno">Release 2.0.1</td>
-      <td></td>
-    </tr>
-  </table>
+  <div>
+    <h1>
+      The Expat XML Parser
+      <small>Release 2.4.6</small>
+    </h1>
+  </div>
 <div class="content">
 
 <p>Expat is a library, written in C, for parsing XML documents. It's
@@ -120,6 +149,13 @@
       <li><a href="#XML_GetInputContext">XML_GetInputContext</a></li>
     </ul>
     </li>
+    <li>
+      <a href="#billion-laughs">Billion Laughs Attack Protection</a>
+      <ul>
+        <li><a href="#XML_SetBillionLaughsAttackProtectionMaximumAmplification">XML_SetBillionLaughsAttackProtectionMaximumAmplification</a></li>
+        <li><a href="#XML_SetBillionLaughsAttackProtectionActivationThreshold">XML_SetBillionLaughsAttackProtectionActivationThreshold</a></li>
+      </ul>
+    </li>
     <li><a href="#miscellaneous">Miscellaneous Functions</a>
     <ul>
       <li><a href="#XML_SetUserData">XML_SetUserData</a></li>
@@ -900,7 +936,8 @@
 
 <h3><a name="creation">Parser Creation</a></h3>
 
-<pre class="fcndec" id="XML_ParserCreate">
+<h4 id="XML_ParserCreate">XML_ParserCreate</h4>
+<pre class="fcndec">
 XML_Parser XMLCALL
 XML_ParserCreate(const XML_Char *encoding);
 </pre>
@@ -917,7 +954,8 @@
 Any other value will invoke a call to the UnknownEncodingHandler.
 </div>
 
-<pre class="fcndec" id="XML_ParserCreateNS">
+<h4 id="XML_ParserCreateNS">XML_ParserCreateNS</h4>
+<pre class="fcndec">
 XML_Parser XMLCALL
 XML_ParserCreateNS(const XML_Char *encoding,
                    XML_Char sep);
@@ -936,7 +974,8 @@
 to support RDF processors. It is a programming error to use the null separator
 with <a href= "#XML_SetReturnNSTriplet">namespace triplets</a>.</div>
 
-<pre class="fcndec" id="XML_ParserCreate_MM">
+<h4 id="XML_ParserCreate_MM">XML_ParserCreate_MM</h4>
+<pre class="fcndec">
 XML_Parser XMLCALL
 XML_ParserCreate_MM(const XML_Char *encoding,
                     const XML_Memory_Handling_Suite *ms,
@@ -958,7 +997,8 @@
 the namespace URI and the local part of the name.</p>
 </div>
 
-<pre class="fcndec" id="XML_ExternalEntityParserCreate">
+<h4 id="XML_ExternalEntityParserCreate">XML_ExternalEntityParserCreate</h4>
+<pre class="fcndec">
 XML_Parser XMLCALL
 XML_ExternalEntityParserCreate(XML_Parser p,
                                const XML_Char *context,
@@ -974,7 +1014,8 @@
 differently than the parent parser).
 </div>
 
-<pre class="fcndec" id="XML_ParserFree">
+<h4 id="XML_ParserFree">XML_ParserFree</h4>
+<pre class="fcndec">
 void XMLCALL
 XML_ParserFree(XML_Parser p);
 </pre>
@@ -983,7 +1024,8 @@
 freeing any memory associated with <a href="#userdata">user data</a>.
 </div>
 
-<pre class="fcndec" id="XML_ParserReset">
+<h4 id="XML_ParserReset">XML_ParserReset</h4>
+<pre class="fcndec">
 XML_Bool XMLCALL
 XML_ParserReset(XML_Parser p,
                 const XML_Char *encoding);
@@ -1014,7 +1056,7 @@
 <code><a href= "#XML_ExternalEntityParserCreate"
 >XML_ExternalEntityParserCreate</a></code>.</p>
 
-<p>Note: the <code>len</code> argument passed to these functions
+<p>Note: The <code>len</code> argument passed to these functions
 should be considerably less than the maximum value for an integer,
 as it could create an integer overflow situation if the added
 lengths of a buffer and the unprocessed portion of the previous buffer
@@ -1022,7 +1064,8 @@
 will remain unprocessed if it is part of an XML token for which the
 end is not part of that buffer.</p>
 
-<pre class="fcndec" id="XML_Parse">
+<h4 id="XML_Parse">XML_Parse</h4>
+<pre class="fcndec">
 enum XML_Status XMLCALL
 XML_Parse(XML_Parser p,
           const char *s,
@@ -1049,7 +1092,8 @@
 Otherwise it returns <code>XML_STATUS_OK</code> value.
 </div>
 
-<pre class="fcndec" id="XML_ParseBuffer">
+<h4 id="XML_ParseBuffer">XML_ParseBuffer</h4>
+<pre class="fcndec">
 enum XML_Status XMLCALL
 XML_ParseBuffer(XML_Parser p,
                 int len,
@@ -1063,7 +1107,8 @@
 copying of the input.
 </div>
 
-<pre class="fcndec" id="XML_GetBuffer">
+<h4 id="XML_GetBuffer">XML_GetBuffer</h4>
+<pre class="fcndec">
 void * XMLCALL
 XML_GetBuffer(XML_Parser p,
               int len);
@@ -1071,7 +1116,8 @@
 <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. This has to be called prior to every call to
+this buffer. A NULL 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:
 
@@ -1098,7 +1144,8 @@
 </pre>
 </div>
 
-<pre class="fcndec" id="XML_StopParser">
+<h4 id="XML_StopParser">XML_StopParser</h4>
+<pre class="fcndec">
 enum XML_Status XMLCALL
 XML_StopParser(XML_Parser p,
                XML_Bool resumable);
@@ -1111,7 +1158,7 @@
 call-back handler, except when aborting (when <code>resumable</code>
 is <code>XML_FALSE</code>) an already suspended parser.  Some
 call-backs may still follow because they would otherwise get
-lost, including
+lost, including</p>
 <ul>
   <li> the end element handler for empty elements when stopped in the
        start element handler,</li>
@@ -1120,7 +1167,7 @@
   <li> the character data handler when stopped in the character data handler
        while making multiple call-backs on a contiguous chunk of characters,</li>
 </ul>
-and possibly others.</p>
+<p>and possibly others.</p>
 
 <p>This can be called from most handlers, including DTD related
 call-backs, except when parsing an external parameter entity and
@@ -1166,7 +1213,8 @@
 <p>New in Expat 1.95.8.</p>
 </div>
 
-<pre class="fcndec" id="XML_ResumeParser">
+<h4 id="XML_ResumeParser">XML_ResumeParser</h4>
+<pre class="fcndec">
 enum XML_Status XMLCALL
 XML_ResumeParser(XML_Parser p);
 </pre>
@@ -1191,7 +1239,8 @@
 <p>New in Expat 1.95.8.</p>
 </div>
 
-<pre class="fcndec" id="XML_GetParsingStatus">
+<h4 id="XML_GetParsingStatus">XML_GetParsingStatus</h4>
+<pre class="fcndec">
 void XMLCALL
 XML_GetParsingStatus(XML_Parser p,
                      XML_ParsingStatus *status);
@@ -1240,7 +1289,8 @@
 encoding of the document.</p>
 
 <div class="handler">
-<pre class="setter" id="XML_SetStartElementHandler">
+<h4 id="XML_SetStartElementHandler">XML_SetStartElementHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetStartElementHandler(XML_Parser p,
                            XML_StartElementHandler start);
@@ -1261,7 +1311,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetEndElementHandler">
+<h4 id="XML_SetEndElementHandler">XML_SetEndElementHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetEndElementHandler(XML_Parser p,
                          XML_EndElementHandler);
@@ -1276,7 +1327,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetElementHandler">
+<h4 id="XML_SetElementHandler">XML_SetElementHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetElementHandler(XML_Parser p,
                       XML_StartElementHandler start,
@@ -1286,7 +1338,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetCharacterDataHandler">
+<h4 id="XML_SetCharacterDataHandler">XML_SetCharacterDataHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetCharacterDataHandler(XML_Parser p,
                             XML_CharacterDataHandler charhndl)
@@ -1309,7 +1362,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetProcessingInstructionHandler">
+<h4 id="XML_SetProcessingInstructionHandler">XML_SetProcessingInstructionHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetProcessingInstructionHandler(XML_Parser p,
                                     XML_ProcessingInstructionHandler proc)
@@ -1327,7 +1381,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetCommentHandler">
+<h4 id="XML_SetCommentHandler">XML_SetCommentHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetCommentHandler(XML_Parser p,
                       XML_CommentHandler cmnt)
@@ -1342,7 +1397,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetStartCdataSectionHandler">
+<h4 id="XML_SetStartCdataSectionHandler">XML_SetStartCdataSectionHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetStartCdataSectionHandler(XML_Parser p,
                                 XML_StartCdataSectionHandler start);
@@ -1355,7 +1411,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetEndCdataSectionHandler">
+<h4 id="XML_SetEndCdataSectionHandler">XML_SetEndCdataSectionHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetEndCdataSectionHandler(XML_Parser p,
                               XML_EndCdataSectionHandler end);
@@ -1368,7 +1425,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetCdataSectionHandler">
+<h4 id="XML_SetCdataSectionHandler">XML_SetCdataSectionHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetCdataSectionHandler(XML_Parser p,
                            XML_StartCdataSectionHandler start,
@@ -1378,7 +1436,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetDefaultHandler">
+<h4 id="XML_SetDefaultHandler">XML_SetDefaultHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetDefaultHandler(XML_Parser p,
                       XML_DefaultHandler hndl)
@@ -1409,7 +1468,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetDefaultHandlerExpand">
+<h4 id="XML_SetDefaultHandlerExpand">XML_SetDefaultHandlerExpand</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetDefaultHandlerExpand(XML_Parser p,
                             XML_DefaultHandler hndl)
@@ -1429,7 +1489,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetExternalEntityRefHandler">
+<h4 id="XML_SetExternalEntityRefHandler">XML_SetExternalEntityRefHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetExternalEntityRefHandler(XML_Parser p,
                                 XML_ExternalEntityRefHandler hndl)
@@ -1482,7 +1543,8 @@
 information into global or static variables.</p>
 </div>
 
-<pre class="fcndec" id="XML_SetExternalEntityRefHandlerArg">
+<h4 id="XML_SetExternalEntityRefHandlerArg">XML_SetExternalEntityRefHandlerArg</h4>
+<pre class="fcndec">
 void XMLCALL
 XML_SetExternalEntityRefHandlerArg(XML_Parser p,
                                    void *arg)
@@ -1508,7 +1570,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetSkippedEntityHandler">
+<h4 id="XML_SetSkippedEntityHandler">XML_SetSkippedEntityHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetSkippedEntityHandler(XML_Parser p,
                             XML_SkippedEntityHandler handler)
@@ -1528,14 +1591,15 @@
 	   has been called.</li>
 </ol>
 <p>The <code>is_parameter_entity</code> argument will be non-zero for
-a parameter entity and zero for a general entity.</p> <p>Note: skipped
+a parameter entity and zero for a general entity.</p> <p>Note: Skipped
 parameter entities in declarations and skipped general entities in
 attribute values cannot be reported, because the event would be out of
 sync with the reporting of the declarations or attribute values</p>
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetUnknownEncodingHandler">
+<h4 id="XML_SetUnknownEncodingHandler">XML_SetUnknownEncodingHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetUnknownEncodingHandler(XML_Parser p,
                               XML_UnknownEncodingHandler enchandler,
@@ -1584,7 +1648,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetStartNamespaceDeclHandler">
+<h4 id="XML_SetStartNamespaceDeclHandler">XML_SetStartNamespaceDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetStartNamespaceDeclHandler(XML_Parser p,
 			         XML_StartNamespaceDeclHandler start);
@@ -1602,7 +1667,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetEndNamespaceDeclHandler">
+<h4 id="XML_SetEndNamespaceDeclHandler">XML_SetEndNamespaceDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetEndNamespaceDeclHandler(XML_Parser p,
 			       XML_EndNamespaceDeclHandler end);
@@ -1619,7 +1685,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetNamespaceDeclHandler">
+<h4 id="XML_SetNamespaceDeclHandler">XML_SetNamespaceDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetNamespaceDeclHandler(XML_Parser p,
                             XML_StartNamespaceDeclHandler start,
@@ -1629,7 +1696,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetXmlDeclHandler">
+<h4 id="XML_SetXmlDeclHandler">XML_SetXmlDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetXmlDeclHandler(XML_Parser p,
 		      XML_XmlDeclHandler xmldecl);
@@ -1652,7 +1720,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetStartDoctypeDeclHandler">
+<h4 id="XML_SetStartDoctypeDeclHandler">XML_SetStartDoctypeDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetStartDoctypeDeclHandler(XML_Parser p,
 			       XML_StartDoctypeDeclHandler start);
@@ -1672,7 +1741,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetEndDoctypeDeclHandler">
+<h4 id="XML_SetEndDoctypeDeclHandler">XML_SetEndDoctypeDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetEndDoctypeDeclHandler(XML_Parser p,
 			     XML_EndDoctypeDeclHandler end);
@@ -1686,7 +1756,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetDoctypeDeclHandler">
+<h4 id="XML_SetDoctypeDeclHandler">XML_SetDoctypeDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetDoctypeDeclHandler(XML_Parser p,
 			  XML_StartDoctypeDeclHandler start,
@@ -1696,7 +1767,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetElementDeclHandler">
+<h4 id="XML_SetElementDeclHandler">XML_SetElementDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetElementDeclHandler(XML_Parser p,
 			  XML_ElementDeclHandler eldecl);
@@ -1768,7 +1840,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetAttlistDeclHandler">
+<h4 id="XML_SetAttlistDeclHandler">XML_SetAttlistDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetAttlistDeclHandler(XML_Parser p,
                           XML_AttlistDeclHandler attdecl);
@@ -1801,7 +1874,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetEntityDeclHandler">
+<h4 id="XML_SetEntityDeclHandler">XML_SetEntityDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetEntityDeclHandler(XML_Parser p,
 			 XML_EntityDeclHandler handler);
@@ -1835,7 +1909,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetUnparsedEntityDeclHandler">
+<h4 id="XML_SetUnparsedEntityDeclHandler">XML_SetUnparsedEntityDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetUnparsedEntityDeclHandler(XML_Parser p,
                                  XML_UnparsedEntityDeclHandler h)
@@ -1861,7 +1936,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetNotationDeclHandler">
+<h4 id="XML_SetNotationDeclHandler">XML_SetNotationDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetNotationDeclHandler(XML_Parser p,
                            XML_NotationDeclHandler h)
@@ -1878,7 +1954,8 @@
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetNotStandaloneHandler">
+<h4 id="XML_SetNotStandaloneHandler">XML_SetNotStandaloneHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetNotStandaloneHandler(XML_Parser p,
                             XML_NotStandaloneHandler h)
@@ -1913,7 +1990,8 @@
 DTD.  In other words, they usually return bogus information when
 called from within a DTD declaration handler.</p>
 
-<pre class="fcndec" id="XML_GetErrorCode">
+<h4 id="XML_GetErrorCode">XML_GetErrorCode</h4>
+<pre class="fcndec">
 enum XML_Error XMLCALL
 XML_GetErrorCode(XML_Parser p);
 </pre>
@@ -1921,7 +1999,8 @@
 Return what type of error has occurred.
 </div>
 
-<pre class="fcndec" id="XML_ErrorString">
+<h4 id="XML_ErrorString">XML_ErrorString</h4>
+<pre class="fcndec">
 const XML_LChar * XMLCALL
 XML_ErrorString(enum XML_Error code);
 </pre>
@@ -1931,7 +2010,8 @@
 <code><a href= "#XML_GetErrorCode" >XML_GetErrorCode</a></code>.
 </div>
 
-<pre class="fcndec" id="XML_GetCurrentByteIndex">
+<h4 id="XML_GetCurrentByteIndex">XML_GetCurrentByteIndex</h4>
+<pre class="fcndec">
 XML_Index XMLCALL
 XML_GetCurrentByteIndex(XML_Parser p);
 </pre>
@@ -1942,7 +2022,8 @@
 "#XML_GetCurrentColumnNumber" >XML_GetCurrentColumnNumber</a></code>.
 </div>
 
-<pre class="fcndec" id="XML_GetCurrentLineNumber">
+<h4 id="XML_GetCurrentLineNumber">XML_GetCurrentLineNumber</h4>
+<pre class="fcndec">
 XML_Size XMLCALL
 XML_GetCurrentLineNumber(XML_Parser p);
 </pre>
@@ -1951,7 +2032,8 @@
 <code>1</code>.
 </div>
 
-<pre class="fcndec" id="XML_GetCurrentColumnNumber">
+<h4 id="XML_GetCurrentColumnNumber">XML_GetCurrentColumnNumber</h4>
+<pre class="fcndec">
 XML_Size XMLCALL
 XML_GetCurrentColumnNumber(XML_Parser p);
 </pre>
@@ -1960,7 +2042,8 @@
 the position.
 </div>
 
-<pre class="fcndec" id="XML_GetCurrentByteCount">
+<h4 id="XML_GetCurrentByteCount">XML_GetCurrentByteCount</h4>
+<pre class="fcndec">
 int XMLCALL
 XML_GetCurrentByteCount(XML_Parser p);
 </pre>
@@ -1972,7 +2055,8 @@
 separate start and end tags).
 </div>
 
-<pre class="fcndec" id="XML_GetInputContext">
+<h4 id="XML_GetInputContext">XML_GetInputContext</h4>
+<pre class="fcndec">
 const char * XMLCALL
 XML_GetInputContext(XML_Parser p,
                     int *offset,
@@ -1998,12 +2082,105 @@
 return NULL.</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>
+
+<h4 id="XML_SetBillionLaughsAttackProtectionMaximumAmplification">XML_SetBillionLaughsAttackProtectionMaximumAmplification</h4>
+<pre class="fcndec">
+/* Added in Expat 2.4.0. */
+XML_Bool XMLCALL
+XML_SetBillionLaughsAttackProtectionMaximumAmplification(XML_Parser p,
+                                                         float maximumAmplificationFactor);
+</pre>
+<div class="fcndef">
+  <p>
+    Sets the maximum tolerated amplification factor
+    for protection against
+    <a href="https://en.wikipedia.org/wiki/Billion_laughs_attack">billion laughs attacks</a>
+    (default: <code>100.0</code>)
+    of parser <code>p</code> to <code>maximumAmplificationFactor</code>, and
+    returns <code>XML_TRUE</code> upon success and <code>XML_FALSE</code> upon error.
+  </p>
+
+  The amplification factor is calculated as ..
+  <pre>
+    amplification := (direct + indirect) / direct
+  </pre>
+  .. while parsing, whereas
+  <code>direct</code> is the number of bytes read from the primary document in parsing and
+  <code>indirect</code> is the number of bytes added by expanding entities and reading of external DTD files, combined.
+
+  <p>For a call to <code>XML_SetBillionLaughsAttackProtectionMaximumAmplification</code> to succeed:</p>
+  <ul>
+    <li>parser <code>p</code> must be a non-<code>NULL</code> root parser (without any parent parsers) and</li>
+    <li><code>maximumAmplificationFactor</code> must be non-<code>NaN</code> and greater than or equal to <code>1.0</code>.</li>
+  </ul>
+
+  <p>
+    <strong>Note:</strong>
+    If you ever need to increase this value for non-attack payload,
+    please <a href="https://github.com/libexpat/libexpat/issues">file a bug report</a>.
+  </p>
+
+  <p>
+    <strong>Note:</strong>
+    Peak amplifications
+    of factor 15,000 for the entire payload and
+    of factor 30,000 in the middle of parsing
+    have been observed with small benign files in practice.
+
+    So if you do reduce the maximum allowed amplification,
+    please make sure that the activation threshold is still big enough
+    to not end up with undesired false positives (i.e. benign files being rejected).
+  </p>
+</div>
+
+<h4 id="XML_SetBillionLaughsAttackProtectionActivationThreshold">XML_SetBillionLaughsAttackProtectionActivationThreshold</h4>
+<pre class="fcndec">
+/* Added in Expat 2.4.0. */
+XML_Bool XMLCALL
+XML_SetBillionLaughsAttackProtectionActivationThreshold(XML_Parser p,
+                                                        unsigned long long activationThresholdBytes);
+</pre>
+<div class="fcndef">
+  <p>
+    Sets number of output bytes (including amplification from entity expansion and reading DTD files)
+    needed to activate protection against
+    <a href="https://en.wikipedia.org/wiki/Billion_laughs_attack">billion laughs attacks</a>
+    (default: <code>8 MiB</code>)
+    of parser <code>p</code> to <code>activationThresholdBytes</code>, and
+    returns <code>XML_TRUE</code> upon success and <code>XML_FALSE</code> upon error.
+  </p>
+
+  <p>For a call to <code>XML_SetBillionLaughsAttackProtectionActivationThreshold</code> to succeed:</p>
+  <ul>
+    <li>parser <code>p</code> must be a non-<code>NULL</code> root parser (without any parent parsers).</li>
+  </ul>
+
+  <p>
+    <strong>Note:</strong>
+    If you ever need to increase this value for non-attack payload,
+    please <a href="https://github.com/libexpat/libexpat/issues">file a bug report</a>.
+  </p>
+
+  <p>
+    <strong>Note:</strong>
+    Activation thresholds below 4 MiB are known to break support for
+    <a href="https://en.wikipedia.org/wiki/Darwin_Information_Typing_Architecture">DITA</a> 1.3 payload
+    and are hence not recommended.
+  </p>
+</div>
+
 <h3><a name="miscellaneous">Miscellaneous functions</a></h3>
 
 <p>The functions in this section either obtain state information from
 the parser or can be used to dynamically set parser options.</p>
 
-<pre class="fcndec" id="XML_SetUserData">
+<h4 id="XML_SetUserData">XML_SetUserData</h4>
+<pre class="fcndec">
 void XMLCALL
 XML_SetUserData(XML_Parser p,
                 void *userData);
@@ -2018,7 +2195,8 @@
 memory.
 </div>
 
-<pre class="fcndec" id="XML_GetUserData">
+<h4 id="XML_GetUserData">XML_GetUserData</h4>
+<pre class="fcndec">
 void * XMLCALL
 XML_GetUserData(XML_Parser p);
 </pre>
@@ -2027,7 +2205,8 @@
 It is actually implemented as a macro.
 </div>
 
-<pre class="fcndec" id="XML_UseParserAsHandlerArg">
+<h4 id="XML_UseParserAsHandlerArg">XML_UseParserAsHandlerArg</h4>
+<pre class="fcndec">
 void XMLCALL
 XML_UseParserAsHandlerArg(XML_Parser p);
 </pre>
@@ -2038,7 +2217,8 @@
 >XML_GetUserData</a></code> function.
 </div>
 
-<pre class="fcndec" id="XML_SetBase">
+<h4 id="XML_SetBase">XML_SetBase</h4>
+<pre class="fcndec">
 enum XML_Status XMLCALL
 XML_SetBase(XML_Parser p,
             const XML_Char *base);
@@ -2050,7 +2230,8 @@
 <code>XML_STATUS_OK</code>.
 </div>
 
-<pre class="fcndec" id="XML_GetBase">
+<h4 id="XML_GetBase">XML_GetBase</h4>
+<pre class="fcndec">
 const XML_Char * XMLCALL
 XML_GetBase(XML_Parser p);
 </pre>
@@ -2058,7 +2239,8 @@
 Return the base for resolving relative URIs.
 </div>
 
-<pre class="fcndec" id="XML_GetSpecifiedAttributeCount">
+<h4 id="XML_GetSpecifiedAttributeCount">XML_GetSpecifiedAttributeCount</h4>
+<pre class="fcndec">
 int XMLCALL
 XML_GetSpecifiedAttributeCount(XML_Parser p);
 </pre>
@@ -2074,7 +2256,8 @@
 means the current call.
 </div>
 
-<pre class="fcndec" id="XML_GetIdAttributeIndex">
+<h4 id="XML_GetIdAttributeIndex">XML_GetIdAttributeIndex</h4>
+<pre class="fcndec">
 int XMLCALL
 XML_GetIdAttributeIndex(XML_Parser p);
 </pre>
@@ -2086,7 +2269,8 @@
 current call.
 </div>
 
-<pre class="fcndec" id="XML_GetAttributeInfo">
+<h4 id="XML_GetAttributeInfo">XML_GetAttributeInfo</h4>
+<pre class="fcndec">
 const XML_AttrInfo * XMLCALL
 XML_GetAttributeInfo(XML_Parser parser);
 </pre>
@@ -2107,7 +2291,8 @@
 <code>XML_GetSpecifiedAttributeCount(parser) / 2</code>.
 </div>
 
-<pre class="fcndec" id="XML_SetEncoding">
+<h4 id="XML_SetEncoding">XML_SetEncoding</h4>
+<pre class="fcndec">
 enum XML_Status XMLCALL
 XML_SetEncoding(XML_Parser p,
                 const XML_Char *encoding);
@@ -2122,7 +2307,8 @@
 <code>XML_STATUS_ERROR</code> on error.
 </div>
 
-<pre class="fcndec" id="XML_SetParamEntityParsing">
+<h4 id="XML_SetParamEntityParsing">XML_SetParamEntityParsing</h4>
+<pre class="fcndec">
 int XMLCALL
 XML_SetParamEntityParsing(XML_Parser p,
                           enum XML_ParamEntityParsing code);
@@ -2142,7 +2328,8 @@
 no effect and will always return 0.
 </div>
 
-<pre class="fcndec" id="XML_SetHashSalt">
+<h4 id="XML_SetHashSalt">XML_SetHashSalt</h4>
+<pre class="fcndec">
 int XMLCALL
 XML_SetHashSalt(XML_Parser p,
                 unsigned long hash_salt);
@@ -2153,15 +2340,16 @@
 function behavior. In order to have an effect this must be called
 before parsing has started. Returns 1 if successful, 0 when called
 after <code>XML_Parse</code> or <code>XML_ParseBuffer</code>.
-<p><b>Note:</b>This call is optional, as the parser will auto-generate 
+<p><b>Note:</b> This call is optional, as the parser will auto-generate
 a new random salt value if no value has been set at the start of parsing.</p>
-<p><b>Note:</b>One should not call <code>XML_SetHashSalt</code> with a
+<p><b>Note:</b> One should not call <code>XML_SetHashSalt</code> with a
 hash salt value of 0, as this value is used as sentinel value to indicate
 that <code>XML_SetHashSalt</code> has <b>not</b> been called. Consequently
 such a call will have no effect, even if it returns 1.</p>
 </div>
 
-<pre class="fcndec" id="XML_UseForeignDTD">
+<h4 id="XML_UseForeignDTD">XML_UseForeignDTD</h4>
+<pre class="fcndec">
 enum XML_Error XMLCALL
 XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD);
 </pre>
@@ -2198,7 +2386,8 @@
 the external entity reference handler returns without action.</p>
 </div>
 
-<pre class="fcndec" id="XML_SetReturnNSTriplet">
+<h4 id="XML_SetReturnNSTriplet">XML_SetReturnNSTriplet</h4>
+<pre class="fcndec">
 void XMLCALL
 XML_SetReturnNSTriplet(XML_Parser parser,
                        int        do_nst);
@@ -2220,7 +2409,8 @@
 separator.</p>
 </div>
 
-<pre class="fcndec" id="XML_DefaultCurrent">
+<h4 id="XML_DefaultCurrent">XML_DefaultCurrent</h4>
+<pre class="fcndec">
 void XMLCALL
 XML_DefaultCurrent(XML_Parser parser);
 </pre>
@@ -2234,7 +2424,8 @@
 not a default handler.
 </div>
 
-<pre class="fcndec" id="XML_ExpatVersion">
+<h4 id="XML_ExpatVersion">XML_ExpatVersion</h4>
+<pre class="fcndec">
 XML_LChar * XMLCALL
 XML_ExpatVersion();
 </pre>
@@ -2242,7 +2433,8 @@
 Return the library version as a string (e.g. <code>"expat_1.95.1"</code>).
 </div>
 
-<pre class="fcndec" id="XML_ExpatVersionInfo">
+<h4 id="XML_ExpatVersionInfo">XML_ExpatVersionInfo</h4>
+<pre class="fcndec">
 struct XML_Expat_Version XMLCALL
 XML_ExpatVersionInfo();
 </pre>
@@ -2266,7 +2458,8 @@
 particular parts of the Expat API are available.
 </div>
 
-<pre class="fcndec" id="XML_GetFeatureList">
+<h4 id="XML_GetFeatureList">XML_GetFeatureList</h4>
+<pre class="fcndec">
 const XML_Feature * XMLCALL
 XML_GetFeatureList();
 </pre>
@@ -2327,7 +2520,8 @@
 </dl>
 </div>
 
-<pre class="fcndec" id="XML_FreeContentModel">
+<h4 id="XML_FreeContentModel">XML_FreeContentModel</h4>
+<pre class="fcndec">
 void XMLCALL
 XML_FreeContentModel(XML_Parser parser, XML_Content *model);
 </pre>
@@ -2346,7 +2540,8 @@
 libraries which use different C standard libraries (this can happen on
 Windows, at least).</p>
 
-<pre class="fcndec" id="XML_MemMalloc">
+<h4 id="XML_MemMalloc">XML_MemMalloc</h4>
+<pre class="fcndec">
 void * XMLCALL
 XML_MemMalloc(XML_Parser parser, size_t size);
 </pre>
@@ -2358,7 +2553,8 @@
 >XML_MemFree</a></code>.
 </div>
 
-<pre class="fcndec" id="XML_MemRealloc">
+<h4 id="XML_MemRealloc">XML_MemRealloc</h4>
+<pre class="fcndec">
 void * XMLCALL
 XML_MemRealloc(XML_Parser parser, void *ptr, size_t size);
 </pre>
@@ -2377,7 +2573,8 @@
 >XML_MemFree</a></code>.
 </div>
 
-<pre class="fcndec" id="XML_MemFree">
+<h4 id="XML_MemFree">XML_MemFree</h4>
+<pre class="fcndec">
 void XMLCALL
 XML_MemFree(XML_Parser parser, void *ptr);
 </pre>
@@ -2388,9 +2585,12 @@
 </div>
 
 <hr />
-<p><a href="http://validator.w3.org/check/referer"><img
-        src="valid-xhtml10.png" alt="Valid XHTML 1.0!"
-        height="31" width="88" class="noborder" /></a></p>
+
+  <div class="footer">
+    Found a bug in the documentation?
+    <a href="https://github.com/libexpat/libexpat/issues">Please file a bug report.</a>
+  </div>
+
 </div>
 </body>
 </html>
diff --git a/doc/style.css b/doc/style.css
index 69df30b..1b8cd10 100644
--- a/doc/style.css
+++ b/doc/style.css
@@ -1,101 +1,47 @@
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2000-2004 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2021      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.
+*/
+
+/* Stop not using half the screen */
 body {
-  background-color: white;
-  border: 0px;
-  margin: 0px;
-  padding: 0px;
-}
-
-.corner {
-  width: 200px;
-  height: 80px;
-  text-align: center;
-}
-
-.banner {
-  background-color: rgb(110,139,61);
-  color: rgb(255,236,176);
-  padding-left: 2em;
-}
-
-.banner h1 {
-  font-size: 200%;
-}
-
-.content {
-  padding: 0em 2em 1em 2em;
-}
-
-.releaseno {
-  background-color: rgb(110,139,61);
-  color: rgb(255,236,176);
-  padding-bottom: 0.3em;
-  padding-top: 0.5em;
-  text-align: center;
-  font-weight: bold;
-}
-
-.noborder {
-  border-width: 0px;
-}
-
-.eg {
-  padding-left: 1em;
-  padding-top: .5em;
-  padding-bottom: .5em;
-  border: solid thin;
-  margin: 1em 0;
-  background-color: tan;
-  margin-left: 2em;
-  margin-right: 10%;
-}
-
-.pseudocode {
-  padding-left: 1em;
-  padding-top: .5em;
-  padding-bottom: .5em;
-  border: solid thin;
-  margin: 1em 0;
-  background-color: rgb(250,220,180);
-  margin-left: 2em;
-  margin-right: 10%;
-}
-
-.handler {
-  width: 100%;
-  border-top-width: thin;  
-  margin-bottom: 1em;
-}
-
-.handler p {
-  margin-left: 2em;
-}
-
-.setter {
-  font-weight: bold;
-}
-
-.signature {
-  color: navy;
-}
-
-.fcndec {
-  width: 100%;
-  border-top-width: thin;
-  font-weight: bold;
-}
-
-.fcndef {
-  margin-left: 2em;
-  margin-bottom: 2em;
-}
-
-dd {
-  margin-bottom: 2em;
+  max-width: none; /* was: 80ch */
 }
 
 .cpp-symbols dt {
   font-family: monospace;
 }
-.cpp-symbols dd {
-  margin-bottom: 1em;
+
+/* Resemble style of <footer> which is not part of xhtml1-strict */
+.footer {
+  font-size: var(--ok-fs-5);
+  color: var(--ok-tc-1);
 }
diff --git a/doc/valid-xhtml10.png b/doc/valid-xhtml10.png
deleted file mode 100644
index 8b5f1ba..0000000
--- a/doc/valid-xhtml10.png
+++ /dev/null
Binary files differ
diff --git a/doc/xmlwf.1 b/doc/xmlwf.1
index 0644610..f931d63 100644
--- a/doc/xmlwf.1
+++ b/doc/xmlwf.1
@@ -5,7 +5,7 @@
 \\$2 \(la\\$1\(ra\\$3
 ..
 .if \n(.g .mso www.tmac
-.TH XMLWF 1 "March 11, 2016" "" ""
+.TH XMLWF 1 "February 20, 2022" "" ""
 .SH NAME
 xmlwf \- Determines if an XML document is well-formed
 .SH SYNOPSIS
@@ -15,7 +15,27 @@
 \fBxmlwf\fR \kx
 .if (\nx>(\n(.l/2)) .nr x (\n(.l/5)
 'in \n(.iu+\nxu
-[\fB-s\fR] [\fB-n\fR] [\fB-p\fR] [\fB-x\fR] [\fB-e \fIencoding\fB\fR] [\fB-w\fR] [\fB-d \fIoutput-dir\fB\fR] [\fB-c\fR] [\fB-m\fR] [\fB-r\fR] [\fB-t\fR] [\fB-N\fR] [\fB-k\fR] [\fB-v\fR] [file ...]
+[\fIOPTIONS\fR] [\fIFILE\fR ...]
+'in \n(.iu-\nxu
+.ad b
+'hy
+'nh
+.fi
+.ad l
+\fBxmlwf\fR \kx
+.if (\nx>(\n(.l/2)) .nr x (\n(.l/5)
+'in \n(.iu+\nxu
+\fB-h\fR 
+'in \n(.iu-\nxu
+.ad b
+'hy
+'nh
+.fi
+.ad l
+\fBxmlwf\fR \kx
+.if (\nx>(\n(.l/2)) .nr x (\n(.l/5)
+'in \n(.iu+\nxu
+\fB-v\fR 
 'in \n(.iu-\nxu
 .ad b
 'hy
@@ -34,7 +54,7 @@
 \(bu
 The file begins with an XML declaration. For instance,
 \*(T<<?xml version="1.0" standalone="yes"?>\*(T>.
-\fINOTE:\fR
+\fINOTE\fR:
 \fBxmlwf\fR does not currently
 check for a valid XML declaration.
 .TP 0.2i
@@ -62,10 +82,42 @@
 external entities (see the \*(T<\fB\-x\fR\*(T> option).
 .SH OPTIONS
 When an option includes an argument, you may specify the argument either
-separately ("\*(T<\fB\-d\fR\*(T> output") or concatenated with the
-option ("\*(T<\fB\-d\fR\*(T>output"). \fBxmlwf\fR
+separately ("\*(T<\fB\-d\fR\*(T> \fIoutput\fR") or concatenated with the
+option ("\*(T<\fB\-d\fR\*(T>\fIoutput\fR"). \fBxmlwf\fR
 supports both.
 .TP 
+\*(T<\fB\-a\fR\*(T> \fIfactor\fR
+Sets the maximum tolerated amplification factor
+for protection against billion laughs attacks (default: 100.0).
+The amplification factor is calculated as ..
+
+.nf
+
+            amplification := (direct + indirect) / direct
+          
+.fi
+
+\&.. while parsing, whereas
+<direct> is the number of bytes read
+from the primary document in parsing and
+<indirect> is the number of bytes
+added by expanding entities and reading of external DTD files,
+combined.
+
+\fINOTE\fR:
+If you ever need to increase this value for non-attack payload,
+please file a bug report.
+.TP 
+\*(T<\fB\-b\fR\*(T> \fIbytes\fR
+Sets the number of output bytes (including amplification)
+needed to activate protection against billion laughs attacks
+(default: 8 MiB).
+This can be thought of as an "activation threshold".
+
+\fINOTE\fR:
+If you ever need to increase this value for non-attack payload,
+please file a bug report.
+.TP 
 \*(T<\fB\-c\fR\*(T>
 If the input file is well-formed and \fBxmlwf\fR
 doesn't encounter any errors, the input file is simply copied to
@@ -73,7 +125,7 @@
 This implies no namespaces (turns off \*(T<\fB\-n\fR\*(T>) and
 requires \*(T<\fB\-d\fR\*(T> to specify an output directory.
 .TP 
-\*(T<\fB\-d output\-dir\fR\*(T>
+\*(T<\fB\-d\fR\*(T> \fIoutput-dir\fR
 Specifies a directory to contain transformed
 representations of the input files.
 By default, \*(T<\fB\-d\fR\*(T> outputs a canonical representation
@@ -96,7 +148,7 @@
 More on canonical XML can be found at
 http://www.jclark.com/xml/canonxml.html .
 .TP 
-\*(T<\fB\-e encoding\fR\*(T>
+\*(T<\fB\-e\fR\*(T> \fIencoding\fR
 Specifies the character encoding for the document, overriding
 any document encoding declaration. \fBxmlwf\fR
 supports four built-in encodings:
@@ -106,6 +158,15 @@
 \*(T<ISO\-8859\-1\*(T>.
 Also see the \*(T<\fB\-w\fR\*(T> option.
 .TP 
+\*(T<\fB\-k\fR\*(T>
+When processing multiple files, \fBxmlwf\fR
+by default halts after the the first file with an error.
+This tells \fBxmlwf\fR to report the error
+but to keep processing.
+This can be useful, for example, when testing a filter that converts
+many files to XML and you want to quickly find out which conversions
+failed.
+.TP 
 \*(T<\fB\-m\fR\*(T>
 Outputs some strange sort of XML file that completely
 describes the input file, including character positions.
@@ -121,7 +182,7 @@
 Requires \*(T<\fB\-d\fR\*(T> to specify an output file.
 .TP 
 \*(T<\fB\-p\fR\*(T>
-Tells xmlwf to process external DTDs and parameter
+Tells \fBxmlwf\fR to process external DTDs and parameter
 entities.
 
 Normally \fBxmlwf\fR never parses parameter
@@ -156,14 +217,6 @@
 \*(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\-k\fR\*(T>
-When processing multiple files, Expat by default halts after the 
-the first file with an error. This tells Expat to report the error
-but to keep processing. 
-This can be useful, for example, when testing a filter that converts
-many files to XML and you want to quickly find out which conversions
-failed.
-.TP 
 \*(T<\fB\-v\fR\*(T>
 Prints the version of the Expat library being used, including some
 information on the compile-time configuration of the library, and
@@ -173,7 +226,7 @@
 Enables support for Windows code pages.
 Normally, \fBxmlwf\fR will throw an error if it
 runs across an encoding that it is not equipped to handle itself. With
-\*(T<\fB\-w\fR\*(T>, xmlwf will try to use a Windows code
+\*(T<\fB\-w\fR\*(T>, \fBxmlwf\fR will try to use a Windows code
 page. See also \*(T<\fB\-e\fR\*(T>.
 .TP 
 \*(T<\fB\-x\fR\*(T>
@@ -255,24 +308,16 @@
 \*(T<\fB\-d\fR\*(T>, \*(T<\fB\-c\fR\*(T>, and
 \*(T<\fB\-m\fR\*(T> options. If someone could explain it to
 me, I'd like to add this information to this manpage.
-.SH ALTERNATIVES
-Here are some XML validators on the web:
-
-.nf
-
-http://www.hcrc.ed.ac.uk/~richard/xml\-check.html
-http://www.stg.brown.edu/service/xmlvalid/
-http://www.scripting.com/frontier5/xml/code/xmlValidator.html
-http://www.xml.com/pub/a/tools/ruwf/check.html
-.fi
 .SH "SEE ALSO"
 .nf
 
-The Expat home page:        http://www.libexpat.org/
-The W3 XML specification:   http://www.w3.org/TR/REC\-xml
+The Expat home page:                            https://libexpat.github.io/
+The W3 XML 1.0 specification (fourth edition):  https://www.w3.org/TR/2006/REC\-xml\-20060816/
+Billion laughs attack:                          https://en.wikipedia.org/wiki/Billion_laughs_attack
 .fi
 .SH AUTHOR
-This manual page was written by Scott Bronson <\*(T<bronson@rinspin.com\*(T>> for
+This manual page was originally written by Scott Bronson <\*(T<bronson@rinspin.com\*(T>>
+in December 2001 for
 the Debian GNU/Linux system (but may be used by others). Permission is
 granted to copy, distribute and/or modify this document under
 the terms of the GNU Free Documentation
diff --git a/doc/xmlwf.xml b/doc/xmlwf.xml
index 448df4e..79ed585 100644
--- a/doc/xmlwf.xml
+++ b/doc/xmlwf.xml
@@ -1,11 +1,28 @@
-<!DOCTYPE refentry [
-  <!-- Fill in your name for FIRSTNAME and SURNAME. -->
+<!--
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   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      Ardo van Rangelrooij <ardo@debian.org>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2020      Joe Orton <jorton@redhat.com>
+   Copyright (c) 2021      Tim Bray <tbray@textuality.com>
+   Unlike most of Expat,
+   this file is copyrighted under the GNU Free Documentation License 1.1.
+-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+          "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
   <!ENTITY dhfirstname "<firstname>Scott</firstname>">
   <!ENTITY dhsurname   "<surname>Bronson</surname>">
-  <!-- Please adjust the date whenever revising the manpage. -->
-  <!ENTITY dhdate      "<date>March 11, 2016</date>">
-  <!-- SECTION should be 1-8, maybe w/ subsection other parameters are
-       allowed: see man(7), man(1). -->
+  <!ENTITY dhdate      "<date>February 20, 2022</date>">
+  <!-- Please adjust this^^ date whenever cutting a new release. -->
   <!ENTITY dhsection   "<manvolnum>1</manvolnum>">
   <!ENTITY dhemail     "<email>bronson@rinspin.com</email>">
   <!ENTITY dhusername  "Scott Bronson">
@@ -44,26 +61,16 @@
   <refsynopsisdiv>
     <cmdsynopsis>
       <command>&dhpackage;</command>
-	  <arg><option>-s</option></arg>
-	  <arg><option>-n</option></arg>
-	  <arg><option>-p</option></arg>
-	  <arg><option>-x</option></arg>
-
-	  <arg><option>-e <replaceable>encoding</replaceable></option></arg>
-	  <arg><option>-w</option></arg>
-
-	  <arg><option>-d <replaceable>output-dir</replaceable></option></arg>
-	  <arg><option>-c</option></arg>
-	  <arg><option>-m</option></arg>
-
-	  <arg><option>-r</option></arg>
-	  <arg><option>-t</option></arg>
-          <arg><option>-N</option></arg>
-
-	  <arg><option>-k</option></arg>
-	  <arg><option>-v</option></arg>
-
-	  <arg>file ...</arg>
+      <arg><replaceable>OPTIONS</replaceable></arg>
+      <arg><replaceable>FILE</replaceable> ...</arg>
+    </cmdsynopsis>
+    <cmdsynopsis>
+      <command>&dhpackage;</command>
+      <arg choice="plain"><option>-h</option></arg>
+    </cmdsynopsis>
+    <cmdsynopsis>
+      <command>&dhpackage;</command>
+      <arg choice="plain"><option>-v</option></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
  
@@ -96,7 +103,7 @@
       <listitem><para>
 	    The file begins with an XML declaration.  For instance,
 		<literal>&lt;?xml version="1.0" standalone="yes"?&gt;</literal>.
-		<emphasis>NOTE:</emphasis>
+		<emphasis>NOTE</emphasis>:
 		<command>&dhpackage;</command> does not currently
 		check for a valid XML declaration.
       </para></listitem>
@@ -133,14 +140,58 @@
 
 <para>
 When an option includes an argument, you may specify the argument either
-separately ("<option>-d</option> output") or concatenated with the
-option ("<option>-d</option>output").  <command>&dhpackage;</command>
+separately ("<option>-d</option> <replaceable>output</replaceable>") or concatenated with the
+option ("<option>-d</option><replaceable>output</replaceable>").  <command>&dhpackage;</command>
 supports both.
 </para>
 
     <variablelist>
 
       <varlistentry>
+        <term><option>-a</option> <replaceable>factor</replaceable></term>
+        <listitem>
+          <para>
+            Sets the maximum tolerated amplification factor
+            for protection against billion laughs attacks (default: 100.0).
+            The amplification factor is calculated as ..
+          </para>
+          <literallayout>
+            amplification := (direct + indirect) / direct
+          </literallayout>
+          <para>
+            .. while parsing, whereas
+            &lt;direct&gt; is the number of bytes read
+              from the primary document in parsing and
+            &lt;indirect&gt; is the number of bytes
+              added by expanding entities and reading of external DTD files,
+              combined.
+          </para>
+          <para>
+            <emphasis>NOTE</emphasis>:
+            If you ever need to increase this value for non-attack payload,
+            please file a bug report.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-b</option> <replaceable>bytes</replaceable></term>
+        <listitem>
+          <para>
+            Sets the number of output bytes (including amplification)
+            needed to activate protection against billion laughs attacks
+            (default: 8 MiB).
+            This can be thought of as an &quot;activation threshold&quot;.
+          </para>
+          <para>
+            <emphasis>NOTE</emphasis>:
+            If you ever need to increase this value for non-attack payload,
+            please file a bug report.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><option>-c</option></term>
         <listitem>
 		<para>
@@ -154,7 +205,7 @@
       </varlistentry>
 
       <varlistentry>
-        <term><option>-d output-dir</option></term>
+        <term><option>-d</option> <replaceable>output-dir</replaceable></term>
         <listitem>
 		<para>
   Specifies a directory to contain transformed
@@ -185,7 +236,7 @@
       </varlistentry>
 
       <varlistentry>
-        <term><option>-e encoding</option></term>
+        <term><option>-e</option> <replaceable>encoding</replaceable></term>
         <listitem>
 		<para>
    Specifies the character encoding for the document, overriding
@@ -201,6 +252,21 @@
       </varlistentry>
 
       <varlistentry>
+        <term><option>-k</option></term>
+        <listitem>
+          <para>
+            When processing multiple files, <command>&dhpackage;</command>
+            by default halts after the the first file with an error.
+            This tells <command>&dhpackage;</command> to report the error
+            but to keep processing.
+            This can be useful, for example, when testing a filter that converts
+            many files to XML and you want to quickly find out which conversions
+            failed.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><option>-m</option></term>
         <listitem>
 		<para>
@@ -236,7 +302,7 @@
         <term><option>-p</option></term>
         <listitem>
 		<para>
-    Tells xmlwf to process external DTDs and parameter
+    Tells <command>&dhpackage;</command> to process external DTDs and parameter
     entities.
 	 </para>
 	 <para>
@@ -293,20 +359,6 @@
 	   </para>
         </listitem>
       </varlistentry>
-
-      <varlistentry>
-	<term><option>-k</option></term>
-	<listitem>
-		<para>
-  When processing multiple files, Expat by default halts after the	    
-  the first file with an error. This tells Expat to report the error
-  but to keep processing. 
-  This can be useful, for example, when testing a filter that converts
-  many files to XML and you want to quickly find out which conversions
-  failed.
-	  </para>
-	</listitem>
-      </varlistentry>
       
       <varlistentry>
         <term><option>-v</option></term>
@@ -326,7 +378,7 @@
   Enables support for Windows code pages.
   Normally, <command>&dhpackage;</command> will throw an error if it
   runs across an encoding that it is not equipped to handle itself.  With
-  <option>-w</option>, &dhpackage; will try to use a Windows code
+  <option>-w</option>, <command>&dhpackage;</command> will try to use a Windows code
   page.  See also <option>-e</option>.
 	   </para>
         </listitem>
@@ -460,27 +512,13 @@
   </refsect1>
 
   <refsect1>
-    <title>ALTERNATIVES</title>
-	<para>
-	  Here are some XML validators on the web:
-
-<literallayout>
-http://www.hcrc.ed.ac.uk/~richard/xml-check.html
-http://www.stg.brown.edu/service/xmlvalid/
-http://www.scripting.com/frontier5/xml/code/xmlValidator.html
-http://www.xml.com/pub/a/tools/ruwf/check.html
-</literallayout>
-
-		 </para>
-  </refsect1>
-
-  <refsect1>
     <title>SEE ALSO</title>
 	<para>
 
 <literallayout>
-The Expat home page:        http://www.libexpat.org/
-The W3 XML specification:   http://www.w3.org/TR/REC-xml
+The Expat home page:                            https://libexpat.github.io/
+The W3 XML 1.0 specification (fourth edition):  https://www.w3.org/TR/2006/REC-xml-20060816/
+Billion laughs attack:                          https://en.wikipedia.org/wiki/Billion_laughs_attack
 </literallayout>
 
 	</para>
@@ -489,7 +527,8 @@
   <refsect1>
     <title>AUTHOR</title>
     <para>
-	  This manual page was written by &dhusername; &dhemail; for
+      This manual page was originally written by &dhusername; &dhemail;
+      in December 2001 for
       the &debian; system (but may be used by others).  Permission is
       granted to copy, distribute and/or modify this document under
       the terms of the <acronym>GNU</acronym> Free Documentation
diff --git a/examples/Makefile.am b/examples/Makefile.am
index c3b1f7b..d386b59 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -6,7 +6,8 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017 Expat development team
+# Copyright (c) 2017 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
diff --git a/examples/Makefile.in b/examples/Makefile.in
index 513d5ef..8528d43 100644
--- a/examples/Makefile.in
+++ b/examples/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -22,7 +22,8 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017 Expat development team
+# Copyright (c) 2017 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
@@ -210,8 +211,6 @@
   unique=`for i in $$list; do \
     if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
   done | $(am__uniquify_input)`
-ETAGS = etags
-CTAGS = ctags
 am__DIST_COMMON = $(srcdir)/Makefile.in \
 	$(top_srcdir)/conftools/depcomp
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
@@ -231,7 +230,10 @@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+CMAKE_SHARED_LIBRARY_PREFIX = @CMAKE_SHARED_LIBRARY_PREFIX@
 CPPFLAGS = @CPPFLAGS@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
 CXX = @CXX@
 CXXCPP = @CXXCPP@
 CXXDEPMODE = @CXXDEPMODE@
@@ -247,6 +249,7 @@
 ECHO_N = @ECHO_N@
 ECHO_T = @ECHO_T@
 EGREP = @EGREP@
+ETAGS = @ETAGS@
 EXEEXT = @EXEEXT@
 EXPAT_ATTR_INFO = @EXPAT_ATTR_INFO@
 EXPAT_CHAR_TYPE = @EXPAT_CHAR_TYPE@
@@ -268,6 +271,7 @@
 LIBAGE = @LIBAGE@
 LIBCURRENT = @LIBCURRENT@
 LIBDIR_BASENAME = @LIBDIR_BASENAME@
+LIBM = @LIBM@
 LIBOBJS = @LIBOBJS@
 LIBREVISION = @LIBREVISION@
 LIBS = @LIBS@
@@ -302,7 +306,6 @@
 SO_PATCH = @SO_PATCH@
 STRIP = @STRIP@
 VERSION = @VERSION@
-_EXPAT_OUTPUT_NAME = @_EXPAT_OUTPUT_NAME@
 abs_builddir = @abs_builddir@
 abs_srcdir = @abs_srcdir@
 abs_top_builddir = @abs_top_builddir@
@@ -311,6 +314,7 @@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_cv_sizeof_void_p = @ac_cv_sizeof_void_p@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -505,7 +509,6 @@
 
 distclean-tags:
 	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-
 distdir: $(BUILT_SOURCES)
 	$(MAKE) $(AM_MAKEFLAGS) distdir-am
 
diff --git a/examples/elements.c b/examples/elements.c
index eb0c729..481d444 100644
--- a/examples/elements.c
+++ b/examples/elements.c
@@ -11,7 +11,12 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   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-2019 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
diff --git a/examples/outline.c b/examples/outline.c
index d996b8e..936f0e0 100644
--- a/examples/outline.c
+++ b/examples/outline.c
@@ -8,8 +8,12 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2001-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
+   Copyright (c) 2005-2006 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2019 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/expat.pc.cmake b/expat.pc.cmake
new file mode 100644
index 0000000..fa2291a
--- /dev/null
+++ b/expat.pc.cmake
@@ -0,0 +1,11 @@
+prefix=$<TARGET_PROPERTY:expat,pkgconfig_prefix>
+exec_prefix=$<TARGET_PROPERTY:expat,pkgconfig_exec_prefix>
+libdir=$<TARGET_PROPERTY:expat,pkgconfig_libdir>
+includedir=$<TARGET_PROPERTY:expat,pkgconfig_includedir>
+
+Name: $<TARGET_PROPERTY:expat,pkgconfig_$<LOWER_CASE:$<CONFIG>>_name>
+Version: $<TARGET_PROPERTY:expat,pkgconfig_version>
+Description: expat XML parser
+URL: https://libexpat.github.io/
+Libs: -L${libdir} -l$<TARGET_PROPERTY:expat,pkgconfig_$<LOWER_CASE:$<CONFIG>>_name> $<TARGET_PROPERTY:expat,pkgconfig_libm>
+Cflags: -I${includedir}
diff --git a/expat.pc.in b/expat.pc.in
index bdfa47f..6be6465 100644
--- a/expat.pc.in
+++ b/expat.pc.in
@@ -3,9 +3,9 @@
 libdir=@libdir@
 includedir=@includedir@
 
-Name: @_EXPAT_OUTPUT_NAME@
+Name: @PACKAGE_NAME@
 Version: @PACKAGE_VERSION@
 Description: expat XML parser
-URL: http://www.libexpat.org
-Libs: -L${libdir} -l@_EXPAT_OUTPUT_NAME@
+URL: https://libexpat.github.io/
+Libs: -L${libdir} -l@PACKAGE_NAME@ @LIBM@
 Cflags: -I${includedir}
diff --git a/expat_config.h b/expat_config.h
index 7253eb5..e1d7eab 100644
--- a/expat_config.h
+++ b/expat_config.h
@@ -27,7 +27,7 @@
 #define HAVE_GETPAGESIZE 1
 
 /* Define to 1 if you have the `getrandom' function. */
-#if defined(__BIONIC__)
+#if defined(__BIONIC__) || defined(ANDROID_HOST_MUSL)
 #define HAVE_GETRANDOM 1
 #endif
 
@@ -56,7 +56,7 @@
 #define HAVE_STRING_H 1
 
 /* Define to 1 if you have `syscall' and `SYS_getrandom'. */
-#if defined(__BIONIC__)
+#if 0 /* We only get here for glibc and Windows (where the better choices aren't available) and they don't have this fallback either. */
 #define HAVE_SYSCALL_GETRANDOM 1
 #endif
 
@@ -85,7 +85,7 @@
 #define PACKAGE_NAME "expat"
 
 /* Define to the full name and version of this package. */
-#define PACKAGE_STRING "expat 2.3.0"
+#define PACKAGE_STRING "expat 2.4.6"
 
 /* Define to the one symbol short name of this package. */
 #define PACKAGE_TARNAME "expat"
@@ -94,7 +94,7 @@
 #define PACKAGE_URL ""
 
 /* Define to the version of this package. */
-#define PACKAGE_VERSION "2.3.0"
+#define PACKAGE_VERSION "2.4.6"
 
 /* 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
@@ -102,7 +102,7 @@
 #define STDC_HEADERS 1
 
 /* Version number of package */
-#define VERSION "2.3.0"
+#define VERSION "2.4.6"
 
 /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
    significant byte first (like Motorola and SPARC, unlike Intel). */
diff --git a/fix-xmltest-log.sh b/fix-xmltest-log.sh
index c143e93..164ea4e 100755
--- a/fix-xmltest-log.sh
+++ b/fix-xmltest-log.sh
@@ -6,7 +6,7 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2019 Expat development team
+# Copyright (c) 2019 Sebastian Pipping <sebastian@pipping.org>
 # Licensed under the MIT license:
 #
 # Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/fuzz/xml_parse_fuzzer.c b/fuzz/xml_parse_fuzzer.c
new file mode 100644
index 0000000..48b5021
--- /dev/null
+++ b/fuzz/xml_parse_fuzzer.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+
+#include "expat.h"
+#include "siphash.h"
+
+// Macros to convert preprocessor macros to string literals. See
+// https://gcc.gnu.org/onlinedocs/gcc-3.4.3/cpp/Stringification.html
+#define xstr(s) str(s)
+#define str(s) #s
+
+// The encoder type that we wish to fuzz should come from the compile-time
+// definition `ENCODING_FOR_FUZZING`. This allows us to have a separate fuzzer
+// binary for
+#ifndef ENCODING_FOR_FUZZING
+#  error "ENCODING_FOR_FUZZING was not provided to this fuzz target."
+#endif
+
+// 16-byte deterministic hash key.
+static unsigned char hash_key[16] = "FUZZING IS FUN!";
+
+static void XMLCALL
+start(void *userData, const XML_Char *name, const XML_Char **atts) {
+  (void)userData;
+  (void)name;
+  (void)atts;
+}
+static void XMLCALL
+end(void *userData, const XML_Char *name) {
+  (void)userData;
+  (void)name;
+}
+
+int
+LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+  XML_Parser p = XML_ParserCreate(xstr(ENCODING_FOR_FUZZING));
+  assert(p);
+
+  // 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));
+
+  XML_SetElementHandler(p, start, end);
+  XML_Parse(p, (const XML_Char *)data, size, 0);
+  XML_Parse(p, (const XML_Char *)data, size, 1);
+  XML_ParserFree(p);
+  return 0;
+}
diff --git a/fuzz/xml_parsebuffer_fuzzer.c b/fuzz/xml_parsebuffer_fuzzer.c
new file mode 100644
index 0000000..0c7a8f2
--- /dev/null
+++ b/fuzz/xml_parsebuffer_fuzzer.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "expat.h"
+#include "siphash.h"
+
+// Macros to convert preprocessor macros to string literals. See
+// https://gcc.gnu.org/onlinedocs/gcc-3.4.3/cpp/Stringification.html
+#define xstr(s) str(s)
+#define str(s) #s
+
+// The encoder type that we wish to fuzz should come from the compile-time
+// definition `ENCODING_FOR_FUZZING`. This allows us to have a separate fuzzer
+// binary for
+#ifndef ENCODING_FOR_FUZZING
+#  error "ENCODING_FOR_FUZZING was not provided to this fuzz target."
+#endif
+
+// 16-byte deterministic hash key.
+static unsigned char hash_key[16] = "FUZZING IS FUN!";
+
+static void XMLCALL
+start(void *userData, const XML_Char *name, const XML_Char **atts) {
+  (void)userData;
+  (void)name;
+  (void)atts;
+}
+static void XMLCALL
+end(void *userData, const XML_Char *name) {
+  (void)userData;
+  (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);
+
+  // 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 *buf = XML_GetBuffer(p, size);
+  assert(buf);
+
+  memcpy(buf, data, size);
+  XML_ParseBuffer(p, size, size == 0);
+  XML_ParserFree(p);
+  return 0;
+}
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 05343e2..d540249 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -6,7 +6,9 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017 Expat development team
+# Copyright (c) 2017-2021 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:
 #
 # Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -34,17 +36,26 @@
     expat_external.h
 
 lib_LTLIBRARIES = libexpat.la
+noinst_LTLIBRARIES = libexpatinternal.la
 
 libexpat_la_LDFLAGS = \
     @AM_LDFLAGS@ \
+    @LIBM@ \
     -no-undefined \
     -version-info @LIBCURRENT@:@LIBREVISION@:@LIBAGE@
 
-libexpat_la_SOURCES = \
+libexpat_la_SOURCES =
+
+# This layer of indirection allows
+# the test suite to access internal symbols
+# despite compiling with -fvisibility=hidden
+libexpatinternal_la_SOURCES = \
     xmlparse.c \
     xmltok.c \
     xmlrole.c
 
+libexpat_la_LIBADD = libexpatinternal.la
+
 doc_DATA = \
     ../AUTHORS \
     ../Changes
diff --git a/lib/Makefile.in b/lib/Makefile.in
index 1799b8b..3581b6b 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -22,7 +22,9 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017 Expat development team
+# Copyright (c) 2017-2021 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:
 #
 # Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -171,9 +173,9 @@
   }
 am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(docdir)" \
 	"$(DESTDIR)$(includedir)"
-LTLIBRARIES = $(lib_LTLIBRARIES)
-libexpat_la_LIBADD =
-am_libexpat_la_OBJECTS = xmlparse.lo xmltok.lo xmlrole.lo
+LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES)
+libexpat_la_DEPENDENCIES = libexpatinternal.la
+am_libexpat_la_OBJECTS =
 libexpat_la_OBJECTS = $(am_libexpat_la_OBJECTS)
 AM_V_lt = $(am__v_lt_@AM_V@)
 am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
@@ -182,6 +184,9 @@
 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)
 AM_V_P = $(am__v_P_@AM_V@)
 am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
 am__v_P_0 = false
@@ -218,8 +223,8 @@
 am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
 am__v_CCLD_0 = @echo "  CCLD    " $@;
 am__v_CCLD_1 = 
-SOURCES = $(libexpat_la_SOURCES)
-DIST_SOURCES = $(libexpat_la_SOURCES)
+SOURCES = $(libexpat_la_SOURCES) $(libexpatinternal_la_SOURCES)
+DIST_SOURCES = $(libexpat_la_SOURCES) $(libexpatinternal_la_SOURCES)
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
@@ -244,8 +249,6 @@
   unique=`for i in $$list; do \
     if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
   done | $(am__uniquify_input)`
-ETAGS = etags
-CTAGS = ctags
 am__DIST_COMMON = $(srcdir)/Makefile.in \
 	$(top_srcdir)/conftools/depcomp
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
@@ -265,7 +268,10 @@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+CMAKE_SHARED_LIBRARY_PREFIX = @CMAKE_SHARED_LIBRARY_PREFIX@
 CPPFLAGS = @CPPFLAGS@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
 CXX = @CXX@
 CXXCPP = @CXXCPP@
 CXXDEPMODE = @CXXDEPMODE@
@@ -281,6 +287,7 @@
 ECHO_N = @ECHO_N@
 ECHO_T = @ECHO_T@
 EGREP = @EGREP@
+ETAGS = @ETAGS@
 EXEEXT = @EXEEXT@
 EXPAT_ATTR_INFO = @EXPAT_ATTR_INFO@
 EXPAT_CHAR_TYPE = @EXPAT_CHAR_TYPE@
@@ -302,6 +309,7 @@
 LIBAGE = @LIBAGE@
 LIBCURRENT = @LIBCURRENT@
 LIBDIR_BASENAME = @LIBDIR_BASENAME@
+LIBM = @LIBM@
 LIBOBJS = @LIBOBJS@
 LIBREVISION = @LIBREVISION@
 LIBS = @LIBS@
@@ -336,7 +344,6 @@
 SO_PATCH = @SO_PATCH@
 STRIP = @STRIP@
 VERSION = @VERSION@
-_EXPAT_OUTPUT_NAME = @_EXPAT_OUTPUT_NAME@
 abs_builddir = @abs_builddir@
 abs_srcdir = @abs_srcdir@
 abs_top_builddir = @abs_top_builddir@
@@ -345,6 +352,7 @@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_cv_sizeof_void_p = @ac_cv_sizeof_void_p@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -397,16 +405,24 @@
     expat_external.h
 
 lib_LTLIBRARIES = libexpat.la
+noinst_LTLIBRARIES = libexpatinternal.la
 libexpat_la_LDFLAGS = \
     @AM_LDFLAGS@ \
+    @LIBM@ \
     -no-undefined \
     -version-info @LIBCURRENT@:@LIBREVISION@:@LIBAGE@
 
-libexpat_la_SOURCES = \
+libexpat_la_SOURCES = 
+
+# This layer of indirection allows
+# the test suite to access internal symbols
+# despite compiling with -fvisibility=hidden
+libexpatinternal_la_SOURCES = \
     xmlparse.c \
     xmltok.c \
     xmlrole.c
 
+libexpat_la_LIBADD = libexpatinternal.la
 doc_DATA = \
     ../AUTHORS \
     ../Changes
@@ -500,9 +516,23 @@
 	  rm -f $${locs}; \
 	}
 
+clean-noinstLTLIBRARIES:
+	-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+	@list='$(noinst_LTLIBRARIES)'; \
+	locs=`for p in $$list; do echo $$p; done | \
+	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+	      sort -u`; \
+	test -z "$$locs" || { \
+	  echo rm -f $${locs}; \
+	  rm -f $${locs}; \
+	}
+
 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)
+
 mostlyclean-compile:
 	-rm -f *.$(OBJEXT)
 
@@ -639,7 +669,6 @@
 
 distclean-tags:
 	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-
 distdir: $(BUILT_SOURCES)
 	$(MAKE) $(AM_MAKEFLAGS) distdir-am
 
@@ -713,7 +742,7 @@
 clean: clean-am
 
 clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
-	mostlyclean-am
+	clean-noinstLTLIBRARIES mostlyclean-am
 
 distclean: distclean-am
 		-rm -f ./$(DEPDIR)/xmlparse.Plo
@@ -790,16 +819,17 @@
 .MAKE: install-am install-data-am install-strip
 
 .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
-	clean-generic clean-libLTLIBRARIES 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-data-hook install-docDATA install-dvi \
-	install-dvi-am install-exec install-exec-am install-html \
-	install-html-am install-includeHEADERS install-info \
-	install-info-am install-libLTLIBRARIES install-man install-pdf \
-	install-pdf-am install-ps install-ps-am install-strip \
-	installcheck installcheck-am installdirs maintainer-clean \
+	clean-generic clean-libLTLIBRARIES clean-libtool \
+	clean-noinstLTLIBRARIES 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-data-hook install-docDATA install-dvi install-dvi-am \
+	install-exec install-exec-am install-html install-html-am \
+	install-includeHEADERS install-info install-info-am \
+	install-libLTLIBRARIES install-man install-pdf install-pdf-am \
+	install-ps install-ps-am install-strip installcheck \
+	installcheck-am installdirs maintainer-clean \
 	maintainer-clean-generic mostlyclean mostlyclean-compile \
 	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
 	tags tags-am uninstall uninstall-am uninstall-docDATA \
diff --git a/lib/ascii.h b/lib/ascii.h
index c3587e5..1f594d2 100644
--- a/lib/ascii.h
+++ b/lib/ascii.h
@@ -6,8 +6,11 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 1999-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2007      Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/lib/asciitab.h b/lib/asciitab.h
index 63b1d1b..af766fb 100644
--- a/lib/asciitab.h
+++ b/lib/asciitab.h
@@ -7,7 +7,9 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/lib/expat.h b/lib/expat.h
index 7aa60f3..46a0e1b 100644
--- a/lib/expat.h
+++ b/lib/expat.h
@@ -7,7 +7,14 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   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      Cristian Rodríguez <crrodriguez@opensuse.org>
+   Copyright (c) 2016      Thomas Beutlich <tc@tbeu.de>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -117,7 +124,9 @@
   /* Added in 2.2.1. */
   XML_ERROR_INVALID_ARGUMENT,
   /* Added in 2.3.0. */
-  XML_ERROR_NO_BUFFER
+  XML_ERROR_NO_BUFFER,
+  /* Added in 2.4.0. */
+  XML_ERROR_AMPLIFICATION_LIMIT_BREACH
 };
 
 enum XML_Content_Type {
@@ -999,7 +1008,10 @@
   XML_FEATURE_SIZEOF_XML_LCHAR,
   XML_FEATURE_NS,
   XML_FEATURE_LARGE_SIZE,
-  XML_FEATURE_ATTR_INFO
+  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
   /* Additional features must be added to the end of this enum. */
 };
 
@@ -1012,12 +1024,24 @@
 XMLPARSEAPI(const XML_Feature *)
 XML_GetFeatureList(void);
 
+#ifdef XML_DTD
+/* Added in Expat 2.4.0. */
+XMLPARSEAPI(XML_Bool)
+XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+    XML_Parser parser, float maximumAmplificationFactor);
+
+/* Added in Expat 2.4.0. */
+XMLPARSEAPI(XML_Bool)
+XML_SetBillionLaughsAttackProtectionActivationThreshold(
+    XML_Parser parser, unsigned long long activationThresholdBytes);
+#endif
+
 /* Expat follows the semantic versioning convention.
    See http://semver.org.
 */
 #define XML_MAJOR_VERSION 2
-#define XML_MINOR_VERSION 3
-#define XML_MICRO_VERSION 0
+#define XML_MINOR_VERSION 4
+#define XML_MICRO_VERSION 6
 
 #ifdef __cplusplus
 }
diff --git a/lib/expat_external.h b/lib/expat_external.h
index b3b6e74..8829f77 100644
--- a/lib/expat_external.h
+++ b/lib/expat_external.h
@@ -7,7 +7,14 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2000-2004 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2001-2002 Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2002-2006 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016      Cristian Rodríguez <crrodriguez@opensuse.org>
+   Copyright (c) 2016-2019 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2018      Yury Gribov <tetra2005@gmail.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/lib/iasciitab.h b/lib/iasciitab.h
index ea97cfc..5d8646f 100644
--- a/lib/iasciitab.h
+++ b/lib/iasciitab.h
@@ -7,7 +7,9 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/lib/internal.h b/lib/internal.h
index 60913da..444eba0 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -25,8 +25,12 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   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-2021 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2018      Yury Gribov <tetra2005@gmail.com>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -101,22 +105,58 @@
 #  endif
 #endif
 
+#include <limits.h> // ULONG_MAX
+
+#if defined(_WIN32) && ! defined(__USE_MINGW_ANSI_STDIO)
+#  define EXPAT_FMT_ULL(midpart) "%" midpart "I64u"
+#  if defined(_WIN64) // Note: modifiers "td" and "zu" do not work for MinGW
+#    define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "I64d"
+#    define EXPAT_FMT_SIZE_T(midpart) "%" midpart "I64u"
+#  else
+#    define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d"
+#    define EXPAT_FMT_SIZE_T(midpart) "%" midpart "u"
+#  endif
+#else
+#  define EXPAT_FMT_ULL(midpart) "%" midpart "llu"
+#  if ! defined(ULONG_MAX)
+#    error Compiler did not define ULONG_MAX for us
+#  elif ULONG_MAX == 18446744073709551615u // 2^64-1
+#    define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "ld"
+#    define EXPAT_FMT_SIZE_T(midpart) "%" midpart "lu"
+#  else
+#    define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d"
+#    define EXPAT_FMT_SIZE_T(midpart) "%" midpart "u"
+#  endif
+#endif
+
 #ifndef UNUSED_P
 #  define UNUSED_P(p) (void)p
 #endif
 
+/* NOTE BEGIN If you ever patch these defaults to greater values
+              for non-attack XML payload in your environment,
+              please file a bug report with libexpat.  Thank you!
+*/
+#define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT   \
+  100.0f
+#define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT    \
+  8388608 // 8 MiB, 2^23
+/* NOTE END */
+
+#include "expat.h" // so we can use type XML_Parser below
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-#ifdef XML_ENABLE_VISIBILITY
-#  if XML_ENABLE_VISIBILITY
-__attribute__((visibility("default")))
-#  endif
+void _INTERNAL_trim_to_complete_utf8_characters(const char *from,
+                                                const char **fromLimRef);
+
+#if defined(XML_DTD)
+unsigned long long testingAccountingGetCountBytesDirect(XML_Parser parser);
+unsigned long long testingAccountingGetCountBytesIndirect(XML_Parser parser);
+const char *unsignedCharToPrintable(unsigned char c);
 #endif
-void
-_INTERNAL_trim_to_complete_utf8_characters(const char *from,
-                                           const char **fromLimRef);
 
 #ifdef __cplusplus
 }
diff --git a/lib/latin1tab.h b/lib/latin1tab.h
index 6f91604..b681d27 100644
--- a/lib/latin1tab.h
+++ b/lib/latin1tab.h
@@ -7,7 +7,9 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/lib/libexpat.def b/lib/libexpat.def
index 16faf59..5aebbd0 100644
--- a/lib/libexpat.def
+++ b/lib/libexpat.def
@@ -74,5 +74,7 @@
 ; added with version 2.1.1
 ; XML_GetAttributeInfo @66
   XML_SetHashSalt @67
-; added with version 2.2.5
-  _INTERNAL_trim_to_complete_utf8_characters @68
+; internal @68 removed with version 2.3.1
+; added with version 2.4.0
+  XML_SetBillionLaughsAttackProtectionActivationThreshold @69
+  XML_SetBillionLaughsAttackProtectionMaximumAmplification @70
diff --git a/lib/libexpatw.def b/lib/libexpatw.def
index 16faf59..5aebbd0 100644
--- a/lib/libexpatw.def
+++ b/lib/libexpatw.def
@@ -74,5 +74,7 @@
 ; added with version 2.1.1
 ; XML_GetAttributeInfo @66
   XML_SetHashSalt @67
-; added with version 2.2.5
-  _INTERNAL_trim_to_complete_utf8_characters @68
+; internal @68 removed with version 2.3.1
+; added with version 2.4.0
+  XML_SetBillionLaughsAttackProtectionActivationThreshold @69
+  XML_SetBillionLaughsAttackProtectionMaximumAmplification @70
diff --git a/lib/nametab.h b/lib/nametab.h
index 3681df3..6348544 100644
--- a/lib/nametab.h
+++ b/lib/nametab.h
@@ -6,8 +6,8 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/lib/utf8tab.h b/lib/utf8tab.h
index a22986a..88efcf9 100644
--- a/lib/utf8tab.h
+++ b/lib/utf8tab.h
@@ -7,7 +7,9 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/lib/winconfig.h b/lib/winconfig.h
index 562a4a8..2ecd61b 100644
--- a/lib/winconfig.h
+++ b/lib/winconfig.h
@@ -6,8 +6,10 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   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>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -40,17 +42,4 @@
 #include <memory.h>
 #include <string.h>
 
-#if defined(HAVE_EXPAT_CONFIG_H) /* e.g. MinGW */
-#  include <expat_config.h>
-#else /* !defined(HAVE_EXPAT_CONFIG_H) */
-
-#  define XML_NS 1
-#  define XML_DTD 1
-#  define XML_CONTEXT_BYTES 1024
-
-/* we will assume all Windows platforms are little endian */
-#  define BYTEORDER 1234
-
-#endif /* !defined(HAVE_EXPAT_CONFIG_H) */
-
 #endif /* ndef WINCONFIG_H */
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index 78ad787..7db28d0 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -1,4 +1,4 @@
-/* d667b5f8e56e24fdfaf5e38596d419d924a9fadceb987d81d5613ecb7ca51b0e (2.3.0+)
+/* a30d2613dcfdef81475a9d1a349134d2d42722172fdaa7d5bb12ed2aa74b9596 (2.4.6+)
                             __  __            _
                          ___\ \/ /_ __   __ _| |_
                         / _ \\  /| '_ \ / _` | __|
@@ -7,7 +7,33 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2000-2006 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) 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      Gaurav <g.gupta@samsung.com>
+   Copyright (c) 2016      Thomas Beutlich <tc@tbeu.de>
+   Copyright (c) 2016      Gustavo Grieco <gustavo.grieco@imag.fr>
+   Copyright (c) 2016      Pascal Cuoq <cuoq@trust-in-soft.com>
+   Copyright (c) 2016      Ed Schouten <ed@nuxi.nl>
+   Copyright (c) 2017-2018 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Václav Slavík <vaclav@slavik.io>
+   Copyright (c) 2017      Viktor Szakats <commit@vsz.me>
+   Copyright (c) 2017      Chanho Park <chanho61.park@samsung.com>
+   Copyright (c) 2017      Rolf Eike Beer <eike@sf-mail.de>
+   Copyright (c) 2017      Hans Wennborg <hans@chromium.org>
+   Copyright (c) 2018      Anton Maklakov <antmak.pub@gmail.com>
+   Copyright (c) 2018      Benjamin Peterson <benjamin@python.org>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2018      Mariusz Zaborski <oshogbo@vexillium.org>
+   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) 2022      Samanta Navarro <ferivoz@riseup.net>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -30,6 +56,10 @@
    USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
+#define XML_BUILDING_EXPAT 1
+
+#include <expat_config.h>
+
 #if ! defined(_GNU_SOURCE)
 #  define _GNU_SOURCE 1 /* syscall prototype */
 #endif
@@ -48,6 +78,7 @@
 #include <stdio.h>  /* fprintf */
 #include <stdlib.h> /* getenv, rand_s */
 #include <stdint.h> /* uintptr_t */
+#include <math.h>   /* isnan */
 
 #ifdef _WIN32
 #  define getpid GetCurrentProcessId
@@ -59,13 +90,9 @@
 #  include <errno.h>
 #endif
 
-#define XML_BUILDING_EXPAT 1
-
 #ifdef _WIN32
 #  include "winconfig.h"
-#elif defined(HAVE_EXPAT_CONFIG_H)
-#  include <expat_config.h>
-#endif /* ndef _WIN32 */
+#endif
 
 #include "ascii.h"
 #include "expat.h"
@@ -372,6 +399,31 @@
   XML_Bool betweenDecl; /* WFC: PE Between Declarations */
 } OPEN_INTERNAL_ENTITY;
 
+enum XML_Account {
+  XML_ACCOUNT_DIRECT,           /* bytes directly passed to the Expat parser */
+  XML_ACCOUNT_ENTITY_EXPANSION, /* intermediate bytes produced during entity
+                                   expansion */
+  XML_ACCOUNT_NONE              /* i.e. do not account, was accounted already */
+};
+
+#ifdef XML_DTD
+typedef unsigned long long XmlBigCount;
+typedef struct accounting {
+  XmlBigCount countBytesDirect;
+  XmlBigCount countBytesIndirect;
+  int debugLevel;
+  float maximumAmplificationFactor; // >=1.0
+  unsigned long long activationThresholdBytes;
+} ACCOUNTING;
+
+typedef struct entity_stats {
+  unsigned int countEverOpened;
+  unsigned int currentDepth;
+  unsigned int maximumDepthSeen;
+  int debugLevel;
+} ENTITY_STATS;
+#endif /* XML_DTD */
+
 typedef enum XML_Error PTRCALL Processor(XML_Parser parser, const char *start,
                                          const char *end, const char **endPtr);
 
@@ -402,16 +454,18 @@
 static enum XML_Error doProlog(XML_Parser parser, const ENCODING *enc,
                                const char *s, const char *end, int tok,
                                const char *next, const char **nextPtr,
-                               XML_Bool haveMore, XML_Bool allowClosingDoctype);
+                               XML_Bool haveMore, XML_Bool allowClosingDoctype,
+                               enum XML_Account account);
 static enum XML_Error processInternalEntity(XML_Parser parser, ENTITY *entity,
                                             XML_Bool betweenDecl);
 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);
+                                XML_Bool haveMore, enum XML_Account account);
 static enum XML_Error doCdataSection(XML_Parser parser, const ENCODING *,
                                      const char **startPtr, const char *end,
-                                     const char **nextPtr, XML_Bool haveMore);
+                                     const char **nextPtr, XML_Bool haveMore,
+                                     enum XML_Account account);
 #ifdef XML_DTD
 static enum XML_Error doIgnoreSection(XML_Parser parser, const ENCODING *,
                                       const char **startPtr, const char *end,
@@ -421,7 +475,8 @@
 static void freeBindings(XML_Parser parser, BINDING *bindings);
 static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *,
                                 const char *s, TAG_NAME *tagNamePtr,
-                                BINDING **bindingsPtr);
+                                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);
@@ -430,15 +485,18 @@
                            XML_Parser parser);
 static enum XML_Error storeAttributeValue(XML_Parser parser, const ENCODING *,
                                           XML_Bool isCdata, const char *,
-                                          const char *, STRING_POOL *);
+                                          const char *, STRING_POOL *,
+                                          enum XML_Account account);
 static enum XML_Error appendAttributeValue(XML_Parser parser, const ENCODING *,
                                            XML_Bool isCdata, const char *,
-                                           const char *, STRING_POOL *);
+                                           const char *, STRING_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 enum XML_Error storeEntityValue(XML_Parser parser, const ENCODING *enc,
-                                       const char *start, const char *end);
+                                       const char *start, const char *end,
+                                       enum XML_Account account);
 static int reportProcessingInstruction(XML_Parser parser, const ENCODING *enc,
                                        const char *start, const char *end);
 static int reportComment(XML_Parser parser, const ENCODING *enc,
@@ -502,6 +560,34 @@
 
 static void parserInit(XML_Parser parser, const XML_Char *encodingName);
 
+#ifdef XML_DTD
+static float accountingGetCurrentAmplification(XML_Parser rootParser);
+static void accountingReportStats(XML_Parser originParser, const char *epilog);
+static void accountingOnAbort(XML_Parser originParser);
+static void accountingReportDiff(XML_Parser rootParser,
+                                 unsigned int levelsAwayFromRootParser,
+                                 const char *before, const char *after,
+                                 ptrdiff_t bytesMore, int source_line,
+                                 enum XML_Account account);
+static XML_Bool accountingDiffTolerated(XML_Parser originParser, int tok,
+                                        const char *before, const char *after,
+                                        int source_line,
+                                        enum XML_Account account);
+
+static void entityTrackingReportStats(XML_Parser parser, ENTITY *entity,
+                                      const char *action, int sourceLine);
+static void entityTrackingOnOpen(XML_Parser parser, ENTITY *entity,
+                                 int sourceLine);
+static void entityTrackingOnClose(XML_Parser parser, ENTITY *entity,
+                                  int sourceLine);
+
+static XML_Parser getRootParserOf(XML_Parser parser,
+                                  unsigned int *outLevelDiff);
+#endif /* XML_DTD */
+
+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)
@@ -615,6 +701,10 @@
   enum XML_ParamEntityParsing m_paramEntityParsing;
 #endif
   unsigned long m_hash_secret_salt;
+#ifdef XML_DTD
+  ACCOUNTING m_accounting;
+  ENTITY_STATS m_entity_stats;
+#endif
 };
 
 #define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s)))
@@ -628,8 +718,7 @@
 
 XML_Parser XMLCALL
 XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) {
-  XML_Char tmp[2];
-  *tmp = nsSep;
+  XML_Char tmp[2] = {nsSep, 0};
   return XML_ParserCreate_MM(encodingName, NULL, tmp);
 }
 
@@ -799,9 +888,8 @@
 
 static unsigned long
 ENTROPY_DEBUG(const char *label, unsigned long entropy) {
-  const char *const EXPAT_ENTROPY_DEBUG = getenv("EXPAT_ENTROPY_DEBUG");
-  if (EXPAT_ENTROPY_DEBUG && ! strcmp(EXPAT_ENTROPY_DEBUG, "1")) {
-    fprintf(stderr, "Entropy: %s --> 0x%0*lx (%lu bytes)\n", label,
+  if (getDebugLevel("EXPAT_ENTROPY_DEBUG", 0) >= 1u) {
+    fprintf(stderr, "expat: Entropy: %s --> 0x%0*lx (%lu bytes)\n", label,
             (int)sizeof(entropy) * 2, entropy, (unsigned long)sizeof(entropy));
   }
   return entropy;
@@ -886,7 +974,7 @@
 
   if (memsuite) {
     XML_Memory_Handling_Suite *mtemp;
-    parser = (XML_Parser)memsuite->malloc_fcn(sizeof(struct XML_ParserStruct));
+    parser = memsuite->malloc_fcn(sizeof(struct XML_ParserStruct));
     if (parser != NULL) {
       mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem);
       mtemp->malloc_fcn = memsuite->malloc_fcn;
@@ -1063,6 +1151,18 @@
   parser->m_paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER;
 #endif
   parser->m_hash_secret_salt = 0;
+
+#ifdef XML_DTD
+  memset(&parser->m_accounting, 0, sizeof(ACCOUNTING));
+  parser->m_accounting.debugLevel = getDebugLevel("EXPAT_ACCOUNTING_DEBUG", 0u);
+  parser->m_accounting.maximumAmplificationFactor
+      = EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT;
+  parser->m_accounting.activationThresholdBytes
+      = EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT;
+
+  memset(&parser->m_entity_stats, 0, sizeof(ENTITY_STATS));
+  parser->m_entity_stats.debugLevel = getDebugLevel("EXPAT_ENTITY_DEBUG", 0u);
+#endif
 }
 
 /* moves list of bindings to m_freeBindingList */
@@ -1243,8 +1343,7 @@
      would be otherwise.
   */
   if (parser->m_ns) {
-    XML_Char tmp[2];
-    *tmp = parser->m_namespaceSeparator;
+    XML_Char tmp[2] = {parser->m_namespaceSeparator, 0};
     parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd);
   } else {
     parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd);
@@ -2342,6 +2441,10 @@
   case XML_ERROR_NO_BUFFER:
     return XML_L(
         "a successful prior call to function XML_GetBuffer is required");
+  /* Added in 2.4.0. */
+  case XML_ERROR_AMPLIFICATION_LIMIT_BREACH:
+    return XML_L(
+        "limit on input amplification factor (from DTD and entities) breached");
   }
   return NULL;
 }
@@ -2378,41 +2481,75 @@
 
 const XML_Feature *XMLCALL
 XML_GetFeatureList(void) {
-  static const XML_Feature features[]
-      = {{XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"),
-          sizeof(XML_Char)},
-         {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"),
-          sizeof(XML_LChar)},
+  static const XML_Feature features[] = {
+      {XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"),
+       sizeof(XML_Char)},
+      {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"),
+       sizeof(XML_LChar)},
 #ifdef XML_UNICODE
-         {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0},
+      {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0},
 #endif
 #ifdef XML_UNICODE_WCHAR_T
-         {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0},
+      {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0},
 #endif
 #ifdef XML_DTD
-         {XML_FEATURE_DTD, XML_L("XML_DTD"), 0},
+      {XML_FEATURE_DTD, XML_L("XML_DTD"), 0},
 #endif
 #ifdef XML_CONTEXT_BYTES
-         {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"),
-          XML_CONTEXT_BYTES},
+      {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"),
+       XML_CONTEXT_BYTES},
 #endif
 #ifdef XML_MIN_SIZE
-         {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0},
+      {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0},
 #endif
 #ifdef XML_NS
-         {XML_FEATURE_NS, XML_L("XML_NS"), 0},
+      {XML_FEATURE_NS, XML_L("XML_NS"), 0},
 #endif
 #ifdef XML_LARGE_SIZE
-         {XML_FEATURE_LARGE_SIZE, XML_L("XML_LARGE_SIZE"), 0},
+      {XML_FEATURE_LARGE_SIZE, XML_L("XML_LARGE_SIZE"), 0},
 #endif
 #ifdef XML_ATTR_INFO
-         {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0},
+      {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0},
 #endif
-         {XML_FEATURE_END, NULL, 0}};
+#ifdef XML_DTD
+      /* Added in Expat 2.4.0. */
+      {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT,
+       XML_L("XML_BLAP_MAX_AMP"),
+       (long int)
+           EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT},
+      {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT,
+       XML_L("XML_BLAP_ACT_THRES"),
+       EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT},
+#endif
+      {XML_FEATURE_END, NULL, 0}};
 
   return features;
 }
 
+#ifdef XML_DTD
+XML_Bool XMLCALL
+XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+    XML_Parser parser, float maximumAmplificationFactor) {
+  if ((parser == NULL) || (parser->m_parentParser != NULL)
+      || isnan(maximumAmplificationFactor)
+      || (maximumAmplificationFactor < 1.0f)) {
+    return XML_FALSE;
+  }
+  parser->m_accounting.maximumAmplificationFactor = maximumAmplificationFactor;
+  return XML_TRUE;
+}
+
+XML_Bool XMLCALL
+XML_SetBillionLaughsAttackProtectionActivationThreshold(
+    XML_Parser parser, unsigned long long activationThresholdBytes) {
+  if ((parser == NULL) || (parser->m_parentParser != NULL)) {
+    return XML_FALSE;
+  }
+  parser->m_accounting.activationThresholdBytes = activationThresholdBytes;
+  return XML_TRUE;
+}
+#endif /* XML_DTD */
+
 /* Initially tag->rawName always points into the parse buffer;
    for those TAG instances opened while the current parse buffer was
    processed, and not yet closed, we need to store tag->rawName in a more
@@ -2424,6 +2561,7 @@
   while (tag) {
     int bufSize;
     int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1);
+    size_t rawNameLen;
     char *rawNameBuf = tag->buf + nameLen;
     /* Stop if already stored.  Since m_tagStack is a stack, we can stop
        at the first entry that has already been copied; everything
@@ -2435,7 +2573,11 @@
     /* For re-use purposes we need to ensure that the
        size of tag->buf is a multiple of sizeof(XML_Char).
     */
-    bufSize = nameLen + ROUND_UP(tag->rawNameLength, sizeof(XML_Char));
+    rawNameLen = ROUND_UP(tag->rawNameLength, sizeof(XML_Char));
+    /* Detect and prevent integer overflow. */
+    if (rawNameLen > (size_t)INT_MAX - nameLen)
+      return XML_FALSE;
+    bufSize = nameLen + (int)rawNameLen;
     if (bufSize > tag->bufEnd - tag->buf) {
       char *temp = (char *)REALLOC(parser, tag->buf, bufSize);
       if (temp == NULL)
@@ -2465,9 +2607,9 @@
 static enum XML_Error PTRCALL
 contentProcessor(XML_Parser parser, const char *start, const char *end,
                  const char **endPtr) {
-  enum XML_Error result
-      = doContent(parser, 0, parser->m_encoding, start, end, endPtr,
-                  (XML_Bool)! parser->m_parsingStatus.finalBuffer);
+  enum XML_Error result = doContent(
+      parser, 0, parser->m_encoding, start, end, endPtr,
+      (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT);
   if (result == XML_ERROR_NONE) {
     if (! storeRawNames(parser))
       return XML_ERROR_NO_MEMORY;
@@ -2492,6 +2634,14 @@
   int tok = XmlContentTok(parser->m_encoding, start, end, &next);
   switch (tok) {
   case XML_TOK_BOM:
+#ifdef XML_DTD
+    if (! accountingDiffTolerated(parser, tok, start, next, __LINE__,
+                                  XML_ACCOUNT_DIRECT)) {
+      accountingOnAbort(parser);
+      return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+    }
+#endif /* XML_DTD */
+
     /* If we are at the end of the buffer, this would cause the next stage,
        i.e. externalEntityInitProcessor3, to pass control directly to
        doContent (by detecting XML_TOK_NONE) without processing any xml text
@@ -2529,6 +2679,10 @@
   const char *next = start; /* XmlContentTok doesn't always set the last arg */
   parser->m_eventPtr = start;
   tok = XmlContentTok(parser->m_encoding, start, end, &next);
+  /* Note: These bytes are accounted later in:
+           - processXmlDecl
+           - externalEntityContentProcessor
+  */
   parser->m_eventEndPtr = next;
 
   switch (tok) {
@@ -2570,7 +2724,8 @@
                                const char *end, const char **endPtr) {
   enum XML_Error result
       = doContent(parser, 1, parser->m_encoding, start, end, endPtr,
-                  (XML_Bool)! parser->m_parsingStatus.finalBuffer);
+                  (XML_Bool)! parser->m_parsingStatus.finalBuffer,
+                  XML_ACCOUNT_ENTITY_EXPANSION);
   if (result == XML_ERROR_NONE) {
     if (! storeRawNames(parser))
       return XML_ERROR_NO_MEMORY;
@@ -2581,7 +2736,7 @@
 static enum XML_Error
 doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
           const char *s, const char *end, const char **nextPtr,
-          XML_Bool haveMore) {
+          XML_Bool haveMore, enum XML_Account account) {
   /* save one level of indirection */
   DTD *const dtd = parser->m_dtd;
 
@@ -2599,6 +2754,17 @@
   for (;;) {
     const char *next = s; /* XmlContentTok doesn't always set the last arg */
     int tok = XmlContentTok(enc, s, end, &next);
+#ifdef XML_DTD
+    const char *accountAfter
+        = ((tok == XML_TOK_TRAILING_RSQB) || (tok == XML_TOK_TRAILING_CR))
+              ? (haveMore ? s /* i.e. 0 bytes */ : end)
+              : next;
+    if (! accountingDiffTolerated(parser, tok, s, accountAfter, __LINE__,
+                                  account)) {
+      accountingOnAbort(parser);
+      return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+    }
+#endif
     *eventEndPP = next;
     switch (tok) {
     case XML_TOK_TRAILING_CR:
@@ -2654,6 +2820,14 @@
       XML_Char ch = (XML_Char)XmlPredefinedEntityName(
           enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar);
       if (ch) {
+#ifdef XML_DTD
+        /* 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 */
         if (parser->m_characterDataHandler)
           parser->m_characterDataHandler(parser->m_handlerArg, &ch, 1);
         else if (parser->m_defaultHandler)
@@ -2772,7 +2946,8 @@
       }
       tag->name.str = (XML_Char *)tag->buf;
       *toPtr = XML_T('\0');
-      result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings));
+      result
+          = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings), account);
       if (result)
         return result;
       if (parser->m_startElementHandler)
@@ -2796,7 +2971,8 @@
       if (! name.str)
         return XML_ERROR_NO_MEMORY;
       poolFinish(&parser->m_tempPool);
-      result = storeAtts(parser, enc, s, &name, &bindings);
+      result = storeAtts(parser, enc, s, &name, &bindings,
+                         XML_ACCOUNT_NONE /* token spans whole start tag */);
       if (result != XML_ERROR_NONE) {
         freeBindings(parser, bindings);
         return result;
@@ -2931,7 +3107,8 @@
       /* END disabled code */
       else if (parser->m_defaultHandler)
         reportDefault(parser, enc, s, next);
-      result = doCdataSection(parser, enc, &next, end, nextPtr, haveMore);
+      result
+          = doCdataSection(parser, enc, &next, end, nextPtr, haveMore, account);
       if (result != XML_ERROR_NONE)
         return result;
       else if (! next) {
@@ -3060,7 +3237,8 @@
 */
 static enum XML_Error
 storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
-          TAG_NAME *tagNamePtr, BINDING **bindingsPtr) {
+          TAG_NAME *tagNamePtr, BINDING **bindingsPtr,
+          enum XML_Account account) {
   DTD *const dtd = parser->m_dtd; /* save one level of indirection */
   ELEMENT_TYPE *elementType;
   int nDefaultAtts;
@@ -3092,13 +3270,38 @@
 
   /* get the attributes from the tokenizer */
   n = XmlGetAttributes(enc, attStr, parser->m_attsSize, parser->m_atts);
+
+  /* Detect and prevent integer overflow */
+  if (n > INT_MAX - nDefaultAtts) {
+    return XML_ERROR_NO_MEMORY;
+  }
+
   if (n + nDefaultAtts > parser->m_attsSize) {
     int oldAttsSize = parser->m_attsSize;
     ATTRIBUTE *temp;
 #ifdef XML_ATTR_INFO
     XML_AttrInfo *temp2;
 #endif
+
+    /* Detect and prevent integer overflow */
+    if ((nDefaultAtts > INT_MAX - INIT_ATTS_SIZE)
+        || (n > INT_MAX - (nDefaultAtts + INIT_ATTS_SIZE))) {
+      return XML_ERROR_NO_MEMORY;
+    }
+
     parser->m_attsSize = n + nDefaultAtts + INIT_ATTS_SIZE;
+
+    /* Detect and prevent integer overflow.
+     * The preprocessor guard addresses the "always false" warning
+     * from -Wtype-limits on platforms where
+     * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+    if ((unsigned)parser->m_attsSize > (size_t)(-1) / sizeof(ATTRIBUTE)) {
+      parser->m_attsSize = oldAttsSize;
+      return XML_ERROR_NO_MEMORY;
+    }
+#endif
+
     temp = (ATTRIBUTE *)REALLOC(parser, (void *)parser->m_atts,
                                 parser->m_attsSize * sizeof(ATTRIBUTE));
     if (temp == NULL) {
@@ -3107,6 +3310,17 @@
     }
     parser->m_atts = temp;
 #ifdef XML_ATTR_INFO
+    /* Detect and prevent integer overflow.
+     * The preprocessor guard addresses the "always false" warning
+     * from -Wtype-limits on platforms where
+     * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#  if UINT_MAX >= SIZE_MAX
+    if ((unsigned)parser->m_attsSize > (size_t)(-1) / sizeof(XML_AttrInfo)) {
+      parser->m_attsSize = oldAttsSize;
+      return XML_ERROR_NO_MEMORY;
+    }
+#  endif
+
     temp2 = (XML_AttrInfo *)REALLOC(parser, (void *)parser->m_attInfo,
                                     parser->m_attsSize * sizeof(XML_AttrInfo));
     if (temp2 == NULL) {
@@ -3170,7 +3384,7 @@
       /* normalize the attribute value */
       result = storeAttributeValue(
           parser, enc, isCdata, parser->m_atts[i].valuePtr,
-          parser->m_atts[i].valueEnd, &parser->m_tempPool);
+          parser->m_atts[i].valueEnd, &parser->m_tempPool, account);
       if (result)
         return result;
       appAtts[attIndex] = poolStart(&parser->m_tempPool);
@@ -3245,7 +3459,13 @@
   if (nPrefixes) {
     int j; /* hash table index */
     unsigned long version = parser->m_nsAttsVersion;
-    int nsAttsSize = (int)1 << parser->m_nsAttsPower;
+
+    /* Detect and prevent invalid shift */
+    if (parser->m_nsAttsPower >= sizeof(unsigned int) * 8 /* bits per byte */) {
+      return XML_ERROR_NO_MEMORY;
+    }
+
+    unsigned int nsAttsSize = 1u << parser->m_nsAttsPower;
     unsigned char oldNsAttsPower = parser->m_nsAttsPower;
     /* size of hash table must be at least 2 * (# of prefixed attributes) */
     if ((nPrefixes << 1)
@@ -3256,7 +3476,28 @@
         ;
       if (parser->m_nsAttsPower < 3)
         parser->m_nsAttsPower = 3;
-      nsAttsSize = (int)1 << parser->m_nsAttsPower;
+
+      /* Detect and prevent invalid shift */
+      if (parser->m_nsAttsPower >= sizeof(nsAttsSize) * 8 /* bits per byte */) {
+        /* Restore actual size of memory in m_nsAtts */
+        parser->m_nsAttsPower = oldNsAttsPower;
+        return XML_ERROR_NO_MEMORY;
+      }
+
+      nsAttsSize = 1u << parser->m_nsAttsPower;
+
+      /* Detect and prevent integer overflow.
+       * The preprocessor guard addresses the "always false" warning
+       * from -Wtype-limits on platforms where
+       * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+      if (nsAttsSize > (size_t)(-1) / sizeof(NS_ATT)) {
+        /* Restore actual size of memory in m_nsAtts */
+        parser->m_nsAttsPower = oldNsAttsPower;
+        return XML_ERROR_NO_MEMORY;
+      }
+#endif
+
       temp = (NS_ATT *)REALLOC(parser, parser->m_nsAtts,
                                nsAttsSize * sizeof(NS_ATT));
       if (! temp) {
@@ -3414,9 +3655,31 @@
   tagNamePtr->prefixLen = prefixLen;
   for (i = 0; localPart[i++];)
     ; /* i includes null terminator */
+
+  /* Detect and prevent integer overflow */
+  if (binding->uriLen > INT_MAX - prefixLen
+      || i > INT_MAX - (binding->uriLen + prefixLen)) {
+    return XML_ERROR_NO_MEMORY;
+  }
+
   n = i + binding->uriLen + prefixLen;
   if (n > binding->uriAlloc) {
     TAG *p;
+
+    /* Detect and prevent integer overflow */
+    if (n > INT_MAX - EXPAND_SPARE) {
+      return XML_ERROR_NO_MEMORY;
+    }
+    /* Detect and prevent integer overflow.
+     * The preprocessor guard addresses the "always false" warning
+     * from -Wtype-limits on platforms where
+     * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+    if ((unsigned)(n + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) {
+      return XML_ERROR_NO_MEMORY;
+    }
+#endif
+
     uri = (XML_Char *)MALLOC(parser, (n + EXPAND_SPARE) * sizeof(XML_Char));
     if (! uri)
       return XML_ERROR_NO_MEMORY;
@@ -3496,6 +3759,17 @@
     if (! mustBeXML && isXMLNS
         && (len > xmlnsLen || uri[len] != xmlnsNamespace[len]))
       isXMLNS = XML_FALSE;
+
+    // NOTE: While Expat does not validate namespace URIs against RFC 3986,
+    //       we have to at least make sure that the XML processor on top of
+    //       Expat (that is splitting tag names by namespace separator into
+    //       2- or 3-tuples (uri-local or uri-local-prefix)) cannot be confused
+    //       by an attacker putting additional namespace separator characters
+    //       into namespace declarations.  That would be ambiguous and not to
+    //       be expected.
+    if (parser->m_ns && (uri[len] == parser->m_namespaceSeparator)) {
+      return XML_ERROR_SYNTAX;
+    }
   }
   isXML = isXML && len == xmlLen;
   isXMLNS = isXMLNS && len == xmlnsLen;
@@ -3512,6 +3786,21 @@
   if (parser->m_freeBindingList) {
     b = parser->m_freeBindingList;
     if (len > b->uriAlloc) {
+      /* Detect and prevent integer overflow */
+      if (len > INT_MAX - EXPAND_SPARE) {
+        return XML_ERROR_NO_MEMORY;
+      }
+
+      /* Detect and prevent integer overflow.
+       * The preprocessor guard addresses the "always false" warning
+       * from -Wtype-limits on platforms where
+       * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+      if ((unsigned)(len + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) {
+        return XML_ERROR_NO_MEMORY;
+      }
+#endif
+
       XML_Char *temp = (XML_Char *)REALLOC(
           parser, b->uri, sizeof(XML_Char) * (len + EXPAND_SPARE));
       if (temp == NULL)
@@ -3524,6 +3813,21 @@
     b = (BINDING *)MALLOC(parser, sizeof(BINDING));
     if (! b)
       return XML_ERROR_NO_MEMORY;
+
+    /* Detect and prevent integer overflow */
+    if (len > INT_MAX - EXPAND_SPARE) {
+      return XML_ERROR_NO_MEMORY;
+    }
+    /* Detect and prevent integer overflow.
+     * The preprocessor guard addresses the "always false" warning
+     * from -Wtype-limits on platforms where
+     * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+    if ((unsigned)(len + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) {
+      return XML_ERROR_NO_MEMORY;
+    }
+#endif
+
     b->uri
         = (XML_Char *)MALLOC(parser, sizeof(XML_Char) * (len + EXPAND_SPARE));
     if (! b->uri) {
@@ -3559,9 +3863,9 @@
 static enum XML_Error PTRCALL
 cdataSectionProcessor(XML_Parser parser, const char *start, const char *end,
                       const char **endPtr) {
-  enum XML_Error result
-      = doCdataSection(parser, parser->m_encoding, &start, end, endPtr,
-                       (XML_Bool)! parser->m_parsingStatus.finalBuffer);
+  enum XML_Error result = doCdataSection(
+      parser, parser->m_encoding, &start, end, endPtr,
+      (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT);
   if (result != XML_ERROR_NONE)
     return result;
   if (start) {
@@ -3581,7 +3885,8 @@
 */
 static enum XML_Error
 doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
-               const char *end, const char **nextPtr, XML_Bool haveMore) {
+               const char *end, const char **nextPtr, XML_Bool haveMore,
+               enum XML_Account account) {
   const char *s = *startPtr;
   const char **eventPP;
   const char **eventEndPP;
@@ -3599,6 +3904,14 @@
   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 (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) {
+      accountingOnAbort(parser);
+      return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+    }
+#else
+    UNUSED_P(account);
+#endif
     *eventEndPP = next;
     switch (tok) {
     case XML_TOK_CDATA_SECT_CLOSE:
@@ -3743,6 +4056,13 @@
   *eventPP = s;
   *startPtr = NULL;
   tok = XmlIgnoreSectionTok(enc, s, end, &next);
+#  ifdef XML_DTD
+  if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
+                                XML_ACCOUNT_DIRECT)) {
+    accountingOnAbort(parser);
+    return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+  }
+#  endif
   *eventEndPP = next;
   switch (tok) {
   case XML_TOK_IGNORE_SECT:
@@ -3792,7 +4112,7 @@
   const char *s;
 #ifdef XML_UNICODE
   char encodingBuf[128];
-  /* See comments abount `protoclEncodingName` in parserInit() */
+  /* See comments about `protocolEncodingName` in parserInit() */
   if (! parser->m_protocolEncodingName)
     s = NULL;
   else {
@@ -3827,6 +4147,15 @@
   const char *versionend;
   const XML_Char *storedversion = NULL;
   int standalone = -1;
+
+#ifdef XML_DTD
+  if (! accountingDiffTolerated(parser, XML_TOK_XML_DECL, s, next, __LINE__,
+                                XML_ACCOUNT_DIRECT)) {
+    accountingOnAbort(parser);
+    return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+  }
+#endif
+
   if (! (parser->m_ns ? XmlParseXmlDeclNS : XmlParseXmlDecl)(
           isGeneralTextEntity, parser->m_encoding, s, next, &parser->m_eventPtr,
           &version, &versionend, &encodingName, &newEncoding, &standalone)) {
@@ -3976,6 +4305,10 @@
 
   for (;;) {
     tok = XmlPrologTok(parser->m_encoding, start, end, &next);
+    /* Note: Except for XML_TOK_BOM below, these bytes are accounted later in:
+             - storeEntityValue
+             - processXmlDecl
+    */
     parser->m_eventEndPtr = next;
     if (tok <= 0) {
       if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) {
@@ -3994,7 +4327,8 @@
         break;
       }
       /* found end of entity value - can store it now */
-      return storeEntityValue(parser, parser->m_encoding, s, end);
+      return storeEntityValue(parser, parser->m_encoding, s, end,
+                              XML_ACCOUNT_DIRECT);
     } else if (tok == XML_TOK_XML_DECL) {
       enum XML_Error result;
       result = processXmlDecl(parser, 0, start, next);
@@ -4021,6 +4355,14 @@
     */
     else if (tok == XML_TOK_BOM && next == end
              && ! parser->m_parsingStatus.finalBuffer) {
+#  ifdef XML_DTD
+      if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
+                                    XML_ACCOUNT_DIRECT)) {
+        accountingOnAbort(parser);
+        return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+      }
+#  endif
+
       *nextPtr = next;
       return XML_ERROR_NONE;
     }
@@ -4063,16 +4405,24 @@
   }
   /* This would cause the next stage, i.e. doProlog to be passed XML_TOK_BOM.
      However, when parsing an external subset, doProlog will not accept a BOM
-     as valid, and report a syntax error, so we have to skip the BOM
+     as valid, and report a syntax error, so we have to skip the BOM, and
+     account for the BOM bytes.
   */
   else if (tok == XML_TOK_BOM) {
+    if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
+                                  XML_ACCOUNT_DIRECT)) {
+      accountingOnAbort(parser);
+      return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+    }
+
     s = next;
     tok = XmlPrologTok(parser->m_encoding, s, end, &next);
   }
 
   parser->m_processor = prologProcessor;
   return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
-                  (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE);
+                  (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE,
+                  XML_ACCOUNT_DIRECT);
 }
 
 static enum XML_Error PTRCALL
@@ -4085,6 +4435,9 @@
 
   for (;;) {
     tok = XmlPrologTok(enc, start, end, &next);
+    /* Note: These bytes are accounted later in:
+             - storeEntityValue
+    */
     if (tok <= 0) {
       if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) {
         *nextPtr = s;
@@ -4102,7 +4455,7 @@
         break;
       }
       /* found end of entity value - can store it now */
-      return storeEntityValue(parser, enc, s, end);
+      return storeEntityValue(parser, enc, s, end, XML_ACCOUNT_DIRECT);
     }
     start = next;
   }
@@ -4116,13 +4469,14 @@
   const char *next = s;
   int tok = XmlPrologTok(parser->m_encoding, s, end, &next);
   return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
-                  (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE);
+                  (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE,
+                  XML_ACCOUNT_DIRECT);
 }
 
 static enum XML_Error
 doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
          int tok, const char *next, const char **nextPtr, XML_Bool haveMore,
-         XML_Bool allowClosingDoctype) {
+         XML_Bool allowClosingDoctype, enum XML_Account account) {
 #ifdef XML_DTD
   static const XML_Char externalSubsetName[] = {ASCII_HASH, '\0'};
 #endif /* XML_DTD */
@@ -4149,6 +4503,10 @@
   static const XML_Char enumValueSep[] = {ASCII_PIPE, '\0'};
   static const XML_Char enumValueStart[] = {ASCII_LPAREN, '\0'};
 
+#ifndef XML_DTD
+  UNUSED_P(account);
+#endif
+
   /* save one level of indirection */
   DTD *const dtd = parser->m_dtd;
 
@@ -4213,6 +4571,19 @@
       }
     }
     role = XmlTokenRole(&parser->m_prologState, tok, s, next, enc);
+#ifdef XML_DTD
+    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
+      break;
+    default:
+      if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) {
+        accountingOnAbort(parser);
+        return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+      }
+    }
+#endif
     switch (role) {
     case XML_ROLE_XML_DECL: {
       enum XML_Error result = processXmlDecl(parser, 0, s, next);
@@ -4488,7 +4859,8 @@
         const XML_Char *attVal;
         enum XML_Error result = storeAttributeValue(
             parser, enc, parser->m_declAttributeIsCdata,
-            s + enc->minBytesPerChar, next - enc->minBytesPerChar, &dtd->pool);
+            s + enc->minBytesPerChar, next - enc->minBytesPerChar, &dtd->pool,
+            XML_ACCOUNT_NONE);
         if (result)
           return result;
         attVal = poolStart(&dtd->pool);
@@ -4521,8 +4893,9 @@
       break;
     case XML_ROLE_ENTITY_VALUE:
       if (dtd->keepProcessing) {
-        enum XML_Error result = storeEntityValue(
-            parser, enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar);
+        enum XML_Error result
+            = storeEntityValue(parser, enc, s + enc->minBytesPerChar,
+                               next - enc->minBytesPerChar, XML_ACCOUNT_NONE);
         if (parser->m_declEntity) {
           parser->m_declEntity->textPtr = poolStart(&dtd->entityValuePool);
           parser->m_declEntity->textLen
@@ -4781,6 +5154,11 @@
       if (parser->m_prologState.level >= parser->m_groupSize) {
         if (parser->m_groupSize) {
           {
+            /* Detect and prevent integer overflow */
+            if (parser->m_groupSize > (unsigned int)(-1) / 2u) {
+              return XML_ERROR_NO_MEMORY;
+            }
+
             char *const new_connector = (char *)REALLOC(
                 parser, parser->m_groupConnector, parser->m_groupSize *= 2);
             if (new_connector == NULL) {
@@ -4791,6 +5169,16 @@
           }
 
           if (dtd->scaffIndex) {
+            /* Detect and prevent integer overflow.
+             * The preprocessor guard addresses the "always false" warning
+             * from -Wtype-limits on platforms where
+             * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+            if (parser->m_groupSize > (size_t)(-1) / sizeof(int)) {
+              return XML_ERROR_NO_MEMORY;
+            }
+#endif
+
             int *const new_scaff_index = (int *)REALLOC(
                 parser, dtd->scaffIndex, parser->m_groupSize * sizeof(int));
             if (new_scaff_index == NULL)
@@ -4912,12 +5300,15 @@
         if (parser->m_externalEntityRefHandler) {
           dtd->paramEntityRead = XML_FALSE;
           entity->open = XML_TRUE;
+          entityTrackingOnOpen(parser, entity, __LINE__);
           if (! parser->m_externalEntityRefHandler(
                   parser->m_externalEntityRefHandlerArg, 0, entity->base,
                   entity->systemId, entity->publicId)) {
+            entityTrackingOnClose(parser, entity, __LINE__);
             entity->open = XML_FALSE;
             return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
           }
+          entityTrackingOnClose(parser, entity, __LINE__);
           entity->open = XML_FALSE;
           handleDefault = XML_FALSE;
           if (! dtd->paramEntityRead) {
@@ -4996,7 +5387,7 @@
       if (dtd->in_eldecl) {
         ELEMENT_TYPE *el;
         const XML_Char *name;
-        int nameLen;
+        size_t nameLen;
         const char *nxt
             = (quant == XML_CQUANT_NONE ? next : next - enc->minBytesPerChar);
         int myindex = nextScaffoldPart(parser);
@@ -5012,7 +5403,13 @@
         nameLen = 0;
         for (; name[nameLen++];)
           ;
-        dtd->contentStringLen += nameLen;
+
+        /* Detect and prevent integer overflow */
+        if (nameLen > UINT_MAX - dtd->contentStringLen) {
+          return XML_ERROR_NO_MEMORY;
+        }
+
+        dtd->contentStringLen += (unsigned)nameLen;
         if (parser->m_elementDeclHandler)
           handleDefault = XML_FALSE;
       }
@@ -5115,6 +5512,13 @@
   for (;;) {
     const char *next = NULL;
     int tok = XmlPrologTok(parser->m_encoding, s, end, &next);
+#ifdef XML_DTD
+    if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
+                                  XML_ACCOUNT_DIRECT)) {
+      accountingOnAbort(parser);
+      return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+    }
+#endif
     parser->m_eventEndPtr = next;
     switch (tok) {
     /* report partial linebreak - it might be the last token */
@@ -5188,6 +5592,9 @@
       return XML_ERROR_NO_MEMORY;
   }
   entity->open = XML_TRUE;
+#ifdef XML_DTD
+  entityTrackingOnOpen(parser, entity, __LINE__);
+#endif
   entity->processed = 0;
   openEntity->next = parser->m_openInternalEntities;
   parser->m_openInternalEntities = openEntity;
@@ -5206,17 +5613,22 @@
     int tok
         = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
     result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,
-                      tok, next, &next, XML_FALSE, XML_FALSE);
+                      tok, next, &next, XML_FALSE, XML_FALSE,
+                      XML_ACCOUNT_ENTITY_EXPANSION);
   } else
 #endif /* XML_DTD */
     result = doContent(parser, parser->m_tagLevel, parser->m_internalEncoding,
-                       textStart, textEnd, &next, XML_FALSE);
+                       textStart, textEnd, &next, XML_FALSE,
+                       XML_ACCOUNT_ENTITY_EXPANSION);
 
   if (result == XML_ERROR_NONE) {
     if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) {
       entity->processed = (int)(next - textStart);
       parser->m_processor = internalEntityProcessor;
     } else {
+#ifdef XML_DTD
+      entityTrackingOnClose(parser, entity, __LINE__);
+#endif /* XML_DTD */
       entity->open = XML_FALSE;
       parser->m_openInternalEntities = openEntity->next;
       /* put openEntity back in list of free instances */
@@ -5249,12 +5661,13 @@
     int tok
         = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
     result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,
-                      tok, next, &next, XML_FALSE, XML_TRUE);
+                      tok, next, &next, XML_FALSE, XML_TRUE,
+                      XML_ACCOUNT_ENTITY_EXPANSION);
   } else
 #endif /* XML_DTD */
     result = doContent(parser, openEntity->startTagLevel,
                        parser->m_internalEncoding, textStart, textEnd, &next,
-                       XML_FALSE);
+                       XML_FALSE, XML_ACCOUNT_ENTITY_EXPANSION);
 
   if (result != XML_ERROR_NONE)
     return result;
@@ -5263,6 +5676,9 @@
     entity->processed = (int)(next - (const char *)entity->textPtr);
     return result;
   } else {
+#ifdef XML_DTD
+    entityTrackingOnClose(parser, entity, __LINE__);
+#endif
     entity->open = XML_FALSE;
     parser->m_openInternalEntities = openEntity->next;
     /* put openEntity back in list of free instances */
@@ -5276,7 +5692,8 @@
     parser->m_processor = prologProcessor;
     tok = XmlPrologTok(parser->m_encoding, s, end, &next);
     return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
-                    (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE);
+                    (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE,
+                    XML_ACCOUNT_DIRECT);
   } else
 #endif /* XML_DTD */
   {
@@ -5284,7 +5701,8 @@
     /* see externalEntityContentProcessor vs contentProcessor */
     return doContent(parser, parser->m_parentParser ? 1 : 0, parser->m_encoding,
                      s, end, nextPtr,
-                     (XML_Bool)! parser->m_parsingStatus.finalBuffer);
+                     (XML_Bool)! parser->m_parsingStatus.finalBuffer,
+                     XML_ACCOUNT_DIRECT);
   }
 }
 
@@ -5299,9 +5717,10 @@
 
 static enum XML_Error
 storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
-                    const char *ptr, const char *end, STRING_POOL *pool) {
+                    const char *ptr, const char *end, STRING_POOL *pool,
+                    enum XML_Account account) {
   enum XML_Error result
-      = appendAttributeValue(parser, enc, isCdata, ptr, end, pool);
+      = appendAttributeValue(parser, enc, isCdata, ptr, end, pool, account);
   if (result)
     return result;
   if (! isCdata && poolLength(pool) && poolLastChar(pool) == 0x20)
@@ -5313,11 +5732,23 @@
 
 static enum XML_Error
 appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
-                     const char *ptr, const char *end, STRING_POOL *pool) {
+                     const char *ptr, const char *end, STRING_POOL *pool,
+                     enum XML_Account account) {
   DTD *const dtd = parser->m_dtd; /* save one level of indirection */
+#ifndef XML_DTD
+  UNUSED_P(account);
+#endif
+
   for (;;) {
-    const char *next;
+    const char *next
+        = ptr; /* XmlAttributeValueTok doesn't always set the last arg */
     int tok = XmlAttributeValueTok(enc, ptr, end, &next);
+#ifdef XML_DTD
+    if (! accountingDiffTolerated(parser, tok, ptr, next, __LINE__, account)) {
+      accountingOnAbort(parser);
+      return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+    }
+#endif
     switch (tok) {
     case XML_TOK_NONE:
       return XML_ERROR_NONE;
@@ -5377,6 +5808,14 @@
       XML_Char ch = (XML_Char)XmlPredefinedEntityName(
           enc, ptr + enc->minBytesPerChar, next - enc->minBytesPerChar);
       if (ch) {
+#ifdef XML_DTD
+        /* 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 */
         if (! poolAppendChar(pool, ch))
           return XML_ERROR_NO_MEMORY;
         break;
@@ -5454,9 +5893,16 @@
         enum XML_Error result;
         const XML_Char *textEnd = entity->textPtr + entity->textLen;
         entity->open = XML_TRUE;
+#ifdef XML_DTD
+        entityTrackingOnOpen(parser, entity, __LINE__);
+#endif
         result = appendAttributeValue(parser, parser->m_internalEncoding,
                                       isCdata, (const char *)entity->textPtr,
-                                      (const char *)textEnd, pool);
+                                      (const char *)textEnd, pool,
+                                      XML_ACCOUNT_ENTITY_EXPANSION);
+#ifdef XML_DTD
+        entityTrackingOnClose(parser, entity, __LINE__);
+#endif
         entity->open = XML_FALSE;
         if (result)
           return result;
@@ -5486,13 +5932,16 @@
 
 static enum XML_Error
 storeEntityValue(XML_Parser parser, const ENCODING *enc,
-                 const char *entityTextPtr, const char *entityTextEnd) {
+                 const char *entityTextPtr, const char *entityTextEnd,
+                 enum XML_Account account) {
   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
   int oldInEntityValue = parser->m_prologState.inEntityValue;
   parser->m_prologState.inEntityValue = 1;
+#else
+  UNUSED_P(account);
 #endif /* XML_DTD */
   /* never return Null for the value argument in EntityDeclHandler,
      since this would indicate an external entity; therefore we
@@ -5503,8 +5952,19 @@
   }
 
   for (;;) {
-    const char *next;
+    const char *next
+        = 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
@@ -5540,13 +6000,16 @@
           if (parser->m_externalEntityRefHandler) {
             dtd->paramEntityRead = XML_FALSE;
             entity->open = XML_TRUE;
+            entityTrackingOnOpen(parser, entity, __LINE__);
             if (! parser->m_externalEntityRefHandler(
                     parser->m_externalEntityRefHandlerArg, 0, entity->base,
                     entity->systemId, entity->publicId)) {
+              entityTrackingOnClose(parser, entity, __LINE__);
               entity->open = XML_FALSE;
               result = XML_ERROR_EXTERNAL_ENTITY_HANDLING;
               goto endEntityValue;
             }
+            entityTrackingOnClose(parser, entity, __LINE__);
             entity->open = XML_FALSE;
             if (! dtd->paramEntityRead)
               dtd->keepProcessing = dtd->standalone;
@@ -5554,9 +6017,12 @@
             dtd->keepProcessing = dtd->standalone;
         } else {
           entity->open = XML_TRUE;
+          entityTrackingOnOpen(parser, entity, __LINE__);
           result = storeEntityValue(
               parser, parser->m_internalEncoding, (const char *)entity->textPtr,
-              (const char *)(entity->textPtr + entity->textLen));
+              (const char *)(entity->textPtr + entity->textLen),
+              XML_ACCOUNT_ENTITY_EXPANSION);
+          entityTrackingOnClose(parser, entity, __LINE__);
           entity->open = XML_FALSE;
           if (result)
             goto endEntityValue;
@@ -5789,7 +6255,24 @@
       }
     } else {
       DEFAULT_ATTRIBUTE *temp;
+
+      /* Detect and prevent integer overflow */
+      if (type->allocDefaultAtts > INT_MAX / 2) {
+        return 0;
+      }
+
       int count = type->allocDefaultAtts * 2;
+
+      /* Detect and prevent integer overflow.
+       * The preprocessor guard addresses the "always false" warning
+       * from -Wtype-limits on platforms where
+       * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+      if ((unsigned)count > (size_t)(-1) / sizeof(DEFAULT_ATTRIBUTE)) {
+        return 0;
+      }
+#endif
+
       temp = (DEFAULT_ATTRIBUTE *)REALLOC(parser, type->defaultAtts,
                                           (count * sizeof(DEFAULT_ATTRIBUTE)));
       if (temp == NULL)
@@ -6079,7 +6562,7 @@
 
 static DTD *
 dtdCreate(const XML_Memory_Handling_Suite *ms) {
-  DTD *p = (DTD *)ms->malloc_fcn(sizeof(DTD));
+  DTD *p = ms->malloc_fcn(sizeof(DTD));
   if (p == NULL)
     return p;
   poolInit(&(p->pool), ms);
@@ -6252,8 +6735,8 @@
     if (! newE)
       return 0;
     if (oldE->nDefaultAtts) {
-      newE->defaultAtts = (DEFAULT_ATTRIBUTE *)ms->malloc_fcn(
-          oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
+      newE->defaultAtts
+          = ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
       if (! newE->defaultAtts) {
         return 0;
       }
@@ -6415,7 +6898,7 @@
     /* table->size is a power of 2 */
     table->size = (size_t)1 << INIT_POWER;
     tsize = table->size * sizeof(NAMED *);
-    table->v = (NAMED **)table->mem->malloc_fcn(tsize);
+    table->v = table->mem->malloc_fcn(tsize);
     if (! table->v) {
       table->size = 0;
       return NULL;
@@ -6440,10 +6923,22 @@
     /* check for overflow (table is half full) */
     if (table->used >> (table->power - 1)) {
       unsigned char newPower = table->power + 1;
+
+      /* Detect and prevent invalid shift */
+      if (newPower >= sizeof(unsigned long) * 8 /* bits per byte */) {
+        return NULL;
+      }
+
       size_t newSize = (size_t)1 << newPower;
       unsigned long newMask = (unsigned long)newSize - 1;
+
+      /* Detect and prevent integer overflow */
+      if (newSize > (size_t)(-1) / sizeof(NAMED *)) {
+        return NULL;
+      }
+
       size_t tsize = newSize * sizeof(NAMED *);
-      NAMED **newV = (NAMED **)table->mem->malloc_fcn(tsize);
+      NAMED **newV = table->mem->malloc_fcn(tsize);
       if (! newV)
         return NULL;
       memset(newV, 0, tsize);
@@ -6472,7 +6967,7 @@
       }
     }
   }
-  table->v[i] = (NAMED *)table->mem->malloc_fcn(createSize);
+  table->v[i] = table->mem->malloc_fcn(createSize);
   if (! table->v[i])
     return NULL;
   memset(table->v[i], 0, createSize);
@@ -6760,7 +7255,7 @@
     if (bytesToAllocate == 0)
       return XML_FALSE;
 
-    tem = (BLOCK *)pool->mem->malloc_fcn(bytesToAllocate);
+    tem = pool->mem->malloc_fcn(bytesToAllocate);
     if (! tem)
       return XML_FALSE;
     tem->size = blockSize;
@@ -6791,6 +7286,20 @@
   if (dtd->scaffCount >= dtd->scaffSize) {
     CONTENT_SCAFFOLD *temp;
     if (dtd->scaffold) {
+      /* Detect and prevent integer overflow */
+      if (dtd->scaffSize > UINT_MAX / 2u) {
+        return -1;
+      }
+      /* Detect and prevent integer overflow.
+       * The preprocessor guard addresses the "always false" warning
+       * from -Wtype-limits on platforms where
+       * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+      if (dtd->scaffSize > (size_t)(-1) / 2u / sizeof(CONTENT_SCAFFOLD)) {
+        return -1;
+      }
+#endif
+
       temp = (CONTENT_SCAFFOLD *)REALLOC(
           parser, dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD));
       if (temp == NULL)
@@ -6822,55 +7331,130 @@
   return next;
 }
 
-static void
-build_node(XML_Parser parser, int src_node, XML_Content *dest,
-           XML_Content **contpos, XML_Char **strpos) {
-  DTD *const dtd = parser->m_dtd; /* save one level of indirection */
-  dest->type = dtd->scaffold[src_node].type;
-  dest->quant = dtd->scaffold[src_node].quant;
-  if (dest->type == XML_CTYPE_NAME) {
-    const XML_Char *src;
-    dest->name = *strpos;
-    src = dtd->scaffold[src_node].name;
-    for (;;) {
-      *(*strpos)++ = *src;
-      if (! *src)
-        break;
-      src++;
-    }
-    dest->numchildren = 0;
-    dest->children = NULL;
-  } else {
-    unsigned int i;
-    int cn;
-    dest->numchildren = dtd->scaffold[src_node].childcnt;
-    dest->children = *contpos;
-    *contpos += dest->numchildren;
-    for (i = 0, cn = dtd->scaffold[src_node].firstchild; i < dest->numchildren;
-         i++, cn = dtd->scaffold[cn].nextsib) {
-      build_node(parser, cn, &(dest->children[i]), contpos, strpos);
-    }
-    dest->name = NULL;
-  }
-}
-
 static XML_Content *
 build_model(XML_Parser parser) {
+  /* Function build_model transforms the existing parser->m_dtd->scaffold
+   * array of CONTENT_SCAFFOLD tree nodes into a new array of
+   * XML_Content tree nodes followed by a gapless list of zero-terminated
+   * strings. */
   DTD *const dtd = parser->m_dtd; /* save one level of indirection */
   XML_Content *ret;
-  XML_Content *cpos;
-  XML_Char *str;
-  int allocsize = (dtd->scaffCount * sizeof(XML_Content)
-                   + (dtd->contentStringLen * sizeof(XML_Char)));
+  XML_Char *str; /* the current string writing location */
+
+  /* Detect and prevent integer overflow.
+   * The preprocessor guard addresses the "always false" warning
+   * from -Wtype-limits on platforms where
+   * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+  if (dtd->scaffCount > (size_t)(-1) / sizeof(XML_Content)) {
+    return NULL;
+  }
+  if (dtd->contentStringLen > (size_t)(-1) / sizeof(XML_Char)) {
+    return NULL;
+  }
+#endif
+  if (dtd->scaffCount * sizeof(XML_Content)
+      > (size_t)(-1) - dtd->contentStringLen * sizeof(XML_Char)) {
+    return NULL;
+  }
+
+  const size_t allocsize = (dtd->scaffCount * sizeof(XML_Content)
+                            + (dtd->contentStringLen * sizeof(XML_Char)));
 
   ret = (XML_Content *)MALLOC(parser, allocsize);
   if (! ret)
     return NULL;
 
-  str = (XML_Char *)(&ret[dtd->scaffCount]);
-  cpos = &ret[1];
+  /* What follows is an iterative implementation (of what was previously done
+   * recursively in a dedicated function called "build_node".  The old recursive
+   * build_node could be forced into stack exhaustion from input as small as a
+   * few megabyte, and so that was a security issue.  Hence, a function call
+   * stack is avoided now by resolving recursion.)
+   *
+   * The iterative approach works as follows:
+   *
+   * - We have two writing pointers, both walking up the result array; one does
+   *   the work, the other creates "jobs" for its colleague to do, and leads
+   *   the way:
+   *
+   *   - The faster one, pointer jobDest, always leads and writes "what job
+   *     to do" by the other, once they reach that place in the
+   *     array: leader "jobDest" stores the source node array index (relative
+   *     to array dtd->scaffold) in field "numchildren".
+   *
+   *   - The slower one, pointer dest, looks at the value stored in the
+   *     "numchildren" field (which actually holds a source node array index
+   *     at that time) and puts the real data from dtd->scaffold in.
+   *
+   * - Before the loop starts, jobDest writes source array index 0
+   *   (where the root node is located) so that dest will have something to do
+   *   when it starts operation.
+   *
+   * - Whenever nodes with children are encountered, jobDest appends
+   *   them as new jobs, in order.  As a result, tree node siblings are
+   *   adjacent in the resulting array, for example:
+   *
+   *     [0] root, has two children
+   *       [1] first child of 0, has three children
+   *         [3] first child of 1, does not have children
+   *         [4] second child of 1, does not have children
+   *         [5] third child of 1, does not have children
+   *       [2] second child of 0, does not have children
+   *
+   *   Or (the same data) presented in flat array view:
+   *
+   *     [0] root, has two children
+   *
+   *     [1] first child of 0, has three children
+   *     [2] second child of 0, does not have children
+   *
+   *     [3] first child of 1, does not have children
+   *     [4] second child of 1, does not have children
+   *     [5] third child of 1, does not have children
+   *
+   * - The algorithm repeats until all target array indices have been processed.
+   */
+  XML_Content *dest = ret; /* tree node writing location, moves upwards */
+  XML_Content *const destLimit = &ret[dtd->scaffCount];
+  XML_Content *jobDest = ret; /* next free writing location in target array */
+  str = (XML_Char *)&ret[dtd->scaffCount];
 
-  build_node(parser, 0, ret, &cpos, &str);
+  /* Add the starting job, the root node (index 0) of the source tree  */
+  (jobDest++)->numchildren = 0;
+
+  for (; dest < destLimit; dest++) {
+    /* Retrieve source tree array index from job storage */
+    const int src_node = (int)dest->numchildren;
+
+    /* Convert item */
+    dest->type = dtd->scaffold[src_node].type;
+    dest->quant = dtd->scaffold[src_node].quant;
+    if (dest->type == XML_CTYPE_NAME) {
+      const XML_Char *src;
+      dest->name = str;
+      src = dtd->scaffold[src_node].name;
+      for (;;) {
+        *str++ = *src;
+        if (! *src)
+          break;
+        src++;
+      }
+      dest->numchildren = 0;
+      dest->children = NULL;
+    } else {
+      unsigned int i;
+      int cn;
+      dest->name = NULL;
+      dest->numchildren = dtd->scaffold[src_node].childcnt;
+      dest->children = jobDest;
+
+      /* Append scaffold indices of children to array */
+      for (i = 0, cn = dtd->scaffold[src_node].firstchild;
+           i < dest->numchildren; i++, cn = dtd->scaffold[cn].nextsib)
+        (jobDest++)->numchildren = (unsigned int)cn;
+    }
+  }
+
   return ret;
 }
 
@@ -6917,3 +7501,755 @@
   memcpy(result, s, charsRequired * sizeof(XML_Char));
   return result;
 }
+
+#ifdef XML_DTD
+
+static float
+accountingGetCurrentAmplification(XML_Parser rootParser) {
+  const XmlBigCount countBytesOutput
+      = rootParser->m_accounting.countBytesDirect
+        + rootParser->m_accounting.countBytesIndirect;
+  const float amplificationFactor
+      = rootParser->m_accounting.countBytesDirect
+            ? (countBytesOutput
+               / (float)(rootParser->m_accounting.countBytesDirect))
+            : 1.0f;
+  assert(! rootParser->m_parentParser);
+  return amplificationFactor;
+}
+
+static void
+accountingReportStats(XML_Parser originParser, const char *epilog) {
+  const XML_Parser rootParser = getRootParserOf(originParser, NULL);
+  assert(! rootParser->m_parentParser);
+
+  if (rootParser->m_accounting.debugLevel < 1) {
+    return;
+  }
+
+  const float amplificationFactor
+      = accountingGetCurrentAmplification(rootParser);
+  fprintf(stderr,
+          "expat: Accounting(%p): Direct " EXPAT_FMT_ULL(
+              "10") ", indirect " EXPAT_FMT_ULL("10") ", amplification %8.2f%s",
+          (void *)rootParser, rootParser->m_accounting.countBytesDirect,
+          rootParser->m_accounting.countBytesIndirect,
+          (double)amplificationFactor, epilog);
+}
+
+static void
+accountingOnAbort(XML_Parser originParser) {
+  accountingReportStats(originParser, " ABORTING\n");
+}
+
+static void
+accountingReportDiff(XML_Parser rootParser,
+                     unsigned int levelsAwayFromRootParser, const char *before,
+                     const char *after, ptrdiff_t bytesMore, int source_line,
+                     enum XML_Account account) {
+  assert(! rootParser->m_parentParser);
+
+  fprintf(stderr,
+          " (+" EXPAT_FMT_PTRDIFF_T("6") " bytes %s|%d, xmlparse.c:%d) %*s\"",
+          bytesMore, (account == XML_ACCOUNT_DIRECT) ? "DIR" : "EXP",
+          levelsAwayFromRootParser, source_line, 10, "");
+
+  const char ellipis[] = "[..]";
+  const size_t ellipsisLength = sizeof(ellipis) /* because compile-time */ - 1;
+  const unsigned int contextLength = 10;
+
+  /* Note: Performance is of no concern here */
+  const char *walker = before;
+  if ((rootParser->m_accounting.debugLevel >= 3)
+      || (after - before)
+             <= (ptrdiff_t)(contextLength + ellipsisLength + contextLength)) {
+    for (; walker < after; walker++) {
+      fprintf(stderr, "%s", unsignedCharToPrintable(walker[0]));
+    }
+  } else {
+    for (; walker < before + contextLength; walker++) {
+      fprintf(stderr, "%s", unsignedCharToPrintable(walker[0]));
+    }
+    fprintf(stderr, ellipis);
+    walker = after - contextLength;
+    for (; walker < after; walker++) {
+      fprintf(stderr, "%s", unsignedCharToPrintable(walker[0]));
+    }
+  }
+  fprintf(stderr, "\"\n");
+}
+
+static XML_Bool
+accountingDiffTolerated(XML_Parser originParser, int tok, const char *before,
+                        const char *after, int source_line,
+                        enum XML_Account account) {
+  /* Note: We need to check the token type *first* to be sure that
+   *       we can even access variable <after>, safely.
+   *       E.g. for XML_TOK_NONE <after> may hold an invalid pointer. */
+  switch (tok) {
+  case XML_TOK_INVALID:
+  case XML_TOK_PARTIAL:
+  case XML_TOK_PARTIAL_CHAR:
+  case XML_TOK_NONE:
+    return XML_TRUE;
+  }
+
+  if (account == XML_ACCOUNT_NONE)
+    return XML_TRUE; /* because these bytes have been accounted for, already */
+
+  unsigned int levelsAwayFromRootParser;
+  const XML_Parser rootParser
+      = getRootParserOf(originParser, &levelsAwayFromRootParser);
+  assert(! rootParser->m_parentParser);
+
+  const int isDirect
+      = (account == XML_ACCOUNT_DIRECT) && (originParser == rootParser);
+  const ptrdiff_t bytesMore = after - before;
+
+  XmlBigCount *const additionTarget
+      = isDirect ? &rootParser->m_accounting.countBytesDirect
+                 : &rootParser->m_accounting.countBytesIndirect;
+
+  /* Detect and avoid integer overflow */
+  if (*additionTarget > (XmlBigCount)(-1) - (XmlBigCount)bytesMore)
+    return XML_FALSE;
+  *additionTarget += bytesMore;
+
+  const XmlBigCount countBytesOutput
+      = rootParser->m_accounting.countBytesDirect
+        + rootParser->m_accounting.countBytesIndirect;
+  const float amplificationFactor
+      = accountingGetCurrentAmplification(rootParser);
+  const XML_Bool tolerated
+      = (countBytesOutput < rootParser->m_accounting.activationThresholdBytes)
+        || (amplificationFactor
+            <= rootParser->m_accounting.maximumAmplificationFactor);
+
+  if (rootParser->m_accounting.debugLevel >= 2) {
+    accountingReportStats(rootParser, "");
+    accountingReportDiff(rootParser, levelsAwayFromRootParser, before, after,
+                         bytesMore, source_line, account);
+  }
+
+  return tolerated;
+}
+
+unsigned long long
+testingAccountingGetCountBytesDirect(XML_Parser parser) {
+  if (! parser)
+    return 0;
+  return parser->m_accounting.countBytesDirect;
+}
+
+unsigned long long
+testingAccountingGetCountBytesIndirect(XML_Parser parser) {
+  if (! parser)
+    return 0;
+  return parser->m_accounting.countBytesIndirect;
+}
+
+static void
+entityTrackingReportStats(XML_Parser rootParser, ENTITY *entity,
+                          const char *action, int sourceLine) {
+  assert(! rootParser->m_parentParser);
+  if (rootParser->m_entity_stats.debugLevel < 1)
+    return;
+
+#  if defined(XML_UNICODE)
+  const char *const entityName = "[..]";
+#  else
+  const char *const entityName = entity->name;
+#  endif
+
+  fprintf(
+      stderr,
+      "expat: Entities(%p): Count %9d, depth %2d/%2d %*s%s%s; %s length %d (xmlparse.c:%d)\n",
+      (void *)rootParser, rootParser->m_entity_stats.countEverOpened,
+      rootParser->m_entity_stats.currentDepth,
+      rootParser->m_entity_stats.maximumDepthSeen,
+      (rootParser->m_entity_stats.currentDepth - 1) * 2, "",
+      entity->is_param ? "%" : "&", entityName, action, entity->textLen,
+      sourceLine);
+}
+
+static void
+entityTrackingOnOpen(XML_Parser originParser, ENTITY *entity, int sourceLine) {
+  const XML_Parser rootParser = getRootParserOf(originParser, NULL);
+  assert(! rootParser->m_parentParser);
+
+  rootParser->m_entity_stats.countEverOpened++;
+  rootParser->m_entity_stats.currentDepth++;
+  if (rootParser->m_entity_stats.currentDepth
+      > rootParser->m_entity_stats.maximumDepthSeen) {
+    rootParser->m_entity_stats.maximumDepthSeen++;
+  }
+
+  entityTrackingReportStats(rootParser, entity, "OPEN ", sourceLine);
+}
+
+static void
+entityTrackingOnClose(XML_Parser originParser, ENTITY *entity, int sourceLine) {
+  const XML_Parser rootParser = getRootParserOf(originParser, NULL);
+  assert(! rootParser->m_parentParser);
+
+  entityTrackingReportStats(rootParser, entity, "CLOSE", sourceLine);
+  rootParser->m_entity_stats.currentDepth--;
+}
+
+static XML_Parser
+getRootParserOf(XML_Parser parser, unsigned int *outLevelDiff) {
+  XML_Parser rootParser = parser;
+  unsigned int stepsTakenUpwards = 0;
+  while (rootParser->m_parentParser) {
+    rootParser = rootParser->m_parentParser;
+    stepsTakenUpwards++;
+  }
+  assert(! rootParser->m_parentParser);
+  if (outLevelDiff != NULL) {
+    *outLevelDiff = stepsTakenUpwards;
+  }
+  return rootParser;
+}
+
+const char *
+unsignedCharToPrintable(unsigned char c) {
+  switch (c) {
+  case 0:
+    return "\\0";
+  case 1:
+    return "\\x1";
+  case 2:
+    return "\\x2";
+  case 3:
+    return "\\x3";
+  case 4:
+    return "\\x4";
+  case 5:
+    return "\\x5";
+  case 6:
+    return "\\x6";
+  case 7:
+    return "\\x7";
+  case 8:
+    return "\\x8";
+  case 9:
+    return "\\t";
+  case 10:
+    return "\\n";
+  case 11:
+    return "\\xB";
+  case 12:
+    return "\\xC";
+  case 13:
+    return "\\r";
+  case 14:
+    return "\\xE";
+  case 15:
+    return "\\xF";
+  case 16:
+    return "\\x10";
+  case 17:
+    return "\\x11";
+  case 18:
+    return "\\x12";
+  case 19:
+    return "\\x13";
+  case 20:
+    return "\\x14";
+  case 21:
+    return "\\x15";
+  case 22:
+    return "\\x16";
+  case 23:
+    return "\\x17";
+  case 24:
+    return "\\x18";
+  case 25:
+    return "\\x19";
+  case 26:
+    return "\\x1A";
+  case 27:
+    return "\\x1B";
+  case 28:
+    return "\\x1C";
+  case 29:
+    return "\\x1D";
+  case 30:
+    return "\\x1E";
+  case 31:
+    return "\\x1F";
+  case 32:
+    return " ";
+  case 33:
+    return "!";
+  case 34:
+    return "\\\"";
+  case 35:
+    return "#";
+  case 36:
+    return "$";
+  case 37:
+    return "%";
+  case 38:
+    return "&";
+  case 39:
+    return "'";
+  case 40:
+    return "(";
+  case 41:
+    return ")";
+  case 42:
+    return "*";
+  case 43:
+    return "+";
+  case 44:
+    return ",";
+  case 45:
+    return "-";
+  case 46:
+    return ".";
+  case 47:
+    return "/";
+  case 48:
+    return "0";
+  case 49:
+    return "1";
+  case 50:
+    return "2";
+  case 51:
+    return "3";
+  case 52:
+    return "4";
+  case 53:
+    return "5";
+  case 54:
+    return "6";
+  case 55:
+    return "7";
+  case 56:
+    return "8";
+  case 57:
+    return "9";
+  case 58:
+    return ":";
+  case 59:
+    return ";";
+  case 60:
+    return "<";
+  case 61:
+    return "=";
+  case 62:
+    return ">";
+  case 63:
+    return "?";
+  case 64:
+    return "@";
+  case 65:
+    return "A";
+  case 66:
+    return "B";
+  case 67:
+    return "C";
+  case 68:
+    return "D";
+  case 69:
+    return "E";
+  case 70:
+    return "F";
+  case 71:
+    return "G";
+  case 72:
+    return "H";
+  case 73:
+    return "I";
+  case 74:
+    return "J";
+  case 75:
+    return "K";
+  case 76:
+    return "L";
+  case 77:
+    return "M";
+  case 78:
+    return "N";
+  case 79:
+    return "O";
+  case 80:
+    return "P";
+  case 81:
+    return "Q";
+  case 82:
+    return "R";
+  case 83:
+    return "S";
+  case 84:
+    return "T";
+  case 85:
+    return "U";
+  case 86:
+    return "V";
+  case 87:
+    return "W";
+  case 88:
+    return "X";
+  case 89:
+    return "Y";
+  case 90:
+    return "Z";
+  case 91:
+    return "[";
+  case 92:
+    return "\\\\";
+  case 93:
+    return "]";
+  case 94:
+    return "^";
+  case 95:
+    return "_";
+  case 96:
+    return "`";
+  case 97:
+    return "a";
+  case 98:
+    return "b";
+  case 99:
+    return "c";
+  case 100:
+    return "d";
+  case 101:
+    return "e";
+  case 102:
+    return "f";
+  case 103:
+    return "g";
+  case 104:
+    return "h";
+  case 105:
+    return "i";
+  case 106:
+    return "j";
+  case 107:
+    return "k";
+  case 108:
+    return "l";
+  case 109:
+    return "m";
+  case 110:
+    return "n";
+  case 111:
+    return "o";
+  case 112:
+    return "p";
+  case 113:
+    return "q";
+  case 114:
+    return "r";
+  case 115:
+    return "s";
+  case 116:
+    return "t";
+  case 117:
+    return "u";
+  case 118:
+    return "v";
+  case 119:
+    return "w";
+  case 120:
+    return "x";
+  case 121:
+    return "y";
+  case 122:
+    return "z";
+  case 123:
+    return "{";
+  case 124:
+    return "|";
+  case 125:
+    return "}";
+  case 126:
+    return "~";
+  case 127:
+    return "\\x7F";
+  case 128:
+    return "\\x80";
+  case 129:
+    return "\\x81";
+  case 130:
+    return "\\x82";
+  case 131:
+    return "\\x83";
+  case 132:
+    return "\\x84";
+  case 133:
+    return "\\x85";
+  case 134:
+    return "\\x86";
+  case 135:
+    return "\\x87";
+  case 136:
+    return "\\x88";
+  case 137:
+    return "\\x89";
+  case 138:
+    return "\\x8A";
+  case 139:
+    return "\\x8B";
+  case 140:
+    return "\\x8C";
+  case 141:
+    return "\\x8D";
+  case 142:
+    return "\\x8E";
+  case 143:
+    return "\\x8F";
+  case 144:
+    return "\\x90";
+  case 145:
+    return "\\x91";
+  case 146:
+    return "\\x92";
+  case 147:
+    return "\\x93";
+  case 148:
+    return "\\x94";
+  case 149:
+    return "\\x95";
+  case 150:
+    return "\\x96";
+  case 151:
+    return "\\x97";
+  case 152:
+    return "\\x98";
+  case 153:
+    return "\\x99";
+  case 154:
+    return "\\x9A";
+  case 155:
+    return "\\x9B";
+  case 156:
+    return "\\x9C";
+  case 157:
+    return "\\x9D";
+  case 158:
+    return "\\x9E";
+  case 159:
+    return "\\x9F";
+  case 160:
+    return "\\xA0";
+  case 161:
+    return "\\xA1";
+  case 162:
+    return "\\xA2";
+  case 163:
+    return "\\xA3";
+  case 164:
+    return "\\xA4";
+  case 165:
+    return "\\xA5";
+  case 166:
+    return "\\xA6";
+  case 167:
+    return "\\xA7";
+  case 168:
+    return "\\xA8";
+  case 169:
+    return "\\xA9";
+  case 170:
+    return "\\xAA";
+  case 171:
+    return "\\xAB";
+  case 172:
+    return "\\xAC";
+  case 173:
+    return "\\xAD";
+  case 174:
+    return "\\xAE";
+  case 175:
+    return "\\xAF";
+  case 176:
+    return "\\xB0";
+  case 177:
+    return "\\xB1";
+  case 178:
+    return "\\xB2";
+  case 179:
+    return "\\xB3";
+  case 180:
+    return "\\xB4";
+  case 181:
+    return "\\xB5";
+  case 182:
+    return "\\xB6";
+  case 183:
+    return "\\xB7";
+  case 184:
+    return "\\xB8";
+  case 185:
+    return "\\xB9";
+  case 186:
+    return "\\xBA";
+  case 187:
+    return "\\xBB";
+  case 188:
+    return "\\xBC";
+  case 189:
+    return "\\xBD";
+  case 190:
+    return "\\xBE";
+  case 191:
+    return "\\xBF";
+  case 192:
+    return "\\xC0";
+  case 193:
+    return "\\xC1";
+  case 194:
+    return "\\xC2";
+  case 195:
+    return "\\xC3";
+  case 196:
+    return "\\xC4";
+  case 197:
+    return "\\xC5";
+  case 198:
+    return "\\xC6";
+  case 199:
+    return "\\xC7";
+  case 200:
+    return "\\xC8";
+  case 201:
+    return "\\xC9";
+  case 202:
+    return "\\xCA";
+  case 203:
+    return "\\xCB";
+  case 204:
+    return "\\xCC";
+  case 205:
+    return "\\xCD";
+  case 206:
+    return "\\xCE";
+  case 207:
+    return "\\xCF";
+  case 208:
+    return "\\xD0";
+  case 209:
+    return "\\xD1";
+  case 210:
+    return "\\xD2";
+  case 211:
+    return "\\xD3";
+  case 212:
+    return "\\xD4";
+  case 213:
+    return "\\xD5";
+  case 214:
+    return "\\xD6";
+  case 215:
+    return "\\xD7";
+  case 216:
+    return "\\xD8";
+  case 217:
+    return "\\xD9";
+  case 218:
+    return "\\xDA";
+  case 219:
+    return "\\xDB";
+  case 220:
+    return "\\xDC";
+  case 221:
+    return "\\xDD";
+  case 222:
+    return "\\xDE";
+  case 223:
+    return "\\xDF";
+  case 224:
+    return "\\xE0";
+  case 225:
+    return "\\xE1";
+  case 226:
+    return "\\xE2";
+  case 227:
+    return "\\xE3";
+  case 228:
+    return "\\xE4";
+  case 229:
+    return "\\xE5";
+  case 230:
+    return "\\xE6";
+  case 231:
+    return "\\xE7";
+  case 232:
+    return "\\xE8";
+  case 233:
+    return "\\xE9";
+  case 234:
+    return "\\xEA";
+  case 235:
+    return "\\xEB";
+  case 236:
+    return "\\xEC";
+  case 237:
+    return "\\xED";
+  case 238:
+    return "\\xEE";
+  case 239:
+    return "\\xEF";
+  case 240:
+    return "\\xF0";
+  case 241:
+    return "\\xF1";
+  case 242:
+    return "\\xF2";
+  case 243:
+    return "\\xF3";
+  case 244:
+    return "\\xF4";
+  case 245:
+    return "\\xF5";
+  case 246:
+    return "\\xF6";
+  case 247:
+    return "\\xF7";
+  case 248:
+    return "\\xF8";
+  case 249:
+    return "\\xF9";
+  case 250:
+    return "\\xFA";
+  case 251:
+    return "\\xFB";
+  case 252:
+    return "\\xFC";
+  case 253:
+    return "\\xFD";
+  case 254:
+    return "\\xFE";
+  case 255:
+    return "\\xFF";
+  default:
+    assert(0); /* never gets here */
+    return "dead code";
+  }
+  assert(0); /* never gets here */
+}
+
+#endif /* XML_DTD */
+
+static unsigned long
+getDebugLevel(const char *variableName, unsigned long defaultDebugLevel) {
+  const char *const valueOrNull = getenv(variableName);
+  if (valueOrNull == NULL) {
+    return defaultDebugLevel;
+  }
+  const char *const value = valueOrNull;
+
+  errno = 0;
+  char *afterValue = (char *)value;
+  unsigned long debugLevel = strtoul(value, &afterValue, 10);
+  if ((errno != 0) || (afterValue[0] != '\0')) {
+    errno = 0;
+    return defaultDebugLevel;
+  }
+
+  return debugLevel;
+}
diff --git a/lib/xmlrole.c b/lib/xmlrole.c
index 3b676a4..3f0f5c1 100644
--- a/lib/xmlrole.c
+++ b/lib/xmlrole.c
@@ -7,7 +7,15 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Greg Stein <gstein@users.sourceforge.net>
+   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) 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>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -30,15 +38,13 @@
    USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
+#include <expat_config.h>
+
 #include <stddef.h>
 
 #ifdef _WIN32
 #  include "winconfig.h"
-#else
-#  ifdef HAVE_EXPAT_CONFIG_H
-#    include <expat_config.h>
-#  endif
-#endif /* ndef _WIN32 */
+#endif
 
 #include "expat_external.h"
 #include "internal.h"
diff --git a/lib/xmlrole.h b/lib/xmlrole.h
index 036aba6..d6e1fa1 100644
--- a/lib/xmlrole.h
+++ b/lib/xmlrole.h
@@ -7,7 +7,10 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   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>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/lib/xmltok.c b/lib/xmltok.c
index 58dce90..c659983 100644
--- a/lib/xmltok.c
+++ b/lib/xmltok.c
@@ -7,7 +7,20 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2001-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   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      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      Alexander Bluhm <alexander.bluhm@gmx.net>
+   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>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -30,17 +43,15 @@
    USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
+#include <expat_config.h>
+
 #include <stddef.h>
 #include <string.h> /* memcpy */
 #include <stdbool.h>
 
 #ifdef _WIN32
 #  include "winconfig.h"
-#else
-#  ifdef HAVE_EXPAT_CONFIG_H
-#    include <expat_config.h>
-#  endif
-#endif /* ndef _WIN32 */
+#endif
 
 #include "expat_external.h"
 #include "internal.h"
@@ -87,11 +98,6 @@
         + ((((byte)[1]) & 3) << 1) + ((((byte)[2]) >> 5) & 1)]                 \
    & (1u << (((byte)[2]) & 0x1F)))
 
-#define UTF8_GET_NAMING(pages, p, n)                                           \
-  ((n) == 2                                                                    \
-       ? UTF8_GET_NAMING2(pages, (const unsigned char *)(p))                   \
-       : ((n) == 3 ? UTF8_GET_NAMING3(pages, (const unsigned char *)(p)) : 0))
-
 /* Detection of invalid UTF-8 sequences is based on Table 3.1B
    of Unicode 3.2: http://www.unicode.org/unicode/reports/tr28/
    with the additional restriction of not allowing the Unicode
@@ -261,8 +267,14 @@
 
 #define IS_NAME_CHAR(enc, p, n) (AS_NORMAL_ENCODING(enc)->isName##n(enc, p))
 #define IS_NMSTRT_CHAR(enc, p, n) (AS_NORMAL_ENCODING(enc)->isNmstrt##n(enc, p))
-#define IS_INVALID_CHAR(enc, p, n)                                             \
-  (AS_NORMAL_ENCODING(enc)->isInvalid##n(enc, p))
+#ifdef XML_MIN_SIZE
+#  define IS_INVALID_CHAR(enc, p, n)                                           \
+    (AS_NORMAL_ENCODING(enc)->isInvalid##n                                     \
+     && AS_NORMAL_ENCODING(enc)->isInvalid##n(enc, p))
+#else
+#  define IS_INVALID_CHAR(enc, p, n)                                           \
+    (AS_NORMAL_ENCODING(enc)->isInvalid##n(enc, p))
+#endif
 
 #ifdef XML_MIN_SIZE
 #  define IS_NAME_CHAR_MINBPC(enc, p)                                          \
diff --git a/lib/xmltok.h b/lib/xmltok.h
index 2adbf53..6f630c2 100644
--- a/lib/xmltok.h
+++ b/lib/xmltok.h
@@ -7,7 +7,11 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   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) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/lib/xmltok_impl.c b/lib/xmltok_impl.c
index 06d5c90..4072b06 100644
--- a/lib/xmltok_impl.c
+++ b/lib/xmltok_impl.c
@@ -1,4 +1,4 @@
-/* This file is included!
+/* This file is included (from xmltok.c, 1-3 times depending on XML_MIN_SIZE)!
                             __  __            _
                          ___\ \/ /_ __   __ _| |_
                         / _ \\  /| '_ \ / _` | __|
@@ -7,7 +7,15 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2002-2016 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2018      Benjamin Peterson <benjamin@python.org>
+   Copyright (c) 2018      Anton Maklakov <antmak.pub@gmail.com>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2020      Boris Kolpackov <boris@codesynthesis.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -32,7 +40,7 @@
 
 #ifdef XML_TOK_IMPL_C
 
-#  ifndef IS_INVALID_CHAR
+#  ifndef IS_INVALID_CHAR // i.e. for UTF-16 and XML_MIN_SIZE not defined
 #    define IS_INVALID_CHAR(enc, ptr, n) (0)
 #  endif
 
@@ -61,7 +69,7 @@
   case BT_LEAD##n:                                                             \
     if (end - ptr < n)                                                         \
       return XML_TOK_PARTIAL_CHAR;                                             \
-    if (! IS_NAME_CHAR(enc, ptr, n)) {                                         \
+    if (IS_INVALID_CHAR(enc, ptr, n) || ! IS_NAME_CHAR(enc, ptr, n)) {         \
       *nextTokPtr = ptr;                                                       \
       return XML_TOK_INVALID;                                                  \
     }                                                                          \
@@ -90,7 +98,7 @@
   case BT_LEAD##n:                                                             \
     if (end - ptr < n)                                                         \
       return XML_TOK_PARTIAL_CHAR;                                             \
-    if (! IS_NMSTRT_CHAR(enc, ptr, n)) {                                       \
+    if (IS_INVALID_CHAR(enc, ptr, n) || ! IS_NMSTRT_CHAR(enc, ptr, n)) {       \
       *nextTokPtr = ptr;                                                       \
       return XML_TOK_INVALID;                                                  \
     }                                                                          \
@@ -1134,6 +1142,10 @@
   case BT_LEAD##n:                                                             \
     if (end - ptr < n)                                                         \
       return XML_TOK_PARTIAL_CHAR;                                             \
+    if (IS_INVALID_CHAR(enc, ptr, n)) {                                        \
+      *nextTokPtr = ptr;                                                       \
+      return XML_TOK_INVALID;                                                  \
+    }                                                                          \
     if (IS_NMSTRT_CHAR(enc, ptr, n)) {                                         \
       ptr += n;                                                                \
       tok = XML_TOK_NAME;                                                      \
@@ -1262,7 +1274,7 @@
     switch (BYTE_TYPE(enc, ptr)) {
 #  define LEAD_CASE(n)                                                         \
   case BT_LEAD##n:                                                             \
-    ptr += n;                                                                  \
+    ptr += n; /* NOTE: The encoding has already been validated. */             \
     break;
       LEAD_CASE(2)
       LEAD_CASE(3)
@@ -1331,7 +1343,7 @@
     switch (BYTE_TYPE(enc, ptr)) {
 #  define LEAD_CASE(n)                                                         \
   case BT_LEAD##n:                                                             \
-    ptr += n;                                                                  \
+    ptr += n; /* NOTE: The encoding has already been validated. */             \
     break;
       LEAD_CASE(2)
       LEAD_CASE(3)
@@ -1510,7 +1522,7 @@
       state = inName;                                                          \
     }
 #  define LEAD_CASE(n)                                                         \
-  case BT_LEAD##n:                                                             \
+  case BT_LEAD##n: /* NOTE: The encoding has already been validated. */        \
     START_NAME ptr += (n - MINBPC(enc));                                       \
     break;
       LEAD_CASE(2)
@@ -1722,7 +1734,7 @@
     switch (BYTE_TYPE(enc, ptr)) {
 #  define LEAD_CASE(n)                                                         \
   case BT_LEAD##n:                                                             \
-    ptr += n;                                                                  \
+    ptr += n; /* NOTE: The encoding has already been validated. */             \
     break;
       LEAD_CASE(2)
       LEAD_CASE(3)
@@ -1767,7 +1779,7 @@
     switch (BYTE_TYPE(enc, ptr)) {
 #  define LEAD_CASE(n)                                                         \
   case BT_LEAD##n:                                                             \
-    ptr += n;                                                                  \
+    ptr += n; /* NOTE: The encoding has already been validated. */             \
     pos->columnNumber++;                                                       \
     break;
       LEAD_CASE(2)
diff --git a/lib/xmltok_impl.h b/lib/xmltok_impl.h
index e925dbc..c518aad 100644
--- a/lib/xmltok_impl.h
+++ b/lib/xmltok_impl.h
@@ -7,7 +7,8 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2017-2019 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/lib/xmltok_ns.c b/lib/xmltok_ns.c
index 919c74e..fbdd3e3 100644
--- a/lib/xmltok_ns.c
+++ b/lib/xmltok_ns.c
@@ -7,7 +7,11 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2002-2006 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -89,7 +93,7 @@
 static const ENCODING *
 NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end) {
 #  define ENCODING_MAX 128
-  char buf[ENCODING_MAX];
+  char buf[ENCODING_MAX] = "";
   char *p = buf;
   int i;
   XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1);
diff --git a/patches/config.diff b/patches/config.diff
index 340889a..d9a9eaa 100644
--- a/patches/config.diff
+++ b/patches/config.diff
@@ -23,12 +23,22 @@
  #define HAVE_GETPAGESIZE 1
  
  /* Define to 1 if you have the `getrandom' function. */
-+#if defined(__BIONIC__)
++#if defined(__BIONIC__) || defined(ANDROID_HOST_MUSL)
  #define HAVE_GETRANDOM 1
 +#endif
  
  /* Define to 1 if you have the <inttypes.h> header file. */
  #define HAVE_INTTYPES_H 1
+@@ -56,7 +56,9 @@
+ #define HAVE_STRING_H 1
+ 
+ /* Define to 1 if you have `syscall' and `SYS_getrandom'. */
++#if 0 /* We only get here for glibc and Windows (where the better choices aren't available) and they don't have this fallback either. */
+ #define HAVE_SYSCALL_GETRANDOM 1
++#endif
+ 
+ /* Define to 1 if you have the <sys/param.h> header file. */
+ #define HAVE_SYS_PARAM_H 1
 @@ -115,7 +121,9 @@
  #define XML_CONTEXT_BYTES 1024
  
diff --git a/run.sh.in b/run.sh.in
index ff3fb36..72f86b6 100644
--- a/run.sh.in
+++ b/run.sh.in
@@ -1,10 +1,45 @@
 #! /usr/bin/env bash
-# Copyright (C) 2017 Expat development team
-# Licensed under the MIT license
+#                          __  __            _
+#                       ___\ \/ /_ __   __ _| |_
+#                      / _ \\  /| '_ \ / _` | __|
+#                     |  __//  \| |_) | (_| | |_
+#                      \___/_/\_\ .__/ \__,_|\__|
+#                               |_| XML parser
+#
+# Copyright (c) 2017-2022 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.
 
 case "@host@" in
 *-mingw*)
-    exec wine "$@"
+    case "$(uname -o)" in
+    Cygwin|Msys)
+        # Windows binary on Windows host
+        exec "$@"
+        ;;
+    *)
+        # Windows binary on non-Windows host
+        exec wine "$@"
+        ;;
+    esac
     ;;
 *)
     exec "$@"
diff --git a/test-driver-wrapper.sh b/test-driver-wrapper.sh
index 36a1852..305c6f5 100755
--- a/test-driver-wrapper.sh
+++ b/test-driver-wrapper.sh
@@ -6,7 +6,8 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017 Expat development team
+# Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2019 Jeffrey Walton <noloader@gmail.com>
 # Licensed under the MIT license:
 #
 # Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/tests/Makefile.am b/tests/Makefile.am
index e19fc1a..cb68e11 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -6,7 +6,9 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017 Expat development team
+# Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+# Copyright (c) 2020      Jeffrey Walton <noloader@gmail.com>
 # Licensed under the MIT license:
 #
 # Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -52,8 +54,11 @@
 runtestspp_SOURCES = \
     runtestspp.cpp
 
-runtests_LDADD = libruntests.a ../lib/libexpat.la
-runtestspp_LDADD = libruntests.a ../lib/libexpat.la
+runtests_LDADD = libruntests.a ../lib/libexpatinternal.la
+runtestspp_LDADD = libruntests.a ../lib/libexpatinternal.la
+
+runtests_LDFLAGS = @AM_LDFLAGS@ @LIBM@
+runtestspp_LDFLAGS = @AM_LDFLAGS@ @LIBM@
 
 EXTRA_DIST = \
     chardata.h \
diff --git a/tests/Makefile.in b/tests/Makefile.in
index 90cc4f7..024ddd9 100644
--- a/tests/Makefile.in
+++ b/tests/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -22,7 +22,9 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017 Expat development team
+# Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+# Copyright (c) 2020      Jeffrey Walton <noloader@gmail.com>
 # Licensed under the MIT license:
 #
 # Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -154,14 +156,20 @@
 libruntests_a_OBJECTS = $(am_libruntests_a_OBJECTS)
 am_runtests_OBJECTS = runtests.$(OBJEXT)
 runtests_OBJECTS = $(am_runtests_OBJECTS)
-runtests_DEPENDENCIES = libruntests.a ../lib/libexpat.la
+runtests_DEPENDENCIES = libruntests.a ../lib/libexpatinternal.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 = 
+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/libexpat.la
+runtestspp_DEPENDENCIES = libruntests.a ../lib/libexpatinternal.la
+runtestspp_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+	$(CXXFLAGS) $(runtestspp_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
@@ -259,8 +267,6 @@
   unique=`for i in $$list; do \
     if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
   done | $(am__uniquify_input)`
-ETAGS = etags
-CTAGS = ctags
 am__tty_colors_dummy = \
   mgn= red= grn= lgn= blu= brg= std=; \
   am__color_tests=no
@@ -510,7 +516,10 @@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+CMAKE_SHARED_LIBRARY_PREFIX = @CMAKE_SHARED_LIBRARY_PREFIX@
 CPPFLAGS = @CPPFLAGS@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
 CXX = @CXX@
 CXXCPP = @CXXCPP@
 CXXDEPMODE = @CXXDEPMODE@
@@ -526,6 +535,7 @@
 ECHO_N = @ECHO_N@
 ECHO_T = @ECHO_T@
 EGREP = @EGREP@
+ETAGS = @ETAGS@
 EXEEXT = @EXEEXT@
 EXPAT_ATTR_INFO = @EXPAT_ATTR_INFO@
 EXPAT_CHAR_TYPE = @EXPAT_CHAR_TYPE@
@@ -547,6 +557,7 @@
 LIBAGE = @LIBAGE@
 LIBCURRENT = @LIBCURRENT@
 LIBDIR_BASENAME = @LIBDIR_BASENAME@
+LIBM = @LIBM@
 LIBOBJS = @LIBOBJS@
 LIBREVISION = @LIBREVISION@
 LIBS = @LIBS@
@@ -581,7 +592,6 @@
 SO_PATCH = @SO_PATCH@
 STRIP = @STRIP@
 VERSION = @VERSION@
-_EXPAT_OUTPUT_NAME = @_EXPAT_OUTPUT_NAME@
 abs_builddir = @abs_builddir@
 abs_srcdir = @abs_srcdir@
 abs_top_builddir = @abs_top_builddir@
@@ -590,6 +600,7 @@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_cv_sizeof_void_p = @ac_cv_sizeof_void_p@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -653,8 +664,10 @@
 runtestspp_SOURCES = \
     runtestspp.cpp
 
-runtests_LDADD = libruntests.a ../lib/libexpat.la
-runtestspp_LDADD = libruntests.a ../lib/libexpat.la
+runtests_LDADD = libruntests.a ../lib/libexpatinternal.la
+runtestspp_LDADD = libruntests.a ../lib/libexpatinternal.la
+runtests_LDFLAGS = @AM_LDFLAGS@ @LIBM@
+runtestspp_LDFLAGS = @AM_LDFLAGS@ @LIBM@
 EXTRA_DIST = \
     chardata.h \
     structdata.h \
@@ -718,11 +731,11 @@
 
 runtests$(EXEEXT): $(runtests_OBJECTS) $(runtests_DEPENDENCIES) $(EXTRA_runtests_DEPENDENCIES) 
 	@rm -f runtests$(EXEEXT)
-	$(AM_V_CCLD)$(LINK) $(runtests_OBJECTS) $(runtests_LDADD) $(LIBS)
+	$(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)$(CXXLINK) $(runtestspp_OBJECTS) $(runtestspp_LDADD) $(LIBS)
+	$(AM_V_CXXLD)$(runtestspp_LINK) $(runtestspp_OBJECTS) $(runtestspp_LDADD) $(LIBS)
 
 mostlyclean-compile:
 	-rm -f *.$(OBJEXT)
@@ -1059,7 +1072,6 @@
 @am__EXEEXT_TRUE@	--log-file $$b.log --trs-file $$b.trs \
 @am__EXEEXT_TRUE@	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
 @am__EXEEXT_TRUE@	"$$tst" $(AM_TESTS_FD_REDIRECT)
-
 distdir: $(BUILT_SOURCES)
 	$(MAKE) $(AM_MAKEFLAGS) distdir-am
 
diff --git a/tests/benchmark/Makefile.am b/tests/benchmark/Makefile.am
index 5ea8dc1..f1bf336 100644
--- a/tests/benchmark/Makefile.am
+++ b/tests/benchmark/Makefile.am
@@ -6,7 +6,8 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017 Expat development team
+# Copyright (c) 2017 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
diff --git a/tests/benchmark/Makefile.in b/tests/benchmark/Makefile.in
index 5882f7d..64238f1 100644
--- a/tests/benchmark/Makefile.in
+++ b/tests/benchmark/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -22,7 +22,8 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017 Expat development team
+# Copyright (c) 2017 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
@@ -207,8 +208,6 @@
   unique=`for i in $$list; do \
     if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
   done | $(am__uniquify_input)`
-ETAGS = etags
-CTAGS = ctags
 am__DIST_COMMON = $(srcdir)/Makefile.in \
 	$(top_srcdir)/conftools/depcomp
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
@@ -228,7 +227,10 @@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+CMAKE_SHARED_LIBRARY_PREFIX = @CMAKE_SHARED_LIBRARY_PREFIX@
 CPPFLAGS = @CPPFLAGS@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
 CXX = @CXX@
 CXXCPP = @CXXCPP@
 CXXDEPMODE = @CXXDEPMODE@
@@ -244,6 +246,7 @@
 ECHO_N = @ECHO_N@
 ECHO_T = @ECHO_T@
 EGREP = @EGREP@
+ETAGS = @ETAGS@
 EXEEXT = @EXEEXT@
 EXPAT_ATTR_INFO = @EXPAT_ATTR_INFO@
 EXPAT_CHAR_TYPE = @EXPAT_CHAR_TYPE@
@@ -265,6 +268,7 @@
 LIBAGE = @LIBAGE@
 LIBCURRENT = @LIBCURRENT@
 LIBDIR_BASENAME = @LIBDIR_BASENAME@
+LIBM = @LIBM@
 LIBOBJS = @LIBOBJS@
 LIBREVISION = @LIBREVISION@
 LIBS = @LIBS@
@@ -299,7 +303,6 @@
 SO_PATCH = @SO_PATCH@
 STRIP = @STRIP@
 VERSION = @VERSION@
-_EXPAT_OUTPUT_NAME = @_EXPAT_OUTPUT_NAME@
 abs_builddir = @abs_builddir@
 abs_srcdir = @abs_srcdir@
 abs_top_builddir = @abs_top_builddir@
@@ -308,6 +311,7 @@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_cv_sizeof_void_p = @ac_cv_sizeof_void_p@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -498,7 +502,6 @@
 
 distclean-tags:
 	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-
 distdir: $(BUILT_SOURCES)
 	$(MAKE) $(AM_MAKEFLAGS) distdir-am
 
diff --git a/tests/benchmark/benchmark.c b/tests/benchmark/benchmark.c
index fda8268..2c4eb78 100644
--- a/tests/benchmark/benchmark.c
+++ b/tests/benchmark/benchmark.c
@@ -6,8 +6,10 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   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      Rhodri James <rhodri@wildebeest.org.uk>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/tests/chardata.c b/tests/chardata.c
index 75a5016..d1989a8 100644
--- a/tests/chardata.c
+++ b/tests/chardata.c
@@ -6,8 +6,12 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   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) 2017      Joe Orton <jorton@redhat.com>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -30,9 +34,7 @@
    USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
-#ifdef HAVE_EXPAT_CONFIG_H
-#  include <expat_config.h>
-#endif
+#include <expat_config.h>
 #include "minicheck.h"
 
 #include <assert.h>
diff --git a/tests/chardata.h b/tests/chardata.h
index 4001b9b..ccb631f 100644
--- a/tests/chardata.h
+++ b/tests/chardata.h
@@ -7,8 +7,9 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2002-2004 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/tests/memcheck.c b/tests/memcheck.c
index 41355f6..48822e5 100644
--- a/tests/memcheck.c
+++ b/tests/memcheck.c
@@ -6,8 +6,8 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/tests/memcheck.h b/tests/memcheck.h
index 4d20f4b..4c0cb72 100644
--- a/tests/memcheck.h
+++ b/tests/memcheck.h
@@ -7,8 +7,8 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/tests/minicheck.c b/tests/minicheck.c
index ab0c35f..1c65748 100644
--- a/tests/minicheck.c
+++ b/tests/minicheck.c
@@ -10,8 +10,11 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2004-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2016-2020 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>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/tests/minicheck.h b/tests/minicheck.h
index 88a1658..cc1f835 100644
--- a/tests/minicheck.h
+++ b/tests/minicheck.h
@@ -12,8 +12,9 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   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>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/tests/runtests.c b/tests/runtests.c
index 91ab4f2..6d6f669 100644
--- a/tests/runtests.c
+++ b/tests/runtests.c
@@ -6,8 +6,18 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   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-2018 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>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -30,14 +40,12 @@
    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
 
-#ifdef HAVE_EXPAT_CONFIG_H
-#  include <expat_config.h>
-#endif
-
 #include <assert.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -46,6 +54,7 @@
 #include <ctype.h>
 #include <limits.h>
 #include <stdint.h> /* intptr_t uint64_t */
+#include <math.h>   /* NAN, INFINITY, isnan */
 
 #if ! defined(__cplusplus)
 #  include <stdbool.h>
@@ -54,7 +63,7 @@
 #include "expat.h"
 #include "chardata.h"
 #include "structdata.h"
-#include "internal.h" /* for UNUSED_P only */
+#include "internal.h"
 #include "minicheck.h"
 #include "memcheck.h"
 #include "siphash.h"
@@ -2247,7 +2256,6 @@
 END_TEST
 
 /* Test handling of multiple unit UTF-16 characters */
-#ifndef XML_MIN_SIZE /* FIXME workaround -DXML_MIN_SIZE + ASan (issue #332) */
 START_TEST(test_multichar_cdata_utf16) {
   /* Test data is:
    *   <?xml version='1.0' encoding='utf-16'?>
@@ -2269,11 +2277,11 @@
                       "\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
+#ifdef XML_UNICODE
   const XML_Char *expected = XCS("\xd834\xdd5e\xd834\xdd5f");
-#  else
+#else
   const XML_Char *expected = XCS("\xf0\x9d\x85\x9e\xf0\x9d\x85\x9f");
-#  endif
+#endif
   CharData storage;
 
   CharData_Init(&storage);
@@ -2286,7 +2294,6 @@
   CharData_CheckXMLChars(&storage, expected);
 }
 END_TEST
-#endif /* ifndef XML_MIN_SIZE */
 
 /* Test that an element name with a UTF-16 surrogate pair is rejected */
 START_TEST(test_utf16_bad_surrogate_pair) {
@@ -2371,7 +2378,6 @@
 END_TEST
 
 /* Test failures in UTF-16 CDATA */
-#ifndef XML_MIN_SIZE /* FIXME workaround -DXML_MIN_SIZE + ASan (issue #332) */
 START_TEST(test_bad_cdata_utf16) {
   struct CaseData {
     size_t text_bytes;
@@ -2444,7 +2450,6 @@
   }
 }
 END_TEST
-#endif /* ifndef XML_MIN_SIZE */
 
 static const char *long_cdata_text
     = "<s><![CDATA["
@@ -2659,6 +2664,82 @@
 }
 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";
@@ -3842,6 +3923,30 @@
 }
 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>";
@@ -5969,6 +6074,105 @@
 }
 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) {
@@ -6146,6 +6350,14 @@
 }
 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/>
@@ -7191,6 +7403,35 @@
 }
 END_TEST
 
+START_TEST(test_ns_separator_in_uri) {
+  struct test_case {
+    enum XML_Status expectedStatus;
+    const char *doc;
+  };
+  struct test_case cases[] = {
+      {XML_STATUS_OK, "<doc xmlns='one_two' />"},
+      {XML_STATUS_ERROR, "<doc xmlns='one&#x0A;two' />"},
+  };
+
+  size_t i = 0;
+  size_t failCount = 0;
+  for (; i < sizeof(cases) / sizeof(cases[0]); i++) {
+    XML_Parser parser = XML_ParserCreateNS(NULL, '\n');
+    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)
@@ -7347,7 +7588,7 @@
     fail("Version mismatch");
 
 #if ! defined(XML_UNICODE) || defined(XML_UNICODE_WCHAR_T)
-  if (xcstrcmp(version_text, XCS("expat_2.3.0"))) /* needs bump on releases */
+  if (xcstrcmp(version_text, XCS("expat_2.4.6"))) /* 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
@@ -11222,6 +11463,389 @@
 }
 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
+
+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, 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++) {
+    const char *const printable = unsignedCharToPrintable(uc);
+    if (printable == NULL)
+      fail("unsignedCharToPrintable returned NULL");
+    if (strlen(printable) < (size_t)1)
+      fail("unsignedCharToPrintable returned empty string");
+  }
+
+  // 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)
+
 static Suite *
 make_suite(void) {
   Suite *s = suite_create("basic");
@@ -11230,6 +11854,9 @@
   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);
@@ -11303,19 +11930,16 @@
   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);
-#ifndef XML_MIN_SIZE /* FIXME workaround -DXML_MIN_SIZE + ASan (issue #332) */
   tcase_add_test(tc_basic, test_multichar_cdata_utf16);
-#endif
   tcase_add_test(tc_basic, test_utf16_bad_surrogate_pair);
   tcase_add_test(tc_basic, test_bad_cdata);
-#ifndef XML_MIN_SIZE /* FIXME workaround -DXML_MIN_SIZE + ASan (issue #332) */
   tcase_add_test(tc_basic, test_bad_cdata_utf16);
-#endif
   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);
@@ -11344,6 +11968,9 @@
   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);
@@ -11427,6 +12054,7 @@
   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);
@@ -11435,6 +12063,7 @@
   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);
@@ -11491,6 +12120,7 @@
   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);
@@ -11594,6 +12224,13 @@
   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);
+#endif
+
   return s;
 }
 
diff --git a/tests/runtestspp.cpp b/tests/runtestspp.cpp
index fd3ceaa..52f5305 100644
--- a/tests/runtestspp.cpp
+++ b/tests/runtestspp.cpp
@@ -9,8 +9,8 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2005 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/tests/structdata.c b/tests/structdata.c
index e81b7b1..d40e6c4 100644
--- a/tests/structdata.c
+++ b/tests/structdata.c
@@ -6,8 +6,8 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -30,9 +30,7 @@
    USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
-#ifdef HAVE_EXPAT_CONFIG_H
-#  include "expat_config.h"
-#endif
+#include "expat_config.h"
 
 #include <assert.h>
 #include <stdlib.h>
diff --git a/tests/structdata.h b/tests/structdata.h
index 870ffaf..09881b1 100644
--- a/tests/structdata.h
+++ b/tests/structdata.h
@@ -7,8 +7,7 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/tests/udiffer.py b/tests/udiffer.py
index 6fb91be..2b58a4c 100755
--- a/tests/udiffer.py
+++ b/tests/udiffer.py
@@ -6,7 +6,8 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017 Expat development team
+# Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk>
+# Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
 # Licensed under the MIT license:
 #
 # Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/tests/xmltest.sh b/tests/xmltest.sh
index 9b50035..dc409d0 100755
--- a/tests/xmltest.sh
+++ b/tests/xmltest.sh
@@ -1,24 +1,55 @@
 #! /usr/bin/env bash
-
-#   EXPAT TEST SCRIPT FOR W3C XML TEST SUITE
-
+# EXPAT TEST SCRIPT FOR W3C XML TEST SUITE
+#
 # This script can be used to exercise Expat against the
 # w3c.org xml test suite, available from
 # http://www.w3.org/XML/Test/xmlts20020606.zip.
-
+#
 # To run this script, first set XMLWF below so that xmlwf can be
 # found, then set the output directory with OUTPUT.
-
+#
 # The script lists all test cases where Expat shows a discrepancy
 # from the expected result. Test cases where only the canonical
 # output differs are prefixed with "Output differs:", and a diff file
 # is generated in the appropriate subdirectory under $OUTPUT.
-
+#
 # If there are output files provided, the script will use
 # output from xmlwf and compare the desired output against it.
 # However, one has to take into account that the canonical output
 # produced by xmlwf conforms to an older definition of canonical XML
 # and does not generate notation declarations.
+#
+#                          __  __            _
+#                       ___\ \/ /_ __   __ _| |_
+#                      / _ \\  /| '_ \ / _` | __|
+#                     |  __//  \| |_) | (_| | |_
+#                      \___/_/\_\ .__/ \__,_|\__|
+#                               |_| XML parser
+#
+# Copyright (c) 2002-2004 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+# Copyright (c) 2002      Karl Waclawek <karl@waclawek.net>
+# Copyright (c) 2008-2019 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+# 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.
 
 shopt -s nullglob
 
diff --git a/win32/build_expat_iss.bat b/win32/build_expat_iss.bat
index 4e31c17..2e2d6fd 100644
--- a/win32/build_expat_iss.bat
+++ b/win32/build_expat_iss.bat
@@ -7,7 +7,7 @@
 REM                      \___/_/\_\ .__/ \__,_|\__|
 REM                               |_| XML parser
 REM
-REM Copyright (C) 2019 Expat development team
+REM Copyright (c) 2019 Sebastian Pipping <sebastian@pipping.org>
 REM Licensed under the MIT license:
 REM
 REM Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/win32/expat.iss b/win32/expat.iss
index 0d5533f..2bbfdab 100644
--- a/win32/expat.iss
+++ b/win32/expat.iss
@@ -1,21 +1,53 @@
 ; Basic setup script for the Inno Setup installer builder.  For more
 ; information on the free installer builder, see www.jrsoftware.org.
 ;
-; This script was contributed by Tim Peters.
+; This script was originally contributed by Tim Peters.
 ; It was designed for Inno Setup 2.0.19 but works with later versions as well.
+;
+;                          __  __            _
+;                       ___\ \/ /_ __   __ _| |_
+;                      / _ \\  /| '_ \ / _` | __|
+;                     |  __//  \| |_) | (_| | |_
+;                      \___/_/\_\ .__/ \__,_|\__|
+;                               |_| XML parser
+;
+; 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>
+; 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.
 
-#define expatVer "2.3.0"
+#define expatVer "2.4.6"
 
 [Setup]
 AppName=Expat
 AppId=expat
 AppVersion={#expatVer}
 AppVerName=Expat {#expatVer}
-AppCopyright=Copyright © 1998-2017 Thai Open Source Software Center, Clark Cooper, and the Expat maintainers
+AppCopyright=Copyright © 1997-2022 Thai Open Source Software Center, Clark Cooper, and the Expat maintainers
 AppPublisher=The Expat Developers
-AppPublisherURL=http://www.libexpat.org/
-AppSupportURL=http://www.libexpat.org/
-AppUpdatesURL=http://www.libexpat.org/
+AppPublisherURL=https://libexpat.github.io/
+AppSupportURL=https://libexpat.github.io/
+AppUpdatesURL=https://libexpat.github.io/
 UninstallDisplayName=Expat XML Parser {#expatVer}
 VersionInfoVersion={#expatVer}
 OutputBaseFilename=expat-win32bin-{#expatVer}
@@ -41,16 +73,20 @@
 Flags: ignoreversion; Source: README.md;                    DestDir: "{app}"; DestName: README.txt
 Flags: ignoreversion; Source: doc\*.html;                   DestDir: "{app}\Doc"
 Flags: ignoreversion; Source: doc\*.css;                    DestDir: "{app}\Doc"
-Flags: ignoreversion; Source: doc\*.png;                    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\README.txt;             DestDir: "{app}\Source"
+Flags: ignoreversion; Source: AUTHORS;                      DestDir: "{app}\Source"
 Flags: ignoreversion; Source: Changes;                      DestDir: "{app}\Source"
 Flags: ignoreversion; Source: CMake.README;                 DestDir: "{app}\Source"
 Flags: ignoreversion; Source: CMakeLists.txt;               DestDir: "{app}\Source"
 Flags: ignoreversion; Source: ConfigureChecks.cmake;        DestDir: "{app}\Source"
+Flags: ignoreversion; Source: expat.pc.cmake;               DestDir: "{app}\Source"
 Flags: ignoreversion; Source: expat_config.h.cmake;         DestDir: "{app}\Source"
+Flags: ignoreversion; Source: run.sh.in;                    DestDir: "{app}\Source"
 Flags: ignoreversion; Source: cmake\expat-config.cmake.in;  DestDir: "{app}\Source\cmake"
+Flags: ignoreversion; Source: fuzz\*.c;                     DestDir: "{app}\Source\fuzz"
 Flags: ignoreversion; Source: lib\*.c;                      DestDir: "{app}\Source\lib"
 Flags: ignoreversion; Source: lib\*.h;                      DestDir: "{app}\Source\lib"
 Flags: ignoreversion; Source: lib\*.def;                    DestDir: "{app}\Source\lib"
diff --git a/xmlwf/Makefile.am b/xmlwf/Makefile.am
index f35ade7..c7eebd1 100644
--- a/xmlwf/Makefile.am
+++ b/xmlwf/Makefile.am
@@ -6,7 +6,7 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017 Expat development team
+# Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org>
 # Licensed under the MIT license:
 #
 # Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -38,7 +38,7 @@
     @FILEMAP@.c
 
 xmlwf_CPPFLAGS = @AM_CPPFLAGS@ -I$(srcdir)/../lib
-xmlwf_LDFLAGS = @AM_LDFLAGS@
+xmlwf_LDFLAGS = @AM_LDFLAGS@ @LIBM@
 
 if MINGW
 if UNICODE
diff --git a/xmlwf/Makefile.in b/xmlwf/Makefile.in
index c764030..93b13a9 100644
--- a/xmlwf/Makefile.in
+++ b/xmlwf/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -22,7 +22,7 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017 Expat development team
+# Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org>
 # Licensed under the MIT license:
 #
 # Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -216,8 +216,6 @@
   unique=`for i in $$list; do \
     if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
   done | $(am__uniquify_input)`
-ETAGS = etags
-CTAGS = ctags
 am__DIST_COMMON = $(srcdir)/Makefile.in \
 	$(top_srcdir)/conftools/depcomp
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
@@ -237,7 +235,10 @@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+CMAKE_SHARED_LIBRARY_PREFIX = @CMAKE_SHARED_LIBRARY_PREFIX@
 CPPFLAGS = @CPPFLAGS@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
 CXX = @CXX@
 CXXCPP = @CXXCPP@
 CXXDEPMODE = @CXXDEPMODE@
@@ -253,6 +254,7 @@
 ECHO_N = @ECHO_N@
 ECHO_T = @ECHO_T@
 EGREP = @EGREP@
+ETAGS = @ETAGS@
 EXEEXT = @EXEEXT@
 EXPAT_ATTR_INFO = @EXPAT_ATTR_INFO@
 EXPAT_CHAR_TYPE = @EXPAT_CHAR_TYPE@
@@ -274,6 +276,7 @@
 LIBAGE = @LIBAGE@
 LIBCURRENT = @LIBCURRENT@
 LIBDIR_BASENAME = @LIBDIR_BASENAME@
+LIBM = @LIBM@
 LIBOBJS = @LIBOBJS@
 LIBREVISION = @LIBREVISION@
 LIBS = @LIBS@
@@ -308,7 +311,6 @@
 SO_PATCH = @SO_PATCH@
 STRIP = @STRIP@
 VERSION = @VERSION@
-_EXPAT_OUTPUT_NAME = @_EXPAT_OUTPUT_NAME@
 abs_builddir = @abs_builddir@
 abs_srcdir = @abs_srcdir@
 abs_top_builddir = @abs_top_builddir@
@@ -317,6 +319,7 @@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_cv_sizeof_void_p = @ac_cv_sizeof_void_p@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -371,7 +374,7 @@
     @FILEMAP@.c
 
 xmlwf_CPPFLAGS = @AM_CPPFLAGS@ -I$(srcdir)/../lib $(am__append_1)
-xmlwf_LDFLAGS = @AM_LDFLAGS@ $(am__append_2)
+xmlwf_LDFLAGS = @AM_LDFLAGS@ @LIBM@ $(am__append_2)
 EXTRA_DIST = \
     codepage.h \
     ct.c \
@@ -622,7 +625,6 @@
 
 distclean-tags:
 	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-
 distdir: $(BUILT_SOURCES)
 	$(MAKE) $(AM_MAKEFLAGS) distdir-am
 
diff --git a/xmlwf/codepage.c b/xmlwf/codepage.c
index 44d2035..8e0b5d1 100644
--- a/xmlwf/codepage.c
+++ b/xmlwf/codepage.c
@@ -7,7 +7,11 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2005-2006 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2019 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/xmlwf/codepage.h b/xmlwf/codepage.h
index 1b75d58..75ddbce 100644
--- a/xmlwf/codepage.h
+++ b/xmlwf/codepage.h
@@ -7,7 +7,9 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2016-2017 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/xmlwf/ct.c b/xmlwf/ct.c
index dcf92ba..d4c0e7a 100644
--- a/xmlwf/ct.c
+++ b/xmlwf/ct.c
@@ -7,7 +7,8 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2016-2017 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/xmlwf/filemap.h b/xmlwf/filemap.h
index ef66114..5e0c70f 100644
--- a/xmlwf/filemap.h
+++ b/xmlwf/filemap.h
@@ -7,7 +7,9 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2016-2017 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/xmlwf/readfilemap.c b/xmlwf/readfilemap.c
index d5b84f9..59121f5 100644
--- a/xmlwf/readfilemap.c
+++ b/xmlwf/readfilemap.c
@@ -7,7 +7,12 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2001-2004 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2002-2009 Karl Waclawek <karl@waclawek.net>
+   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>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/xmlwf/unixfilemap.c b/xmlwf/unixfilemap.c
index 0d0dc04..d0ce9cc 100644
--- a/xmlwf/unixfilemap.c
+++ b/xmlwf/unixfilemap.c
@@ -7,7 +7,11 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2001-2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2006      Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2017 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/xmlwf/win32filemap.c b/xmlwf/win32filemap.c
index 0c9b7ec..bde4777 100644
--- a/xmlwf/win32filemap.c
+++ b/xmlwf/win32filemap.c
@@ -7,7 +7,9 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2016-2017 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/xmlwf/xmlfile.c b/xmlwf/xmlfile.c
index e3d22c5..e352167 100644
--- a/xmlwf/xmlfile.c
+++ b/xmlwf/xmlfile.c
@@ -7,7 +7,14 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   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) 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>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -30,6 +37,8 @@
    USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
+#include <expat_config.h>
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
@@ -38,9 +47,7 @@
 
 #ifdef _WIN32
 #  include "winconfig.h"
-#elif defined(HAVE_EXPAT_CONFIG_H)
-#  include <expat_config.h>
-#endif /* ndef _WIN32 */
+#endif
 
 #include "expat.h"
 #include "internal.h" /* for UNUSED_P only */
diff --git a/xmlwf/xmlfile.h b/xmlwf/xmlfile.h
index 6e5d5b4..d75dda2 100644
--- a/xmlwf/xmlfile.h
+++ b/xmlwf/xmlfile.h
@@ -7,7 +7,10 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   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>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/xmlwf/xmlmime.c b/xmlwf/xmlmime.c
index 39160d7..883e293 100644
--- a/xmlwf/xmlmime.c
+++ b/xmlwf/xmlmime.c
@@ -7,7 +7,9 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2016-2018 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/xmlwf/xmlmime.h b/xmlwf/xmlmime.h
index 0471286..591a884 100644
--- a/xmlwf/xmlmime.h
+++ b/xmlwf/xmlmime.h
@@ -7,7 +7,8 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2016-2017 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
diff --git a/xmlwf/xmltchar.h b/xmlwf/xmltchar.h
index d7e7b41..30283d0 100644
--- a/xmlwf/xmltchar.h
+++ b/xmlwf/xmltchar.h
@@ -7,7 +7,8 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2016-2021 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -54,6 +55,8 @@
 #  define tmain wmain
 #  define tremove _wremove
 #  define tchar wchar_t
+#  define tcstof wcstof
+#  define tcstoull wcstoull
 #else /* not XML_UNICODE */
 #  define T(x) x
 #  define ftprintf fprintf
@@ -71,4 +74,6 @@
 #  define tmain main
 #  define tremove remove
 #  define tchar char
+#  define tcstof strtof
+#  define tcstoull strtoull
 #endif /* not XML_UNICODE */
diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c
index 493b697..b0cd212 100644
--- a/xmlwf/xmlwf.c
+++ b/xmlwf/xmlwf.c
@@ -7,7 +7,16 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   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) 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>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -30,11 +39,15 @@
    USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
+#include <expat_config.h>
+
 #include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
 #include <string.h>
+#include <math.h> /* for isnan */
+#include <errno.h>
 
 #include "expat.h"
 #include "codepage.h"
@@ -50,6 +63,14 @@
 #  include <wchar.h>
 #endif
 
+enum ExitCode {
+  XMLWF_EXIT_SUCCESS = 0,
+  XMLWF_EXIT_INTERNAL_ERROR = 1,
+  XMLWF_EXIT_NOT_WELLFORMED = 2,
+  XMLWF_EXIT_OUTPUT_ERROR = 3,
+  XMLWF_EXIT_USAGE_ERROR = 4,
+};
+
 /* Structures for handler user data */
 typedef struct NotationList {
   struct NotationList *next;
@@ -859,9 +880,10 @@
        * xmlwf/xmlwf_helpgen.sh in here.
        */
       /* clang-format off */
-      T("usage: %s [-s] [-n] [-p] [-x] [-e ENCODING] [-w] [-r] [-k] [-d DIRECTORY]\n")
-      T("             [-c | -m | -t] [-N]\n")
-      T("             [FILE [FILE ...]]\n")
+      T("usage:\n")
+      T("  %s [OPTIONS] [FILE ...]\n")
+      T("  %s -h\n")
+      T("  %s -v\n")
       T("\n")
       T("xmlwf - Determines if an XML document is well-formed\n")
       T("\n")
@@ -885,6 +907,12 @@
       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("\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")
@@ -899,7 +927,7 @@
       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")
       , /* clang-format on */
-      prog);
+      prog, prog, prog);
   exit(rc);
 }
 
@@ -908,6 +936,19 @@
 int wmain(int argc, XML_Char **argv);
 #endif
 
+#define XMLWF_SHIFT_ARG_INTO(constCharStarTarget, argc, argv, i, j)            \
+  {                                                                            \
+    if (argv[i][j + 1] == T('\0')) {                                           \
+      if (++i == argc)                                                         \
+        usage(argv[0], XMLWF_EXIT_USAGE_ERROR);                                \
+      constCharStarTarget = argv[i];                                           \
+    } else {                                                                   \
+      constCharStarTarget = argv[i] + j + 1;                                   \
+    }                                                                          \
+    i++;                                                                       \
+    j = 0;                                                                     \
+  }
+
 int
 tmain(int argc, XML_Char **argv) {
   int i, j;
@@ -920,7 +961,12 @@
   int requireStandalone = 0;
   int requiresNotations = 0;
   int continueOnError = 0;
-  int exitCode = 0;
+
+  float attackMaximumAmplification = -1.0f; /* signaling "not set" */
+  unsigned long long attackThresholdBytes;
+  XML_Bool attackThresholdGiven = XML_FALSE;
+
+  int exitCode = XMLWF_EXIT_SUCCESS;
   enum XML_ParamEntityParsing paramEntityParsing
       = XML_PARAM_ENTITY_PARSING_NEVER;
   int useStdin = 0;
@@ -984,27 +1030,13 @@
       j++;
       break;
     case T('d'):
-      if (argv[i][j + 1] == T('\0')) {
-        if (++i == argc)
-          usage(argv[0], 4);
-        outputDir = argv[i];
-      } else
-        outputDir = argv[i] + j + 1;
-      i++;
-      j = 0;
+      XMLWF_SHIFT_ARG_INTO(outputDir, argc, argv, i, j);
       break;
     case T('e'):
-      if (argv[i][j + 1] == T('\0')) {
-        if (++i == argc)
-          usage(argv[0], 4);
-        encoding = argv[i];
-      } else
-        encoding = argv[i] + j + 1;
-      i++;
-      j = 0;
+      XMLWF_SHIFT_ARG_INTO(encoding, argc, argv, i, j);
       break;
     case T('h'):
-      usage(argv[0], 0);
+      usage(argv[0], XMLWF_EXIT_SUCCESS);
       return 0;
     case T('v'):
       showVersion(argv[0]);
@@ -1013,6 +1045,49 @@
       continueOnError = 1;
       j++;
       break;
+    case T('a'): {
+      const XML_Char *valueText = NULL;
+      XMLWF_SHIFT_ARG_INTO(valueText, argc, argv, i, j);
+
+      errno = 0;
+      XML_Char *afterValueText = (XML_Char *)valueText;
+      attackMaximumAmplification = tcstof(valueText, &afterValueText);
+      if ((errno != 0) || (afterValueText[0] != T('\0'))
+          || isnan(attackMaximumAmplification)
+          || (attackMaximumAmplification < 1.0f)) {
+        // This prevents tperror(..) from reporting misleading "[..]: Success"
+        errno = ERANGE;
+        tperror(T("invalid amplification limit") T(
+            " (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"));
+#endif
+      break;
+    }
+    case T('b'): {
+      const XML_Char *valueText = NULL;
+      XMLWF_SHIFT_ARG_INTO(valueText, argc, argv, i, j);
+
+      errno = 0;
+      XML_Char *afterValueText = (XML_Char *)valueText;
+      attackThresholdBytes = tcstoull(valueText, &afterValueText, 10);
+      if ((errno != 0) || (afterValueText[0] != T('\0'))) {
+        // This prevents tperror(..) from reporting misleading "[..]: Success"
+        errno = ERANGE;
+        tperror(T("invalid ignore threshold")
+                    T(" (needs an integer from 0 to 2^64-1)"));
+        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"));
+#endif
+      break;
+    }
     case T('\0'):
       if (j > 1) {
         i++;
@@ -1021,7 +1096,7 @@
       }
       /* fall through */
     default:
-      usage(argv[0], 4);
+      usage(argv[0], XMLWF_EXIT_USAGE_ERROR);
     }
   }
   if (i == argc) {
@@ -1040,7 +1115,22 @@
 
     if (! parser) {
       tperror(T("Could not instantiate parser"));
-      exit(1);
+      exit(XMLWF_EXIT_INTERNAL_ERROR);
+    }
+
+    if (attackMaximumAmplification != -1.0f) {
+#ifdef XML_DTD
+      XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+          parser, attackMaximumAmplification);
+#endif
+    }
+    if (attackThresholdGiven) {
+#ifdef XML_DTD
+      XML_SetBillionLaughsAttackProtectionActivationThreshold(
+          parser, attackThresholdBytes);
+#else
+      (void)attackThresholdBytes; // silence -Wunused-but-set-variable
+#endif
     }
 
     if (requireStandalone)
@@ -1076,7 +1166,7 @@
                                    * sizeof(XML_Char));
       if (! outName) {
         tperror(T("Could not allocate memory"));
-        exit(1);
+        exit(XMLWF_EXIT_INTERNAL_ERROR);
       }
       tcscpy(outName, outputDir);
       tcscat(outName, delim);
@@ -1084,10 +1174,10 @@
       userData.fp = tfopen(outName, T("wb"));
       if (! userData.fp) {
         tperror(outName);
-        exitCode = 3;
+        exitCode = XMLWF_EXIT_OUTPUT_ERROR;
+        free(outName);
+        XML_ParserFree(parser);
         if (continueOnError) {
-          free(outName);
-          cleanupUserData(&userData);
           continue;
         } else {
           break;
@@ -1153,7 +1243,7 @@
     }
     XML_ParserFree(parser);
     if (! result) {
-      exitCode = 2;
+      exitCode = XMLWF_EXIT_NOT_WELLFORMED;
       cleanupUserData(&userData);
       if (! continueOnError) {
         break;
diff --git a/xmlwf/xmlwf_helpgen.py b/xmlwf/xmlwf_helpgen.py
index 60a8651..c2a527f 100755
--- a/xmlwf/xmlwf_helpgen.py
+++ b/xmlwf/xmlwf_helpgen.py
@@ -6,7 +6,8 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2019 Expat development team
+# Copyright (c) 2019-2021 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2021      Tim Bray <tbray@textuality.com>
 # Licensed under the MIT license:
 #
 # Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -42,7 +43,14 @@
 Please report bugs at https://github.com/libexpat/libexpat/issues.  Thank you!
 """
 
+usage = """
+  %(prog)s [OPTIONS] [FILE ...]
+  %(prog)s -h
+  %(prog)s -v
+"""
+
 parser = argparse.ArgumentParser(prog='xmlwf', add_help=False,
+                                 usage=usage,
                                  description='xmlwf - Determines if an XML document is well-formed',
                                  formatter_class=argparse.RawTextHelpFormatter,
                                  epilog=epilog)
@@ -65,6 +73,14 @@
 output_mode.add_argument('-t', action='store_true', help='write no XML output for [t]iming of plain parsing')
 output_related.add_argument('-N', action='store_true', help='enable adding doctype and [n]otation declarations')
 
+billion_laughs = parser.add_argument_group('billion laughs attack protection',
+                                           description='NOTE: '
+                                                       'If you ever need to increase these values '
+                                                       'for non-attack payload, please file a bug report.')
+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)')
+
 parser.add_argument('files', metavar='FILE', nargs='*', help='file to process (default: STDIN)')
 
 info = parser.add_argument_group('info arguments')
diff --git a/xmlwf/xmlwf_helpgen.sh b/xmlwf/xmlwf_helpgen.sh
index 6b0fbf9..864e470 100755
--- a/xmlwf/xmlwf_helpgen.sh
+++ b/xmlwf/xmlwf_helpgen.sh
@@ -6,7 +6,7 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2019 Expat development team
+# Copyright (c) 2019-2021 Sebastian Pipping <sebastian@pipping.org>
 # Licensed under the MIT license:
 #
 # Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -29,7 +29,8 @@
 # USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 ./xmlwf/xmlwf_helpgen.py | sed \
-        -e 's,usage: xmlwf,usage: %s,' \
+        -e 's,usage: ,usage:,' \
+        -e 's,  xmlwf,  %s,' \
         -e 's, \[-h | -v\],,' \
         -e 's,^,      T(",' \
         -e 's,$,\\n"),'