Roll libxml to 7279d236

This patch also adds --allow-empty to the git commit command for windows
in the roll.py script since there are usually no changes for windows.
libxslt's roll.py already does this for windows.

Change-Id: Ia75ef906c79abc907c396db5cc21f9e6ccb6f989
Bug: 934413
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2866731
Reviewed-by: Stephen Chenney <schenney@chromium.org>
Commit-Queue: Joey Arhar <jarhar@chromium.org>
Cr-Commit-Position: refs/heads/master@{#879926}
NOKEYCHECK=True
GitOrigin-RevId: b0a66f930c41a1c5e8307c0f7996adc02ccd3cbd
diff --git a/README.chromium b/README.chromium
index 367462e..25d4dd5 100644
--- a/README.chromium
+++ b/README.chromium
@@ -1,6 +1,6 @@
 Name: libxml
 URL: http://xmlsoft.org
-Version: f93ca3e140a371b26366f747a408588c631e0fd1
+Version: 7279d236364739a05657a8a614c15990eb08d0c6
 CPEPrefix: cpe:/a:xmlsoft:libxml2:2.9.9
 License: MIT
 License File: src/Copyright
diff --git a/chromium/roll.py b/chromium/roll.py
index 90928be..5c9b2fe 100755
--- a/chromium/roll.py
+++ b/chromium/roll.py
@@ -402,7 +402,7 @@
             shutil.move('../include/libxml/xmlversion.h',
                         '../../win32/include/libxml/xmlversion.h')
             git('add', '../../win32/include/libxml/xmlversion.h')
-            git('commit', '-m', 'Windows')
+            git('commit', '--allow-empty', '-m', 'Windows')
             git('clean', '-f')
     print('Now push to Mac and run steps there.')
 
diff --git a/mac/config.h b/mac/config.h
index 563202e..ac18495 100644
--- a/mac/config.h
+++ b/mac/config.h
@@ -91,9 +91,6 @@
 /* Define to 1 if you have the <math.h> header file. */
 #define HAVE_MATH_H 1
 
-/* Define to 1 if you have the <memory.h> header file. */
-#define HAVE_MEMORY_H 1
-
 /* Define to 1 if you have the `mmap' function. */
 #define HAVE_MMAP 1
 
@@ -165,6 +162,9 @@
 /* Define to 1 if you have the <stdint.h> header file. */
 #define HAVE_STDINT_H 1
 
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
 /* Define to 1 if you have the <stdlib.h> header file. */
 #define HAVE_STDLIB_H 1
 
@@ -263,7 +263,9 @@
 /* Type cast for the send() function 2nd arg */
 #define SEND_ARG2_CAST /**/
 
-/* Define to 1 if you have the ANSI C header files. */
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+   required in a freestanding environment). This macro is provided for
+   backward compatibility; new code need not use it. */
 #define STDC_HEADERS 1
 
 /* Support for IPv6 */
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index da0e5a9..a437717 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,6 +1,6 @@
 cmake_minimum_required(VERSION 3.15)
 
-project(libxml2 C)
+project(libxml2 VERSION 2.9.10 LANGUAGES C)
 
 include(CheckCSourceCompiles)
 include(CheckFunctionExists)
@@ -11,16 +11,6 @@
 include(CMakePackageConfigHelpers)
 include(GNUInstallDirs)
 
-set(LIBXML_MAJOR_VERSION 2)
-set(LIBXML_MINOR_VERSION 9)
-set(LIBXML_MICRO_VERSION 10)
-
-set(VERSION "${LIBXML_MAJOR_VERSION}.${LIBXML_MINOR_VERSION}.${LIBXML_MICRO_VERSION}")
-set(LIBXML_VERSION ${LIBXML_MAJOR_VERSION}0${LIBXML_MINOR_VERSION}0${LIBXML_MICRO_VERSION})
-set(LIBXML_VERSION_STRING "${LIBXML_VERSION}")
-set(LIBXML_VERSION_EXTRA "")
-set(LIBXML_VERSION_NUMBER ${LIBXML_VERSION})
-
 option(BUILD_SHARED_LIBS "Build shared libraries" ON)
 set(LIBXML2_WITH_AUTOMATA ON)
 option(LIBXML2_WITH_C14N "Add the Canonicalization support" ON)
@@ -105,6 +95,16 @@
 	endif()
 endforeach()
 
+set(LIBXML_MAJOR_VERSION ${PROJECT_VERSION_MAJOR})
+set(LIBXML_MINOR_VERSION ${PROJECT_VERSION_MINOR})
+set(LIBXML_MICRO_VERSION ${PROJECT_VERSION_PATCH})
+
+set(VERSION "${LIBXML_MAJOR_VERSION}.${LIBXML_MINOR_VERSION}.${LIBXML_MICRO_VERSION}")
+set(LIBXML_VERSION ${LIBXML_MAJOR_VERSION}0${LIBXML_MINOR_VERSION}0${LIBXML_MICRO_VERSION})
+set(LIBXML_VERSION_STRING "${LIBXML_VERSION}")
+set(LIBXML_VERSION_EXTRA "")
+set(LIBXML_VERSION_NUMBER ${LIBXML_VERSION})
+
 set(MODULE_EXTENSION "${CMAKE_SHARED_LIBRARY_SUFFIX}")
 
 set(PACKAGE "libxml2")
@@ -493,10 +493,10 @@
 	OUTPUT_NAME xml2
 	POSITION_INDEPENDENT_CODE ON
 	PREFIX lib
-	VERSION ${VERSION}
+	VERSION ${PROJECT_VERSION}
 )
 
-if(WIN32)
+if(MSVC)
 	if(BUILD_SHARED_LIBS)
 		set_target_properties(
 			LibXml2
@@ -637,7 +637,7 @@
 		IMPORT_PREFIX lib
 		OUTPUT_NAME xml2mod
 		PREFIX lib
-		VERSION ${VERSION}
+		VERSION ${PROJECT_VERSION}
 	)
 	install(
 		TARGETS	LibXml2Mod
@@ -659,30 +659,30 @@
 
 configure_package_config_file(
 	libxml2-config.cmake.cmake.in libxml2-config.cmake
-	INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libxml2-${VERSION}
+	INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libxml2-${PROJECT_VERSION}
 )
 
 install(
 	FILES ${CMAKE_CURRENT_BINARY_DIR}/libxml2-config.cmake
-	DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libxml2-${VERSION}
+	DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libxml2-${PROJECT_VERSION}
 	COMPONENT development
 )
 
 write_basic_package_version_file(
 	${CMAKE_CURRENT_BINARY_DIR}/libxml2-config-version.cmake
-	VERSION ${VERSION}
+	VERSION ${PROJECT_VERSION}
 	COMPATIBILITY ExactVersion
 )
 
 install(
 	FILES ${CMAKE_CURRENT_BINARY_DIR}/libxml2-config-version.cmake
-	DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libxml2-${VERSION}
+	DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libxml2-${PROJECT_VERSION}
 	COMPONENT development
 )
 
 install(
 	EXPORT LibXml2
-	DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libxml2-${VERSION}
+	DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libxml2-${PROJECT_VERSION}
 	NAMESPACE LibXml2::
 	FILE libxml2-export.cmake
 	COMPONENT development
diff --git a/src/HTMLparser.c b/src/HTMLparser.c
index 2877f4b..b56363a 100644
--- a/src/HTMLparser.c
+++ b/src/HTMLparser.c
@@ -457,7 +457,12 @@
             ctxt->input->encoding = guess;
             handler = xmlFindCharEncodingHandler((const char *) guess);
             if (handler != NULL) {
-                xmlSwitchToEncoding(ctxt, handler);
+                /*
+                 * Don't use UTF-8 encoder which isn't required and
+                 * can produce invalid UTF-8.
+                 */
+                if (!xmlStrEqual(BAD_CAST handler->name, BAD_CAST "UTF-8"))
+                    xmlSwitchToEncoding(ctxt, handler);
             } else {
                 htmlParseErr(ctxt, XML_ERR_INVALID_ENCODING,
                              "Unsupported encoding %s", guess, NULL);
@@ -570,7 +575,16 @@
 		     BAD_CAST buffer, NULL);
     }
 
-    ctxt->charset = XML_CHAR_ENCODING_8859_1;
+    /*
+     * Don't switch encodings twice. Note that if there's an encoder, we
+     * shouldn't receive invalid UTF-8 anyway.
+     *
+     * Note that if ctxt->input->buf == NULL, switching encodings is
+     * impossible, see Gitlab issue #34.
+     */
+    if ((ctxt->input->buf != NULL) &&
+        (ctxt->input->buf->encoder == NULL))
+        xmlSwitchEncoding(ctxt, XML_CHAR_ENCODING_8859_1);
     *len = 1;
     return((int) *ctxt->input->cur);
 }
@@ -1058,102 +1072,266 @@
 }
 };
 
+typedef struct {
+    const char *oldTag;
+    const char *newTag;
+} htmlStartCloseEntry;
+
 /*
  * start tags that imply the end of current element
  */
