You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openoffice.apache.org by ar...@apache.org on 2022/03/17 19:39:09 UTC

[openoffice] 01/05: Upgrade expat-2.2.10 into 2.2.12

This is an automated email from the ASF dual-hosted git repository.

ardovm pushed a commit to branch AOO42X
in repository https://gitbox.apache.org/repos/asf/openoffice.git

commit 9bd0941698031618c3596bedcbd88476162e4b6c
Author: Arrigo Marchiori <ar...@yahoo.it>
AuthorDate: Sat Mar 5 12:36:36 2022 +0100

    Upgrade expat-2.2.10 into 2.2.12
    
    Also use only one patch file named after the base version
    
    (cherry picked from commit cbf2698a61eb5662bff50e5fce9be9d60c27bb7e)
---
 main/expat/expat-2.2.10.patch | 3504 +++++++++++++++++++++++++++++++++++++++++
 main/expat/expat-2.2.11.patch | 2745 --------------------------------
 main/expat/makefile.mk        |    2 +-
 3 files changed, 3505 insertions(+), 2746 deletions(-)

diff --git a/main/expat/expat-2.2.10.patch b/main/expat/expat-2.2.10.patch
index ff1d16a..b6413d6 100644
--- a/main/expat/expat-2.2.10.patch
+++ b/main/expat/expat-2.2.10.patch
@@ -81,3 +81,3507 @@
 +.INCLUDE :  set_wntx64.mk
 +.INCLUDE :	target.mk
 +.INCLUDE :  tg_wntx64.mk
