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 18:00:36 UTC

[openoffice] 02/02: Produce expat-2.2.13, derived from 2.4.7

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

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

commit 84b800346361a798f2dacc3e03dc6b4a6358f3cf
Author: Arrigo Marchiori <ar...@yahoo.it>
AuthorDate: Wed Mar 9 21:11:35 2022 +0100

    Produce expat-2.2.13, derived from 2.4.7
---
 main/expat/expat-2.2.10.patch | 888 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 786 insertions(+), 102 deletions(-)

diff --git a/main/expat/expat-2.2.10.patch b/main/expat/expat-2.2.10.patch
index b6413d6..0de7936 100644
--- a/main/expat/expat-2.2.10.patch
+++ b/main/expat/expat-2.2.10.patch
@@ -360,7 +360,7 @@ diff -ru misc/expat-2.2.10/doc/xmlwf.xml misc/build/expat-2.2.10/doc/xmlwf.xml
  	</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
++++ misc/build/expat-2.2.10/lib/expat.h	2022-03-09 20:25:36.712575539 +0100
 @@ -115,7 +115,10 @@
    XML_ERROR_RESERVED_PREFIX_XMLNS,
    XML_ERROR_RESERVED_NAMESPACE_URI,
@@ -408,7 +408,7 @@ diff -ru misc/expat-2.2.10/lib/expat.h misc/build/expat-2.2.10/lib/expat.h
  #define XML_MAJOR_VERSION 2
  #define XML_MINOR_VERSION 2
 -#define XML_MICRO_VERSION 10
-+#define XML_MICRO_VERSION 12
++#define XML_MICRO_VERSION 13
  
  #ifdef __cplusplus
  }
@@ -520,7 +520,7 @@ diff -ru misc/expat-2.2.10/lib/Makefile.in misc/build/expat-2.2.10/lib/Makefile.
  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
++++ misc/build/expat-2.2.10/lib/xmlparse.c	2022-03-09 20:25:36.712575539 +0100
 @@ -47,6 +47,7 @@
  #include <limits.h> /* UINT_MAX */
  #include <stdio.h>  /* fprintf */
@@ -540,6 +540,15 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
  #elif defined(HAVE_EXPAT_CONFIG_H)
  #  include <expat_config.h>
  #endif /* ndef _WIN32 */
+@@ -116,7 +121,7 @@
+       * BSD / macOS (including <10.7) (arc4random): HAVE_ARC4RANDOM, \
+       * libbsd (arc4random_buf): HAVE_ARC4RANDOM_BUF + HAVE_LIBBSD, \
+       * libbsd (arc4random): HAVE_ARC4RANDOM + HAVE_LIBBSD, \
+-      * Linux (including <3.17) / BSD / macOS (including <10.7) (/dev/urandom): XML_DEV_URANDOM, \
++      * Linux (including <3.17) / BSD / macOS (including <10.7) / Solaris >=8 (/dev/urandom): XML_DEV_URANDOM, \
+       * Windows >=Vista (rand_s): _WIN32. \
+     \
+     If insist on not using any of these, bypass this error by defining \
 @@ -382,6 +387,31 @@
    XML_Bool betweenDecl; /* WFC: PE Between Declarations */
  } OPEN_INTERNAL_ENTITY;
@@ -672,7 +681,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
  };
  
  #define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s)))
-@@ -640,6 +708,7 @@
+@@ -640,9 +708,11 @@
  XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) {
    XML_Char tmp[2];
    *tmp = nsSep;
@@ -680,7 +689,11 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
    return XML_ParserCreate_MM(encodingName, NULL, tmp);
  }
  
-@@ -809,9 +878,8 @@
++// "xml=http://www.w3.org/XML/1998/namespace"
+ static const XML_Char implicitContext[]
+     = {ASCII_x,     ASCII_m,     ASCII_l,      ASCII_EQUALS, ASCII_h,
+        ASCII_t,     ASCII_t,     ASCII_p,      ASCII_COLON,  ASCII_SLASH,
+@@ -809,9 +879,8 @@
  
  static unsigned long
  ENTROPY_DEBUG(const char *label, unsigned long entropy) {
@@ -692,7 +705,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
              (int)sizeof(entropy) * 2, entropy, (unsigned long)sizeof(entropy));
    }
    return entropy;
-@@ -855,7 +923,7 @@
+@@ -855,7 +924,7 @@
      return ENTROPY_DEBUG("fallback(4)", entropy * 2147483647);
    } else {
      return ENTROPY_DEBUG("fallback(8)",
@@ -701,7 +714,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
    }
  #endif
  }
-@@ -1073,6 +1141,18 @@
+@@ -1073,6 +1142,18 @@
    parser->m_paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER;
  #endif
    parser->m_hash_secret_salt = 0;
@@ -720,7 +733,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
  }
  
  /* moves list of bindings to m_freeBindingList */
-@@ -1255,6 +1335,7 @@
+@@ -1255,6 +1336,7 @@
    if (parser->m_ns) {
      XML_Char tmp[2];
      *tmp = parser->m_namespaceSeparator;
@@ -728,7 +741,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
      parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd);
    } else {
      parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd);
-@@ -1893,6 +1974,12 @@
+@@ -1893,6 +1975,12 @@
      parser->m_errorCode = XML_ERROR_FINISHED;
      return XML_STATUS_ERROR;
    case XML_INITIALIZED:
@@ -741,7 +754,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
      if (parser->m_parentParser == NULL && ! startParsing(parser)) {
        parser->m_errorCode = XML_ERROR_NO_MEMORY;
        return XML_STATUS_ERROR;
-@@ -1971,6 +2058,11 @@
+@@ -1971,6 +2059,11 @@
      keep = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer);
      if (keep > XML_CONTEXT_BYTES)
        keep = XML_CONTEXT_BYTES;
@@ -753,7 +766,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
      neededSize += keep;
  #endif /* defined XML_CONTEXT_BYTES */
      if (neededSize
-@@ -2337,6 +2429,13 @@
+@@ -2337,6 +2430,13 @@
    /* Added in 2.2.5. */
    case XML_ERROR_INVALID_ARGUMENT: /* Constant added in 2.2.1, already */
      return XML_L("invalid argument");
@@ -767,7 +780,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
    }
    return NULL;
  }