-static const char * const htmlStartClose[] = {
-"form",		"form", "p", "hr", "h1", "h2", "h3", "h4", "h5", "h6",
-		"dl", "ul", "ol", "menu", "dir", "address", "pre",
-		"listing", "xmp", "head", NULL,
-"head",		"p", NULL,
-"title",	"p", NULL,
-"body",		"head", "style", "link", "title", "p", NULL,
-"frameset",	"head", "style", "link", "title", "p", NULL,
-"li",		"p", "h1", "h2", "h3", "h4", "h5", "h6", "dl", "address",
-		"pre", "listing", "xmp", "head", "li", NULL,
-"hr",		"p", "head", NULL,
-"h1",		"p", "head", NULL,
-"h2",		"p", "head", NULL,
-"h3",		"p", "head", NULL,
-"h4",		"p", "head", NULL,
-"h5",		"p", "head", NULL,
-"h6",		"p", "head", NULL,
-"dir",		"p", "head", NULL,
-"address",	"p", "head", "ul", NULL,
-"pre",		"p", "head", "ul", NULL,
-"listing",	"p", "head", NULL,
-"xmp",		"p", "head", NULL,
-"blockquote",	"p", "head", NULL,
-"dl",		"p", "dt", "menu", "dir", "address", "pre", "listing",
-		"xmp", "head", NULL,
-"dt",		"p", "menu", "dir", "address", "pre", "listing", "xmp",
-                "head", "dd", NULL,
-"dd",		"p", "menu", "dir", "address", "pre", "listing", "xmp",
-                "head", "dt", NULL,
-"ul",		"p", "head", "ol", "menu", "dir", "address", "pre",
-		"listing", "xmp", NULL,
-"ol",		"p", "head", "ul", NULL,
-"menu",		"p", "head", "ul", NULL,
-"p",		"p", "head", "h1", "h2", "h3", "h4", "h5", "h6", FONTSTYLE, NULL,
-"div",		"p", "head", NULL,
-"noscript",	"script", NULL,
-"center",	"font", "b", "i", "p", "head", NULL,
-"a",		"a", "head", NULL,
-"caption",	"p", NULL,
-"colgroup",	"caption", "colgroup", "col", "p", NULL,
-"col",		"caption", "col", "p", NULL,
-"table",	"p", "head", "h1", "h2", "h3", "h4", "h5", "h6", "pre",
-		"listing", "xmp", "a", NULL,
-"th",		"th", "td", "p", "span", "font", "a", "b", "i", "u", NULL,
-"td",		"th", "td", "p", "span", "font", "a", "b", "i", "u", NULL,
-"tr",		"th", "td", "tr", "caption", "col", "colgroup", "p", NULL,
-"thead",	"caption", "col", "colgroup", NULL,
-"tfoot",	"th", "td", "tr", "caption", "col", "colgroup", "thead",
-		"tbody", "p", NULL,
-"tbody",	"th", "td", "tr", "caption", "col", "colgroup", "thead",
-		"tfoot", "tbody", "p", NULL,
-"optgroup",	"option", NULL,
-"option",	"option", NULL,
-"fieldset",	"legend", "p", "head", "h1", "h2", "h3", "h4", "h5", "h6",
-		"pre", "listing", "xmp", "a", NULL,
-/* most tags in in FONTSTYLE, PHRASE and SPECIAL should close <head> */
-"tt",		"head", NULL,
-"i",		"head", NULL,
-"b",		"head", NULL,
-"u",		"head", NULL,
-"s",		"head", NULL,
-"strike",	"head", NULL,
-"big",		"head", NULL,
-"small",	"head", NULL,
-
-"em",		"head", NULL,
-"strong",	"head", NULL,
-"dfn",		"head", NULL,
-"code",		"head", NULL,
-"samp",		"head", NULL,
-"kbd",		"head", NULL,
-"var",		"head", NULL,
-"cite",		"head", NULL,
-"abbr",		"head", NULL,
-"acronym",	"head", NULL,
-
-/* "a" */
-"img",		"head", NULL,
-/* "applet" */
-/* "embed" */
-/* "object" */
-"font",		"head", NULL,
-/* "basefont" */
-"br",		"head", NULL,
-/* "script" */
-"map",		"head", NULL,
-"q",		"head", NULL,
-"sub",		"head", NULL,
-"sup",		"head", NULL,
-"span",		"head", NULL,
-"bdo",		"head", NULL,
-"iframe",	"head", NULL,
-NULL
+static const htmlStartCloseEntry htmlStartClose[] = {
+    { "a", "a" },
+    { "a", "fieldset" },
+    { "a", "table" },
+    { "a", "td" },
+    { "a", "th" },
+    { "address", "dd" },
+    { "address", "dl" },
+    { "address", "dt" },
+    { "address", "form" },
+    { "address", "li" },
+    { "address", "ul" },
+    { "b", "center" },
+    { "b", "p" },
+    { "b", "td" },
+    { "b", "th" },
+    { "big", "p" },
+    { "caption", "col" },
+    { "caption", "colgroup" },
+    { "caption", "tbody" },
+    { "caption", "tfoot" },
+    { "caption", "thead" },
+    { "caption", "tr" },
+    { "col", "col" },
+    { "col", "colgroup" },
+    { "col", "tbody" },
+    { "col", "tfoot" },
+    { "col", "thead" },
+    { "col", "tr" },
+    { "colgroup", "colgroup" },
+    { "colgroup", "tbody" },
+    { "colgroup", "tfoot" },
+    { "colgroup", "thead" },
+    { "colgroup", "tr" },
+    { "dd", "dt" },
+    { "dir", "dd" },
+    { "dir", "dl" },
+    { "dir", "dt" },
+    { "dir", "form" },
+    { "dir", "ul" },
+    { "dl", "form" },
+    { "dl", "li" },
+    { "dt", "dd" },
+    { "dt", "dl" },
+    { "font", "center" },
+    { "font", "td" },
+    { "font", "th" },
+    { "form", "form" },
+    { "h1", "fieldset" },
+    { "h1", "form" },
+    { "h1", "li" },
+    { "h1", "p" },
+    { "h1", "table" },
+    { "h2", "fieldset" },
+    { "h2", "form" },
+    { "h2", "li" },
+    { "h2", "p" },
+    { "h2", "table" },
+    { "h3", "fieldset" },
+    { "h3", "form" },
+    { "h3", "li" },
+    { "h3", "p" },
+    { "h3", "table" },
+    { "h4", "fieldset" },
+    { "h4", "form" },
+    { "h4", "li" },
+    { "h4", "p" },
+    { "h4", "table" },
+    { "h5", "fieldset" },
+    { "h5", "form" },
+    { "h5", "li" },
+    { "h5", "p" },
+    { "h5", "table" },
+    { "h6", "fieldset" },
+    { "h6", "form" },
+    { "h6", "li" },
+    { "h6", "p" },
+    { "h6", "table" },
+    { "head", "a" },
+    { "head", "abbr" },
+    { "head", "acronym" },
+    { "head", "address" },
+    { "head", "b" },
+    { "head", "bdo" },
+    { "head", "big" },
+    { "head", "blockquote" },
+    { "head", "body" },
+    { "head", "br" },
+    { "head", "center" },
+    { "head", "cite" },
+    { "head", "code" },
+    { "head", "dd" },
+    { "head", "dfn" },
+    { "head", "dir" },
+    { "head", "div" },
+    { "head", "dl" },
+    { "head", "dt" },
+    { "head", "em" },
+    { "head", "fieldset" },
+    { "head", "font" },
+    { "head", "form" },
+    { "head", "frameset" },
+    { "head", "h1" },
+    { "head", "h2" },
+    { "head", "h3" },
+    { "head", "h4" },
+    { "head", "h5" },
+    { "head", "h6" },
+    { "head", "hr" },
+    { "head", "i" },
+    { "head", "iframe" },
+    { "head", "img" },
+    { "head", "kbd" },
+    { "head", "li" },
+    { "head", "listing" },
+    { "head", "map" },
+    { "head", "menu" },
+    { "head", "ol" },
+    { "head", "p" },
+    { "head", "pre" },
+    { "head", "q" },
+    { "head", "s" },
+    { "head", "samp" },
+    { "head", "small" },
+    { "head", "span" },
+    { "head", "strike" },
+    { "head", "strong" },
+    { "head", "sub" },
+    { "head", "sup" },
+    { "head", "table" },
+    { "head", "tt" },
+    { "head", "u" },
+    { "head", "ul" },
+    { "head", "var" },
+    { "head", "xmp" },
+    { "hr", "form" },
+    { "i", "center" },
+    { "i", "p" },
+    { "i", "td" },
+    { "i", "th" },
+    { "legend", "fieldset" },
+    { "li", "li" },
+    { "link", "body" },
+    { "link", "frameset" },
+    { "listing", "dd" },
+    { "listing", "dl" },
+    { "listing", "dt" },
+    { "listing", "fieldset" },
+    { "listing", "form" },
+    { "listing", "li" },
+    { "listing", "table" },
+    { "listing", "ul" },
+    { "menu", "dd" },
+    { "menu", "dl" },
+    { "menu", "dt" },
+    { "menu", "form" },
+    { "menu", "ul" },
+    { "ol", "form" },
+    { "ol", "ul" },
+    { "option", "optgroup" },
+    { "option", "option" },
+    { "p", "address" },
+    { "p", "blockquote" },
+    { "p", "body" },
+    { "p", "caption" },
+    { "p", "center" },
+    { "p", "col" },
+    { "p", "colgroup" },
+    { "p", "dd" },
+    { "p", "dir" },
+    { "p", "div" },
+    { "p", "dl" },
+    { "p", "dt" },
+    { "p", "fieldset" },
+    { "p", "form" },
+    { "p", "frameset" },
+    { "p", "h1" },
+    { "p", "h2" },
+    { "p", "h3" },
+    { "p", "h4" },
+    { "p", "h5" },
+    { "p", "h6" },
+    { "p", "head" },
+    { "p", "hr" },
+    { "p", "li" },
+    { "p", "listing" },
+    { "p", "menu" },
+    { "p", "ol" },
+    { "p", "p" },
+    { "p", "pre" },
+    { "p", "table" },
+    { "p", "tbody" },
+    { "p", "td" },
+    { "p", "tfoot" },
+    { "p", "th" },
+    { "p", "title" },
+    { "p", "tr" },
+    { "p", "ul" },
+    { "p", "xmp" },
+    { "pre", "dd" },
+    { "pre", "dl" },
+    { "pre", "dt" },
+    { "pre", "fieldset" },
+    { "pre", "form" },
+    { "pre", "li" },
+    { "pre", "table" },
+    { "pre", "ul" },
+    { "s", "p" },
+    { "script", "noscript" },
+    { "small", "p" },
+    { "span", "td" },
+    { "span", "th" },
+    { "strike", "p" },
+    { "style", "body" },
+    { "style", "frameset" },
+    { "tbody", "tbody" },
+    { "tbody", "tfoot" },
+    { "td", "tbody" },
+    { "td", "td" },
+    { "td", "tfoot" },
+    { "td", "th" },
+    { "td", "tr" },
+    { "tfoot", "tbody" },
+    { "th", "tbody" },
+    { "th", "td" },
+    { "th", "tfoot" },
+    { "th", "th" },
+    { "th", "tr" },
+    { "thead", "tbody" },
+    { "thead", "tfoot" },
+    { "title", "body" },
+    { "title", "frameset" },
+    { "tr", "tbody" },
+    { "tr", "tfoot" },
+    { "tr", "tr" },
+    { "tt", "p" },
+    { "u", "p" },
+    { "u", "td" },
+    { "u", "th" },
+    { "ul", "address" },
+    { "ul", "form" },
+    { "ul", "menu" },
+    { "ul", "ol" },
+    { "ul", "pre" },
+    { "xmp", "dd" },
+    { "xmp", "dl" },
+    { "xmp", "dt" },
+    { "xmp", "fieldset" },
+    { "xmp", "form" },
+    { "xmp", "li" },
+    { "xmp", "table" },
+    { "xmp", "ul" }
 };
 
 /*
@@ -1223,9 +1401,6 @@
     {NULL,    100} /* Default priority */
 };
 