+diff -ru misc/expat-2.2.10/Changes misc/build/expat-2.2.10/Changes
+--- misc/expat-2.2.10/Changes	2020-10-03 17:14:57.000000000 +0200
++++ misc/build/expat-2.2.10/Changes	2022-03-05 12:25:27.575396494 +0100
+@@ -2,6 +2,43 @@
+       https://github.com/libexpat/libexpat/labels/help%20wanted
+       If you can help, please get in touch.  Thanks!
+ 
++
++Release 2.2.11 XXX XXXXX XX XXXX
++        Security fixes:
++        #34 #466  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.
++                    A new error code XML_ERROR_AMPLIFICATION_LIMIT_BREACH signals
++                    this condition.
++        New features:
++        #34 #466  Add two new API functions to further tighten billion laughs
++                    protection parameters when desired.
++                    - XML_SetBillionLaughsAttackProtectionMaximumAmplification
++                    - XML_SetBillionLaughsAttackProtectionActivationThreshold
++                    Please see file "doc/reference.html" for more details.
++                    If you ever need to increase the defaults for non-attack XML
++                    payload, please file a bug report with libexpat.
++        #34 #466  Introduce environment 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.
++        #34 #466  xmlwf: Add arguments "-a FACTOR" and "-b BYTES" 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.
++
++        Special thanks to:
++            Nick Wellnhofer
++            Yury Gribov
++                 and
++            Clang LeakSan
++            JetBrains
++
+ Release 2.2.10 Sat October 3 2020
+         Bug fixes:
+   #390 #395 #398  Fix undefined behavior during parsing caused by
+diff -ru misc/expat-2.2.10/CMakeLists.txt misc/build/expat-2.2.10/CMakeLists.txt
+--- misc/expat-2.2.10/CMakeLists.txt	2020-10-03 17:14:57.000000000 +0200
++++ misc/build/expat-2.2.10/CMakeLists.txt	2022-03-05 12:25:27.575396494 +0100
+@@ -448,14 +448,12 @@
+         endif()
+     endfunction()
+ 
+-    add_executable(runtests tests/runtests.c ${test_SRCS})
++    add_executable(runtests tests/runtests.c ${test_SRCS} ${expat_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})
++    add_executable(runtestspp tests/runtestspp.cpp ${test_SRCS} ${expat_SRCS})
+     set_property(TARGET runtestspp PROPERTY RUNTIME_OUTPUT_DIRECTORY tests)
+-    target_link_libraries(runtestspp expat)
+     expat_add_test(runtestspp $<TARGET_FILE:runtestspp>)
+ endif()
+ 
+diff -ru misc/expat-2.2.10/doc/Makefile.in misc/build/expat-2.2.10/doc/Makefile.in
+--- misc/expat-2.2.10/doc/Makefile.in	2020-10-03 17:37:06.000000000 +0200
++++ misc/build/expat-2.2.10/doc/Makefile.in	2022-03-05 12:25:27.575396494 +0100
+@@ -1,7 +1,7 @@
+-# Makefile.in generated by automake 1.16.2 from Makefile.am.
++# Makefile.in generated by automake 1.16.1 from Makefile.am.
+ # @configure_input@
+ 
+-# Copyright (C) 1994-2020 Free Software Foundation, Inc.
++# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+ 
+ # This Makefile.in is free software; the Free Software Foundation
+ # gives unlimited permission to copy and/or distribute it,
+@@ -314,7 +314,6 @@
+ prefix = @prefix@
+ program_transform_name = @program_transform_name@
+ psdir = @psdir@
+-runstatedir = @runstatedir@
+ sbindir = @sbindir@
+ sharedstatedir = @sharedstatedir@
+ srcdir = @srcdir@
+diff -ru misc/expat-2.2.10/doc/reference.html misc/build/expat-2.2.10/doc/reference.html
+--- misc/expat-2.2.10/doc/reference.html	2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/doc/reference.html	2022-03-05 12:25:27.575396494 +0100
+@@ -120,6 +120,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>
+@@ -1998,6 +2005,98 @@
+ 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>
++
++<pre class="fcndec" id="XML_SetBillionLaughsAttackProtectionMaximumAmplification">
++/* Added in Expat 2.2.11. */
++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_TRUE</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>
++
++<pre class="fcndec" id="XML_SetBillionLaughsAttackProtectionActivationThreshold">
++/* Added in Expat 2.2.11. */
++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_TRUE</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
+diff -ru misc/expat-2.2.10/doc/xmlwf.xml misc/build/expat-2.2.10/doc/xmlwf.xml
+--- misc/expat-2.2.10/doc/xmlwf.xml	2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/doc/xmlwf.xml	2022-03-05 12:25:27.575396494 +0100
+@@ -3,7 +3,7 @@
+   <!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>">
++  <!ENTITY dhdate      "<date>July 18, 2021</date>">
+   <!-- SECTION should be 1-8, maybe w/ subsection other parameters are
+        allowed: see man(7), man(1). -->
+   <!ENTITY dhsection   "<manvolnum>1</manvolnum>">
+@@ -138,6 +138,52 @@
+ </para>
+ 
+     <variablelist>
++    
++    <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>
+@@ -455,6 +501,7 @@
+ <literallayout>
+ The Expat home page:        http://www.libexpat.org/
+ The W3 XML specification:   http://www.w3.org/TR/REC-xml
++Billion laughs attack:      https://en.wikipedia.org/wiki/Billion_laughs_attack
+ </literallayout>
+ 
+ 	</para>
+diff -ru misc/expat-2.2.10/lib/expat.h misc/build/expat-2.2.10/lib/expat.h
+--- misc/expat-2.2.10/lib/expat.h	2020-10-03 17:14:57.000000000 +0200
++++ misc/build/expat-2.2.10/lib/expat.h	2022-03-05 12:25:27.583396678 +0100
+@@ -115,7 +115,10 @@
+   XML_ERROR_RESERVED_PREFIX_XMLNS,
+   XML_ERROR_RESERVED_NAMESPACE_URI,
+   /* Added in 2.2.1. */
+-  XML_ERROR_INVALID_ARGUMENT
++  XML_ERROR_INVALID_ARGUMENT,
++  /* Added in 2.2.11 */
++  XML_ERROR_NO_BUFFER,
++  XML_ERROR_AMPLIFICATION_LIMIT_BREACH
+ };
+ 
+ enum XML_Content_Type {
+@@ -997,7 +1000,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.2.11 */
++  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. */
+ };
+ 
+@@ -1010,12 +1016,25 @@
+ XMLPARSEAPI(const XML_Feature *)
+ XML_GetFeatureList(void);
+ 
++
++#ifdef XML_DTD
++/* Added in Expat 2.2.11 */
++XMLPARSEAPI(XML_Bool)
++XML_SetBillionLaughsAttackProtectionMaximumAmplification(
++    XML_Parser parser, float maximumAmplificationFactor);
++
++/* Added in Expat 2.2.11 */
++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 2
+-#define XML_MICRO_VERSION 10
++#define XML_MICRO_VERSION 12
+ 
+ #ifdef __cplusplus
+ }
+diff -ru misc/expat-2.2.10/lib/internal.h misc/build/expat-2.2.10/lib/internal.h
+--- misc/expat-2.2.10/lib/internal.h	2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/lib/internal.h	2022-03-05 12:25:27.575396494 +0100
+@@ -101,10 +101,47 @@
+ #  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
+@@ -117,6 +154,11 @@
+ 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
+ 
+ #ifdef __cplusplus
+ }
+diff -ru misc/expat-2.2.10/lib/libexpat.def misc/build/expat-2.2.10/lib/libexpat.def
+--- misc/expat-2.2.10/lib/libexpat.def	2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/lib/libexpat.def	2022-03-05 12:25:27.575396494 +0100
+@@ -76,3 +76,6 @@
+   XML_SetHashSalt @67
+ ; added with version 2.2.5
+   _INTERNAL_trim_to_complete_utf8_characters @68
++; added with version 2.2.11
++  XML_SetBillionLaughsAttackProtectionActivationThreshold @69
++  XML_SetBillionLaughsAttackProtectionMaximumAmplification @70  
+\ No newline at end of file
+diff -ru misc/expat-2.2.10/lib/libexpatw.def misc/build/expat-2.2.10/lib/libexpatw.def
+--- misc/expat-2.2.10/lib/libexpatw.def	2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/lib/libexpatw.def	2022-03-05 12:25:27.579396586 +0100
+@@ -76,3 +76,6 @@
+   XML_SetHashSalt @67
+ ; added with version 2.2.5
+   _INTERNAL_trim_to_complete_utf8_characters @68
++; added with version 2.2.11
++  XML_SetBillionLaughsAttackProtectionActivationThreshold @69
++  XML_SetBillionLaughsAttackProtectionMaximumAmplification @70
+\ No newline at end of file
+diff -ru misc/expat-2.2.10/lib/Makefile.in misc/build/expat-2.2.10/lib/Makefile.in
+--- misc/expat-2.2.10/lib/Makefile.in	2020-10-03 17:37:06.000000000 +0200
++++ misc/build/expat-2.2.10/lib/Makefile.in	2022-03-05 12:25:27.579396586 +0100
+@@ -1,7 +1,7 @@
+-# Makefile.in generated by automake 1.16.2 from Makefile.am.
++# Makefile.in generated by automake 1.16.1 from Makefile.am.
+ # @configure_input@
+ 
+-# Copyright (C) 1994-2020 Free Software Foundation, Inc.
++# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+ 
+ # This Makefile.in is free software; the Free Software Foundation
+ # gives unlimited permission to copy and/or distribute it,
+@@ -372,7 +372,6 @@
+ prefix = @prefix@
+ program_transform_name = @program_transform_name@
+ psdir = @psdir@
+-runstatedir = @runstatedir@
+ sbindir = @sbindir@
+ sharedstatedir = @sharedstatedir@
+ srcdir = @srcdir@
+diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
+--- misc/expat-2.2.10/lib/xmlparse.c	2020-10-03 17:14:57.000000000 +0200
++++ misc/build/expat-2.2.10/lib/xmlparse.c	2022-03-05 12:25:27.583396678 +0100
+@@ -47,6 +47,7 @@
+ #include <limits.h> /* UINT_MAX */
+ #include <stdio.h>  /* fprintf */
+ #include <stdlib.h> /* getenv, rand_s */
++#include <math.h>   /* isnan */
+ 
+ #if defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER < 1600)
+ /* vs2008/9.0 and earlier lack stdint.h; _MSC_VER 1600 is vs2010/10.0 */
+@@ -73,6 +74,10 @@
+ 
+ #ifdef _WIN32
+ #  include "winconfig.h"
++#include <float.h>
++#ifndef isnan
++#define isnan _isnan
++#endif
+ #elif defined(HAVE_EXPAT_CONFIG_H)
+ #  include <expat_config.h>
+ #endif /* ndef _WIN32 */
+@@ -382,6 +387,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);
+ 
+@@ -412,16 +442,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,
+@@ -431,7 +463,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);
+@@ -440,15 +473,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,
+@@ -512,6 +548,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)
+@@ -625,6 +689,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)))
+@@ -640,6 +708,7 @@
+ XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) {
+   XML_Char tmp[2];
+   *tmp = nsSep;
++  tmp[1] = 0;
+   return XML_ParserCreate_MM(encodingName, NULL, tmp);
+ }
+ 
+@@ -809,9 +878,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;
+@@ -855,7 +923,7 @@
+     return ENTROPY_DEBUG("fallback(4)", entropy * 2147483647);
+   } else {
+     return ENTROPY_DEBUG("fallback(8)",
+-                         entropy * (unsigned long)2305843009213693951ULL);
++                         entropy * (unsigned long long)2305843009213693951ULL);
+   }
+ #endif
+ }
+@@ -1073,6 +1141,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 */
+@@ -1255,6 +1335,7 @@
+   if (parser->m_ns) {
+     XML_Char tmp[2];
+     *tmp = parser->m_namespaceSeparator;
++    tmp[1] = 0;
+     parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd);
+   } else {
+     parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd);
+@@ -1893,6 +1974,12 @@
+     parser->m_errorCode = XML_ERROR_FINISHED;
+     return XML_STATUS_ERROR;
+   case XML_INITIALIZED:
++    /* Has someone called XML_GetBuffer successfully before? */
++    if (! parser->m_bufferPtr) {
++      parser->m_errorCode = XML_ERROR_NO_BUFFER;
++      return XML_STATUS_ERROR;
++    }
++
+     if (parser->m_parentParser == NULL && ! startParsing(parser)) {
+       parser->m_errorCode = XML_ERROR_NO_MEMORY;
+       return XML_STATUS_ERROR;
+@@ -1971,6 +2058,11 @@
+     keep = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer);
+     if (keep > XML_CONTEXT_BYTES)
+       keep = XML_CONTEXT_BYTES;
++    /* Detect and prevent integer overflow */
++    if (keep > INT_MAX - neededSize) {
++      parser->m_errorCode = XML_ERROR_NO_MEMORY;
++      return NULL;
++    }
+     neededSize += keep;
+ #endif /* defined XML_CONTEXT_BYTES */
+     if (neededSize
+@@ -2337,6 +2429,13 @@
+   /* Added in 2.2.5. */
+   case XML_ERROR_INVALID_ARGUMENT: /* Constant added in 2.2.1, already */
+     return XML_L("invalid argument");
++  /* Added in 2.2.11. */
++  case XML_ERROR_NO_BUFFER:
++    return XML_L(
++        "a successful prior call to function XML_GetBuffer is required");
++  case XML_ERROR_AMPLIFICATION_LIMIT_BREACH:
++    return XML_L(
++        "limit on input amplification factor (from DTD and entities) breached");
+   }
+   return NULL;
+ }
+@@ -2373,41 +2472,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
++#ifdef XML_DTD
++      /* Added in Expat 2.2.11. */
++      {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}};
++      {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
+@@ -2419,6 +2552,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
+@@ -2430,7 +2564,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)
+@@ -2460,9 +2598,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;
+@@ -2487,6 +2625,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
+@@ -2524,6 +2670,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) {
+@@ -2565,7 +2715,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;
+@@ -2576,7 +2727,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;
+ 
+@@ -2594,6 +2745,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:
+@@ -2649,6 +2811,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)
+@@ -2767,7 +2937,7 @@
+       }
+       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)
+@@ -2791,7 +2961,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;
+@@ -2926,7 +3097,7 @@
+       /* 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) {
+@@ -3055,7 +3226,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;
+@@ -3087,13 +3259,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) {
+@@ -3102,6 +3299,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) {
+@@ -3165,7 +3373,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);
+@@ -3240,8 +3448,16 @@
+   if (nPrefixes) {
+     int j; /* hash table index */
+     unsigned long version = parser->m_nsAttsVersion;
+-    int nsAttsSize = (int)1 << parser->m_nsAttsPower;
+-    unsigned char oldNsAttsPower = parser->m_nsAttsPower;
++    unsigned int nsAttsSize;
++    unsigned char oldNsAttsPower;
++
++    /* Detect and prevent invalid shift */
++    if (parser->m_nsAttsPower >= sizeof(unsigned int) * 8 /* bits per byte */) {
++      return XML_ERROR_NO_MEMORY;
++    }
++
++    nsAttsSize = 1u << parser->m_nsAttsPower;
++    oldNsAttsPower = parser->m_nsAttsPower;
+     /* size of hash table must be at least 2 * (# of prefixed attributes) */
+     if ((nPrefixes << 1)
+         >> parser->m_nsAttsPower) { /* true for m_nsAttsPower = 0 */
+@@ -3251,7 +3467,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) {
+@@ -3409,9 +3646,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;
+@@ -3491,6 +3750,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;
+@@ -3507,7 +3777,24 @@
+   if (parser->m_freeBindingList) {
+     b = parser->m_freeBindingList;
+     if (len > b->uriAlloc) {
+-      XML_Char *temp = (XML_Char *)REALLOC(
++
++      XML_Char *temp;
++      /* 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
++
++      temp = (XML_Char *)REALLOC(
+           parser, b->uri, sizeof(XML_Char) * (len + EXPAND_SPARE));
+       if (temp == NULL)
+         return XML_ERROR_NO_MEMORY;
+@@ -3519,6 +3806,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) {
+@@ -3556,7 +3858,7 @@
+                       const char **endPtr) {
+   enum XML_Error result
+       = doCdataSection(parser, parser->m_encoding, &start, end, endPtr,
+-                       (XML_Bool)! parser->m_parsingStatus.finalBuffer);
++                       (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT);
+   if (result != XML_ERROR_NONE)
+     return result;
+   if (start) {
+@@ -3576,7 +3878,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;
+@@ -3594,6 +3897,12 @@
+   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;
++    }
++#endif
+     *eventEndPP = next;
+     switch (tok) {
+     case XML_TOK_CDATA_SECT_CLOSE:
+@@ -3738,6 +4047,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:
+@@ -3822,6 +4138,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)) {
+@@ -3971,6 +4296,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) {
+@@ -3989,7 +4318,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);
+@@ -4016,6 +4346,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;
+     }
+@@ -4058,16 +4396,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
+@@ -4080,6 +4426,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;
+@@ -4097,7 +4446,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;
+   }
+@@ -4111,13 +4460,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 */
+@@ -4144,6 +4494,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;
+ 
+@@ -4208,6 +4562,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);
+@@ -4483,7 +4850,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);
+@@ -4516,8 +4884,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
+@@ -4776,7 +5145,13 @@
+       if (parser->m_prologState.level >= parser->m_groupSize) {
+         if (parser->m_groupSize) {
+           {
+-            char *const new_connector = (char *)REALLOC(
++            char * new_connector;
++            /* Detect and prevent integer overflow */
++            if (parser->m_groupSize > (unsigned int)(-1) / 2u) {
++              return XML_ERROR_NO_MEMORY;
++            }
++
++            new_connector = (char *)REALLOC(
+                 parser, parser->m_groupConnector, parser->m_groupSize *= 2);
+             if (new_connector == NULL) {
+               parser->m_groupSize /= 2;
+@@ -4786,7 +5161,18 @@
+           }
+ 
+           if (dtd->scaffIndex) {
+-            int *const new_scaff_index = (int *)REALLOC(
++            int *new_scaff_index;
++            /* 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
++
++            new_scaff_index = (int *)REALLOC(
+                 parser, dtd->scaffIndex, parser->m_groupSize * sizeof(int));
+             if (new_scaff_index == NULL)
+               return XML_ERROR_NO_MEMORY;
+@@ -4907,12 +5293,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) {
+@@ -4991,7 +5380,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);
+@@ -5007,7 +5396,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;
+       }
+@@ -5110,6 +5505,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 */
+@@ -5183,6 +5585,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;
+@@ -5201,17 +5606,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 */
+@@ -5244,12 +5654,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;
+@@ -5258,6 +5669,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 */
+@@ -5271,7 +5685,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 */
+   {
+@@ -5279,7 +5694,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);
+   }
+ }
+ 
+@@ -5294,9 +5710,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)
+@@ -5308,11 +5725,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;
+@@ -5372,6 +5801,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;
+@@ -5449,9 +5886,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;
+@@ -5481,13 +5925,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
+@@ -5498,8 +5945,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
+@@ -5535,13 +5993,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;
+@@ -5549,9 +6010,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;
+@@ -5784,7 +6248,25 @@
+       }
+     } else {
+       DEFAULT_ATTRIBUTE *temp;
+-      int count = type->allocDefaultAtts * 2;
++      int count;
++
++      /* Detect and prevent integer overflow */
++      if (type->allocDefaultAtts > INT_MAX / 2) {
++        return 0;
++      }
++
++      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)
+@@ -6435,10 +6917,26 @@
+     /* check for overflow (table is half full) */
+     if (table->used >> (table->power - 1)) {
+       unsigned char newPower = table->power + 1;
+-      size_t newSize = (size_t)1 << newPower;
+-      unsigned long newMask = (unsigned long)newSize - 1;
+-      size_t tsize = newSize * sizeof(NAMED *);
+-      NAMED **newV = (NAMED **)table->mem->malloc_fcn(tsize);
++      size_t newSize;
++      unsigned long newMask;
++      size_t tsize;
++      NAMED **newV;
++
++      /* Detect and prevent invalid shift */
++      if (newPower >= sizeof(unsigned long) * 8 /* bits per byte */) {
++        return NULL;
++      }
++
++      newSize = (size_t)1 << newPower;
++      newMask = (unsigned long)newSize - 1;
++
++      /* Detect and prevent integer overflow */
++      if (newSize > (size_t)(-1) / sizeof(NAMED *)) {
++        return NULL;
++      }
++
++      tsize = newSize * sizeof(NAMED *);
++      newV = (NAMED **)table->mem->malloc_fcn(tsize);
+       if (! newV)
+         return NULL;
+       memset(newV, 0, tsize);
+@@ -6786,6 +7284,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)
+@@ -6817,55 +7329,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) {
+   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 */
++  size_t allocsize;
++  XML_Content *dest; /* tree node writing location, moves upwards */
++  XML_Content *destLimit;
++  XML_Content *jobDest; /* next free writing location in target array */
++
++  /* 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;
++  }
++
++  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.
++   */
++  dest = ret; /* tree node writing location, moves upwards */
++  destLimit = &ret[dtd->scaffCount];
++  jobDest = ret; /* next free writing location in target array */
++  str = (XML_Char *)&ret[dtd->scaffCount];
++
++  /* 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;
++    }
++  }
+ 
+-  build_node(parser, 0, ret, &cpos, &str);
+   return ret;
+ }
+ 
+@@ -6894,7 +7481,7 @@
+ 
+ static XML_Char *
+ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
+-  int charsRequired = 0;
++  size_t charsRequired = 0;
+   XML_Char *result;
+ 
+   /* First determine how long the string is */
+@@ -6912,3 +7499,766 @@
+   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);
++  float amplificationFactor;
++  assert(! rootParser->m_parentParser);
++
++  if (rootParser->m_accounting.debugLevel < 1) {
++    return;
++  }
++
++  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) {
++  const char ellipis[] = "[..]";
++  const size_t ellipsisLength = sizeof(ellipis) /* because compile-time */ - 1;
++  const unsigned int contextLength = 10;
++  const char *walker = before;
++
++  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, "");
++
++  /* Note: Performance is of no concern here */
++  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) {
++  unsigned int levelsAwayFromRootParser;
++  XML_Parser rootParser;
++  int isDirect;
++  ptrdiff_t bytesMore;
++  XmlBigCount * additionTarget;
++  XmlBigCount countBytesOutput;
++  float amplificationFactor;
++  XML_Bool tolerated;
++  /* 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 */
++
++  rootParser
++      = getRootParserOf(originParser, &levelsAwayFromRootParser);
++  assert(! rootParser->m_parentParser);
++
++  isDirect
++      = (account == XML_ACCOUNT_DIRECT) && (originParser == rootParser);
++  bytesMore = after - before;
++
++  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;
++
++  countBytesOutput
++      = rootParser->m_accounting.countBytesDirect
++        + rootParser->m_accounting.countBytesIndirect;
++  amplificationFactor
++      = accountingGetCurrentAmplification(rootParser);
++  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) {
++  const char * entityName;
++  assert(! rootParser->m_parentParser);
++  if (rootParser->m_entity_stats.debugLevel < 1)
++    return;
++
++#  if defined(XML_UNICODE)
++  entityName = "[..]";
++#  else
++  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);
++  const char * value;
++  char *afterValue;
++  unsigned long debugLevel;
++  if (valueOrNull == NULL) {
++    return defaultDebugLevel;
++  }
++  value = valueOrNull;
++
++  errno = 0;
++  afterValue = (char *)value;
++  debugLevel = strtoul(value, &afterValue, 10);
++  if ((errno != 0) || (afterValue[0] != '\0')) {
++    errno = 0;
++    return defaultDebugLevel;
++  }
++
++  return debugLevel;
++}
+diff -ru misc/expat-2.2.10/lib/xmltok.c misc/build/expat-2.2.10/lib/xmltok.c
+--- misc/expat-2.2.10/lib/xmltok.c	2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/lib/xmltok.c	2022-03-05 12:25:27.583396678 +0100
+@@ -269,8 +269,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 -ru misc/expat-2.2.10/lib/xmltok_impl.c misc/build/expat-2.2.10/lib/xmltok_impl.c
+--- misc/expat-2.2.10/lib/xmltok_impl.c	2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/lib/xmltok_impl.c	2022-03-05 12:25:27.583396678 +0100
+@@ -61,7 +61,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 +90,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 +1134,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;                                                      \
+diff -ru misc/expat-2.2.10/lib/xmltok_ns.c misc/build/expat-2.2.10/lib/xmltok_ns.c
+--- misc/expat-2.2.10/lib/xmltok_ns.c	2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/lib/xmltok_ns.c	2022-03-05 12:25:27.583396678 +0100
+@@ -89,7 +89,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 -ru misc/expat-2.2.10/tests/benchmark/Makefile.in misc/build/expat-2.2.10/tests/benchmark/Makefile.in
+--- misc/expat-2.2.10/tests/benchmark/Makefile.in	2020-10-03 17:37:06.000000000 +0200
++++ misc/build/expat-2.2.10/tests/benchmark/Makefile.in	2022-03-05 12:25:27.579396586 +0100
+@@ -1,7 +1,7 @@
+-# Makefile.in generated by automake 1.16.2 from Makefile.am.
++# Makefile.in generated by automake 1.16.1 from Makefile.am.
+ # @configure_input@
+ 
+-# Copyright (C) 1994-2020 Free Software Foundation, Inc.
++# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+ 
+ # This Makefile.in is free software; the Free Software Foundation
+ # gives unlimited permission to copy and/or distribute it,
+@@ -335,7 +335,6 @@
+ prefix = @prefix@
+ program_transform_name = @program_transform_name@
+ psdir = @psdir@
+-runstatedir = @runstatedir@
+ sbindir = @sbindir@
+ sharedstatedir = @sharedstatedir@
+ srcdir = @srcdir@
+diff -ru misc/expat-2.2.10/tests/Makefile.in misc/build/expat-2.2.10/tests/Makefile.in
+--- misc/expat-2.2.10/tests/Makefile.in	2020-10-03 17:37:06.000000000 +0200
++++ misc/build/expat-2.2.10/tests/Makefile.in	2022-03-05 12:25:27.579396586 +0100
+@@ -1,7 +1,7 @@
+-# Makefile.in generated by automake 1.16.2 from Makefile.am.
++# Makefile.in generated by automake 1.16.1 from Makefile.am.
+ # @configure_input@
+ 
+-# Copyright (C) 1994-2020 Free Software Foundation, Inc.
++# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+ 
+ # This Makefile.in is free software; the Free Software Foundation
+ # gives unlimited permission to copy and/or distribute it,
+@@ -616,7 +616,6 @@
+ prefix = @prefix@
+ program_transform_name = @program_transform_name@
+ psdir = @psdir@
+-runstatedir = @runstatedir@
+ sbindir = @sbindir@
+ sharedstatedir = @sharedstatedir@
+ srcdir = @srcdir@
+diff -ru misc/expat-2.2.10/tests/minicheck.c misc/build/expat-2.2.10/tests/minicheck.c
+--- misc/expat-2.2.10/tests/minicheck.c	2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/tests/minicheck.c	2022-03-05 12:25:27.587396769 +0100
+@@ -141,11 +141,18 @@
+ }
+ 
+ static void
+-add_failure(SRunner *runner, int verbosity) {
+-  runner->nfailures++;
++handle_success(int verbosity) {
+   if (verbosity >= CK_VERBOSE) {
+-    printf("%s:%d: %s\n", _check_current_filename, _check_current_lineno,
+-           _check_current_function);
++    printf("PASS: %s\n", _check_current_function);
++  }
++}
++
++static void
++handle_failure(SRunner *runner, int verbosity, const char *phase_info) {
++  runner->nfailures++;
++  if (verbosity != CK_SILENT) {
++    printf("FAIL: %s (%s at %s:%d)\n", _check_current_function, phase_info,
++           _check_current_filename, _check_current_lineno);
+   }
+ }
+ 
+@@ -164,14 +171,14 @@
+       if (tc->setup != NULL) {
+         /* setup */
+         if (setjmp(env)) {
+-          add_failure(runner, verbosity);
++          handle_failure(runner, verbosity, "during setup");
+           continue;
+         }
+         tc->setup();
+       }
+       /* test */
+       if (setjmp(env)) {
+-        add_failure(runner, verbosity);
++        handle_failure(runner, verbosity, "during actual test");
+         continue;
+       }
+       (tc->tests[i])();
+@@ -179,15 +186,17 @@
+       /* teardown */
+       if (tc->teardown != NULL) {
+         if (setjmp(env)) {
+-          add_failure(runner, verbosity);
++          handle_failure(runner, verbosity, "during teardown");
+           continue;
+         }
+         tc->teardown();
+       }
++
++      handle_success(verbosity);
+     }
+     tc = tc->next_tcase;
+   }
+-  if (verbosity) {
++  if (verbosity != CK_SILENT) {
+     int passed = runner->nchecks - runner->nfailures;
+     double percentage = ((double)passed) / runner->nchecks;
+     int display = (int)(percentage * 100);
+@@ -203,8 +212,8 @@
+      it is.
+   */
+   UNUSED_P(condition);
+-  UNUSED_P(file);
+-  UNUSED_P(line);
++  _check_current_filename = file;
++  _check_current_lineno = line;
+   if (msg != NULL) {
+     const int has_newline = (msg[strlen(msg) - 1] == '\n');
+     fprintf(stderr, "ERROR: %s%s", msg, has_newline ? "" : "\n");
+diff -ru misc/expat-2.2.10/tests/runtests.c misc/build/expat-2.2.10/tests/runtests.c
+--- misc/expat-2.2.10/tests/runtests.c	2020-10-03 17:14:57.000000000 +0200
++++ misc/build/expat-2.2.10/tests/runtests.c	2022-03-05 12:25:27.583396678 +0100
+@@ -45,6 +45,7 @@
+ #include <stddef.h> /* ptrdiff_t */
+ #include <ctype.h>
+ #include <limits.h>
++#include <math.h>   /* NAN, INFINITY, isnan */
+ 
+ #if defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER < 1600)
+ /* For vs2003/7.1 up to vs2008/9.0; _MSC_VER 1600 is vs2010/10.0 */
+@@ -72,7 +73,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"
+@@ -11231,6 +11232,381 @@
+ }
+ 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 entites */, 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 entites */, 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},
++
++      /* 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");
+@@ -11239,6 +11615,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);
+@@ -11603,6 +11982,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 -ru misc/expat-2.2.10/xmlwf/Makefile.in misc/build/expat-2.2.10/xmlwf/Makefile.in
+--- misc/expat-2.2.10/xmlwf/Makefile.in	2020-10-03 17:37:06.000000000 +0200
++++ misc/build/expat-2.2.10/xmlwf/Makefile.in	2022-03-05 12:25:27.583396678 +0100
+@@ -1,7 +1,7 @@
+-# Makefile.in generated by automake 1.16.2 from Makefile.am.
++# Makefile.in generated by automake 1.16.1 from Makefile.am.
+ # @configure_input@
+ 
+-# Copyright (C) 1994-2020 Free Software Foundation, Inc.
++# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+ 
+ # This Makefile.in is free software; the Free Software Foundation
+ # gives unlimited permission to copy and/or distribute it,
+@@ -344,7 +344,6 @@
+ prefix = @prefix@
+ program_transform_name = @program_transform_name@
+ psdir = @psdir@
+-runstatedir = @runstatedir@
+ sbindir = @sbindir@
+ sharedstatedir = @sharedstatedir@
+ srcdir = @srcdir@
+diff -ru misc/expat-2.2.10/xmlwf/xmltchar.h misc/build/expat-2.2.10/xmlwf/xmltchar.h
+--- misc/expat-2.2.10/xmlwf/xmltchar.h	2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/xmlwf/xmltchar.h	2022-03-05 12:25:27.583396678 +0100
+@@ -54,6 +54,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 +73,6 @@
+ #  define tmain main
+ #  define tremove remove
+ #  define tchar char
++#  define tcstof strtof
++#  define tcstoull strtoull
+ #endif /* not XML_UNICODE */
+diff -ru misc/expat-2.2.10/xmlwf/xmlwf.c misc/build/expat-2.2.10/xmlwf/xmlwf.c
+--- misc/expat-2.2.10/xmlwf/xmlwf.c	2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/xmlwf/xmlwf.c	2022-03-05 12:25:27.583396678 +0100
+@@ -30,11 +30,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 +54,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;
+@@ -875,6 +887,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")
+@@ -891,6 +909,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;
+@@ -902,6 +933,11 @@
+   int useNamespaces = 0;
+   int requireStandalone = 0;
+   int requiresNotations = 0;
++
++  float attackMaximumAmplification = -1.0f; /* signaling "not set" */
++  unsigned long long attackThresholdBytes;
++  XML_Bool attackThresholdGiven = XML_FALSE;
++
+   enum XML_ParamEntityParsing paramEntityParsing
+       = XML_PARAM_ENTITY_PARSING_NEVER;
+   int useStdin = 0;
+@@ -990,6 +1026,49 @@
+     case T('v'):
+       showVersion(argv[0]);
+       return 0;
++    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++;
+@@ -1020,6 +1099,19 @@
+       exit(1);
+     }
+ 
++    if (attackMaximumAmplification != -1.0f) {
++#ifdef XML_DTD
++      XML_SetBillionLaughsAttackProtectionMaximumAmplification(
++          parser, attackMaximumAmplification);
++#endif
++    }
++    if (attackThresholdGiven) {
++#ifdef XML_DTD
++      XML_SetBillionLaughsAttackProtectionActivationThreshold(
++          parser, attackThresholdBytes);
++#endif
++    }
++
+     if (requireStandalone)
+       XML_SetNotStandaloneHandler(parser, notStandalone);
+     XML_SetParamEntityParsing(parser, paramEntityParsing);
+diff -ru misc/expat-2.2.10/xmlwf/xmlwf_helpgen.py misc/build/expat-2.2.10/xmlwf/xmlwf_helpgen.py
+--- misc/expat-2.2.10/xmlwf/xmlwf_helpgen.py	2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/xmlwf/xmlwf_helpgen.py	2022-03-05 12:25:27.583396678 +0100
+@@ -57,6 +57,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/main/expat/expat-2.2.11.patch b/main/expat/expat-2.2.11.patch
deleted file mode 100644
index 289362d..0000000
--- a/main/expat/expat-2.2.11.patch
+++ /dev/null
@@ -1,2745 +0,0 @@
---- misc/expat-2.2.10/Changes	2020-10-03 11:14:57.000000000 -0400
-+++ misc/build/expat-2.2.10/Changes	2021-07-18 17:21:48.000000000 -0400
-@@ -2,6 +2,43 @@
-       https://github.com/libexpat/libexpat/labels/help%20wanted
-       If you can help, please get in touch.  Thanks!
- 
-+
-+Release 2.2.11 XXX XXXXX XX XXXX
-+        Security fixes:
-+        #34 #466  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.
-+                    A new error code XML_ERROR_AMPLIFICATION_LIMIT_BREACH signals
-+                    this condition.
-+        New features:
-+        #34 #466  Add two new API functions to further tighten billion laughs
-+                    protection parameters when desired.
-+                    - XML_SetBillionLaughsAttackProtectionMaximumAmplification
-+                    - XML_SetBillionLaughsAttackProtectionActivationThreshold
-+                    Please see file "doc/reference.html" for more details.
-+                    If you ever need to increase the defaults for non-attack XML
-+                    payload, please file a bug report with libexpat.
-+        #34 #466  Introduce environment 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.
-+        #34 #466  xmlwf: Add arguments "-a FACTOR" and "-b BYTES" 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.
-+
-+        Special thanks to:
-+            Nick Wellnhofer
-+            Yury Gribov
-+                 and
-+            Clang LeakSan
-+            JetBrains
-+
- Release 2.2.10 Sat October 3 2020
-         Bug fixes:
-   #390 #395 #398  Fix undefined behavior during parsing caused by
-diff -ru misc/expat-2.2.10/CMakeLists.txt misc/build/expat-2.2.10/CMakeLists.txt
---- misc/expat-2.2.10/CMakeLists.txt	2020-10-03 11:14:57.000000000 -0400
-+++ misc/build/expat-2.2.10/CMakeLists.txt	2021-08-25 18:35:36.000000000 -0400
-@@ -448,14 +448,12 @@
-         endif()
-     endfunction()
- 
--    add_executable(runtests tests/runtests.c ${test_SRCS})
-+    add_executable(runtests tests/runtests.c ${test_SRCS} ${expat_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})
-+    add_executable(runtestspp tests/runtestspp.cpp ${test_SRCS} ${expat_SRCS})
-     set_property(TARGET runtestspp PROPERTY RUNTIME_OUTPUT_DIRECTORY tests)
--    target_link_libraries(runtestspp expat)
-     expat_add_test(runtestspp $<TARGET_FILE:runtestspp>)
- endif()
- 
-diff -ru misc/expat-2.2.10/doc/Makefile.in misc/build/expat-2.2.10/doc/Makefile.in
---- misc/expat-2.2.10/doc/Makefile.in	2020-10-03 11:37:06.000000000 -0400
-+++ misc/build/expat-2.2.10/doc/Makefile.in	2021-07-18 18:17:02.000000000 -0400
-@@ -1,7 +1,7 @@
--# Makefile.in generated by automake 1.16.2 from Makefile.am.
-+# Makefile.in generated by automake 1.16.1 from Makefile.am.
- # @configure_input@
- 
--# Copyright (C) 1994-2020 Free Software Foundation, Inc.
-+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
- 
- # This Makefile.in is free software; the Free Software Foundation
- # gives unlimited permission to copy and/or distribute it,
-@@ -314,7 +314,6 @@
- prefix = @prefix@
- program_transform_name = @program_transform_name@
- psdir = @psdir@
--runstatedir = @runstatedir@
- sbindir = @sbindir@
- sharedstatedir = @sharedstatedir@
- srcdir = @srcdir@
-diff -ru misc/expat-2.2.10/doc/reference.html misc/build/expat-2.2.10/doc/reference.html
---- misc/expat-2.2.10/doc/reference.html	2020-09-25 13:47:39.000000000 -0400
-+++ misc/build/expat-2.2.10/doc/reference.html	2021-07-18 17:21:48.000000000 -0400
-@@ -120,6 +120,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>
-@@ -1998,6 +2005,98 @@
- 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>
-+
-+<pre class="fcndec" id="XML_SetBillionLaughsAttackProtectionMaximumAmplification">
-+/* Added in Expat 2.2.11. */
-+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_TRUE</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>
-+
-+<pre class="fcndec" id="XML_SetBillionLaughsAttackProtectionActivationThreshold">
-+/* Added in Expat 2.2.11. */
-+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_TRUE</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
-Only in misc/expat-2.2.10/doc: xmlwf.1
-diff -ru misc/expat-2.2.10/doc/xmlwf.xml misc/build/expat-2.2.10/doc/xmlwf.xml
---- misc/expat-2.2.10/doc/xmlwf.xml	2020-09-25 13:47:39.000000000 -0400
-+++ misc/build/expat-2.2.10/doc/xmlwf.xml	2021-07-18 17:21:48.000000000 -0400
-@@ -3,7 +3,7 @@
-   <!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>">
-+  <!ENTITY dhdate      "<date>July 18, 2021</date>">
-   <!-- SECTION should be 1-8, maybe w/ subsection other parameters are
-        allowed: see man(7), man(1). -->
-   <!ENTITY dhsection   "<manvolnum>1</manvolnum>">
-@@ -138,6 +138,52 @@
- </para>
- 
-     <variablelist>
-+    
-+    <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>
-@@ -455,6 +501,7 @@
- <literallayout>
- The Expat home page:        http://www.libexpat.org/
- The W3 XML specification:   http://www.w3.org/TR/REC-xml
-+Billion laughs attack:      https://en.wikipedia.org/wiki/Billion_laughs_attack
- </literallayout>
- 
- 	</para>
-diff -ru misc/expat-2.2.10/lib/expat.h misc/build/expat-2.2.10/lib/expat.h
---- misc/expat-2.2.10/lib/expat.h	2020-10-03 11:14:57.000000000 -0400
-+++ misc/build/expat-2.2.10/lib/expat.h	2021-07-18 17:21:48.000000000 -0400
-@@ -115,7 +115,10 @@
-   XML_ERROR_RESERVED_PREFIX_XMLNS,
-   XML_ERROR_RESERVED_NAMESPACE_URI,
-   /* Added in 2.2.1. */
--  XML_ERROR_INVALID_ARGUMENT
-+  XML_ERROR_INVALID_ARGUMENT,
-+  /* Added in 2.2.11 */
-+  XML_ERROR_NO_BUFFER,
-+  XML_ERROR_AMPLIFICATION_LIMIT_BREACH
- };
- 
- enum XML_Content_Type {
-@@ -997,7 +1000,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.2.11 */
-+  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. */
- };
- 
-@@ -1010,6 +1016,19 @@
- XMLPARSEAPI(const XML_Feature *)
- XML_GetFeatureList(void);
- 
-+
-+#ifdef XML_DTD
-+/* Added in Expat 2.2.11 */
-+XMLPARSEAPI(XML_Bool)
-+XML_SetBillionLaughsAttackProtectionMaximumAmplification(
-+    XML_Parser parser, float maximumAmplificationFactor);
-+
-+/* Added in Expat 2.2.11 */
-+XMLPARSEAPI(XML_Bool)
-+XML_SetBillionLaughsAttackProtectionActivationThreshold(
-+    XML_Parser parser, unsigned long long activationThresholdBytes);
-+#endif
-+
- /* Expat follows the semantic versioning convention.
-    See http://semver.org.
- */
-diff -ru misc/expat-2.2.10/lib/internal.h misc/build/expat-2.2.10/lib/internal.h
---- misc/expat-2.2.10/lib/internal.h	2020-09-25 13:47:39.000000000 -0400
-+++ misc/build/expat-2.2.10/lib/internal.h	2021-07-18 17:21:48.000000000 -0400
-@@ -101,10 +101,47 @@
- #  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
-@@ -117,6 +154,11 @@
- 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
- 
- #ifdef __cplusplus
- }
-diff -ru misc/expat-2.2.10/lib/libexpat.def misc/build/expat-2.2.10/lib/libexpat.def
---- misc/expat-2.2.10/lib/libexpat.def	2020-09-25 13:47:39.000000000 -0400
-+++ misc/build/expat-2.2.10/lib/libexpat.def	2021-07-18 17:21:48.000000000 -0400
-@@ -76,3 +76,6 @@
-   XML_SetHashSalt @67
- ; added with version 2.2.5
-   _INTERNAL_trim_to_complete_utf8_characters @68
-+; added with version 2.2.11
-+  XML_SetBillionLaughsAttackProtectionActivationThreshold @69
-+  XML_SetBillionLaughsAttackProtectionMaximumAmplification @70  
-\ No newline at end of file
-diff -ru misc/expat-2.2.10/lib/libexpatw.def misc/build/expat-2.2.10/lib/libexpatw.def
---- misc/expat-2.2.10/lib/libexpatw.def	2020-09-25 13:47:39.000000000 -0400
-+++ misc/build/expat-2.2.10/lib/libexpatw.def	2021-07-18 17:21:48.000000000 -0400
-@@ -76,3 +76,6 @@
-   XML_SetHashSalt @67
- ; added with version 2.2.5
-   _INTERNAL_trim_to_complete_utf8_characters @68
-+; added with version 2.2.11
-+  XML_SetBillionLaughsAttackProtectionActivationThreshold @69
-+  XML_SetBillionLaughsAttackProtectionMaximumAmplification @70
-\ No newline at end of file
-Only in misc/build/expat-2.2.10/lib: Makefile
-diff -ru misc/expat-2.2.10/lib/Makefile.in misc/build/expat-2.2.10/lib/Makefile.in
---- misc/expat-2.2.10/lib/Makefile.in	2020-10-03 11:37:06.000000000 -0400
-+++ misc/build/expat-2.2.10/lib/Makefile.in	2021-07-18 18:17:02.000000000 -0400
-@@ -1,7 +1,7 @@
--# Makefile.in generated by automake 1.16.2 from Makefile.am.
-+# Makefile.in generated by automake 1.16.1 from Makefile.am.
- # @configure_input@
- 
--# Copyright (C) 1994-2020 Free Software Foundation, Inc.
-+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
- 
- # This Makefile.in is free software; the Free Software Foundation
- # gives unlimited permission to copy and/or distribute it,
-@@ -372,7 +372,6 @@
- prefix = @prefix@
- program_transform_name = @program_transform_name@
- psdir = @psdir@
--runstatedir = @runstatedir@
- sbindir = @sbindir@
- sharedstatedir = @sharedstatedir@
- srcdir = @srcdir@
-diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
---- misc/expat-2.2.10/lib/xmlparse.c	2020-10-03 11:14:57.000000000 -0400
-+++ misc/build/expat-2.2.10/lib/xmlparse.c	2021-08-28 18:28:18.000000000 -0400
-@@ -47,6 +47,7 @@
- #include <limits.h> /* UINT_MAX */
- #include <stdio.h>  /* fprintf */
- #include <stdlib.h> /* getenv, rand_s */
-+#include <math.h>   /* isnan */
- 
- #if defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER < 1600)
- /* vs2008/9.0 and earlier lack stdint.h; _MSC_VER 1600 is vs2010/10.0 */
-@@ -73,6 +74,10 @@
- 
- #ifdef _WIN32
- #  include "winconfig.h"
-+#include <float.h>
-+#ifndef isnan
-+#define isnan _isnan
-+#endif
- #elif defined(HAVE_EXPAT_CONFIG_H)
- #  include <expat_config.h>
- #endif /* ndef _WIN32 */
-@@ -382,6 +387,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);
- 
-@@ -412,13 +442,14 @@
- 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);
-@@ -431,7 +462,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);
-@@ -440,15 +472,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,
-@@ -512,6 +547,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)
-@@ -625,6 +688,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)))
-@@ -809,9 +876,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;
-@@ -855,7 +921,7 @@
-     return ENTROPY_DEBUG("fallback(4)", entropy * 2147483647);
-   } else {
-     return ENTROPY_DEBUG("fallback(8)",
--                         entropy * (unsigned long)2305843009213693951ULL);
-+                         entropy * (unsigned long long)2305843009213693951ULL);
-   }
- #endif
- }
-@@ -1073,6 +1139,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 */
-@@ -2337,6 +2415,13 @@
-   /* Added in 2.2.5. */
-   case XML_ERROR_INVALID_ARGUMENT: /* Constant added in 2.2.1, already */
-     return XML_L("invalid argument");
-+  /* Added in 2.2.11. */
-+  case XML_ERROR_NO_BUFFER:
-+    return XML_L(
-+        "a successful prior call to function XML_GetBuffer is required");
-+  case XML_ERROR_AMPLIFICATION_LIMIT_BREACH:
-+    return XML_L(
-+        "limit on input amplification factor (from DTD and entities) breached");
-   }
-   return NULL;
- }
-@@ -2373,41 +2458,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.2.11. */
-+      {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
-@@ -2460,9 +2579,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;
-@@ -2487,6 +2606,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
-@@ -2524,6 +2651,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) {
-@@ -2565,7 +2696,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;
-@@ -2576,7 +2708,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;
- 
-@@ -2594,6 +2726,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:
-@@ -2649,6 +2792,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)
-@@ -2767,7 +2918,7 @@
-       }
-       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)
-@@ -2791,7 +2942,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;
-@@ -3055,7 +3207,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;
-@@ -3165,7 +3318,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);
-@@ -3594,6 +3747,13 @@
-   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__,
-+                                  XML_ACCOUNT_DIRECT)) {
-+      accountingOnAbort(parser);
-+      return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
-+    }
-+#endif
-     *eventEndPP = next;
-     switch (tok) {
-     case XML_TOK_CDATA_SECT_CLOSE:
-@@ -3738,6 +3898,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:
-@@ -3822,6 +3989,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)) {
-@@ -3971,6 +4147,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) {
-@@ -3989,7 +4169,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);
-@@ -4016,6 +4197,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;
-     }
-@@ -4058,16 +4247,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
-@@ -4080,6 +4277,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;
-@@ -4097,7 +4297,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;
-   }
-@@ -4111,13 +4311,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 */
-@@ -4144,6 +4345,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;
- 
-@@ -4208,6 +4413,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);
-@@ -4483,7 +4701,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);
-@@ -4516,8 +4735,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
-@@ -4907,12 +5127,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) {
-@@ -5110,6 +5333,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 */
-@@ -5183,6 +5413,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;
-@@ -5201,17 +5434,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 */
-@@ -5244,12 +5482,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;
-@@ -5258,6 +5497,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 */
-@@ -5271,7 +5513,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 */
-   {
-@@ -5279,7 +5522,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);
-   }
- }
- 
-@@ -5294,9 +5538,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)
-@@ -5308,11 +5553,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;
-@@ -5372,6 +5629,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;
-@@ -5449,9 +5714,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;
-@@ -5481,13 +5753,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
-@@ -5498,8 +5773,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
-@@ -5535,13 +5821,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;
-@@ -5549,9 +5838,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;
-@@ -6912,3 +7204,766 @@
-   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);
-+  float amplificationFactor;
-+  assert(! rootParser->m_parentParser);
-+
-+  if (rootParser->m_accounting.debugLevel < 1) {
-+    return;
-+  }
-+
-+  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) {
-+  const char ellipis[] = "[..]";
-+  const size_t ellipsisLength = sizeof(ellipis) /* because compile-time */ - 1;
-+  const unsigned int contextLength = 10;
-+  const char *walker = before;
-+
-+  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, "");
-+
-+  /* Note: Performance is of no concern here */
-+  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) {
-+  unsigned int levelsAwayFromRootParser;
-+  XML_Parser rootParser;
-+  int isDirect;
-+  ptrdiff_t bytesMore;
-+  XmlBigCount * additionTarget;
-+  XmlBigCount countBytesOutput;
-+  float amplificationFactor;
-+  XML_Bool tolerated;
-+  /* 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 */
-+
-+  rootParser
-+      = getRootParserOf(originParser, &levelsAwayFromRootParser);
-+  assert(! rootParser->m_parentParser);
-+
-+  isDirect
-+      = (account == XML_ACCOUNT_DIRECT) && (originParser == rootParser);
-+  bytesMore = after - before;
-+
-+  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;
-+
-+  countBytesOutput
-+      = rootParser->m_accounting.countBytesDirect
-+        + rootParser->m_accounting.countBytesIndirect;
-+  amplificationFactor
-+      = accountingGetCurrentAmplification(rootParser);
-+  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) {
-+  const char * entityName;
-+  assert(! rootParser->m_parentParser);
-+  if (rootParser->m_entity_stats.debugLevel < 1)
-+    return;
-+
-+#  if defined(XML_UNICODE)
-+  entityName = "[..]";
-+#  else
-+  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);
-+  const char * value;
-+  char *afterValue;
-+  unsigned long debugLevel;
-+  if (valueOrNull == NULL) {
-+    return defaultDebugLevel;
-+  }
-+  value = valueOrNull;
-+
-+  errno = 0;
-+  afterValue = (char *)value;
-+  debugLevel = strtoul(value, &afterValue, 10);
-+  if ((errno != 0) || (afterValue[0] != '\0')) {
-+    errno = 0;
-+    return defaultDebugLevel;
-+  }
-+
-+  return debugLevel;
-+}
-diff -ru misc/expat-2.2.10/tests/benchmark/Makefile.in misc/build/expat-2.2.10/tests/benchmark/Makefile.in
---- misc/expat-2.2.10/tests/benchmark/Makefile.in	2020-10-03 11:37:06.000000000 -0400
-+++ misc/build/expat-2.2.10/tests/benchmark/Makefile.in	2021-07-18 18:17:02.000000000 -0400
-@@ -1,7 +1,7 @@
--# Makefile.in generated by automake 1.16.2 from Makefile.am.
-+# Makefile.in generated by automake 1.16.1 from Makefile.am.
- # @configure_input@
- 
--# Copyright (C) 1994-2020 Free Software Foundation, Inc.
-+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
- 
- # This Makefile.in is free software; the Free Software Foundation
- # gives unlimited permission to copy and/or distribute it,
-@@ -335,7 +335,6 @@
- prefix = @prefix@
- program_transform_name = @program_transform_name@
- psdir = @psdir@
--runstatedir = @runstatedir@
- sbindir = @sbindir@
- sharedstatedir = @sharedstatedir@
- srcdir = @srcdir@
-diff -ru misc/expat-2.2.10/tests/Makefile.in misc/build/expat-2.2.10/tests/Makefile.in
---- misc/expat-2.2.10/tests/Makefile.in	2020-10-03 11:37:06.000000000 -0400
-+++ misc/build/expat-2.2.10/tests/Makefile.in	2021-07-18 18:17:02.000000000 -0400
-@@ -1,7 +1,7 @@
--# Makefile.in generated by automake 1.16.2 from Makefile.am.
-+# Makefile.in generated by automake 1.16.1 from Makefile.am.
- # @configure_input@
- 
--# Copyright (C) 1994-2020 Free Software Foundation, Inc.
-+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
- 
- # This Makefile.in is free software; the Free Software Foundation
- # gives unlimited permission to copy and/or distribute it,
-@@ -616,7 +616,6 @@
- prefix = @prefix@
- program_transform_name = @program_transform_name@
- psdir = @psdir@
--runstatedir = @runstatedir@
- sbindir = @sbindir@
- sharedstatedir = @sharedstatedir@
- srcdir = @srcdir@
-diff -ru misc/expat-2.2.10/tests/runtests.c misc/build/expat-2.2.10/tests/runtests.c
---- misc/expat-2.2.10/tests/runtests.c	2020-10-03 11:14:57.000000000 -0400
-+++ misc/build/expat-2.2.10/tests/runtests.c	2021-07-18 17:27:08.000000000 -0400
-@@ -45,6 +45,7 @@
- #include <stddef.h> /* ptrdiff_t */
- #include <ctype.h>
- #include <limits.h>
-+#include <math.h>   /* NAN, INFINITY, isnan */
- 
- #if defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER < 1600)
- /* For vs2003/7.1 up to vs2008/9.0; _MSC_VER 1600 is vs2010/10.0 */
-@@ -72,7 +73,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"
-@@ -11231,6 +11232,381 @@
- }
- 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 entites */, 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 entites */, 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},
-+
-+      /* 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");
-@@ -11239,6 +11615,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);
-@@ -11603,6 +11982,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 -ru misc/expat-2.2.10/xmlwf/Makefile.in misc/build/expat-2.2.10/xmlwf/Makefile.in
---- misc/expat-2.2.10/xmlwf/Makefile.in	2020-10-03 11:37:06.000000000 -0400
-+++ misc/build/expat-2.2.10/xmlwf/Makefile.in	2021-07-18 18:17:02.000000000 -0400
-@@ -1,7 +1,7 @@
--# Makefile.in generated by automake 1.16.2 from Makefile.am.
-+# Makefile.in generated by automake 1.16.1 from Makefile.am.
- # @configure_input@
- 
--# Copyright (C) 1994-2020 Free Software Foundation, Inc.
-+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
- 
- # This Makefile.in is free software; the Free Software Foundation
- # gives unlimited permission to copy and/or distribute it,
-@@ -344,7 +344,6 @@
- prefix = @prefix@
- program_transform_name = @program_transform_name@
- psdir = @psdir@
--runstatedir = @runstatedir@
- sbindir = @sbindir@
- sharedstatedir = @sharedstatedir@
- srcdir = @srcdir@
-diff -ru misc/expat-2.2.10/xmlwf/xmltchar.h misc/build/expat-2.2.10/xmlwf/xmltchar.h
---- misc/expat-2.2.10/xmlwf/xmltchar.h	2020-09-25 13:47:39.000000000 -0400
-+++ misc/build/expat-2.2.10/xmlwf/xmltchar.h	2021-07-18 17:21:48.000000000 -0400
-@@ -54,6 +54,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 +73,6 @@
- #  define tmain main
- #  define tremove remove
- #  define tchar char
-+#  define tcstof strtof
-+#  define tcstoull strtoull
- #endif /* not XML_UNICODE */
-diff -ru misc/expat-2.2.10/xmlwf/xmlwf.c misc/build/expat-2.2.10/xmlwf/xmlwf.c
---- misc/expat-2.2.10/xmlwf/xmlwf.c	2020-09-25 13:47:39.000000000 -0400
-+++ misc/build/expat-2.2.10/xmlwf/xmlwf.c	2021-08-25 18:35:36.000000000 -0400
-@@ -30,11 +30,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 +54,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;
-@@ -875,6 +887,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")
-@@ -891,6 +909,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;
-@@ -902,6 +933,11 @@
-   int useNamespaces = 0;
-   int requireStandalone = 0;
-   int requiresNotations = 0;
-+
-+  float attackMaximumAmplification = -1.0f; /* signaling "not set" */
-+  unsigned long long attackThresholdBytes;
-+  XML_Bool attackThresholdGiven = XML_FALSE;
-+
-   enum XML_ParamEntityParsing paramEntityParsing
-       = XML_PARAM_ENTITY_PARSING_NEVER;
-   int useStdin = 0;
-@@ -990,6 +1026,49 @@
-     case T('v'):
-       showVersion(argv[0]);
-       return 0;
-+    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++;
-@@ -1020,6 +1099,19 @@
-       exit(1);
-     }
- 
-+    if (attackMaximumAmplification != -1.0f) {
-+#ifdef XML_DTD
-+      XML_SetBillionLaughsAttackProtectionMaximumAmplification(
-+          parser, attackMaximumAmplification);
-+#endif
-+    }
-+    if (attackThresholdGiven) {
-+#ifdef XML_DTD
-+      XML_SetBillionLaughsAttackProtectionActivationThreshold(
-+          parser, attackThresholdBytes);
-+#endif
-+    }
-+
-     if (requireStandalone)
-       XML_SetNotStandaloneHandler(parser, notStandalone);
-     XML_SetParamEntityParsing(parser, paramEntityParsing);
-diff -ru misc/expat-2.2.10/xmlwf/xmlwf_helpgen.py misc/build/expat-2.2.10/xmlwf/xmlwf_helpgen.py
---- misc/expat-2.2.10/xmlwf/xmlwf_helpgen.py	2020-09-25 13:47:39.000000000 -0400
-+++ misc/build/expat-2.2.10/xmlwf/xmlwf_helpgen.py	2021-07-18 17:21:48.000000000 -0400
-@@ -57,6 +57,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/main/expat/makefile.mk b/main/expat/makefile.mk
index 901ee9a..20fd92b 100644
--- a/main/expat/makefile.mk
+++ b/main/expat/makefile.mk
@@ -41,7 +41,7 @@ all:
 TARFILE_NAME=expat-2.2.10
 TARFILE_MD5=9d60de01cc0126dfd11121b04838e154
 ADDITIONAL_FILES=lib$/makefile.mk
-PATCH_FILES=$(TARFILE_NAME).patch expat-2.2.11.patch
+PATCH_FILES=$(TARFILE_NAME).patch
 
 CONFIGURE_DIR=
 .IF "$(OS)"=="WNT"