-@@ -2373,41 +2472,75 @@
+@@ -2373,41 +2473,75 @@
  
  const XML_Feature *XMLCALL
  XML_GetFeatureList(void) {
@@ -858,7 +871,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
  /* 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 @@
+@@ -2419,6 +2553,7 @@
    while (tag) {
      int bufSize;
      int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1);
@@ -866,7 +879,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
      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 @@
+@@ -2430,7 +2565,11 @@
      /* For re-use purposes we need to ensure that the
         size of tag->buf is a multiple of sizeof(XML_Char).
      */
@@ -879,7 +892,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
      if (bufSize > tag->bufEnd - tag->buf) {
        char *temp = (char *)REALLOC(parser, tag->buf, bufSize);
        if (temp == NULL)
-@@ -2460,9 +2598,9 @@
+@@ -2460,9 +2599,9 @@
  static enum XML_Error PTRCALL
  contentProcessor(XML_Parser parser, const char *start, const char *end,
                   const char **endPtr) {
@@ -892,7 +905,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
    if (result == XML_ERROR_NONE) {
      if (! storeRawNames(parser))
        return XML_ERROR_NO_MEMORY;
-@@ -2487,6 +2625,14 @@
+@@ -2487,6 +2626,14 @@
    int tok = XmlContentTok(parser->m_encoding, start, end, &next);
    switch (tok) {
    case XML_TOK_BOM:
@@ -907,7 +920,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
      /* 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 @@
+@@ -2524,6 +2671,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);
@@ -918,7 +931,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
    parser->m_eventEndPtr = next;
  
    switch (tok) {
-@@ -2565,7 +2715,8 @@
+@@ -2565,7 +2716,8 @@
                                 const char *end, const char **endPtr) {
    enum XML_Error result
        = doContent(parser, 1, parser->m_encoding, start, end, endPtr,
@@ -928,7 +941,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
    if (result == XML_ERROR_NONE) {
      if (! storeRawNames(parser))
        return XML_ERROR_NO_MEMORY;
-@@ -2576,7 +2727,7 @@
+@@ -2576,7 +2728,7 @@
  static enum XML_Error
  doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
            const char *s, const char *end, const char **nextPtr,
@@ -937,7 +950,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
    /* save one level of indirection */
    DTD *const dtd = parser->m_dtd;
  
-@@ -2594,6 +2745,17 @@
+@@ -2594,6 +2746,17 @@
    for (;;) {
      const char *next = s; /* XmlContentTok doesn't always set the last arg */
      int tok = XmlContentTok(enc, s, end, &next);
@@ -955,7 +968,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
      *eventEndPP = next;
      switch (tok) {
      case XML_TOK_TRAILING_CR:
-@@ -2649,6 +2811,14 @@
+@@ -2649,6 +2812,14 @@
        XML_Char ch = (XML_Char)XmlPredefinedEntityName(
            enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar);
        if (ch) {
@@ -970,7 +983,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
          if (parser->m_characterDataHandler)
            parser->m_characterDataHandler(parser->m_handlerArg, &ch, 1);
          else if (parser->m_defaultHandler)
-@@ -2767,7 +2937,7 @@
+@@ -2767,7 +2938,7 @@
        }
        tag->name.str = (XML_Char *)tag->buf;
        *toPtr = XML_T('\0');
@@ -979,7 +992,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
        if (result)
          return result;
        if (parser->m_startElementHandler)
-@@ -2791,7 +2961,8 @@
+@@ -2791,7 +2962,8 @@
        if (! name.str)
          return XML_ERROR_NO_MEMORY;
        poolFinish(&parser->m_tempPool);
@@ -989,7 +1002,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
        if (result != XML_ERROR_NONE) {
          freeBindings(parser, bindings);
          return result;
-@@ -2926,7 +3097,7 @@
+@@ -2926,7 +3098,7 @@
        /* END disabled code */
        else if (parser->m_defaultHandler)
          reportDefault(parser, enc, s, next);
@@ -998,7 +1011,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
        if (result != XML_ERROR_NONE)
          return result;
        else if (! next) {
-@@ -3055,7 +3226,8 @@
+@@ -3055,7 +3227,8 @@
  */
  static enum XML_Error
  storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
@@ -1008,7 +1021,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
    DTD *const dtd = parser->m_dtd; /* save one level of indirection */
    ELEMENT_TYPE *elementType;
    int nDefaultAtts;
-@@ -3087,13 +3259,38 @@
+@@ -3087,13 +3260,38 @@
  
    /* get the attributes from the tokenizer */
    n = XmlGetAttributes(enc, attStr, parser->m_attsSize, parser->m_atts);
@@ -1047,7 +1060,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
      temp = (ATTRIBUTE *)REALLOC(parser, (void *)parser->m_atts,
                                  parser->m_attsSize * sizeof(ATTRIBUTE));
      if (temp == NULL) {
-@@ -3102,6 +3299,17 @@
+@@ -3102,6 +3300,17 @@
      }
      parser->m_atts = temp;
  #ifdef XML_ATTR_INFO
@@ -1065,7 +1078,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
      temp2 = (XML_AttrInfo *)REALLOC(parser, (void *)parser->m_attInfo,
                                      parser->m_attsSize * sizeof(XML_AttrInfo));
      if (temp2 == NULL) {
-@@ -3165,7 +3373,7 @@
+@@ -3165,7 +3374,7 @@
        /* normalize the attribute value */
        result = storeAttributeValue(
            parser, enc, isCdata, parser->m_atts[i].valuePtr,
@@ -1074,7 +1087,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
        if (result)
          return result;
        appAtts[attIndex] = poolStart(&parser->m_tempPool);
-@@ -3240,8 +3448,16 @@
+@@ -3240,8 +3449,16 @@
    if (nPrefixes) {
      int j; /* hash table index */
      unsigned long version = parser->m_nsAttsVersion;
@@ -1093,7 +1106,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
      /* 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 @@
+@@ -3251,7 +3468,28 @@
          ;
        if (parser->m_nsAttsPower < 3)
          parser->m_nsAttsPower = 3;
@@ -1123,7 +1136,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
        temp = (NS_ATT *)REALLOC(parser, parser->m_nsAtts,
                                 nsAttsSize * sizeof(NS_ATT));
        if (! temp) {
-@@ -3409,9 +3646,31 @@
+@@ -3409,9 +3647,31 @@
    tagNamePtr->prefixLen = prefixLen;
    for (i = 0; localPart[i++];)
      ; /* i includes null terminator */
@@ -1155,25 +1168,170 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
      uri = (XML_Char *)MALLOC(parser, (n + EXPAND_SPARE) * sizeof(XML_Char));
      if (! uri)
        return XML_ERROR_NO_MEMORY;
-@@ -3491,6 +3750,17 @@
+@@ -3436,12 +3696,124 @@
+   return XML_ERROR_NONE;
+ }
+ 
++static XML_Bool
++is_rfc3986_uri_char(XML_Char candidate) {
++  // For the RFC 3986 ANBF grammar see
++  // https://datatracker.ietf.org/doc/html/rfc3986#appendix-A
++
++  switch (candidate) {
++  // From rule "ALPHA" (uppercase half)
++  case 'A':
++  case 'B':
++  case 'C':
++  case 'D':
++  case 'E':
++  case 'F':
++  case 'G':
++  case 'H':
++  case 'I':
++  case 'J':
++  case 'K':
++  case 'L':
++  case 'M':
++  case 'N':
++  case 'O':
++  case 'P':
++  case 'Q':
++  case 'R':
++  case 'S':
++  case 'T':
++  case 'U':
++  case 'V':
++  case 'W':
++  case 'X':
++  case 'Y':
++  case 'Z':
++
++  // From rule "ALPHA" (lowercase half)
++  case 'a':
++  case 'b':
++  case 'c':
++  case 'd':
++  case 'e':
++  case 'f':
++  case 'g':
++  case 'h':
++  case 'i':
++  case 'j':
++  case 'k':
++  case 'l':
++  case 'm':
++  case 'n':
++  case 'o':
++  case 'p':
++  case 'q':
++  case 'r':
++  case 's':
++  case 't':
++  case 'u':
++  case 'v':
++  case 'w':
++  case 'x':
++  case 'y':
++  case 'z':
++
++  // From rule "DIGIT"
++  case '0':
++  case '1':
++  case '2':
++  case '3':
++  case '4':
++  case '5':
++  case '6':
++  case '7':
++  case '8':
++  case '9':
++
++  // From rule "pct-encoded"
++  case '%':
++
++  // From rule "unreserved"
++  case '-':
++  case '.':
++  case '_':
++  case '~':
++
++  // From rule "gen-delims"
++  case ':':
++  case '/':
++  case '?':
++  case '#':
++  case '[':
++  case ']':
++  case '@':
++
++  // From rule "sub-delims"
++  case '!':
++  case '$':
++  case '&':
++  case '\'':
++  case '(':
++  case ')':
++  case '*':
++  case '+':
++  case ',':
++  case ';':
++  case '=':
++    return XML_TRUE;
++
++  default:
++    return XML_FALSE;
++  }
++}
++
+ /* addBinding() overwrites the value of prefix->binding without checking.
+    Therefore one must keep track of the old value outside of addBinding().
+ */
+ static enum XML_Error
+ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
+            const XML_Char *uri, BINDING **bindingsPtr) {
++  // "http://www.w3.org/XML/1998/namespace"
+   static const XML_Char xmlNamespace[]
+       = {ASCII_h,      ASCII_t,     ASCII_t,     ASCII_p,      ASCII_COLON,
+          ASCII_SLASH,  ASCII_SLASH, ASCII_w,     ASCII_w,      ASCII_w,
+@@ -3452,6 +3824,7 @@
+          ASCII_e,      ASCII_s,     ASCII_p,     ASCII_a,      ASCII_c,
+          ASCII_e,      '\0'};
+   static const int xmlLen = (int)sizeof(xmlNamespace) / sizeof(XML_Char) - 1;
++  // "http://www.w3.org/2000/xmlns/"
+   static const XML_Char xmlnsNamespace[]
+       = {ASCII_h,     ASCII_t,      ASCII_t, ASCII_p, ASCII_COLON,  ASCII_SLASH,
+          ASCII_SLASH, ASCII_w,      ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w,
+@@ -3491,6 +3864,29 @@
      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)) {
++    // NOTE: While Expat does not validate namespace URIs against RFC 3986
++    //       today (and is not REQUIRED to do so with regard to the XML 1.0
++    //       namespaces specification) we have to at least make sure, that
++    //       the application on top of Expat (that is likely splitting expanded
++    //       element names ("qualified names") of form
++    //       "[uri sep] local [sep prefix] '\0'" back into 1, 2 or 3 pieces
++    //       in its element handler code) cannot be confused by an attacker
++    //       putting additional namespace separator characters into namespace
++    //       declarations.  That would be ambiguous and not to be expected.
++    //
++    //       While the HTML API docs of function XML_ParserCreateNS have been
++    //       advising against use of a namespace separator character that can
++    //       appear in a URI for >20 years now, some widespread applications
++    //       are using URI characters (':' (colon) in particular) for a
++    //       namespace separator, in practice.  To keep these applications
++    //       functional, we only reject namespaces URIs containing the
++    //       application-chosen namespace separator if the chosen separator
++    //       is a non-URI character with regard to RFC 3986.
++    if (parser->m_ns && (uri[len] == parser->m_namespaceSeparator)
++        && ! is_rfc3986_uri_char(uri[len])) {
 +      return XML_ERROR_SYNTAX;
 +    }
    }
    isXML = isXML && len == xmlLen;
    isXMLNS = isXMLNS && len == xmlnsLen;
-@@ -3507,7 +3777,24 @@
+@@ -3507,7 +3903,24 @@
    if (parser->m_freeBindingList) {
      b = parser->m_freeBindingList;
      if (len > b->uriAlloc) {
@@ -1199,7 +1357,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
            parser, b->uri, sizeof(XML_Char) * (len + EXPAND_SPARE));
        if (temp == NULL)
          return XML_ERROR_NO_MEMORY;
-@@ -3519,6 +3806,21 @@
+@@ -3519,6 +3932,21 @@
      b = (BINDING *)MALLOC(parser, sizeof(BINDING));
      if (! b)
        return XML_ERROR_NO_MEMORY;
@@ -1221,7 +1379,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
      b->uri
          = (XML_Char *)MALLOC(parser, sizeof(XML_Char) * (len + EXPAND_SPARE));
      if (! b->uri) {
-@@ -3556,7 +3858,7 @@
+@@ -3556,7 +3984,7 @@
                        const char **endPtr) {
    enum XML_Error result
        = doCdataSection(parser, parser->m_encoding, &start, end, endPtr,
@@ -1230,7 +1388,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
    if (result != XML_ERROR_NONE)
      return result;
    if (start) {
-@@ -3576,7 +3878,8 @@
+@@ -3576,7 +4004,8 @@
  */
  static enum XML_Error
  doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
@@ -1240,7 +1398,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
    const char *s = *startPtr;
    const char **eventPP;
    const char **eventEndPP;
-@@ -3594,6 +3897,12 @@
+@@ -3594,6 +4023,12 @@
    for (;;) {
      const char *next = s; /* in case of XML_TOK_NONE or XML_TOK_PARTIAL */
      int tok = XmlCdataSectionTok(enc, s, end, &next);
@@ -1253,7 +1411,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
      *eventEndPP = next;
      switch (tok) {
      case XML_TOK_CDATA_SECT_CLOSE:
-@@ -3738,6 +4047,13 @@
+@@ -3738,6 +4173,13 @@
    *eventPP = s;
    *startPtr = NULL;
    tok = XmlIgnoreSectionTok(enc, s, end, &next);
@@ -1267,7 +1425,16 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
    *eventEndPP = next;
    switch (tok) {
    case XML_TOK_IGNORE_SECT:
-@@ -3822,6 +4138,15 @@
+@@ -3787,7 +4229,7 @@
+   const char *s;
+ #ifdef XML_UNICODE
+   char encodingBuf[128];
+-  /* See comments abount `protoclEncodingName` in parserInit() */
++  /* See comments about `protocolEncodingName` in parserInit() */
+   if (! parser->m_protocolEncodingName)
+     s = NULL;
+   else {
+@@ -3822,6 +4264,15 @@
    const char *versionend;
    const XML_Char *storedversion = NULL;
    int standalone = -1;
@@ -1283,7 +1450,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
    if (! (parser->m_ns ? XmlParseXmlDeclNS : XmlParseXmlDecl)(
            isGeneralTextEntity, parser->m_encoding, s, next, &parser->m_eventPtr,
            &version, &versionend, &encodingName, &newEncoding, &standalone)) {
-@@ -3971,6 +4296,10 @@
+@@ -3971,6 +4422,10 @@
  
    for (;;) {
      tok = XmlPrologTok(parser->m_encoding, start, end, &next);
@@ -1294,7 +1461,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
      parser->m_eventEndPtr = next;
      if (tok <= 0) {
        if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) {
-@@ -3989,7 +4318,8 @@
+@@ -3989,7 +4444,8 @@
          break;
        }
        /* found end of entity value - can store it now */
@@ -1304,7 +1471,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
      } else if (tok == XML_TOK_XML_DECL) {
        enum XML_Error result;
        result = processXmlDecl(parser, 0, start, next);
-@@ -4016,6 +4346,14 @@
+@@ -4016,6 +4472,14 @@
      */
      else if (tok == XML_TOK_BOM && next == end
               && ! parser->m_parsingStatus.finalBuffer) {
@@ -1319,7 +1486,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
        *nextPtr = next;
        return XML_ERROR_NONE;
      }
-@@ -4058,16 +4396,24 @@
+@@ -4058,16 +4522,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
@@ -1346,7 +1513,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
  }
  
  static enum XML_Error PTRCALL
-@@ -4080,6 +4426,9 @@
+@@ -4080,6 +4552,9 @@
  
    for (;;) {
      tok = XmlPrologTok(enc, start, end, &next);
@@ -1356,7 +1523,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
      if (tok <= 0) {
        if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) {
          *nextPtr = s;
-@@ -4097,7 +4446,7 @@
+@@ -4097,7 +4572,7 @@
          break;
        }
        /* found end of entity value - can store it now */
@@ -1365,7 +1532,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
      }
      start = next;
    }
-@@ -4111,13 +4460,14 @@
+@@ -4111,13 +4586,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,
@@ -1382,7 +1549,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
  #ifdef XML_DTD
    static const XML_Char externalSubsetName[] = {ASCII_HASH, '\0'};
  #endif /* XML_DTD */
-@@ -4144,6 +4494,10 @@
+@@ -4144,6 +4620,10 @@
    static const XML_Char enumValueSep[] = {ASCII_PIPE, '\0'};
    static const XML_Char enumValueStart[] = {ASCII_LPAREN, '\0'};
  
@@ -1393,7 +1560,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
    /* save one level of indirection */
    DTD *const dtd = parser->m_dtd;
  
-@@ -4208,6 +4562,19 @@
+@@ -4208,6 +4688,19 @@
        }
      }
      role = XmlTokenRole(&parser->m_prologState, tok, s, next, enc);
@@ -1413,7 +1580,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
      switch (role) {
      case XML_ROLE_XML_DECL: {
        enum XML_Error result = processXmlDecl(parser, 0, s, next);
-@@ -4483,7 +4850,8 @@
+@@ -4483,7 +4976,8 @@
          const XML_Char *attVal;
          enum XML_Error result = storeAttributeValue(
              parser, enc, parser->m_declAttributeIsCdata,
@@ -1423,7 +1590,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
          if (result)
            return result;
          attVal = poolStart(&dtd->pool);
-@@ -4516,8 +4884,9 @@
+@@ -4516,8 +5010,9 @@
        break;
      case XML_ROLE_ENTITY_VALUE:
        if (dtd->keepProcessing) {
@@ -1435,7 +1602,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
          if (parser->m_declEntity) {
            parser->m_declEntity->textPtr = poolStart(&dtd->entityValuePool);
            parser->m_declEntity->textLen
-@@ -4776,7 +5145,13 @@
+@@ -4776,7 +5271,13 @@
        if (parser->m_prologState.level >= parser->m_groupSize) {
          if (parser->m_groupSize) {
            {
@@ -1450,7 +1617,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
                  parser, parser->m_groupConnector, parser->m_groupSize *= 2);
              if (new_connector == NULL) {
                parser->m_groupSize /= 2;
-@@ -4786,7 +5161,18 @@
+@@ -4786,7 +5287,18 @@
            }
  
            if (dtd->scaffIndex) {
@@ -1470,7 +1637,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
                  parser, dtd->scaffIndex, parser->m_groupSize * sizeof(int));
              if (new_scaff_index == NULL)
                return XML_ERROR_NO_MEMORY;
-@@ -4907,12 +5293,15 @@
+@@ -4907,12 +5419,15 @@
          if (parser->m_externalEntityRefHandler) {
            dtd->paramEntityRead = XML_FALSE;
            entity->open = XML_TRUE;
@@ -1486,7 +1653,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
            entity->open = XML_FALSE;
            handleDefault = XML_FALSE;
            if (! dtd->paramEntityRead) {
-@@ -4991,7 +5380,7 @@
+@@ -4991,7 +5506,7 @@
        if (dtd->in_eldecl) {
          ELEMENT_TYPE *el;
          const XML_Char *name;
@@ -1495,7 +1662,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
          const char *nxt
              = (quant == XML_CQUANT_NONE ? next : next - enc->minBytesPerChar);
          int myindex = nextScaffoldPart(parser);
-@@ -5007,7 +5396,13 @@
+@@ -5007,7 +5522,13 @@
          nameLen = 0;
          for (; name[nameLen++];)
            ;
@@ -1510,7 +1677,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
          if (parser->m_elementDeclHandler)
            handleDefault = XML_FALSE;
        }
-@@ -5110,6 +5505,13 @@
+@@ -5110,6 +5631,13 @@
    for (;;) {
      const char *next = NULL;
      int tok = XmlPrologTok(parser->m_encoding, s, end, &next);
@@ -1524,7 +1691,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
      parser->m_eventEndPtr = next;
      switch (tok) {
      /* report partial linebreak - it might be the last token */
-@@ -5183,6 +5585,9 @@
+@@ -5183,6 +5711,9 @@
        return XML_ERROR_NO_MEMORY;
    }
    entity->open = XML_TRUE;
@@ -1534,7 +1701,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
    entity->processed = 0;
    openEntity->next = parser->m_openInternalEntities;
    parser->m_openInternalEntities = openEntity;
-@@ -5201,17 +5606,22 @@
+@@ -5201,17 +5732,22 @@
      int tok
          = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
      result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,
@@ -1559,7 +1726,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
        entity->open = XML_FALSE;
        parser->m_openInternalEntities = openEntity->next;
        /* put openEntity back in list of free instances */
-@@ -5244,12 +5654,13 @@
+@@ -5244,12 +5780,13 @@
      int tok
          = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
      result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,
@@ -1575,7 +1742,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
  
    if (result != XML_ERROR_NONE)
      return result;
-@@ -5258,6 +5669,9 @@
+@@ -5258,6 +5795,9 @@
      entity->processed = (int)(next - (const char *)entity->textPtr);
      return result;
    } else {
@@ -1585,7 +1752,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
      entity->open = XML_FALSE;
      parser->m_openInternalEntities = openEntity->next;
      /* put openEntity back in list of free instances */
-@@ -5271,7 +5685,8 @@
+@@ -5271,7 +5811,8 @@
      parser->m_processor = prologProcessor;
      tok = XmlPrologTok(parser->m_encoding, s, end, &next);
      return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
@@ -1595,7 +1762,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
    } else
  #endif /* XML_DTD */
    {
-@@ -5279,7 +5694,8 @@
+@@ -5279,7 +5820,8 @@
      /* see externalEntityContentProcessor vs contentProcessor */
      return doContent(parser, parser->m_parentParser ? 1 : 0, parser->m_encoding,
                       s, end, nextPtr,
@@ -1605,7 +1772,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
    }
  }
  
-@@ -5294,9 +5710,10 @@
+@@ -5294,9 +5836,10 @@
  
  static enum XML_Error
  storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
@@ -1618,7 +1785,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
    if (result)
      return result;
    if (! isCdata && poolLength(pool) && poolLastChar(pool) == 0x20)
-@@ -5308,11 +5725,23 @@
+@@ -5308,11 +5851,23 @@
  
  static enum XML_Error
  appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
@@ -1644,7 +1811,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
      switch (tok) {
      case XML_TOK_NONE:
        return XML_ERROR_NONE;
-@@ -5372,6 +5801,14 @@
+@@ -5372,6 +5927,14 @@
        XML_Char ch = (XML_Char)XmlPredefinedEntityName(
            enc, ptr + enc->minBytesPerChar, next - enc->minBytesPerChar);
        if (ch) {
@@ -1659,7 +1826,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
          if (! poolAppendChar(pool, ch))
            return XML_ERROR_NO_MEMORY;
          break;
-@@ -5449,9 +5886,16 @@
+@@ -5449,9 +6012,16 @@
          enum XML_Error result;
          const XML_Char *textEnd = entity->textPtr + entity->textLen;
          entity->open = XML_TRUE;
@@ -1677,7 +1844,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
          entity->open = XML_FALSE;
          if (result)
            return result;
-@@ -5481,13 +5925,16 @@
+@@ -5481,13 +6051,16 @@
  
  static enum XML_Error
  storeEntityValue(XML_Parser parser, const ENCODING *enc,
@@ -1695,7 +1862,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
  #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 @@
+@@ -5498,8 +6071,19 @@
    }
  
    for (;;) {
@@ -1716,7 +1883,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
      switch (tok) {
      case XML_TOK_PARAM_ENTITY_REF:
  #ifdef XML_DTD
-@@ -5535,13 +5993,16 @@
+@@ -5535,13 +6119,16 @@
            if (parser->m_externalEntityRefHandler) {
              dtd->paramEntityRead = XML_FALSE;
              entity->open = XML_TRUE;
@@ -1733,7 +1900,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
              entity->open = XML_FALSE;
              if (! dtd->paramEntityRead)
                dtd->keepProcessing = dtd->standalone;
-@@ -5549,9 +6010,12 @@
+@@ -5549,9 +6136,12 @@
              dtd->keepProcessing = dtd->standalone;
          } else {
            entity->open = XML_TRUE;
@@ -1747,7 +1914,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
            entity->open = XML_FALSE;
            if (result)
              goto endEntityValue;
-@@ -5784,7 +6248,25 @@
+@@ -5784,7 +6374,25 @@
        }
      } else {
        DEFAULT_ATTRIBUTE *temp;
@@ -1774,7 +1941,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
        temp = (DEFAULT_ATTRIBUTE *)REALLOC(parser, type->defaultAtts,
                                            (count * sizeof(DEFAULT_ATTRIBUTE)));
        if (temp == NULL)
-@@ -6435,10 +6917,26 @@
+@@ -6435,10 +7043,26 @@
      /* check for overflow (table is half full) */
      if (table->used >> (table->power - 1)) {
        unsigned char newPower = table->power + 1;
@@ -1805,7 +1972,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
        if (! newV)
          return NULL;
        memset(newV, 0, tsize);
-@@ -6786,6 +7284,20 @@
+@@ -6786,6 +7410,20 @@
    if (dtd->scaffCount >= dtd->scaffSize) {
      CONTENT_SCAFFOLD *temp;
      if (dtd->scaffold) {
@@ -1826,7 +1993,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
        temp = (CONTENT_SCAFFOLD *)REALLOC(
            parser, dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD));
        if (temp == NULL)
-@@ -6817,55 +7329,130 @@
+@@ -6817,55 +7455,134 @@
    return next;
  }
  
@@ -1864,6 +2031,10 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
 -
  static XML_Content *
  build_model(XML_Parser parser) {
++  /* Function build_model transforms the existing parser->m_dtd->scaffold
++   * array of CONTENT_SCAFFOLD tree nodes into a new array of
++   * XML_Content tree nodes followed by a gapless list of zero-terminated
++   * strings. */
    DTD *const dtd = parser->m_dtd; /* save one level of indirection */
    XML_Content *ret;
 -  XML_Content *cpos;
@@ -1996,7 +2167,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
    return ret;
  }
  
-@@ -6894,7 +7481,7 @@
+@@ -6894,7 +7611,7 @@
  
  static XML_Char *
  copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
@@ -2005,7 +2176,7 @@ diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
    XML_Char *result;
  
    /* First determine how long the string is */
-@@ -6912,3 +7499,766 @@
+@@ -6912,3 +7629,766 @@
    memcpy(result, s, charsRequired * sizeof(XML_Char));
    return result;
  }
@@ -2954,7 +3125,7 @@ diff -ru misc/expat-2.2.10/tests/minicheck.c misc/build/expat-2.2.10/tests/minic
      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
++++ misc/build/expat-2.2.10/tests/runtests.c	2022-03-09 20:25:36.716575629 +0100
 @@ -45,6 +45,7 @@
  #include <stddef.h> /* ptrdiff_t */
  #include <ctype.h>
@@ -2972,7 +3143,307 @@ diff -ru misc/expat-2.2.10/tests/runtests.c misc/build/expat-2.2.10/tests/runtes
  #include "minicheck.h"
  #include "memcheck.h"
  #include "siphash.h"
-@@ -11231,6 +11232,381 @@
+@@ -2677,6 +2678,82 @@
+ }
+ END_TEST
+ 
++static void XMLCALL
++element_decl_check_model(void *userData, const XML_Char *name,
++                         XML_Content *model) {
++  uint32_t errorFlags = 0;
++  UNUSED_P(userData);
++
++  /* Expected model array structure is this:
++   * [0] (type 6, quant 0)
++   *   [1] (type 5, quant 0)
++   *     [3] (type 4, quant 0, name "bar")
++   *     [4] (type 4, quant 0, name "foo")
++   *     [5] (type 4, quant 3, name "xyz")
++   *   [2] (type 4, quant 2, name "zebra")
++   */
++  errorFlags |= ((xcstrcmp(name, XCS("junk")) == 0) ? 0 : (1u << 0));
++  errorFlags |= ((model != NULL) ? 0 : (1u << 1));
++
++  errorFlags |= ((model[0].type == XML_CTYPE_SEQ) ? 0 : (1u << 2));
++  errorFlags |= ((model[0].quant == XML_CQUANT_NONE) ? 0 : (1u << 3));
++  errorFlags |= ((model[0].numchildren == 2) ? 0 : (1u << 4));
++  errorFlags |= ((model[0].children == &model[1]) ? 0 : (1u << 5));
++  errorFlags |= ((model[0].name == NULL) ? 0 : (1u << 6));
++
++  errorFlags |= ((model[1].type == XML_CTYPE_CHOICE) ? 0 : (1u << 7));
++  errorFlags |= ((model[1].quant == XML_CQUANT_NONE) ? 0 : (1u << 8));
++  errorFlags |= ((model[1].numchildren == 3) ? 0 : (1u << 9));
++  errorFlags |= ((model[1].children == &model[3]) ? 0 : (1u << 10));
++  errorFlags |= ((model[1].name == NULL) ? 0 : (1u << 11));
++
++  errorFlags |= ((model[2].type == XML_CTYPE_NAME) ? 0 : (1u << 12));
++  errorFlags |= ((model[2].quant == XML_CQUANT_REP) ? 0 : (1u << 13));
++  errorFlags |= ((model[2].numchildren == 0) ? 0 : (1u << 14));
++  errorFlags |= ((model[2].children == NULL) ? 0 : (1u << 15));
++  errorFlags |= ((xcstrcmp(model[2].name, XCS("zebra")) == 0) ? 0 : (1u << 16));
++
++  errorFlags |= ((model[3].type == XML_CTYPE_NAME) ? 0 : (1u << 17));
++  errorFlags |= ((model[3].quant == XML_CQUANT_NONE) ? 0 : (1u << 18));
++  errorFlags |= ((model[3].numchildren == 0) ? 0 : (1u << 19));
++  errorFlags |= ((model[3].children == NULL) ? 0 : (1u << 20));
++  errorFlags |= ((xcstrcmp(model[3].name, XCS("bar")) == 0) ? 0 : (1u << 21));
++
++  errorFlags |= ((model[4].type == XML_CTYPE_NAME) ? 0 : (1u << 22));
++  errorFlags |= ((model[4].quant == XML_CQUANT_NONE) ? 0 : (1u << 23));
++  errorFlags |= ((model[4].numchildren == 0) ? 0 : (1u << 24));
++  errorFlags |= ((model[4].children == NULL) ? 0 : (1u << 25));
++  errorFlags |= ((xcstrcmp(model[4].name, XCS("foo")) == 0) ? 0 : (1u << 26));
++
++  errorFlags |= ((model[5].type == XML_CTYPE_NAME) ? 0 : (1u << 27));
++  errorFlags |= ((model[5].quant == XML_CQUANT_PLUS) ? 0 : (1u << 28));
++  errorFlags |= ((model[5].numchildren == 0) ? 0 : (1u << 29));
++  errorFlags |= ((model[5].children == NULL) ? 0 : (1u << 30));
++  errorFlags |= ((xcstrcmp(model[5].name, XCS("xyz")) == 0) ? 0 : (1u << 31));
++
++  XML_SetUserData(g_parser, (void *)(uintptr_t)errorFlags);
++  XML_FreeContentModel(g_parser, model);
++}
++
++START_TEST(test_dtd_elements_nesting) {
++  // Payload inspired by a test in Perl's XML::Parser
++  const char *text = "<!DOCTYPE foo [\n"
++                     "<!ELEMENT junk ((bar|foo|xyz+), zebra*)>\n"
++                     "]>\n"
++                     "<foo/>";
++
++  XML_SetUserData(g_parser, (void *)(uintptr_t)-1);
++
++  XML_SetElementDeclHandler(g_parser, element_decl_check_model);
++  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
++      == XML_STATUS_ERROR)
++    xml_failure(g_parser);
++
++  if ((uint32_t)(uintptr_t)XML_GetUserData(g_parser) != 0)
++    fail("Element declaration model regression detected");
++}
++END_TEST
++
+ /* Test foreign DTD handling */
+ START_TEST(test_set_foreign_dtd) {
+   const char *text1 = "<?xml version='1.0' encoding='us-ascii'?>\n";
+@@ -3860,6 +3937,30 @@
+ }
+ END_TEST
+ 
++/* Test for signed integer overflow CVE-2022-23852 */
++#if defined(XML_CONTEXT_BYTES)
++START_TEST(test_get_buffer_3_overflow) {
++  XML_Parser parser = XML_ParserCreate(NULL);
++
++  const char *const text = "\n";
++  const int expectedKeepValue = (int)strlen(text);
++
++  assert(parser != NULL);
++  // After this call, variable "keep" in XML_GetBuffer will
++  // have value expectedKeepValue
++  if (XML_Parse(parser, text, (int)strlen(text), XML_FALSE /* isFinal */)
++      == XML_STATUS_ERROR)
++    xml_failure(parser);
++
++  assert(expectedKeepValue > 0);
++  if (XML_GetBuffer(parser, INT_MAX - expectedKeepValue + 1) != NULL)
++    fail("enlarging buffer not failed");
++
++  XML_ParserFree(parser);
++}
++END_TEST
++#endif // defined(XML_CONTEXT_BYTES)
++
+ /* Test position information macros */
+ START_TEST(test_byte_info_at_end) {
+   const char *text = "<doc></doc>";
+@@ -5987,6 +6088,107 @@
+ }
+ END_TEST
+ 
++START_TEST(test_utf8_in_start_tags) {
++  struct test_case {
++    bool goodName;
++    bool goodNameStart;
++    const char *tagName;
++  };
++
++  // The idea with the tests below is this:
++  // We want to cover 1-, 2- and 3-byte sequences, 4-byte sequences
++  // go to isNever and are hence not a concern.
++  //
++  // We start with a character that is a valid name character
++  // (or even name-start character, see XML 1.0r4 spec) and then we flip
++  // single bits at places where (1) the result leaves the UTF-8 encoding space
++  // and (2) we stay in the same n-byte sequence family.
++  //
++  // The flipped bits are highlighted in angle brackets in comments,
++  // e.g. "[<1>011 1001]" means we had [0011 1001] but we now flipped
++  // the most significant bit to 1 to leave UTF-8 encoding space.
++  struct test_case cases[] = {
++      // 1-byte UTF-8: [0xxx xxxx]
++      {true, true, "\x3A"},   // [0011 1010] = ASCII colon ':'
++      {false, false, "\xBA"}, // [<1>011 1010]
++      {true, false, "\x39"},  // [0011 1001] = ASCII nine '9'
++      {false, false, "\xB9"}, // [<1>011 1001]
++
++      // 2-byte UTF-8: [110x xxxx] [10xx xxxx]
++      {true, true, "\xDB\xA5"},   // [1101 1011] [1010 0101] =
++                                  // Arabic small waw U+06E5
++      {false, false, "\x9B\xA5"}, // [1<0>01 1011] [1010 0101]
++      {false, false, "\xDB\x25"}, // [1101 1011] [<0>010 0101]
++      {false, false, "\xDB\xE5"}, // [1101 1011] [1<1>10 0101]
++      {true, false, "\xCC\x81"},  // [1100 1100] [1000 0001] =
++                                  // combining char U+0301
++      {false, false, "\x8C\x81"}, // [1<0>00 1100] [1000 0001]
++      {false, false, "\xCC\x01"}, // [1100 1100] [<0>000 0001]
++      {false, false, "\xCC\xC1"}, // [1100 1100] [1<1>00 0001]
++
++      // 3-byte UTF-8: [1110 xxxx] [10xx xxxx] [10xxxxxx]
++      {true, true, "\xE0\xA4\x85"},   // [1110 0000] [1010 0100] [1000 0101] =
++                                      // Devanagari Letter A U+0905
++      {false, false, "\xA0\xA4\x85"}, // [1<0>10 0000] [1010 0100] [1000 0101]
++      {false, false, "\xE0\x24\x85"}, // [1110 0000] [<0>010 0100] [1000 0101]
++      {false, false, "\xE0\xE4\x85"}, // [1110 0000] [1<1>10 0100] [1000 0101]
++      {false, false, "\xE0\xA4\x05"}, // [1110 0000] [1010 0100] [<0>000 0101]
++      {false, false, "\xE0\xA4\xC5"}, // [1110 0000] [1010 0100] [1<1>00 0101]
++      {true, false, "\xE0\xA4\x81"},  // [1110 0000] [1010 0100] [1000 0001] =
++                                      // combining char U+0901
++      {false, false, "\xA0\xA4\x81"}, // [1<0>10 0000] [1010 0100] [1000 0001]
++      {false, false, "\xE0\x24\x81"}, // [1110 0000] [<0>010 0100] [1000 0001]
++      {false, false, "\xE0\xE4\x81"}, // [1110 0000] [1<1>10 0100] [1000 0001]
++      {false, false, "\xE0\xA4\x01"}, // [1110 0000] [1010 0100] [<0>000 0001]
++      {false, false, "\xE0\xA4\xC1"}, // [1110 0000] [1010 0100] [1<1>00 0001]
++  };
++  const bool atNameStart[] = {true, false};
++
++  size_t i = 0;
++  char doc[1024];
++  size_t failCount = 0;
++
++  for (; i < sizeof(cases) / sizeof(cases[0]); i++) {
++    size_t j = 0;
++    for (; j < sizeof(atNameStart) / sizeof(atNameStart[0]); j++) {
++      const bool expectedSuccess
++          = atNameStart[j] ? cases[i].goodNameStart : cases[i].goodName;
++      XML_Parser parser = XML_ParserCreate(NULL);
++      enum XML_Status status;
++      bool success;
++      sprintf(doc, "<%s%s><!--", atNameStart[j] ? "" : "a", cases[i].tagName);
++
++      status
++          = XML_Parse(parser, doc, (int)strlen(doc), /*isFinal=*/XML_FALSE);
++
++      success = true;
++      if ((status == XML_STATUS_OK) != expectedSuccess) {
++        success = false;
++      }
++      if ((status == XML_STATUS_ERROR)
++          && (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN)) {
++        success = false;
++      }
++
++      if (! success) {
++        fprintf(
++            stderr,
++            "FAIL case %2u (%sat name start, %u-byte sequence, error code %d)\n",
++            (unsigned)i + 1u, atNameStart[j] ? "    " : "not ",
++            (unsigned)strlen(cases[i].tagName), XML_GetErrorCode(parser));
++        failCount++;
++      }
++
++      XML_ParserFree(parser);
++    }
++  }
++
++  if (failCount > 0) {
++    fail("UTF-8 regression detected");
++  }
++}
++END_TEST
++
+ /* Test trailing spaces in elements are accepted */
+ static void XMLCALL
+ record_element_end_handler(void *userData, const XML_Char *name) {
+@@ -6164,6 +6366,14 @@
+ }
+ END_TEST
+ 
++START_TEST(test_bad_doctype_utf8) {
++  const char *text = "<!DOCTYPE \xDB\x25"
++                     "doc><doc/>"; // [1101 1011] [<0>010 0101]
++  expect_failure(text, XML_ERROR_INVALID_TOKEN,
++                 "Invalid UTF-8 in DOCTYPE not faulted");
++}
++END_TEST
++
+ START_TEST(test_bad_doctype_utf16) {
+   const char text[] =
+       /* <!DOCTYPE doc [ \x06f2 ]><doc/>
+@@ -7209,6 +7419,37 @@
+ }
+ END_TEST
+ 
++START_TEST(test_ns_separator_in_uri) {
++  struct test_case {
++    enum XML_Status expectedStatus;
++    const char *doc;
++    XML_Char namesep;
++  };
++  struct test_case cases[] = {
++      {XML_STATUS_OK, "<doc xmlns='one_two' />", XCS('\n')},
++      {XML_STATUS_ERROR, "<doc xmlns='one&#x0A;two' />", XCS('\n')},
++      {XML_STATUS_OK, "<doc xmlns='one:two' />", XCS(':')},
++  };
++
++  size_t i = 0;
++  size_t failCount = 0;
++  for (; i < sizeof(cases) / sizeof(cases[0]); i++) {
++    XML_Parser parser = XML_ParserCreateNS(NULL, cases[i].namesep);
++    XML_SetElementHandler(parser, dummy_start_element, dummy_end_element);
++    if (XML_Parse(parser, cases[i].doc, (int)strlen(cases[i].doc),
++                  /*isFinal*/ XML_TRUE)
++        != cases[i].expectedStatus) {
++      failCount++;
++    }
++    XML_ParserFree(parser);
++  }
++
++  if (failCount) {
++    fail("Namespace separator handling is broken");
++  }
++}
++END_TEST
++
+ /* Control variable; the number of times duff_allocator() will successfully
+  * allocate */
+ #define ALLOC_ALWAYS_SUCCEED (-1)
+@@ -7365,7 +7606,7 @@
+     fail("Version mismatch");
+ 
+ #if ! defined(XML_UNICODE) || defined(XML_UNICODE_WCHAR_T)
+-  if (xcstrcmp(version_text, XCS("expat_2.2.10"))) /* needs bump on releases */
++  if (xcstrcmp(version_text, XCS("expat_2.2.13"))) /* needs bump on releases */
+     fail("XML_*_VERSION in expat.h out of sync?\n");
+ #else
+   /* If we have XML_UNICODE defined but not XML_UNICODE_WCHAR_T
+@@ -9851,6 +10092,15 @@
+ 
+   /* Try a parse before the start of the world */
+   /* (Exercises new code path) */
++  if (XML_ParseBuffer(g_parser, 0, XML_FALSE) != XML_STATUS_ERROR)
++    fail("Pre-init XML_ParseBuffer not faulted");
++  if (XML_GetErrorCode(g_parser) != XML_ERROR_NO_BUFFER)
++    fail("Pre-init XML_ParseBuffer faulted for wrong reason");
++
++  buffer = XML_GetBuffer(g_parser, 1 /* any small number greater than 0 */);
++  if (buffer == NULL)
++    fail("Could not acquire parse buffer");
++
+   allocation_count = 0;
+   if (XML_ParseBuffer(g_parser, 0, XML_FALSE) != XML_STATUS_ERROR)
+     fail("Pre-init XML_ParseBuffer not faulted");
+@@ -11231,6 +11481,401 @@
  }
  END_TEST
  
@@ -3068,6 +3539,16 @@ diff -ru misc/expat-2.2.10/tests/runtests.c misc/build/expat-2.2.10/tests/runtes
 +
 +      /* CDATA */
 +      {"<e><![CDATA[one two three]]></e>", NULL, NULL, 0, filled_later},
++      /* The following is the essence of this OSS-Fuzz finding:
++         https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=34302
++         https://oss-fuzz.com/testcase-detail/4860575394955264
++      */
++      {"<!DOCTYPE r [\n"
++       "<!ENTITY e \"111<![CDATA[2 <= 2]]>333\">\n"
++       "]>\n"
++       "<r>&e;</r>\n",
++       NULL, NULL, sizeof(XML_Char) * strlen("111<![CDATA[2 <= 2]]>333"),
++       filled_later},
 +
 +      /* Conditional sections */
 +      {"<!DOCTYPE r [\n"
@@ -3265,6 +3746,16 @@ diff -ru misc/expat-2.2.10/tests/runtests.c misc/build/expat-2.2.10/tests/runtes
 +}
 +END_TEST
 +
++static float
++portableNAN() {
++  return strtof("nan", NULL);
++}
++
++static float
++portableINFINITY() {
++  return strtof("infinity", NULL);
++}
++
 +START_TEST(test_billion_laughs_attack_protection_api) {
 +  XML_Parser parserWithoutParent = XML_ParserCreate(NULL);
 +  XML_Parser parserWithParent
@@ -3283,7 +3774,7 @@ diff -ru misc/expat-2.2.10/tests/runtests.c misc/build/expat-2.2.10/tests/runtes
 +      == XML_TRUE)
 +    fail("Call with non-root parser is NOT supposed to succeed");
 +  if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
-+          parserWithoutParent, NAN)
++          parserWithoutParent, portableNAN())
 +      == XML_TRUE)
 +    fail("Call with NaN limit is NOT supposed to succeed");
 +  if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
@@ -3305,7 +3796,7 @@ diff -ru misc/expat-2.2.10/tests/runtests.c misc/build/expat-2.2.10/tests/runtes
 +      == XML_FALSE)
 +    fail("Call with positive limit >=1.0 is supposed to succeed");
 +  if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
-+          parserWithoutParent, INFINITY)
++          parserWithoutParent, portableINFINITY())
 +      == XML_FALSE)
 +    fail("Call with positive limit >=1.0 is supposed to succeed");
 +
@@ -3354,7 +3845,7 @@ diff -ru misc/expat-2.2.10/tests/runtests.c misc/build/expat-2.2.10/tests/runtes
  static Suite *
  make_suite(void) {
    Suite *s = suite_create("basic");
-@@ -11239,6 +11615,9 @@
+@@ -11239,6 +11884,9 @@
    TCase *tc_misc = tcase_create("miscellaneous tests");
    TCase *tc_alloc = tcase_create("allocation tests");
    TCase *tc_nsalloc = tcase_create("namespace allocation tests");
@@ -3364,7 +3855,49 @@ diff -ru misc/expat-2.2.10/tests/runtests.c misc/build/expat-2.2.10/tests/runtes
  
    suite_add_tcase(s, tc_basic);
    tcase_add_checked_fixture(tc_basic, basic_setup, basic_teardown);
-@@ -11603,6 +11982,13 @@
+@@ -11325,6 +11973,7 @@
+   tcase_add_test(tc_basic, test_memory_allocation);
+   tcase_add_test(tc_basic, test_default_current);
+   tcase_add_test(tc_basic, test_dtd_elements);
++  tcase_add_test(tc_basic, test_dtd_elements_nesting);
+   tcase_add_test__ifdef_xml_dtd(tc_basic, test_set_foreign_dtd);
+   tcase_add_test__ifdef_xml_dtd(tc_basic, test_foreign_dtd_not_standalone);
+   tcase_add_test__ifdef_xml_dtd(tc_basic, test_invalid_foreign_dtd);
+@@ -11353,6 +12002,9 @@
+   tcase_add_test(tc_basic, test_empty_parse);
+   tcase_add_test(tc_basic, test_get_buffer_1);
+   tcase_add_test(tc_basic, test_get_buffer_2);
++#if defined(XML_CONTEXT_BYTES)
++  tcase_add_test(tc_basic, test_get_buffer_3_overflow);
++#endif
+   tcase_add_test(tc_basic, test_byte_info_at_end);
+   tcase_add_test(tc_basic, test_byte_info_at_error);
+   tcase_add_test(tc_basic, test_byte_info_at_cdata);
+@@ -11436,6 +12088,7 @@
+   tcase_add_test(tc_basic, test_ext_entity_utf8_non_bom);
+   tcase_add_test(tc_basic, test_utf8_in_cdata_section);
+   tcase_add_test(tc_basic, test_utf8_in_cdata_section_2);
++  tcase_add_test(tc_basic, test_utf8_in_start_tags);
+   tcase_add_test(tc_basic, test_trailing_spaces_in_elements);
+   tcase_add_test(tc_basic, test_utf16_attribute);
+   tcase_add_test(tc_basic, test_utf16_second_attr);
+@@ -11444,6 +12097,7 @@
+   tcase_add_test(tc_basic, test_bad_attr_desc_keyword);
+   tcase_add_test(tc_basic, test_bad_attr_desc_keyword_utf16);
+   tcase_add_test(tc_basic, test_bad_doctype);
++  tcase_add_test(tc_basic, test_bad_doctype_utf8);
+   tcase_add_test(tc_basic, test_bad_doctype_utf16);
+   tcase_add_test(tc_basic, test_bad_doctype_plus);
+   tcase_add_test(tc_basic, test_bad_doctype_star);
+@@ -11500,6 +12154,7 @@
+   tcase_add_test(tc_namespace, test_ns_utf16_doctype);
+   tcase_add_test(tc_namespace, test_ns_invalid_doctype);
+   tcase_add_test(tc_namespace, test_ns_double_colon_doctype);
++  tcase_add_test(tc_namespace, test_ns_separator_in_uri);
+ 
+   suite_add_tcase(s, tc_misc);
+   tcase_add_checked_fixture(tc_misc, NULL, basic_teardown);
+@@ -11603,6 +12258,13 @@
    tcase_add_test(tc_nsalloc, test_nsalloc_long_systemid_in_ext);
    tcase_add_test(tc_nsalloc, test_nsalloc_prefixed_element);
  
@@ -3378,6 +3911,18 @@ diff -ru misc/expat-2.2.10/tests/runtests.c misc/build/expat-2.2.10/tests/runtes
    return s;
  }
  
+diff -ru misc/expat-2.2.10/win32/expat.iss misc/build/expat-2.2.10/win32/expat.iss
+--- misc/expat-2.2.10/win32/expat.iss	2020-10-03 17:14:57.000000000 +0200
++++ misc/build/expat-2.2.10/win32/expat.iss	2022-03-09 20:25:36.720575719 +0100
+@@ -4,7 +4,7 @@
+ ; This script was contributed by Tim Peters.
+ ; It was designed for Inno Setup 2.0.19 but works with later versions as well.
+ 
+-#define expatVer "2.2.10"
++#define expatVer "2.2.13"
+ 
+ [Setup]
+ AppName=Expat
 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
@@ -3401,7 +3946,7 @@ diff -ru misc/expat-2.2.10/xmlwf/Makefile.in misc/build/expat-2.2.10/xmlwf/Makef
  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
++++ misc/build/expat-2.2.10/xmlwf/xmltchar.h	2022-03-09 20:25:36.720575719 +0100
 @@ -54,6 +54,8 @@
  #  define tmain wmain
  #  define tremove _wremove
@@ -3420,7 +3965,7 @@ diff -ru misc/expat-2.2.10/xmlwf/xmltchar.h misc/build/expat-2.2.10/xmlwf/xmltch
  #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
++++ misc/build/expat-2.2.10/xmlwf/xmlwf.c	2022-03-09 20:25:36.720575719 +0100
 @@ -30,11 +30,15 @@
     USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
@@ -3452,7 +3997,43 @@ diff -ru misc/expat-2.2.10/xmlwf/xmlwf.c misc/build/expat-2.2.10/xmlwf/xmlwf.c
  /* Structures for handler user data */
  typedef struct NotationList {
    struct NotationList *next;
-@@ -875,6 +887,12 @@
+@@ -322,6 +334,13 @@
+   data->notationListHead = NULL;
+ }
+ 
++static void
++cleanupUserData(XmlwfUserData *userData) {
++  free((void *)userData->currentDoctypeName);
++  userData->currentDoctypeName = NULL;
++  freeNotations(userData);
++}
++
+ static int
+ xcscmp(const XML_Char *xs, const XML_Char *xt) {
+   while (*xs != 0 && *xt != 0) {
+@@ -850,9 +869,10 @@
+        * $ xmlwf/xmlwf_helpgen.sh
+        */
+       /* clang-format off */
+-      T("usage: %s [-s] [-n] [-p] [-x] [-e ENCODING] [-w] [-r] [-d DIRECTORY]\n")
+-      T("             [-c | -m | -t] [-N]\n")
+-      T("             [FILE [FILE ...]]\n")
++      T("usage:\n")
++      T("  %s [OPTIONS] [FILE ...]\n")
++      T("  %s -h\n")
++      T("  %s -v\n")
+       T("\n")
+       T("xmlwf - Determines if an XML document is well-formed\n")
+       T("\n")
+@@ -867,6 +887,7 @@
+       T("  -e ENCODING   override any in-document [e]ncoding declaration\n")
+       T("  -w            enable support for [W]indows code pages\n")
+       T("  -r            disable memory-mapping and use normal file [r]ead IO calls instead\n")
++      T("  -k            when processing multiple files, [k]eep processing after first file with error\n")
+       T("\n")
+       T("output control arguments:\n")
+       T("  -d DIRECTORY  output [d]estination directory\n")
+@@ -875,14 +896,27 @@
        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")
@@ -3465,7 +4046,23 @@ diff -ru misc/expat-2.2.10/xmlwf/xmlwf.c misc/build/expat-2.2.10/xmlwf/xmlwf.c
        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 @@
+       T("\n")
++      T("exit status:\n")
++      T("  0             the input files are well-formed and the output (if requested) was written successfully\n")
++      T("  1             could not allocate data structures, signals a serious problem with execution environment\n")
++      T("  2             one or more input files were not well-formed\n")
++      T("  3             could not create an output file\n")
++      T("  4             command-line argument error\n")
++      T("\n")
+       T("xmlwf of libexpat is software libre, licensed under the MIT license.\n")
+       T("Please report bugs at https://github.com/libexpat/libexpat/issues.  Thank you!\n")
+       , /* clang-format on */
+-      prog);
++      prog, prog, prog);
+   exit(rc);
+ }
+ 
+@@ -891,6 +925,19 @@
  int wmain(int argc, XML_Char **argv);
  #endif
  
@@ -3485,22 +4082,56 @@ diff -ru misc/expat-2.2.10/xmlwf/xmlwf.c misc/build/expat-2.2.10/xmlwf/xmlwf.c
  int
  tmain(int argc, XML_Char **argv) {
    int i, j;
-@@ -902,6 +933,11 @@
+@@ -902,6 +949,13 @@
    int useNamespaces = 0;
    int requireStandalone = 0;
    int requiresNotations = 0;
++  int continueOnError = 0;
 +
 +  float attackMaximumAmplification = -1.0f; /* signaling "not set" */
 +  unsigned long long attackThresholdBytes;
 +  XML_Bool attackThresholdGiven = XML_FALSE;
 +
++  int exitCode = XMLWF_EXIT_SUCCESS;
    enum XML_ParamEntityParsing paramEntityParsing
        = XML_PARAM_ENTITY_PARSING_NEVER;
    int useStdin = 0;
-@@ -990,6 +1026,49 @@
+@@ -965,31 +1019,64 @@
+       j++;
+       break;
+     case T('d'):
+-      if (argv[i][j + 1] == T('\0')) {
+-        if (++i == argc)
+-          usage(argv[0], 2);
+-        outputDir = argv[i];
+-      } else
+-        outputDir = argv[i] + j + 1;
+-      i++;
+-      j = 0;
++      XMLWF_SHIFT_ARG_INTO(outputDir, argc, argv, i, j);
+       break;
+     case T('e'):
+-      if (argv[i][j + 1] == T('\0')) {
+-        if (++i == argc)
+-          usage(argv[0], 2);
+-        encoding = argv[i];
+-      } else
+-        encoding = argv[i] + j + 1;
+-      i++;
+-      j = 0;
++      XMLWF_SHIFT_ARG_INTO(encoding, argc, argv, i, j);
+       break;
+     case T('h'):
+-      usage(argv[0], 0);
++      usage(argv[0], XMLWF_EXIT_SUCCESS);
+       return 0;
      case T('v'):
        showVersion(argv[0]);
        return 0;
++    case T('k'):
++      continueOnError = 1;
++      j++;
++      break;
 +    case T('a'): {
 +      const XML_Char *valueText = NULL;
 +      XMLWF_SHIFT_ARG_INTO(valueText, argc, argv, i, j);
@@ -3547,10 +4178,23 @@ diff -ru misc/expat-2.2.10/xmlwf/xmlwf.c misc/build/expat-2.2.10/xmlwf/xmlwf.c
      case T('\0'):
        if (j > 1) {
          i++;
-@@ -1020,6 +1099,19 @@
-       exit(1);
+@@ -998,7 +1085,7 @@
+       }
+       /* fall through */
+     default:
+-      usage(argv[0], 2);
++      usage(argv[0], XMLWF_EXIT_USAGE_ERROR);
      }
+   }
+   if (i == argc) {
+@@ -1017,7 +1104,22 @@
  
+     if (! parser) {
+       tperror(T("Could not instantiate parser"));
+-      exit(1);
++      exit(XMLWF_EXIT_INTERNAL_ERROR);
++    }
++
 +    if (attackMaximumAmplification != -1.0f) {
 +#ifdef XML_DTD
 +      XML_SetBillionLaughsAttackProtectionMaximumAmplification(
@@ -3561,12 +4205,52 @@ diff -ru misc/expat-2.2.10/xmlwf/xmlwf.c misc/build/expat-2.2.10/xmlwf/xmlwf.c
 +#ifdef XML_DTD
 +      XML_SetBillionLaughsAttackProtectionActivationThreshold(
 +          parser, attackThresholdBytes);
++#else
++      (void)attackThresholdBytes; // silence -Wunused-but-set-variable
 +#endif
-+    }
-+
+     }
+ 
      if (requireStandalone)
-       XML_SetNotStandaloneHandler(parser, notStandalone);
-     XML_SetParamEntityParsing(parser, paramEntityParsing);
+@@ -1053,7 +1155,7 @@
+                                    * sizeof(XML_Char));
+       if (! outName) {
+         tperror(T("Could not allocate memory"));
+-        exit(1);
++        exit(XMLWF_EXIT_INTERNAL_ERROR);
+       }
+       tcscpy(outName, outputDir);
+       tcscat(outName, delim);
+@@ -1061,7 +1163,14 @@
+       userData.fp = tfopen(outName, T("wb"));
+       if (! userData.fp) {
+         tperror(outName);
+-        exit(3);
++        exitCode = XMLWF_EXIT_OUTPUT_ERROR;
++        free(outName);
++        XML_ParserFree(parser);
++        if (continueOnError) {
++          continue;
++        } else {
++          break;
++        }
+       }
+       setvbuf(userData.fp, NULL, _IOFBF, 16384);
+ #ifdef XML_UNICODE
+@@ -1123,8 +1232,12 @@
+     }
+     XML_ParserFree(parser);
+     if (! result) {
+-      exit(2);
++      exitCode = XMLWF_EXIT_NOT_WELLFORMED;
++      cleanupUserData(&userData);
++      if (! continueOnError) {
++        break;
++      }
+     }
+   }
+-  return 0;
++  return exitCode;
+ }
 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