-static const char** htmlStartCloseIndex[100];
-static int htmlStartCloseIndexinitialized = 0;
-
 /************************************************************************
  *									*
  *	functions to handle HTML specific data			*
@@ -1235,24 +1410,18 @@
 /**
  * htmlInitAutoClose:
  *
- * Initialize the htmlStartCloseIndex for fast lookup of closing tags names.
- * This is not reentrant. Call xmlInitParser() once before processing in
- * case of use in multithreaded programs.
+ * This is a no-op now.
  */
 void
 htmlInitAutoClose(void) {
-    int indx, i = 0;
+}
 
-    if (htmlStartCloseIndexinitialized) return;
+static int
+htmlCompareTags(const void *key, const void *member) {
+    const xmlChar *tag = (const xmlChar *) key;
+    const htmlElemDesc *desc = (const htmlElemDesc *) member;
 
-    for (indx = 0;indx < 100;indx ++) htmlStartCloseIndex[indx] = NULL;
-    indx = 0;
-    while ((htmlStartClose[i] != NULL) && (indx < 100 - 1)) {
-        htmlStartCloseIndex[indx++] = (const char**) &htmlStartClose[i];
-	while (htmlStartClose[i] != NULL) i++;
-	i++;
-    }
-    htmlStartCloseIndexinitialized = 1;
+    return(xmlStrcasecmp(tag, BAD_CAST desc->name));
 }
 
 /**
@@ -1265,14 +1434,12 @@
  */
 const htmlElemDesc *
 htmlTagLookup(const xmlChar *tag) {
-    unsigned int i;
+    if (tag == NULL)
+        return(NULL);
 
-    for (i = 0; i < (sizeof(html40ElementTable) /
-                     sizeof(html40ElementTable[0]));i++) {
-        if (!xmlStrcasecmp(tag, BAD_CAST html40ElementTable[i].name))
-	    return((htmlElemDescPtr) &html40ElementTable[i]);
-    }
-    return(NULL);
+    return((const htmlElemDesc *) bsearch(tag, html40ElementTable,
+                sizeof(html40ElementTable) / sizeof(htmlElemDesc),
+                sizeof(htmlElemDesc), htmlCompareTags));
 }
 
 /**
@@ -1293,6 +1460,19 @@
 }
 
 
+static int
+htmlCompareStartClose(const void *vkey, const void *member) {
+    const htmlStartCloseEntry *key = (const htmlStartCloseEntry *) vkey;
+    const htmlStartCloseEntry *entry = (const htmlStartCloseEntry *) member;
+    int ret;
+
+    ret = strcmp(key->oldTag, entry->oldTag);
+    if (ret == 0)
+        ret = strcmp(key->newTag, entry->newTag);
+
+    return(ret);
+}
+
 /**
  * htmlCheckAutoClose:
  * @newtag:  The new tag name
@@ -1300,37 +1480,21 @@
  *
  * Checks whether the new tag is one of the registered valid tags for
  * closing old.
- * Initialize the htmlStartCloseIndex for fast lookup of closing tags names.
  *
  * Returns 0 if no, 1 if yes.
  */
 static int
 htmlCheckAutoClose(const xmlChar * newtag, const xmlChar * oldtag)
 {
-    int i, indx;
-    const char **closed = NULL;
+    htmlStartCloseEntry key;
+    void *res;
 
-    if (htmlStartCloseIndexinitialized == 0)
-        htmlInitAutoClose();
-
-    /* inefficient, but not a big deal */
-    for (indx = 0; indx < 100; indx++) {
-        closed = htmlStartCloseIndex[indx];
-        if (closed == NULL)
-            return (0);
-        if (xmlStrEqual(BAD_CAST * closed, newtag))
-            break;
-    }
-
-    i = closed - htmlStartClose;
-    i++;
-    while (htmlStartClose[i] != NULL) {
-        if (xmlStrEqual(BAD_CAST htmlStartClose[i], oldtag)) {
-            return (1);
-        }
-        i++;
-    }
-    return (0);
+    key.oldTag = (const char *) oldtag;
+    key.newTag = (const char *) newtag;
+    res = bsearch(&key, htmlStartClose,
+            sizeof(htmlStartClose) / sizeof(htmlStartCloseEntry),
+            sizeof(htmlStartCloseEntry), htmlCompareStartClose);
+    return(res != NULL);
 }
 
 /**
@@ -4043,12 +4207,10 @@
      * With the exception that the autoclose may have popped stuff out
      * of the stack.
      */
-    if (!xmlStrEqual(name, ctxt->name)) {
-        if ((ctxt->name != NULL) && (!xmlStrEqual(ctxt->name, name))) {
-            htmlParseErr(ctxt, XML_ERR_TAG_NAME_MISMATCH,
-	                 "Opening and ending tag mismatch: %s and %s\n",
-			 name, ctxt->name);
-        }
+    if ((ctxt->name != NULL) && (!xmlStrEqual(ctxt->name, name))) {
+        htmlParseErr(ctxt, XML_ERR_TAG_NAME_MISMATCH,
+                     "Opening and ending tag mismatch: %s and %s\n",
+                     name, ctxt->name);
     }
 
     /*
@@ -5872,7 +6034,8 @@
 			xmlGenericError(xmlGenericErrorContext,
 				"HPP: Parsing char data\n");
 #endif
-                        while ((cur != '<') && (in->cur < in->end)) {
+                        while ((ctxt->instate != XML_PARSER_EOF) &&
+                               (cur != '<') && (in->cur < in->end)) {
                             if (cur == '&') {
 			        htmlParseReference(ctxt);
                             } else {
diff --git a/src/HTMLtree.c b/src/HTMLtree.c
index 8d0c779..24434d4 100644
--- a/src/HTMLtree.c
+++ b/src/HTMLtree.c
@@ -518,7 +518,7 @@
     buf = xmlOutputBufferCreateFile(out, handler);
     if (buf == NULL) return(0);
 
-    htmlNodeDumpFormatOutput(buf, doc, cur, encoding, format);
+    htmlNodeDumpFormatOutput(buf, doc, cur, NULL, format);
 
     ret = xmlOutputBufferClose(buf);
     return(ret);
@@ -670,13 +670,11 @@
  * @buf:  the HTML buffer output
  * @doc:  the document
  * @cur:  the attribute pointer
- * @encoding:  the encoding string
  *
  * Dump an HTML attribute
  */
 static void
-htmlAttrDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlAttrPtr cur,
-	           const char *encoding ATTRIBUTE_UNUSED) {
+htmlAttrDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlAttrPtr cur) {
     xmlChar *value;
 
     /*
@@ -737,14 +735,15 @@
  * @buf:  the HTML buffer output
  * @doc:  the document
  * @cur:  the current node
- * @encoding:  the encoding string
+ * @encoding:  the encoding string (unused)
  * @format:  should formatting spaces been added
  *
  * Dump an HTML node, recursive behaviour,children are printed too.
  */
 void
 htmlNodeDumpFormatOutput(xmlOutputBufferPtr buf, xmlDocPtr doc,
-	                 xmlNodePtr cur, const char *encoding, int format) {
+	                 xmlNodePtr cur, const char *encoding ATTRIBUTE_UNUSED,
+                         int format) {
     xmlNodePtr root;
     xmlAttrPtr attr;
     const htmlElemDesc * info;
@@ -788,7 +787,7 @@
                 xmlNsListDumpOutput(buf, cur->nsDef);
             attr = cur->properties;
             while (attr != NULL) {
-                htmlAttrDumpOutput(buf, doc, attr, encoding);
+                htmlAttrDumpOutput(buf, doc, attr);
                 attr = attr->next;
             }
 
@@ -835,7 +834,7 @@
             break;
 
         case XML_ATTRIBUTE_NODE:
-            htmlAttrDumpOutput(buf, doc, (xmlAttrPtr) cur, encoding);
+            htmlAttrDumpOutput(buf, doc, (xmlAttrPtr) cur);
             break;
 
         case HTML_TEXT_NODE:
@@ -955,44 +954,45 @@
  * @buf:  the HTML buffer output
  * @doc:  the document
  * @cur:  the current node
- * @encoding:  the encoding string
+ * @encoding:  the encoding string (unused)
  *
  * Dump an HTML node, recursive behaviour,children are printed too,
  * and formatting returns/spaces are added.
  */
 void
 htmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc,
-	           xmlNodePtr cur, const char *encoding) {
-    htmlNodeDumpFormatOutput(buf, doc, cur, encoding, 1);
+	           xmlNodePtr cur, const char *encoding ATTRIBUTE_UNUSED) {
+    htmlNodeDumpFormatOutput(buf, doc, cur, NULL, 1);
 }
 
 /**
  * htmlDocContentDumpFormatOutput:
  * @buf:  the HTML buffer output
  * @cur:  the document
- * @encoding:  the encoding string
+ * @encoding:  the encoding string (unused)
  * @format:  should formatting spaces been added
  *
  * Dump an HTML document.
  */
 void
 htmlDocContentDumpFormatOutput(xmlOutputBufferPtr buf, xmlDocPtr cur,
-	                       const char *encoding, int format) {
-    htmlNodeDumpFormatOutput(buf, cur, (xmlNodePtr) cur, encoding, format);
+	                       const char *encoding ATTRIBUTE_UNUSED,
+                               int format) {
+    htmlNodeDumpFormatOutput(buf, cur, (xmlNodePtr) cur, NULL, format);
 }
 
 /**
  * htmlDocContentDumpOutput:
  * @buf:  the HTML buffer output
  * @cur:  the document
- * @encoding:  the encoding string
+ * @encoding:  the encoding string (unused)
  *
  * Dump an HTML document. Formatting return/spaces are added.
  */
 void
 htmlDocContentDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr cur,
-	                 const char *encoding) {
-    htmlNodeDumpFormatOutput(buf, cur, (xmlNodePtr) cur, encoding, 1);
+	                 const char *encoding ATTRIBUTE_UNUSED) {
+    htmlNodeDumpFormatOutput(buf, cur, (xmlNodePtr) cur, NULL, 1);
 }
 
 /************************************************************************
diff --git a/src/SAX2.c b/src/SAX2.c
index 4450a3f..99019a9 100644
--- a/src/SAX2.c
+++ b/src/SAX2.c
@@ -2493,20 +2493,21 @@
 }
 
 /**
- * xmlSAX2Characters:
+ * xmlSAX2Text:
  * @ctx: the user data (XML parser context)
  * @ch:  a xmlChar string
  * @len: the number of xmlChar
+ * @type: text or cdata
  *
- * receiving some chars from the parser.
+ * Append characters.
  */
-void
-xmlSAX2Characters(void *ctx, const xmlChar *ch, int len)
+static void
+xmlSAX2Text(xmlParserCtxtPtr ctxt, const xmlChar *ch, int len,
+            xmlElementType type)
 {
-    xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
     xmlNodePtr lastChild;
 
-    if (ctx == NULL) return;
+    if (ctxt == NULL) return;
 #ifdef DEBUG_SAX
     xmlGenericError(xmlGenericErrorContext,
 	    "SAX.xmlSAX2Characters(%.30s, %d)\n", ch, len);
@@ -2535,7 +2536,10 @@
      * elements. Use an attribute in the structure !!!
      */
     if (lastChild == NULL) {
-        lastChild = xmlSAX2TextNode(ctxt, ch, len);
+        if (type == XML_TEXT_NODE)
+            lastChild = xmlSAX2TextNode(ctxt, ch, len);
+        else
+            lastChild = xmlNewCDataBlock(ctxt->myDoc, ch, len);
 	if (lastChild != NULL) {
 	    ctxt->node->children = lastChild;
 	    ctxt->node->last = lastChild;
@@ -2549,8 +2553,9 @@
 	}
     } else {
 	int coalesceText = (lastChild != NULL) &&
-	    (lastChild->type == XML_TEXT_NODE) &&
-	    (lastChild->name == xmlStringText);
+	    (lastChild->type == type) &&
+	    ((type != XML_TEXT_NODE) ||
+             (lastChild->name == xmlStringText));
 	if ((coalesceText) && (ctxt->nodemem != 0)) {
 	    /*
 	     * The whole point of maintaining nodelen and nodemem,
@@ -2607,7 +2612,10 @@
 	    }
 	} else {
 	    /* Mixed content, first time */
-	    lastChild = xmlSAX2TextNode(ctxt, ch, len);
+            if (type == XML_TEXT_NODE)
+                lastChild = xmlSAX2TextNode(ctxt, ch, len);
+            else
+                lastChild = xmlNewCDataBlock(ctxt->myDoc, ch, len);
 	    if (lastChild != NULL) {
 		xmlAddChild(ctxt->node, lastChild);
 		if (ctxt->node->children != NULL) {
@@ -2620,6 +2628,20 @@
 }
 
 /**
+ * xmlSAX2Characters:
+ * @ctx: the user data (XML parser context)
+ * @ch:  a xmlChar string
+ * @len: the number of xmlChar
+ *
+ * receiving some chars from the parser.
+ */
+void
+xmlSAX2Characters(void *ctx, const xmlChar *ch, int len)
+{
+    xmlSAX2Text((xmlParserCtxtPtr) ctx, ch, len, XML_TEXT_NODE);
+}
+
+/**
  * xmlSAX2IgnorableWhitespace:
  * @ctx: the user data (XML parser context)
  * @ch:  a xmlChar string
@@ -2775,27 +2797,7 @@
 void
 xmlSAX2CDataBlock(void *ctx, const xmlChar *value, int len)
 {
-    xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
-    xmlNodePtr ret, lastChild;
-
-    if (ctx == NULL) return;
-#ifdef DEBUG_SAX
-    xmlGenericError(xmlGenericErrorContext,
-	    "SAX.pcdata(%.10s, %d)\n", value, len);
-#endif
-    lastChild = xmlGetLastChild(ctxt->node);
-#ifdef DEBUG_SAX_TREE
-    xmlGenericError(xmlGenericErrorContext,
-	    "add chars to %s \n", ctxt->node->name);
-#endif
-    if ((lastChild != NULL) &&
-        (lastChild->type == XML_CDATA_SECTION_NODE)) {
-	xmlTextConcat(lastChild, value, len);
-    } else {
-	ret = xmlNewCDataBlock(ctxt->myDoc, value, len);
-	if (xmlAddChild(ctxt->node, ret) == NULL)
-		xmlFreeNode(ret);
-    }
+    xmlSAX2Text((xmlParserCtxtPtr) ctx, value, len, XML_CDATA_SECTION_NODE);
 }
 
 static int xmlSAX2DefaultVersionValue = 2;
diff --git a/src/aclocal.m4 b/src/aclocal.m4
index 1fc8000..a671949 100644
--- a/src/aclocal.m4
+++ b/src/aclocal.m4
@@ -1,4 +1,4 @@
-# generated automatically by aclocal 1.16.2 -*- Autoconf -*-
+# generated automatically by aclocal 1.16.3 -*- Autoconf -*-
 
 # Copyright (C) 1996-2020 Free Software Foundation, Inc.
 
@@ -14,8 +14,8 @@
 m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
 m4_ifndef([AC_AUTOCONF_VERSION],
   [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
-m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],,
-[m4_warning([this file was generated for autoconf 2.69.
+m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.71],,
+[m4_warning([this file was generated for autoconf 2.71.
 You have another version of autoconf.  It may work, but is not guaranteed to.
 If you have problems, you may need to regenerate the build system entirely.
 To do so, use the procedure documented by the package, typically 'autoreconf'.])])
@@ -311,7 +311,7 @@
 [am__api_version='1.16'
 dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
 dnl require some minimum version.  Point them to the right macro.
-m4_if([$1], [1.16.2], [],
+m4_if([$1], [1.16.3], [],
       [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
 ])
 
@@ -327,7 +327,7 @@
 # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
 # This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
 AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
-[AM_AUTOMAKE_VERSION([1.16.2])dnl
+[AM_AUTOMAKE_VERSION([1.16.3])dnl
 m4_ifndef([AC_AUTOCONF_VERSION],
   [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
 _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
@@ -1015,12 +1015,7 @@
 [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
 AC_REQUIRE_AUX_FILE([missing])dnl
 if test x"${MISSING+set}" != xset; then
-  case $am_aux_dir in
-  *\ * | *\	*)
-    MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
-  *)
-    MISSING="\${SHELL} $am_aux_dir/missing" ;;
-  esac
+  MISSING="\${SHELL} '$am_aux_dir/missing'"
 fi
 # Use eval to expand $SHELL
 if eval "$MISSING --is-lightweight"; then
diff --git a/src/config.h.in b/src/config.h.in
index 5f87036..a55d4e1 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -90,9 +90,6 @@
 /* Define to 1 if you have the <math.h> header file. */
 #undef HAVE_MATH_H
 
-/* Define to 1 if you have the <memory.h> header file. */
-#undef HAVE_MEMORY_H
-
 /* Define to 1 if you have the `mmap' function. */
 #undef HAVE_MMAP
 
@@ -164,6 +161,9 @@
 /* Define to 1 if you have the <stdint.h> header file. */
 #undef HAVE_STDINT_H
 
+/* Define to 1 if you have the <stdio.h> header file. */
+#undef HAVE_STDIO_H
+
 /* Define to 1 if you have the <stdlib.h> header file. */
 #undef HAVE_STDLIB_H
 
@@ -262,7 +262,9 @@
 /* Type cast for the send() function 2nd arg */
 #undef SEND_ARG2_CAST
 
-/* Define to 1 if you have the ANSI C header files. */
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+   required in a freestanding environment). This macro is provided for
+   backward compatibility; new code need not use it. */
 #undef STDC_HEADERS
 
 /* Support for IPv6 */
diff --git a/src/encoding.c b/src/encoding.c
index d67c16d..cdff6ae 100644
--- a/src/encoding.c
+++ b/src/encoding.c
@@ -373,6 +373,11 @@
     if (len < 0)
 	return(-1);
 
+    /*
+     * FIXME: Conversion functions must assure valid UTF-8, so we have
+     * to check for UTF-8 validity. Preferably, this converter shouldn't
+     * be used at all.
+     */
     memcpy(out, inb, len);
 
     *outlen = len;
diff --git a/src/entities.c b/src/entities.c
index ea54cc3..8307471 100644
--- a/src/entities.c
+++ b/src/entities.c
@@ -211,7 +211,7 @@
 	  const xmlChar *content) {
     xmlDictPtr dict = NULL;
     xmlEntitiesTablePtr table = NULL;
-    xmlEntityPtr ret;
+    xmlEntityPtr ret, predef;
 
     if (name == NULL)
 	return(NULL);
@@ -224,6 +224,44 @@
         case XML_INTERNAL_GENERAL_ENTITY:
         case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
         case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
+            predef = xmlGetPredefinedEntity(name);
+            if (predef != NULL) {
+                int valid = 0;
+
+                /* 4.6 Predefined Entities */
+                if ((type == XML_INTERNAL_GENERAL_ENTITY) &&
+                    (content != NULL)) {
+                    int c = predef->content[0];
+
+                    if (((content[0] == c) && (content[1] == 0)) &&
+                        ((c == '>') || (c == '\'') || (c == '"'))) {
+                        valid = 1;
+                    } else if ((content[0] == '&') && (content[1] == '#')) {
+                        if (content[2] == 'x') {
+                            xmlChar *hex = BAD_CAST "0123456789ABCDEF";
+                            xmlChar ref[] = "00;";
+
+                            ref[0] = hex[c / 16 % 16];
+                            ref[1] = hex[c % 16];
+                            if (xmlStrcasecmp(&content[3], ref) == 0)
+                                valid = 1;
+                        } else {
+                            xmlChar ref[] = "00;";
+
+                            ref[0] = '0' + c / 10 % 10;
+                            ref[1] = '0' + c % 10;
+                            if (xmlStrEqual(&content[2], ref))
+                                valid = 1;
+                        }
+                    }
+                }
+                if (!valid) {
+                    xmlEntitiesErr(XML_ERR_ENTITY_PROCESSING,
+                            "xmlAddEntity: invalid redeclaration of predefined"
+                            " entity");
+                    return(NULL);
+                }
+            }
 	    if (dtd->entities == NULL)
 		dtd->entities = xmlHashCreateDict(0, dict);
 	    table = dtd->entities;
@@ -667,11 +705,25 @@
 	    } else {
 		/*
 		 * We assume we have UTF-8 input.
+		 * It must match either:
+		 *   110xxxxx 10xxxxxx
+		 *   1110xxxx 10xxxxxx 10xxxxxx
+		 *   11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+		 * That is:
+		 *   cur[0] is 11xxxxxx
+		 *   cur[1] is 10xxxxxx
+		 *   cur[2] is 10xxxxxx if cur[0] is 111xxxxx
+		 *   cur[3] is 10xxxxxx if cur[0] is 1111xxxx
+		 *   cur[0] is not 11111xxx
 		 */
 		char buf[11], *ptr;
 		int val = 0, l = 1;
 
-		if (*cur < 0xC0) {
+		if (((cur[0] & 0xC0) != 0xC0) ||
+		    ((cur[1] & 0xC0) != 0x80) ||
+		    (((cur[0] & 0xE0) == 0xE0) && ((cur[2] & 0xC0) != 0x80)) ||
+		    (((cur[0] & 0xF0) == 0xF0) && ((cur[3] & 0xC0) != 0x80)) ||
+		    (((cur[0] & 0xF8) == 0xF8))) {
 		    xmlEntitiesErr(XML_CHECK_NOT_UTF8,
 			    "xmlEncodeEntities: input not UTF-8");
 		    if (doc != NULL)
diff --git a/src/fuzz/Makefile.am b/src/fuzz/Makefile.am
index 2bbdbb1..7d38347 100644
--- a/src/fuzz/Makefile.am
+++ b/src/fuzz/Makefile.am
@@ -74,7 +74,7 @@
 	./html$(EXEEXT) \
 	    -dict=html.dict \
 	    -max_len=1000000 \
-	    -timeout=20 \
+	    -timeout=10 \
 	    corpus/html seed/html
 
 # Regexp fuzzer
@@ -99,7 +99,7 @@
 	@mkdir -p corpus/uri
 	./uri$(EXEEXT) \
 	    -max_len=10000 \
-	    -timeout=5 \
+	    -timeout=2 \
 	    corpus/uri $(srcdir)/seed/uri
 
 # XML Schema fuzzer
diff --git a/src/fuzz/Makefile.in b/src/fuzz/Makefile.in
index a688e96..e9de41c 100644
--- a/src/fuzz/Makefile.in
+++ b/src/fuzz/Makefile.in
@@ -1,4 +1,4 @@
-# Makefile.in generated by automake 1.16.2 from Makefile.am.
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
 # @configure_input@
 
 # Copyright (C) 1994-2020 Free Software Foundation, Inc.
@@ -480,6 +480,7 @@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
 pythondir = @pythondir@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -919,7 +920,7 @@
 	./html$(EXEEXT) \
 	    -dict=html.dict \
 	    -max_len=1000000 \
-	    -timeout=20 \
+	    -timeout=10 \
 	    corpus/html seed/html
 
 fuzz-regexp: regexp$(EXEEXT)
@@ -934,7 +935,7 @@
 	@mkdir -p corpus/uri
 	./uri$(EXEEXT) \
 	    -max_len=10000 \
-	    -timeout=5 \
+	    -timeout=2 \
 	    corpus/uri $(srcdir)/seed/uri
 
 # XML Schema fuzzer
diff --git a/src/fuzz/genSeed.c b/src/fuzz/genSeed.c
index 68fb87a..2f03802 100644
--- a/src/fuzz/genSeed.c
+++ b/src/fuzz/genSeed.c
@@ -102,6 +102,7 @@
     globalData.oldLoader = NULL;
 }
 
+#ifdef HAVE_XML_FUZZER
 static int
 processXml(const char *docFile, FILE *out) {
     int opts = XML_PARSE_NOENT | XML_PARSE_DTDLOAD;
@@ -119,7 +120,9 @@
 
     return(0);
 }
+#endif
 
+#ifdef HAVE_HTML_FUZZER
 static int
 processHtml(const char *docFile, FILE *out) {
     char buf[SEED_BUF_SIZE];
@@ -144,7 +147,9 @@
 
     return(0);
 }
+#endif
 
+#ifdef HAVE_SCHEMA_FUZZER
 static int
 processSchema(const char *docFile, FILE *out) {
     xmlSchemaPtr schema;
@@ -162,6 +167,7 @@
 
     return(0);
 }
+#endif
 
 static int
 processPattern(const char *pattern) {
@@ -240,6 +246,7 @@
     return(ret);
 }
 
+#ifdef HAVE_XPATH_FUZZER
 static int
 processXPath(const char *testDir, const char *prefix, const char *name,
              const char *data, const char *subdir, int xptr) {
@@ -363,10 +370,11 @@
 
     return(ret);
 }
+#endif
 
 int
 main(int argc, const char **argv) {
-    mainFunc processArg = processPattern;
+    mainFunc processArg = NULL;
     const char *fuzzer;
     int ret = 0;
     int xpath = 0;
@@ -381,13 +389,24 @@
 
     fuzzer = argv[1];
     if (strcmp(fuzzer, "html") == 0) {
+#ifdef HAVE_HTML_FUZZER
+        processArg = processPattern;
         globalData.processFile = processHtml;
+#endif
     } else if (strcmp(fuzzer, "schema") == 0) {
+#ifdef HAVE_SCHEMA_FUZZER
+        processArg = processPattern;
         globalData.processFile = processSchema;
+#endif
     } else if (strcmp(fuzzer, "xml") == 0) {
+#ifdef HAVE_XML_FUZZER
+        processArg = processPattern;
         globalData.processFile = processXml;
+#endif
     } else if (strcmp(fuzzer, "xpath") == 0) {
+#ifdef HAVE_XPATH_FUZZER
         processArg = processXPathDir;
+#endif
     } else {
         fprintf(stderr, "unknown fuzzer %s\n", fuzzer);
         return(1);
@@ -399,8 +418,9 @@
         return(1);
     }
 
-    for (i = 2; i < argc; i++)
-        processArg(argv[i]);
+    if (processArg != NULL)
+        for (i = 2; i < argc; i++)
+            processArg(argv[i]);
 
     return(ret);
 }
diff --git a/src/fuzz/html.c b/src/fuzz/html.c
index d212c1f..116b3df 100644
--- a/src/fuzz/html.c
+++ b/src/fuzz/html.c
@@ -6,12 +6,16 @@
 
 #include <libxml/HTMLparser.h>
 #include <libxml/HTMLtree.h>
+#include <libxml/catalog.h>
 #include "fuzz.h"
 
 int
 LLVMFuzzerInitialize(int *argc ATTRIBUTE_UNUSED,
                      char ***argv ATTRIBUTE_UNUSED) {
     xmlInitParser();
+#ifdef LIBXML_CATALOG_ENABLED
+    xmlInitializeCatalog();
+#endif
     xmlSetGenericErrorFunc(NULL, xmlFuzzErrorFunc);
 
     return 0;
@@ -22,7 +26,7 @@
     static const size_t maxChunkSize = 128;
     htmlDocPtr doc;
     htmlParserCtxtPtr ctxt;
-    xmlChar *out;
+    xmlOutputBufferPtr out;
     const char *docBuffer;
     size_t docSize, consumed, chunkSize;
     int opts, outSize;
@@ -39,9 +43,16 @@
     /* Pull parser */
 
     doc = htmlReadMemory(docBuffer, docSize, NULL, NULL, opts);
-    /* Also test the serializer. */
-    htmlDocDumpMemory(doc, &out, &outSize);
-    xmlFree(out);
+
+    /*
+     * Also test the serializer. Call htmlDocContentDumpOutput with our
+     * own buffer to avoid encoding the output. The HTML encoding is
+     * excruciatingly slow (see htmlEntityValueLookup).
+     */
+    out = xmlAllocOutputBuffer(NULL);
+    htmlDocContentDumpOutput(out, doc, NULL);
+    xmlOutputBufferClose(out);
+
     xmlFreeDoc(doc);
 
     /* Push parser */
@@ -64,6 +75,7 @@
     /* Cleanup */
 
     xmlFuzzDataCleanup();
+    xmlResetLastError();
 
     return(0);
 }
diff --git a/src/fuzz/html.dict b/src/fuzz/html.dict
index 801b7bb..80444c2 100644
--- a/src/fuzz/html.dict
+++ b/src/fuzz/html.dict
@@ -109,3 +109,16 @@
 ref_quot="&quot;"
 ref_dec="&#9;"
 ref_hex="&#xA;"
+
+cs_utf8="UTF-8"
+cs_utf16="UTF-16"
+cs_utf16le="UTF-16LE"
+cs_utf16be="UTF-16BE"
+cs_ucs2="UCS-2"
+cs_ucs4="UCS-4"
+cs_latin1="ISO-8859-1"
+cs_ascii="ASCII"
+cs_ebcdic="EBCDIC"
+cs_iso2022jp="ISO-2022-JP"
+cs_shift_jis="SHIFT_JIS"
+cs_euc_jp="EUC-JP"
diff --git a/src/fuzz/regexp.c b/src/fuzz/regexp.c
index cfffedd..af1210a 100644
--- a/src/fuzz/regexp.c
+++ b/src/fuzz/regexp.c
@@ -40,6 +40,7 @@
 
     xmlFree(str[0]);
     xmlFree(str[1]);
+    xmlResetLastError();
 
     return 0;
 }
diff --git a/src/fuzz/schema.c b/src/fuzz/schema.c
index 7b034ec..689bffe 100644
--- a/src/fuzz/schema.c
+++ b/src/fuzz/schema.c
@@ -4,6 +4,7 @@
  * See Copyright for the status of this software.
  */
 
+#include <libxml/catalog.h>
 #include <libxml/xmlschemas.h>
 #include "fuzz.h"
 
@@ -11,6 +12,9 @@
 LLVMFuzzerInitialize(int *argc ATTRIBUTE_UNUSED,
                      char ***argv ATTRIBUTE_UNUSED) {
     xmlInitParser();
+#ifdef LIBXML_CATALOG_ENABLED
+    xmlInitializeCatalog();
+#endif
     xmlSetGenericErrorFunc(NULL, xmlFuzzErrorFunc);
     xmlSetExternalEntityLoader(xmlFuzzEntityLoader);
 
@@ -33,6 +37,7 @@
     xmlSchemaFreeParserCtxt(pctxt);
 
     xmlFuzzDataCleanup();
+    xmlResetLastError();
 
     return(0);
 }
diff --git a/src/fuzz/testFuzzer.c b/src/fuzz/testFuzzer.c
index 678f324..b0c7ffb 100644
--- a/src/fuzz/testFuzzer.c
+++ b/src/fuzz/testFuzzer.c
@@ -12,41 +12,53 @@
 #include <libxml/xmlstring.h>
 #include "fuzz.h"
 
-#define LLVMFuzzerInitialize fuzzHtmlInit
-#define LLVMFuzzerTestOneInput fuzzHtml
-#include "html.c"
-#undef LLVMFuzzerInitialize
-#undef LLVMFuzzerTestOneInput
+#ifdef HAVE_HTML_FUZZER
+  #define LLVMFuzzerInitialize fuzzHtmlInit
+  #define LLVMFuzzerTestOneInput fuzzHtml
+  #include "html.c"
+  #undef LLVMFuzzerInitialize
+  #undef LLVMFuzzerTestOneInput
+#endif
 
-#define LLVMFuzzerInitialize fuzzRegexpInit
-#define LLVMFuzzerTestOneInput fuzzRegexp
-#include "regexp.c"
-#undef LLVMFuzzerInitialize
-#undef LLVMFuzzerTestOneInput
+#ifdef HAVE_REGEXP_FUZZER
+  #define LLVMFuzzerInitialize fuzzRegexpInit
+  #define LLVMFuzzerTestOneInput fuzzRegexp
+  #include "regexp.c"
+  #undef LLVMFuzzerInitialize
+  #undef LLVMFuzzerTestOneInput
+#endif
 
-#define LLVMFuzzerInitialize fuzzSchemaInit
-#define LLVMFuzzerTestOneInput fuzzSchema
-#include "schema.c"
-#undef LLVMFuzzerInitialize
-#undef LLVMFuzzerTestOneInput
+#ifdef HAVE_SCHEMA_FUZZER
+  #define LLVMFuzzerInitialize fuzzSchemaInit
+  #define LLVMFuzzerTestOneInput fuzzSchema
+  #include "schema.c"
+  #undef LLVMFuzzerInitialize
+  #undef LLVMFuzzerTestOneInput
+#endif
 
-#define LLVMFuzzerInitialize fuzzUriInit
-#define LLVMFuzzerTestOneInput fuzzUri
-#include "uri.c"
-#undef LLVMFuzzerInitialize
-#undef LLVMFuzzerTestOneInput
+#ifdef HAVE_URI_FUZZER
+  #define LLVMFuzzerInitialize fuzzUriInit
+  #define LLVMFuzzerTestOneInput fuzzUri
+  #include "uri.c"
+  #undef LLVMFuzzerInitialize
+  #undef LLVMFuzzerTestOneInput
+#endif
 
-#define LLVMFuzzerInitialize fuzzXmlInit
-#define LLVMFuzzerTestOneInput fuzzXml
-#include "xml.c"
-#undef LLVMFuzzerInitialize
-#undef LLVMFuzzerTestOneInput
+#ifdef HAVE_XML_FUZZER
+  #define LLVMFuzzerInitialize fuzzXmlInit
+  #define LLVMFuzzerTestOneInput fuzzXml
+  #include "xml.c"
+  #undef LLVMFuzzerInitialize
+  #undef LLVMFuzzerTestOneInput
+#endif
 
-#define LLVMFuzzerInitialize fuzzXPathInit
-#define LLVMFuzzerTestOneInput fuzzXPath
-#include "xpath.c"
-#undef LLVMFuzzerInitialize
-#undef LLVMFuzzerTestOneInput
+#ifdef HAVE_XPATH_FUZZER
+  #define LLVMFuzzerInitialize fuzzXPathInit
+  #define LLVMFuzzerTestOneInput fuzzXPath
+  #include "xpath.c"
+  #undef LLVMFuzzerInitialize
+  #undef LLVMFuzzerTestOneInput
+#endif
 
 typedef int
 (*initFunc)(int *argc, char ***argv);
@@ -91,6 +103,7 @@
     return(ret);
 }
 
+#ifdef HAVE_XML_FUZZER
 static int
 testEntityLoader() {
     static const char data[] =
@@ -132,25 +145,40 @@
 
     return(ret);
 }
+#endif
 
 int
 main() {
     int ret = 0;
 
+#ifdef HAVE_XML_FUZZER
     if (testEntityLoader() != 0)
         ret = 1;
+#endif
+#ifdef HAVE_HTML_FUZZER
     if (testFuzzer(fuzzHtmlInit, fuzzHtml, "seed/html/*") != 0)
         ret = 1;
+#endif
+#ifdef HAVE_REGEXP_FUZZER
     if (testFuzzer(fuzzRegexpInit, fuzzRegexp, "seed/regexp/*") != 0)
         ret = 1;
+#endif
+#ifdef HAVE_SCHEMA_FUZZER
     if (testFuzzer(fuzzSchemaInit, fuzzSchema, "seed/schema/*") != 0)
         ret = 1;
+#endif
+#ifdef HAVE_URI_FUZZER
     if (testFuzzer(NULL, fuzzUri, "seed/uri/*") != 0)
         ret = 1;
+#endif
+#ifdef HAVE_XML_FUZZER
     if (testFuzzer(fuzzXmlInit, fuzzXml, "seed/xml/*") != 0)
         ret = 1;
+#endif
+#ifdef HAVE_XPATH_FUZZER
     if (testFuzzer(fuzzXPathInit, fuzzXPath, "seed/xpath/*") != 0)
         ret = 1;
+#endif
 
     if (ret == 0)
         printf("Successfully tested %d inputs\n", numInputs);
diff --git a/src/fuzz/xml.c b/src/fuzz/xml.c
index f0dcfcc..8b4c4ef 100644
--- a/src/fuzz/xml.c
+++ b/src/fuzz/xml.c
@@ -4,6 +4,7 @@
  * See Copyright for the status of this software.
  */
 
+#include <libxml/catalog.h>
 #include <libxml/parser.h>
 #include <libxml/tree.h>
 #include <libxml/xmlerror.h>
@@ -15,6 +16,9 @@
 LLVMFuzzerInitialize(int *argc ATTRIBUTE_UNUSED,
                      char ***argv ATTRIBUTE_UNUSED) {
     xmlInitParser();
+#ifdef LIBXML_CATALOG_ENABLED
+    xmlInitializeCatalog();
+#endif
     xmlSetGenericErrorFunc(NULL, xmlFuzzErrorFunc);
     xmlSetExternalEntityLoader(xmlFuzzEntityLoader);
 
@@ -94,6 +98,7 @@
 
 exit:
     xmlFuzzDataCleanup();
+    xmlResetLastError();
     return(0);
 }
 
diff --git a/src/fuzz/xml.dict b/src/fuzz/xml.dict
index 2573aea..a539e6a 100644
--- a/src/fuzz/xml.dict
+++ b/src/fuzz/xml.dict
@@ -74,3 +74,16 @@
 
 notation_decl_public="<!NOTATION a PUBLIC 'a'>"
 notation_decl_system="<!NOTATION a SYSTEM 'a'>"
+
+cs_utf8="UTF-8"
+cs_utf16="UTF-16"
+cs_utf16le="UTF-16LE"
+cs_utf16be="UTF-16BE"
+cs_ucs2="UCS-2"
+cs_ucs4="UCS-4"
+cs_latin1="ISO-8859-1"
+cs_ascii="ASCII"
+cs_ebcdic="EBCDIC"
+cs_iso2022jp="ISO-2022-JP"
+cs_shift_jis="SHIFT_JIS"
+cs_euc_jp="EUC-JP"
diff --git a/src/fuzz/xpath.c b/src/fuzz/xpath.c
index 4cb29f6..47652bd 100644
--- a/src/fuzz/xpath.c
+++ b/src/fuzz/xpath.c
@@ -45,6 +45,7 @@
     xmlFreeDoc(doc);
 
     xmlFuzzDataCleanup();
+    xmlResetLastError();
 
     return(0);
 }
diff --git a/src/include/libxml/c14n.h b/src/include/libxml/c14n.h
index d74847d..af93de6 100644
--- a/src/include/libxml/c14n.h
+++ b/src/include/libxml/c14n.h
@@ -16,17 +16,19 @@
  */
 #ifndef __XML_C14N_H__
 #define __XML_C14N_H__
+
+#include <libxml/xmlversion.h>
+
 #ifdef LIBXML_C14N_ENABLED
 #ifdef LIBXML_OUTPUT_ENABLED
 
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif /* __cplusplus */
 
-#include <libxml/xmlversion.h>
-#include <libxml/tree.h>
-#include <libxml/xpath.h>
-
 /*
  * XML Canonicalization
  * http://www.w3.org/TR/xml-c14n
diff --git a/src/libxml.m4 b/src/libxml.m4
index b5df915..09de9fe 100644
--- a/src/libxml.m4
+++ b/src/libxml.m4
@@ -1,4 +1,6 @@
 # Configure paths for LIBXML2
+# Simon Josefsson 2020-02-12
+# Fix autoconf 2.70+ warnings
 # Mike Hommey 2004-06-19
 # use CPPFLAGS instead of CFLAGS
 # Toshio Kuratomi 2001-04-21
@@ -58,7 +60,8 @@
 dnl (Also sanity checks the results of xml2-config to some extent)
 dnl
       rm -f conf.xmltest
-      AC_TRY_RUN([
+      AC_RUN_IFELSE(
+            [AC_LANG_SOURCE([[
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -133,7 +136,7 @@
     }
   return 1;
 }
-],, no_xml=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+]])],, no_xml=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
        CPPFLAGS="$ac_save_CPPFLAGS"
        LIBS="$ac_save_LIBS"
      fi
@@ -156,10 +159,11 @@
           echo "*** Could not run libxml test program, checking why..."
           CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS"
           LIBS="$LIBS $XML_LIBS"
-          AC_TRY_LINK([
+	  AC_LINK_IFELSE(
+            [AC_LANG_PROGRAM([[
 #include <libxml/xmlversion.h>
 #include <stdio.h>
-],      [ LIBXML_TEST_VERSION; return 0;],
+]],    [[ LIBXML_TEST_VERSION; return 0;]])],
         [ echo "*** The test program compiled, but did not run. This usually means"
           echo "*** that the run-time linker is not finding LIBXML or finding the wrong"
           echo "*** version of LIBXML. If it is not finding LIBXML, you'll need to set your"
diff --git a/src/libxml2.spec b/src/libxml2.spec
index 626f900..017e34e 100644
--- a/src/libxml2.spec
+++ b/src/libxml2.spec
@@ -204,6 +204,6 @@
 %endif # with_python3
 
 %changelog
-* Mon Feb  1 2021 Daniel Veillard <veillard@redhat.com>
+* Thu May  6 2021 Daniel Veillard <veillard@redhat.com>
 - upstream release 2.9.10 see http://xmlsoft.org/news.html
 
diff --git a/src/parser.c b/src/parser.c
index 3ac2ca6..c69a1c3 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -2680,8 +2680,10 @@
 		rep = xmlStringDecodeEntities(ctxt, ent->content, what,
 			                      0, 0, 0);
 		ctxt->depth--;
-		if (rep == NULL)
+		if (rep == NULL) {
+                    ent->content[0] = 0;
                     goto int_error;
+                }
 
                 current = rep;
                 while (*current != 0) { /* non input consuming loop */
@@ -2736,8 +2738,11 @@
 		rep = xmlStringDecodeEntities(ctxt, ent->content, what,
 			                      0, 0, 0);
 		ctxt->depth--;
-		if (rep == NULL)
+		if (rep == NULL) {
+                    if (ent->content != NULL)
+                        ent->content[0] = 0;
                     goto int_error;
+                }
                 current = rep;
                 while (*current != 0) { /* non input consuming loop */
                     buffer[nbchars++] = *current++;
@@ -4200,6 +4205,7 @@
 	}
 	count++;
 	if (count > 50) {
+	    SHRINK;
 	    GROW;
 	    count = 0;
             if (ctxt->instate == XML_PARSER_EOF) {
@@ -4287,6 +4293,7 @@
 	buf[len++] = cur;
 	count++;
 	if (count > 50) {
+	    SHRINK;
 	    GROW;
 	    count = 0;
             if (ctxt->instate == XML_PARSER_EOF) {
@@ -4567,6 +4574,7 @@
 	}
 	count++;
 	if (count > 50) {
+	    SHRINK;
 	    GROW;
 	    count = 0;
             if (ctxt->instate == XML_PARSER_EOF)
@@ -4772,6 +4780,7 @@
 
 	count++;
 	if (count > 50) {
+	    SHRINK;
 	    GROW;
 	    count = 0;
             if (ctxt->instate == XML_PARSER_EOF) {
@@ -5182,6 +5191,7 @@
 		}
 		count++;
 		if (count > 50) {
+		    SHRINK;
 		    GROW;
                     if (ctxt->instate == XML_PARSER_EOF) {
                         xmlFree(buf);
@@ -6194,6 +6204,8 @@
 	SKIP_BLANKS;
         cur = ret = xmlParseElementChildrenContentDeclPriv(ctxt, inputid,
                                                            depth + 1);
+        if (cur == NULL)
+            return(NULL);
 	SKIP_BLANKS;
 	GROW;
     } else {
@@ -6327,6 +6339,11 @@
 	    SKIP_BLANKS;
 	    last = xmlParseElementChildrenContentDeclPriv(ctxt, inputid,
                                                           depth + 1);
+            if (last == NULL) {
+		if (ret != NULL)
+		    xmlFreeDocElementContent(ctxt->myDoc, ret);
+		return(NULL);
+            }
 	    SKIP_BLANKS;
 	} else {
 	    elem = xmlParseName(ctxt);
@@ -9790,6 +9807,7 @@
 	sl = l;
 	count++;
 	if (count > 50) {
+	    SHRINK;
 	    GROW;
             if (ctxt->instate == XML_PARSER_EOF) {
 		xmlFree(buf);
diff --git a/src/parserInternals.c b/src/parserInternals.c
index e3828af..a731c16 100644
--- a/src/parserInternals.c
+++ b/src/parserInternals.c
@@ -1153,6 +1153,11 @@
              * Note: this is a bit dangerous, but that's what it
              * takes to use nearly compatible signature for different
              * encodings.
+             *
+             * FIXME: Encoders might buffer partial byte sequences, so
+             * this probably can't work. We should return an error and
+             * make sure that callers never try to switch the encoding
+             * twice.
              */
             xmlCharEncCloseFunc(input->buf->encoder);
             input->buf->encoder = handler;
diff --git a/src/tree.c b/src/tree.c
index 2130d55..c707f59 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -1310,6 +1310,16 @@
 		else
 		    tmp = 0;
 		while (tmp != ';') { /* Non input consuming loop */
+                    /*
+                     * If you find an integer overflow here when fuzzing,
+                     * the bug is probably elsewhere. This function should
+                     * only receive entities that were already validated by
+                     * the parser, typically by xmlParseAttValueComplex
+                     * calling xmlStringDecodeEntities.
+                     *
+                     * So it's better *not* to check for overflow to
+                     * potentially discover new bugs.
+                     */
 		    if ((tmp >= '0') && (tmp <= '9'))
 			charval = charval * 16 + (tmp - '0');
 		    else if ((tmp >= 'a') && (tmp <= 'f'))
@@ -1338,6 +1348,7 @@
 		else
 		    tmp = 0;
 		while (tmp != ';') { /* Non input consuming loops */
+                    /* Don't check for integer overflow, see above. */
 		    if ((tmp >= '0') && (tmp <= '9'))
 			charval = charval * 10 + (tmp - '0');
 		    else {
@@ -1517,6 +1528,7 @@
 		cur += 3;
 		tmp = *cur;
 		while (tmp != ';') { /* Non input consuming loop */
+                    /* Don't check for integer overflow, see above. */
 		    if ((tmp >= '0') && (tmp <= '9'))
 			charval = charval * 16 + (tmp - '0');
 		    else if ((tmp >= 'a') && (tmp <= 'f'))
@@ -1539,6 +1551,7 @@
 		cur += 2;
 		tmp = *cur;
 		while (tmp != ';') { /* Non input consuming loops */
+                    /* Don't check for integer overflow, see above. */
 		    if ((tmp >= '0') && (tmp <= '9'))
 			charval = charval * 10 + (tmp - '0');
 		    else {
@@ -1888,12 +1901,6 @@
     if (value != NULL) {
         xmlNodePtr tmp;
 
-        if(!xmlCheckUTF8(value)) {
-            xmlTreeErr(XML_TREE_NOT_UTF8, (xmlNodePtr) doc,
-                       NULL);
-            if (doc != NULL)
-                doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
-        }
         cur->children = xmlNewDocText(doc, value);
         cur->last = NULL;
         tmp = cur->children;
@@ -2013,6 +2020,11 @@
  * @value:  the value of the attribute
  *
  * Create a new property carried by a document.
+ * NOTE: @value is supposed to be a piece of XML CDATA, so it allows entity
+ *       references, but XML special chars need to be escaped first by using
+ *       xmlEncodeEntitiesReentrant(). Use xmlNewProp() if you don't need
+ *       entities support.
+ *
  * Returns a pointer to the attribute
  */
 xmlAttrPtr
@@ -4558,6 +4570,7 @@
     if (doc == NULL) return(NULL);
     ret = xmlNewDoc(doc->version);
     if (ret == NULL) return(NULL);
+    ret->type = doc->type;
     if (doc->name != NULL)
         ret->name = xmlMemStrdup(doc->name);
     if (doc->encoding != NULL)
@@ -4880,7 +4893,9 @@
             }
             next = ((xmlAttrPtr) cur)->parent;
         } else {
-            next = cur->parent;
+            xmlFree(buf);
+            xmlFree(buffer);
+            return (NULL);
         }
 
         /*
@@ -6575,6 +6590,16 @@
 		attrDecl = xmlGetDtdQAttrDesc(doc->extSubset,
 		    elemQName, name, NULL);
 	    }
+        } else if (xmlStrEqual(nsName, XML_XML_NAMESPACE)) {
+	    /*
+	    * The XML namespace must be bound to prefix 'xml'.
+	    */
+	    attrDecl = xmlGetDtdQAttrDesc(doc->intSubset,
+		elemQName, name, BAD_CAST "xml");
+	    if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
+		attrDecl = xmlGetDtdQAttrDesc(doc->extSubset,
+		    elemQName, name, BAD_CAST "xml");
+	    }
 	} else {
 	    xmlNsPtr *nsList, *cur;
 
@@ -6921,12 +6946,6 @@
 	if (value != NULL) {
 	    xmlNodePtr tmp;
 
-	    if(!xmlCheckUTF8(value)) {
-	        xmlTreeErr(XML_TREE_NOT_UTF8, (xmlNodePtr) node->doc,
-	                   NULL);
-                if (node->doc != NULL)
-                    node->doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
-	    }
 	    prop->children = xmlNewDocText(node->doc, value);
 	    prop->last = NULL;
 	    tmp = prop->children;
diff --git a/src/xpath.c b/src/xpath.c
index 90f10ca..711bd2a 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -488,14 +488,6 @@
  *									*
  ************************************************************************/
 
-#ifndef INFINITY
-#define INFINITY (DBL_MAX * DBL_MAX)
-#endif
-
-#ifndef NAN
-#define NAN (INFINITY / INFINITY)
-#endif
-
 double xmlXPathNAN;
 double xmlXPathPINF;
 double xmlXPathNINF;
@@ -505,11 +497,14 @@
  *
  * Initialize the XPath environment
  */
+ATTRIBUTE_NO_SANITIZE("float-divide-by-zero")
 void
 xmlXPathInit(void) {
-    xmlXPathNAN = NAN;
-    xmlXPathPINF = INFINITY;
-    xmlXPathNINF = -INFINITY;
+    /* MSVC doesn't allow division by zero in constant expressions. */
+    double zero = 0.0;
+    xmlXPathNAN = 0.0 / zero;
+    xmlXPathPINF = 1.0 / zero;
+    xmlXPathNINF = -xmlXPathPINF;
 }
 
 /**
@@ -538,9 +533,9 @@
 #ifdef isinf
     return isinf(val) ? (val > 0 ? 1 : -1) : 0;
 #else
-    if (val >= INFINITY)
+    if (val >= xmlXPathPINF)
         return 1;
-    if (val <= -INFINITY)
+    if (val <= -xmlXPathPINF)
         return -1;
     return 0;
 #endif
@@ -5873,10 +5868,10 @@
     double ret;
 
     if (node == NULL)
-	return(NAN);
+	return(xmlXPathNAN);
     strval = xmlXPathCastNodeToString(node);
     if (strval == NULL)
-	return(NAN);
+	return(xmlXPathNAN);
     ret = xmlXPathCastStringToNumber(strval);
     xmlFree(strval);
 
@@ -5897,7 +5892,7 @@
     double ret;
 
     if (ns == NULL)
-	return(NAN);
+	return(xmlXPathNAN);
     str = xmlXPathCastNodeSetToString(ns);
     ret = xmlXPathCastStringToNumber(str);
     xmlFree(str);
@@ -5917,13 +5912,13 @@
     double ret = 0.0;
 
     if (val == NULL)
-	return(NAN);
+	return(xmlXPathNAN);
     switch (val->type) {
     case XPATH_UNDEFINED:
 #ifdef DEBUG_EXPR
 	xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
 #endif
-	ret = NAN;
+	ret = xmlXPathNAN;
 	break;
     case XPATH_NODESET:
     case XPATH_XSLT_TREE:
@@ -5943,7 +5938,7 @@
     case XPATH_RANGE:
     case XPATH_LOCATIONSET:
 	TODO;
-	ret = NAN;
+	ret = xmlXPathNAN;
 	break;
     }
     return(ret);
@@ -7570,7 +7565,7 @@
     CHECK_TYPE(XPATH_NUMBER);
     arg1 = ctxt->value->floatval;
     if (arg2 == 0)
-	ctxt->value->floatval = NAN;
+	ctxt->value->floatval = xmlXPathNAN;
     else {
 	ctxt->value->floatval = fmod(arg1, arg2);
     }
@@ -10000,7 +9995,7 @@
     if (cur == NULL) return(0);
     while (IS_BLANK_CH(*cur)) cur++;
     if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
-        return(NAN);
+        return(xmlXPathNAN);
     }
     if (*cur == '-') {
 	isneg = 1;
@@ -10036,7 +10031,7 @@
 
         cur++;
 	if (((*cur < '0') || (*cur > '9')) && (!ok)) {
-	    return(NAN);
+	    return(xmlXPathNAN);
 	}
         while (*cur == '0') {
 	    frac = frac + 1;
@@ -10069,7 +10064,7 @@
       }
     }
     while (IS_BLANK_CH(*cur)) cur++;
-    if (*cur != 0) return(NAN);
+    if (*cur != 0) return(xmlXPathNAN);
     if (isneg) ret = -ret;
     if (is_exponent_negative) exponent = -exponent;
     ret *= pow(10.0, (double)exponent);