Просмотр исходного кода

Merge branch 'thirdparty' into thirdparty_dev

Source commit: 16a3fcd9924c68d577c999d95f813f16dd801085
Martin Prikryl 7 месяцев назад
Родитель
Сommit
b3445db1be

+ 6 - 6
libs/expat/CMake.README

@@ -3,25 +3,25 @@
 The cmake based buildsystem for expat works on Windows (cygwin, mingw, Visual
 Studio) and should work on all other platform cmake supports.
 
-Assuming ~/expat-2.6.4 is the source directory of expat, add a subdirectory
+Assuming ~/expat-2.7.0 is the source directory of expat, add a subdirectory
 build and change into that directory:
-~/expat-2.6.4$ mkdir build && cd build
-~/expat-2.6.4/build$
+~/expat-2.7.0$ mkdir build && cd build
+~/expat-2.7.0/build$
 
 From that directory, call cmake first, then call make, make test and
 make install in the usual way:
-~/expat-2.6.4/build$ cmake ..
+~/expat-2.7.0/build$ cmake ..
 -- The C compiler identification is GNU
 -- The CXX compiler identification is GNU
 ....
 -- Configuring done
 -- Generating done
--- Build files have been written to: /home/patrick/expat-2.6.4/build
+-- Build files have been written to: /home/patrick/expat-2.7.0/build
 
 If you want to specify the install location for your files, append
 -DCMAKE_INSTALL_PREFIX=/your/install/path to the cmake call.
 
-~/expat-2.6.4/build$ make && make test && make install
+~/expat-2.7.0/build$ make && make test && make install
 Scanning dependencies of target expat
 [  5%] Building C object CMakeFiles/expat.dir/lib/xmlparse.c.o
 [ 11%] Building C object CMakeFiles/expat.dir/lib/xmlrole.c.o

+ 77 - 24
libs/expat/CMakeLists.txt

@@ -7,7 +7,7 @@
 #
 # Copyright (c) 2010      Patrick Spendrin <[email protected]>
 # Copyright (c) 2012      Karl Waclawek <[email protected]>
-# Copyright (c) 2016-2024 Sebastian Pipping <[email protected]>
+# Copyright (c) 2016-2025 Sebastian Pipping <[email protected]>
 # Copyright (c) 2016      Sergei Nikulov <[email protected]>
 # Copyright (c) 2016      Björn Lindahl <[email protected]>
 # Copyright (c) 2016      Tobias Taschner <[email protected]>
@@ -30,15 +30,16 @@
 # Copyright (c) 2020      Thomas Beutlich <[email protected]>
 # Copyright (c) 2021      Alex Richardson <[email protected]>
 # Copyright (c) 2022      Johnny Jazeix <[email protected]>
+# Copyright (c) 2022      Mark Brand <[email protected]>
 # Copyright (c) 2022      David Faure <[email protected]>
 # Unlike most of Expat,
 # this file is copyrighted under the BSD-license for buildsystem files of KDE.
 
-cmake_minimum_required(VERSION 3.5.0)
+cmake_minimum_required(VERSION 3.13.0)
 
 project(expat
     VERSION
-        2.6.4
+        2.7.0
     LANGUAGES
         C
 )
@@ -134,7 +135,7 @@ expat_shy_set(EXPAT_SHARED_LIBS ${_EXPAT_SHARED_LIBS_DEFAULT} CACHE BOOL "Build
 expat_shy_set(EXPAT_BUILD_DOCS ${_EXPAT_BUILD_DOCS_DEFAULT} CACHE BOOL "Build man page for xmlwf")
 expat_shy_set(EXPAT_BUILD_FUZZERS OFF CACHE BOOL "Build fuzzers for the expat library")
 expat_shy_set(EXPAT_BUILD_PKGCONFIG ${_EXPAT_BUILD_PKGCONFIG_DEFAULT} CACHE BOOL "Build pkg-config file")
-expat_shy_set(EXPAT_OSSFUZZ_BUILD OFF CACHE BOOL "Build fuzzers via ossfuzz for the expat library")
+expat_shy_set(EXPAT_OSSFUZZ_BUILD OFF CACHE BOOL "Build fuzzers via OSS-Fuzz for the expat library")
 if(UNIX OR _EXPAT_HELP)
     expat_shy_set(EXPAT_WITH_LIBBSD OFF CACHE BOOL "Utilize libbsd (for arc4random_buf)")
 endif()
@@ -169,11 +170,15 @@ if(NOT _EXPAT_HELP)
     mark_as_advanced(_EXPAT_M32)
 endif()
 
-if(EXPAT_BUILD_TESTS)
+if(EXPAT_BUILD_TESTS OR EXPAT_BUILD_FUZZERS)
     # We have to call enable_language() before modifying any CMAKE_CXX_* variables
     enable_language(CXX)
 
-    set(CMAKE_CXX_STANDARD 11)
+    if (EXPAT_BUILD_FUZZERS)
+        set(CMAKE_CXX_STANDARD 17)  # for std::string_view for Abseil for libprotobuf-mutator
+    else()
+        set(CMAKE_CXX_STANDARD 11)
+    endif()
     set(CMAKE_CXX_STANDARD_REQUIRED ON)
     set(CMAKE_CXX_EXTENSIONS OFF)  # i.e. -std=c++11 rather than default -std=gnu++11
 endif()
@@ -320,7 +325,7 @@ if(FLAG_VISIBILITY)
   endif()
   set(EXTRA_COMPILE_FLAGS "${EXTRA_COMPILE_FLAGS} -fvisibility=hidden")
 endif()
-if(MINGW)
+if(MINGW AND ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU"))
     # Without __USE_MINGW_ANSI_STDIO the compiler produces a false positive
     set(EXTRA_COMPILE_FLAGS "${EXTRA_COMPILE_FLAGS} -Wno-pedantic-ms-format")
 endif()
@@ -467,7 +472,7 @@ foreach(build_type_upper
 endforeach()
 
 set(LIBCURRENT 11)  # sync
-set(LIBREVISION 0)  # with
+set(LIBREVISION 1)  # with
 set(LIBAGE 10)      # configure.ac!
 math(EXPR LIBCURRENT_MINUS_AGE "${LIBCURRENT} - ${LIBAGE}")
 
@@ -587,7 +592,7 @@ if(EXPAT_BUILD_TOOLS)
 
     if(MINGW AND _EXPAT_UNICODE_WCHAR_T)
         # https://gcc.gnu.org/onlinedocs/gcc/x86-Windows-Options.html
-        set_target_properties(xmlwf PROPERTIES LINK_FLAGS -municode)
+        target_link_options(xmlwf PRIVATE -municode)
     endif()
 
     if(EXPAT_BUILD_DOCS)
@@ -724,7 +729,7 @@ if(EXPAT_BUILD_FUZZERS)
         message(SEND_ERROR
             "OSS-Fuzz builds require the environment variable "
             "LIB_FUZZING_ENGINE to be set. If you are seeing this "
-            "warning, it points to a deeper problem in the ossfuzz "
+            "warning, it points to a deeper problem in the OSS-Fuzz "
             "build setup.")
     endif()
 
@@ -743,24 +748,78 @@ if(EXPAT_BUILD_FUZZERS)
             target_link_libraries(${target_name} fuzzpat)
             target_compile_definitions(${target_name}
                 PRIVATE ENCODING_FOR_FUZZING=${encoding_type})
-            if(NOT EXPAT_OSSFUZZ_BUILD)
-                target_compile_options(${target_name} PRIVATE -fsanitize=fuzzer-no-link)
-            endif()
-            # NOTE: Avoiding target_link_options here only because it needs CMake >=3.13
             if(EXPAT_OSSFUZZ_BUILD)
-                set_target_properties(${target_name} PROPERTIES LINK_FLAGS $ENV{LIB_FUZZING_ENGINE})
+                target_link_options(${target_name} PRIVATE $ENV{LIB_FUZZING_ENGINE})
                 set_target_properties(${target_name} PROPERTIES LINKER_LANGUAGE "CXX")
             else()
-                set_target_properties(${target_name} PROPERTIES LINK_FLAGS -fsanitize=fuzzer)
+                target_compile_options(${target_name} PRIVATE -fsanitize=fuzzer)
+                target_link_options(${target_name} PRIVATE -fsanitize=fuzzer)
             endif()
             set_property(
                 TARGET ${target_name} PROPERTY RUNTIME_OUTPUT_DIRECTORY fuzz)
         endforeach()
     endforeach()
+
+    find_package(Protobuf REQUIRED)
+
+    # Only include libprotobuf-mutator here so we don't build it in non-fuzz
+    # configurations.
+    include(ExternalProject)
+
+    set(ProtobufMutator_PREFIX libprotobuf-mutator)
+    set(ProtobufMutator_PATH ${CMAKE_CURRENT_BINARY_DIR}/${ProtobufMutator_PREFIX}/src/${ProtobufMutator_PREFIX})
+    set(ProtobufMutator_BUILD_PATH ${ProtobufMutator_PATH}-build)
+    set(ProtobufMutator_INCLUDE_DIR ${ProtobufMutator_PATH})
+    set(ProtobufMutator_LIBRARIES ${ProtobufMutator_BUILD_PATH}/src/libfuzzer/libprotobuf-mutator-libfuzzer.a ${ProtobufMutator_BUILD_PATH}/src/libprotobuf-mutator.a)
+
+    ExternalProject_Add(
+        ${ProtobufMutator_PREFIX}
+        PREFIX ${ProtobufMutator_PREFIX}
+        GIT_REPOSITORY https://github.com/google/libprotobuf-mutator.git
+        GIT_TAG 57928f41ae52bb27666aa15b310130d086dac245  # v1.4-16-g57928f4
+        CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
+        CMAKE_CACHE_ARGS
+            -DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER}
+            -DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER}
+            -DLIB_PROTO_MUTATOR_EXAMPLES:BOOL=OFF
+            -DLIB_PROTO_MUTATOR_TESTING:BOOL=OFF
+        BUILD_BYPRODUCTS ${ProtobufMutator_LIBRARIES}
+        UPDATE_COMMAND true
+        INSTALL_COMMAND true)
+
+    # Check for availability of protobuf compiler to avoid hard-to-understand
+    # errors from make(1) down the line as seen with CMake 3.25.1 on Debian
+    if(NOT Protobuf_PROTOC_EXECUTABLE)
+        message(SEND_ERROR
+            "The protobuf compiler (protoc) could not be found. "
+            "Is it installed and working properly?")
+    endif()
+
+    protobuf_generate_cpp(XML_LPM_FUZZER_PROTO_SRCS
+                          XML_LPM_FUZZER_PROTO_HDRS
+                          fuzz/xml_lpm_fuzzer.proto)
+
+    add_executable(xml_lpm_fuzzer
+                   fuzz/xml_lpm_fuzzer.cpp
+                   ${XML_LPM_FUZZER_PROTO_SRCS})
+    target_include_directories(xml_lpm_fuzzer PUBLIC ${ProtobufMutator_INCLUDE_DIR})
+    target_link_libraries(xml_lpm_fuzzer
+                          fuzzpat
+                          ${ProtobufMutator_LIBRARIES}
+                          ${Protobuf_LIBRARIES})
+    add_dependencies(xml_lpm_fuzzer ${ProtobufMutator_PREFIX})
+
+    if(EXPAT_OSSFUZZ_BUILD)
+        target_link_options(xml_lpm_fuzzer PRIVATE $ENV{LIB_FUZZING_ENGINE})
+    else()
+        target_compile_options(xml_lpm_fuzzer PRIVATE -fsanitize=fuzzer)
+        target_link_options(xml_lpm_fuzzer PRIVATE -fsanitize=fuzzer)
+    endif()
+    set_property(TARGET xml_lpm_fuzzer PROPERTY RUNTIME_OUTPUT_DIRECTORY fuzz)
 else()
     if(EXPAT_OSSFUZZ_BUILD)
         message(SEND_ERROR
-                "Attempting to perform an ossfuzz build without turning on the fuzzer build. "
+                "Attempting to perform an OSS-Fuzz build without turning on the fuzzer build. "
                 "This is likely in error - consider adding "
                 "-DEXPAT_BUILD_FUZZERS=ON to your cmake execution.")
     endif()
@@ -913,13 +972,7 @@ elseif(EXPAT_CHAR_TYPE STREQUAL "wchar_t")
 else()
     set(_EXPAT_CHAR_TYPE_SUMMARY "ERROR")
 endif()
-# NOTE: We're not accessing global property GENERATOR_IS_MULTI_CONFIG
-#       because that would require CMake >=3.9
-if(CMAKE_CONFIGURATION_TYPES)
-    set(_EXPAT_GENERATOR_IS_MULTI_CONFIG TRUE)
-else()
-    set(_EXPAT_GENERATOR_IS_MULTI_CONFIG FALSE)
-endif()
+get_property(_EXPAT_GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
 
 message(STATUS "===========================================================================")
 message(STATUS "")

+ 83 - 2
libs/expat/Changes

@@ -11,16 +11,23 @@
 !! The following topics need *additional skilled C developers* to progress   !!
 !! in a timely manner or at all (loosely ordered by descending priority):    !!
 !!                                                                           !!
-!! - <blink>fixing a complex non-public security issue</blink>,              !!
 !! - teaming up on researching and fixing future security reports and        !!
 !!   ClusterFuzz findings with few-days-max response times in communication  !!
 !!   in order to (1) have a sound fix ready before the end of a 90 days      !!
 !!   grace period and (2) in a sustainable manner,                           !!
+!! - helping CPython Expat bindings with supporting Expat's billion laughs   !!
+!!   attack protection API (https://github.com/python/cpython/issues/90949): !!
+!!   - XML_SetBillionLaughsAttackProtectionActivationThreshold               !!
+!!   - XML_SetBillionLaughsAttackProtectionMaximumAmplification              !!
+!! - helping Perl's XML::Parser Expat bindings with supporting Expat's       !!
+!!   security API (https://github.com/cpan-authors/XML-Parser/issues/102):   !!
+!!   - XML_SetBillionLaughsAttackProtectionActivationThreshold               !!
+!!   - XML_SetBillionLaughsAttackProtectionMaximumAmplification              !!
+!!   - XML_SetReparseDeferralEnabled                                         !!
 !! - implementing and auto-testing XML 1.0r5 support                         !!
 !!   (needs discussion before pull requests),                                !!
 !! - smart ideas on fixing the Autotools CMake files generation issue        !!
 !!   without breaking CI (needs discussion before pull requests),            !!
-!! - the Windows binaries topic (needs requirements engineering first),      !!
 !! - pushing migration from `int` to `size_t` further                        !!
 !!   including edge-cases test coverage (needs discussion before anything).  !!
 !!                                                                           !!
@@ -30,6 +37,78 @@
 !! THANK YOU!                        Sebastian Pipping -- Berlin, 2024-03-09 !!
 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
+Release 2.7.0 Thu March 13 2025
+        Security fixes:
+       #893 #973  CVE-2024-8176 -- Fix crash from chaining a large number
+                    of entities caused by stack overflow by resolving use of
+                    recursion, for all three uses of entities:
+                    - general entities in character data ("<e>&g1;</e>")
+                    - general entities in attribute values ("<e k1='&g1;'/>")
+                    - parameter entities ("%p1;")
+                    Known impact is (reliable and easy) denial of service:
+                    CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H/E:H/RL:O/RC:C
+                    (Base Score: 7.5, Temporal Score: 7.2)
+                    Please note that a layer of compression around XML can
+                    significantly reduce the minimum attack payload size.
+
+        Other changes:
+       #935 #937  Autotools: Make generated CMake files look for
+                    libexpat.@[email protected] on macOS
+            #925  Autotools: Sync CMake templates with CMake 3.29
+  #945 #962 #966  CMake: Drop support for CMake <3.13
+            #942  CMake: Small fuzzing related improvements
+            #921  docs: Add missing documentation of error code
+                    XML_ERROR_NOT_STARTED that was introduced with 2.6.4
+            #941  docs: Document need for C++11 compiler for use from C++
+            #959  tests/benchmark: Fix a (harmless) TOCTTOU
+            #944  Windows: Fix installer target location of file xmlwf.xml
+                    for CMake
+            #953  Windows: Address warning -Wunknown-warning-option
+                    about -Wno-pedantic-ms-format from LLVM MinGW
+            #971  Address Cppcheck warnings
+       #969 #970  Mass-migrate links from http:// to https://
+    #947 #958 ..
+       #974 #975  Document changes since the previous release
+       #974 #975  Version info bumped from 11:0:10 (libexpat*.so.1.10.0)
+                    to 11:1:10 (libexpat*.so.1.10.1); see https://verbump.de/
+                    for what these numbers do
+
+        Infrastructure:
+            #926  tests: Increase robustness
+    #927 #932 ..
+       #930 #933  tests: Increase test coverage
+    #617 #950 ..
+    #951 #952 ..
+    #954 #955 ..  Fuzzing: Add new fuzzer "xml_lpm_fuzzer" based on
+            #961    Google's libprotobuf-mutator ("LPM")
+            #957  Fuzzing|CI: Start producing fuzzing code coverage reports
+            #936  CI: Pass -q -q for LCOV >=2.1 in coverage.sh
+            #942  CI: Small fuzzing related improvements
+    #139 #203 ..
+       #791 #946  CI: Make GitHub Actions build using MSVC on Windows and
+                      produce 32bit and 64bit Windows binaries
+            #956  CI: Get off of about-to-be-removed Ubuntu 20.04
+       #960 #964  CI: Start uploading to Coverity Scan for static analysis
+            #972  CI: Stop loading DTD from the internet to address flaky CI
+            #971  CI: Adapt to breaking changes in Cppcheck
+
+        Special thanks to:
+            Alexander Gieringer
+            Berkay Eren Ürün
+            Hanno Böck
+            Jann Horn
+            Mark Brand
+            Sebastian Andrzej Siewior
+            Snild Dolkow
+            Thomas Pröll
+            Tomas Korbar
+            valord577
+                 and
+            Google Project Zero
+            Linutronix
+            Red Hat
+            Siemens
+
 Release 2.6.4 Wed November 6 2024
         Security fixes:
             #915  CVE-2024-50602 -- Fix crash within function XML_ResumeParser
@@ -46,6 +125,8 @@ Release 2.6.4 Wed November 6 2024
             #904  tests: Resolve duplicate handler
        #317 #918  tests: Improve tests on doctype closing (ex CVE-2019-15903)
             #914  Fix signedness of format strings
+            #915  For use from C++, expat.h started requiring C++11 due to
+                    use of C99 features
        #919 #920  Version info bumped from 10:3:9 (libexpat*.so.1.9.3)
                     to 11:0:10 (libexpat*.so.1.10.0); see https://verbump.de/
                     for what these numbers do

+ 6 - 11
libs/expat/README.md

@@ -11,7 +11,7 @@
 > at the top of the `Changes` file.
 
 
-# Expat, Release 2.6.4
+# Expat, Release 2.7.0
 
 This is Expat, a C99 library for parsing
 [XML 1.0 Fourth Edition](https://www.w3.org/TR/2006/REC-xml-20060816/), started by
@@ -22,9 +22,9 @@ are called when the parser discovers the associated structures in the
 document being parsed.  A start tag is an example of the kind of
 structures for which you may register handlers.
 
-Expat supports the following compilers:
+Expat supports the following C99 compilers:
 
-- GNU GCC >=4.5
+- GNU GCC >=4.5 (for use from C) or GNU GCC >=4.8.1 (for use from C++)
 - LLVM Clang >=3.5
 - Microsoft Visual Studio >=16.0/2019 (rolling `${today} minus 5 years`)
 
@@ -52,7 +52,7 @@ This approach leverages CMake's own [module `FindEXPAT`](https://cmake.org/cmake
 Notice the *uppercase* `EXPAT` in the following example:
 
 ```cmake
-cmake_minimum_required(VERSION 3.0)  # or 3.10, see below
+cmake_minimum_required(VERSION 3.10)
 
 project(hello VERSION 1.0.0)
 
@@ -62,12 +62,7 @@ add_executable(hello
     hello.c
 )
 
-# a) for CMake >=3.10 (see CMake's FindEXPAT docs)
 target_link_libraries(hello PUBLIC EXPAT::EXPAT)
-
-# b) for CMake >=3.0
-target_include_directories(hello PRIVATE ${EXPAT_INCLUDE_DIRS})
-target_link_libraries(hello PUBLIC ${EXPAT_LIBRARIES})
 ```
 
 ### b) `find_package` with Config Mode
@@ -85,7 +80,7 @@ or
 Notice the *lowercase* `expat` in the following example:
 
 ```cmake
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.10)
 
 project(hello VERSION 1.0.0)
 
@@ -295,7 +290,7 @@ EXPAT_ENABLE_INSTALL:BOOL=ON
 // Use /MT flag (static CRT) when compiling in MSVC
 EXPAT_MSVC_STATIC_CRT:BOOL=OFF
 
-// Build fuzzers via ossfuzz for the expat library
+// Build fuzzers via OSS-Fuzz for the expat library
 EXPAT_OSSFUZZ_BUILD:BOOL=OFF
 
 // Build a shared expat library

+ 2 - 2
libs/expat/cmake/autotools/expat-noconfig__macos.cmake.in

@@ -8,12 +8,12 @@ set(CMAKE_IMPORT_FILE_VERSION 1)
 # Import target "expat::expat" for configuration "NoConfig"
 set_property(TARGET expat::expat APPEND PROPERTY IMPORTED_CONFIGURATIONS NOCONFIG)
 set_target_properties(expat::expat PROPERTIES
-  IMPORTED_LOCATION_NOCONFIG "${_IMPORT_PREFIX}/@LIBDIR_BASENAME@/libexpat.@SO_MAJOR@.@SO_MINOR@.@SO_PATCH@.dylib"
+  IMPORTED_LOCATION_NOCONFIG "${_IMPORT_PREFIX}/@LIBDIR_BASENAME@/libexpat.@[email protected]"
   IMPORTED_SONAME_NOCONFIG "@rpath/libexpat.@[email protected]"
   )
 
 list(APPEND _cmake_import_check_targets expat::expat )
-list(APPEND _cmake_import_check_files_for_expat::expat "${_IMPORT_PREFIX}/@LIBDIR_BASENAME@/libexpat.@SO_MAJOR@.@SO_MINOR@.@SO_PATCH@.dylib" )
+list(APPEND _cmake_import_check_files_for_expat::expat "${_IMPORT_PREFIX}/@LIBDIR_BASENAME@/libexpat.@[email protected]" )
 
 # Commands beyond this point should not need to know the version.
 set(CMAKE_IMPORT_FILE_VERSION)

+ 2 - 2
libs/expat/cmake/autotools/expat.cmake

@@ -1,13 +1,13 @@
 # Generated by CMake
 
 if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.8)
-   message(FATAL_ERROR "CMake >= 2.8.0 required")
+   message(FATAL_ERROR "CMake >= 2.8.12 required")
 endif()
 if(CMAKE_VERSION VERSION_LESS "2.8.12")
    message(FATAL_ERROR "CMake >= 2.8.12 required")
 endif()
 cmake_policy(PUSH)
-cmake_policy(VERSION 2.8.12...3.28)
+cmake_policy(VERSION 2.8.12...3.29)
 #----------------------------------------------------------------
 # Generated CMake target import file.
 #----------------------------------------------------------------

Разница между файлами не показана из-за своего большого размера
+ 651 - 83
libs/expat/configure


+ 2 - 2
libs/expat/configure.ac

@@ -11,7 +11,7 @@ dnl   Copyright (c) 2000      Clark Cooper <[email protected]>
 dnl   Copyright (c) 2000-2005 Fred L. Drake, Jr. <[email protected]>
 dnl   Copyright (c) 2001-2003 Greg Stein <[email protected]>
 dnl   Copyright (c) 2006-2012 Karl Waclawek <[email protected]>
-dnl   Copyright (c) 2016-2024 Sebastian Pipping <[email protected]>
+dnl   Copyright (c) 2016-2025 Sebastian Pipping <[email protected]>
 dnl   Copyright (c) 2017      S. P. Zeidler <[email protected]>
 dnl   Copyright (c) 2017      Stephen Groat <[email protected]>
 dnl   Copyright (c) 2017-2020 Joe Orton <[email protected]>
@@ -85,7 +85,7 @@ dnl If the API changes incompatibly set LIBAGE back to 0
 dnl
 
 LIBCURRENT=11  # sync
-LIBREVISION=0  # with
+LIBREVISION=1  # with
 LIBAGE=10      # CMakeLists.txt!
 
 AC_CONFIG_HEADERS([expat_config.h])

+ 203 - 54
libs/expat/conftools/ltmain.sh

@@ -2,7 +2,7 @@
 ## DO NOT EDIT - This file generated from ./build-aux/ltmain.in
 ##               by inline-source v2019-02-19.15
 
-# libtool (GNU libtool) 2.5.3
+# libtool (GNU libtool) 2.5.4
 # Provide generalized library-building support services.
 # Written by Gordon Matzigkeit <[email protected]>, 1996
 
@@ -31,8 +31,8 @@
 
 PROGRAM=libtool
 PACKAGE=libtool
-VERSION=2.5.3
-package_revision=2.5.3
+VERSION=2.5.4
+package_revision=2.5.4
 
 
 ## ------ ##
@@ -589,7 +589,7 @@ func_require_term_colors ()
 
   # _G_HAVE_PLUSEQ_OP
   # Can be empty, in which case the shell is probed, "yes" if += is
-  # useable or anything else if it does not work.
+  # usable or anything else if it does not work.
   test -z "$_G_HAVE_PLUSEQ_OP" \
     && (eval 'x=a; x+=" b"; test "a b" = "$x"') 2>/dev/null \
     && _G_HAVE_PLUSEQ_OP=yes
@@ -739,7 +739,7 @@ eval 'func_dirname ()
 #             to NONDIR_REPLACEMENT.
 #             value returned in "$func_dirname_result"
 #   basename: Compute filename of FILE.
-#             value retuned in "$func_basename_result"
+#             value returned in "$func_basename_result"
 # For efficiency, we do not delegate to the functions above but instead
 # duplicate the functionality here.
 eval 'func_dirname_and_basename ()
@@ -897,7 +897,7 @@ func_mkdir_p ()
       # While some portion of DIR does not yet exist...
       while test ! -d "$_G_directory_path"; do
         # ...make a list in topmost first order.  Use a colon delimited
-	# list incase some portion of path contains whitespace.
+	# list in case some portion of path contains whitespace.
         _G_dir_list=$_G_directory_path:$_G_dir_list
 
         # If the last portion added has no slash in it, the list is done
@@ -2215,7 +2215,30 @@ func_version ()
 # End:
 
 # Set a version string.
-scriptversion='(GNU libtool) 2.5.3'
+scriptversion='(GNU libtool) 2.5.4'
+
+# func_version
+# ------------
+# Echo version message to standard output and exit.
+func_version ()
+{
+    $debug_cmd
+
+	year=`date +%Y`
+
+	cat <<EOF
+$progname $scriptversion
+Copyright (C) $year Free Software Foundation, Inc.
+License GPLv2+: GNU GPL version 2 or later <https://gnu.org/licenses/gpl.html>
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.
+
+Originally written by Gordon Matzigkeit, 1996
+(See AUTHORS for complete contributor listing)
+EOF
+
+    exit $?
+}
 
 
 # func_echo ARG...
@@ -2238,18 +2261,6 @@ func_echo ()
 }
 
 
-# func_warning ARG...
-# -------------------
-# Libtool warnings are not categorized, so override funclib.sh
-# func_warning with this simpler definition.
-func_warning ()
-{
-    $debug_cmd
-
-    $warning_func ${1+"$@"}
-}
-
-
 ## ---------------- ##
 ## Options parsing. ##
 ## ---------------- ##
@@ -2261,19 +2272,23 @@ usage='$progpath [OPTION]... [MODE-ARG]...'
 
 # Short help message in response to '-h'.
 usage_message="Options:
-       --config             show all configuration variables
-       --debug              enable verbose shell tracing
-   -n, --dry-run            display commands without modifying any files
-       --features           display basic configuration information and exit
-       --mode=MODE          use operation mode MODE
-       --no-warnings        equivalent to '-Wnone'
-       --preserve-dup-deps  don't remove duplicate dependency libraries
-       --quiet, --silent    don't print informational messages
-       --tag=TAG            use configuration variables from tag TAG
-   -v, --verbose            print more informational messages than default
-       --version            print version information
-   -W, --warnings=CATEGORY  report the warnings falling in CATEGORY [all]
-   -h, --help, --help-all   print short, long, or detailed help message
+       --config                 show all configuration variables
+       --debug                  enable verbose shell tracing
+   -n, --dry-run                display commands without modifying any files
+       --features               display basic configuration information
+       --finish                 use operation '--mode=finish'
+       --mode=MODE              use operation mode MODE
+       --no-finish              don't update shared library cache
+       --no-quiet, --no-silent  print default informational messages
+       --no-warnings            equivalent to '-Wnone'
+       --preserve-dup-deps      don't remove duplicate dependency libraries
+       --quiet, --silent        don't print informational messages
+       --reorder-cache=DIRS     reorder shared library cache for preferred DIRS
+       --tag=TAG                use configuration variables from tag TAG
+   -v, --verbose                print more informational messages than default
+       --version                print version information
+   -W, --warnings=CATEGORY      report the warnings falling in CATEGORY [all]
+   -h, --help, --help-all       print short, long, or detailed help message
 "
 
 # Additional text appended to 'usage_message' in response to '--help'.
@@ -2306,7 +2321,7 @@ include the following information:
        compiler:       $LTCC
        compiler flags: $LTCFLAGS
        linker:         $LD (gnu? $with_gnu_ld)
-       version:        $progname (GNU libtool) 2.5.3
+       version:        $progname $scriptversion
        automake:       `($AUTOMAKE --version) 2>/dev/null |$SED 1q`
        autoconf:       `($AUTOCONF --version) 2>/dev/null |$SED 1q`
 
@@ -2502,8 +2517,11 @@ libtool_options_prep ()
     opt_dry_run=false
     opt_help=false
     opt_mode=
+    opt_reorder_cache=false
     opt_preserve_dup_deps=false
     opt_quiet=false
+    opt_finishing=true
+    opt_warning=
 
     nonopt=
     preserve_args=
@@ -2593,14 +2611,18 @@ libtool_parse_options ()
                           clean|compile|execute|finish|install|link|relink|uninstall) ;;
 
                           # Catch anything else as an error
-                          *) func_error "invalid argument for $_G_opt"
+                          *) func_error "invalid argument '$1' for $_G_opt"
                              exit_cmd=exit
-                             break
                              ;;
                         esac
                         shift
                         ;;
 
+        --no-finish)
+                        opt_finishing=false
+                        func_append preserve_args " $_G_opt"
+                        ;;
+
         --no-silent|--no-quiet)
                         opt_quiet=false
                         func_append preserve_args " $_G_opt"
@@ -2616,6 +2638,24 @@ libtool_parse_options ()
                         func_append preserve_args " $_G_opt"
                         ;;
 
+        --reorder-cache)
+                        opt_reorder_cache=true
+                        shared_lib_dirs=$1
+                        if test -n "$shared_lib_dirs"; then
+                          case $1 in
+                            # Must begin with /:
+                            /*) ;;
+
+                            # Catch anything else as an error (relative paths)
+                            *) func_error "invalid argument '$1' for $_G_opt"
+                               func_error "absolute paths are required for $_G_opt"
+                               exit_cmd=exit
+                               ;;
+                          esac
+                        fi
+                        shift
+                        ;;
+
         --silent|--quiet)
                         opt_quiet=:
                         opt_verbose=false
@@ -2652,6 +2692,18 @@ libtool_parse_options ()
 func_add_hook func_parse_options libtool_parse_options
 
 
+# func_warning ARG...
+# -------------------
+# Libtool warnings are not categorized, so override funclib.sh
+# func_warning with this simpler definition.
+func_warning ()
+{
+    if $opt_warning; then
+        $debug_cmd
+        $warning_func ${1+"$@"}
+    fi
+}
+
 
 # libtool_validate_options [ARG]...
 # ---------------------------------
@@ -3181,6 +3233,15 @@ func_convert_path_front_back_pathsep ()
 # end func_convert_path_front_back_pathsep
 
 
+# func_convert_delimited_path PATH ORIG_DELIMITER NEW_DELIMITER
+# Replaces a delimiter for a given path.
+func_convert_delimited_path ()
+{
+	converted_path=`$ECHO "$1" | $SED "s#$2#$3#g"`
+}
+# end func_convert_delimited_path
+
+
 ##################################################
 # $build to $host FILE NAME CONVERSION FUNCTIONS #
 ##################################################
@@ -3515,6 +3576,65 @@ func_dll_def_p ()
 }
 
 
+# func_reorder_shared_lib_cache DIRS
+# Reorder the shared library cache by unconfiguring previous shared library cache
+# and configuring preferred search directories before previous search directories.
+# Previous shared library cache: /usr/lib /usr/local/lib
+# Preferred search directories: /tmp/testing
+# Reordered shared library cache: /tmp/testing /usr/lib /usr/local/lib
+func_reorder_shared_lib_cache ()
+{
+	$debug_cmd
+
+	case $host_os in
+	  openbsd*)
+	    get_search_directories=`PATH="$PATH:/sbin" ldconfig -r | $GREP "search directories" | $SED "s#.*search directories:\ ##g"`
+	    func_convert_delimited_path "$get_search_directories" ':' '\ '
+	    save_search_directories=$converted_path
+	    func_convert_delimited_path "$1" ':' '\ '
+
+	    # Ensure directories exist
+	    for dir in $converted_path; do
+	      # Ensure each directory is an absolute path
+	      case $dir in
+	        /*) ;;
+	        *) func_error "Directory '$dir' is not an absolute path"
+	           exit $EXIT_FAILURE ;;
+	      esac
+	      # Ensure no trailing slashes
+	      func_stripname '' '/' "$dir"
+	      dir=$func_stripname_result
+	      if test -d "$dir"; then
+	        if test -n "$preferred_search_directories"; then
+	          preferred_search_directories="$preferred_search_directories $dir"
+	        else
+	          preferred_search_directories=$dir
+	        fi
+	      else
+	        func_error "Directory '$dir' does not exist"
+	        exit $EXIT_FAILURE
+	      fi
+	    done
+
+	    PATH="$PATH:/sbin" ldconfig -U $save_search_directories
+	    PATH="$PATH:/sbin" ldconfig -m $preferred_search_directories $save_search_directories
+	    get_search_directories=`PATH="$PATH:/sbin" ldconfig -r | $GREP "search directories" | $SED "s#.*search directories:\ ##g"`
+	    func_convert_delimited_path "$get_search_directories" ':' '\ '
+	    reordered_search_directories=$converted_path
+
+	    $ECHO "Original: $save_search_directories"
+	    $ECHO "Reordered: $reordered_search_directories"
+	    exit $EXIT_SUCCESS
+	  ;;
+	  *)
+	    func_error "--reorder-cache is not supported for host_os=$host_os."
+	    exit $EXIT_FAILURE
+	  ;;
+	esac
+}
+# end func_reorder_shared_lib_cache
+
+
 # func_mode_compile arg...
 func_mode_compile ()
 {
@@ -4087,6 +4207,12 @@ if $opt_help; then
 fi
 
 
+# If option '--reorder-cache', reorder the shared library cache and exit.
+if $opt_reorder_cache; then
+    func_reorder_shared_lib_cache $shared_lib_dirs
+fi
+
+
 # func_mode_execute arg...
 func_mode_execute ()
 {
@@ -4271,7 +4397,7 @@ func_mode_finish ()
       fi
     fi
 
-    if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+    if test -n "$finish_cmds$finish_eval" && test -n "$libdirs" && $opt_finishing; then
       for libdir in $libdirs; do
 	if test -n "$finish_cmds"; then
 	  # Do each command in the finish commands.
@@ -4296,6 +4422,12 @@ func_mode_finish ()
       for libdir in $libdirs; do
 	$ECHO "   $libdir"
       done
+      if test "false" = "$opt_finishing"; then
+        echo
+        echo "NOTE: finish_cmds were not executed during testing, so you must"
+        echo "manually run ldconfig to add a given test directory, LIBDIR, to"
+        echo "the search path for generated executables."
+      fi
       echo
       echo "If you ever happen to want to link against installed libraries"
       echo "in a given directory, LIBDIR, you must either use libtool, and"
@@ -4532,8 +4664,15 @@ func_mode_install ()
 	func_append dir "$objdir"
 
 	if test -n "$relink_command"; then
+	  # Strip any trailing slash from the destination.
+	  func_stripname '' '/' "$libdir"
+	  destlibdir=$func_stripname_result
+
+	  func_stripname '' '/' "$destdir"
+	  s_destdir=$func_stripname_result
+
 	  # Determine the prefix the user has applied to our future dir.
-	  inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"`
+	  inst_prefix_dir=`$ECHO "X$s_destdir" | $Xsed -e "s%$destlibdir\$%%"`
 
 	  # Don't allow the user to place us outside of our expected
 	  # location b/c this prevents finding dependent libraries that
@@ -6782,6 +6921,7 @@ func_mode_link ()
     finalize_command=$nonopt
 
     compile_rpath=
+    compile_rpath_tail=
     finalize_rpath=
     compile_shlibpath=
     finalize_shlibpath=
@@ -7337,7 +7477,8 @@ func_mode_link ()
       # Tru64 UNIX uses -model [arg] to determine the layout of C++
       # classes, name mangling, and exception handling.
       # Darwin uses the -arch flag to determine output architecture.
-      -model|-arch|-isysroot|--sysroot)
+      # -q <option> for IBM XL C/C++ compiler.
+      -model|-arch|-isysroot|--sysroot|-q)
 	func_append compiler_flags " $arg"
 	func_append compile_command " $arg"
 	func_append finalize_command " $arg"
@@ -8433,10 +8574,11 @@ func_mode_link ()
 	    case " $sys_lib_dlsearch_path " in
 	    *" $absdir "*) ;;
 	    *)
-	      case "$compile_rpath " in
+	      case "$compile_rpath$compile_rpath_tail " in
 	      *" $absdir "*) ;;
 	      *) case $absdir in
                  "$progdir/"*) func_append compile_rpath " $absdir" ;;
+                 *) func_append compile_rpath_tail " $absdir" ;;
 		 esac
 	      esac
 	      ;;
@@ -8509,10 +8651,11 @@ func_mode_link ()
 	    case " $sys_lib_dlsearch_path " in
 	    *" $absdir "*) ;;
 	    *)
-	      case "$compile_rpath " in
+	      case "$compile_rpath$compile_rpath_tail " in
 	      *" $absdir "*) ;;
 	      *) case $absdir in
                  "$progdir/"*) func_append compile_rpath " $absdir" ;;
+                 *) func_append compile_rpath_tail " $absdir" ;;
 		 esac
 	      esac
 	      ;;
@@ -8588,6 +8731,7 @@ func_mode_link ()
 		case $host in
 		  *-*-sco3.2v5.0.[024]*) add_dir=-L$dir ;;
 		  *-*-sysv4*uw2*) add_dir=-L$dir ;;
+		  *-*-emscripten*) add_dir=-L$dir ;;
 		  *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \
 		    *-*-unixware7*) add_dir=-L$dir ;;
 		  *-*-darwin* )
@@ -8876,6 +9020,8 @@ func_mode_link ()
       done # for deplib in $libs
 
       func_append temp_rpath "$temp_rpath_tail"
+      func_append compile_rpath "$compile_rpath_tail"
+
       if test link = "$pass"; then
 	if test prog = "$linkmode"; then
 	  compile_deplibs="$new_inherited_linker_flags $compile_deplibs"
@@ -9163,6 +9309,9 @@ func_mode_link ()
 	    revision=$number_minor
 	    lt_irix_increment=no
 	    ;;
+	  *)
+	    func_fatal_configuration "$modename: unknown library version type '$version_type'"
+	    ;;
 	  esac
 	  ;;
 	no)
@@ -10059,20 +10208,7 @@ func_mode_link ()
 	  last_robj=
 	  k=1
 
-	  if test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then
-	    output=$output_objdir/$output_la.lnkscript
-	    func_verbose "creating GNU ld script: $output"
-	    echo 'INPUT (' > $output
-	    for obj in $save_libobjs
-	    do
-	      func_to_tool_file "$obj"
-	      $ECHO "$func_to_tool_file_result" >> $output
-	    done
-	    echo ')' >> $output
-	    func_append delfiles " $output"
-	    func_to_tool_file "$output"
-	    output=$func_to_tool_file_result
-	  elif test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then
+	  if test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then
 	    output=$output_objdir/$output_la.lnk
 	    func_verbose "creating linker input file list: $output"
 	    : > $output
@@ -10091,6 +10227,19 @@ func_mode_link ()
 	    func_append delfiles " $output"
 	    func_to_tool_file "$output"
 	    output=$firstobj\"$file_list_spec$func_to_tool_file_result\"
+	  elif test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then
+	    output=$output_objdir/$output_la.lnkscript
+	    func_verbose "creating GNU ld script: $output"
+	    echo 'INPUT (' > $output
+	    for obj in $save_libobjs
+	    do
+	      func_to_tool_file "$obj"
+	      $ECHO "$func_to_tool_file_result" >> $output
+	    done
+	    echo ')' >> $output
+	    func_append delfiles " $output"
+	    func_to_tool_file "$output"
+	    output=$func_to_tool_file_result
 	  else
 	    if test -n "$save_libobjs"; then
 	      func_verbose "creating reloadable object files..."

+ 7 - 2
libs/expat/doc/reference.html

@@ -14,7 +14,7 @@
    Copyright (c) 2000      Clark Cooper <[email protected]>
    Copyright (c) 2000-2004 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2002-2012 Karl Waclawek <[email protected]>
-   Copyright (c) 2017-2024 Sebastian Pipping <[email protected]>
+   Copyright (c) 2017-2025 Sebastian Pipping <[email protected]>
    Copyright (c) 2017      Jakub Wilk <[email protected]>
    Copyright (c) 2021      Tomas Korbar <[email protected]>
    Copyright (c) 2021      Nicolas Cavallari <[email protected]>
@@ -52,7 +52,7 @@
   <div>
     <h1>
       The Expat XML Parser
-      <small>Release 2.6.4</small>
+      <small>Release 2.7.0</small>
     </h1>
   </div>
 <div class="content">
@@ -1267,6 +1267,11 @@ call-backs, except when parsing an external parameter entity and
 <code>XML_STATUS_ERROR</code> otherwise.  The possible error codes
 are:</p>
 <dl>
+  <dt><code>XML_ERROR_NOT_STARTED</code></dt>
+  <dd>
+    when stopping or suspending a parser before it has started,
+    added in Expat 2.6.4.
+  </dd>
   <dt><code>XML_ERROR_SUSPENDED</code></dt>
   <dd>when suspending an already suspended parser.</dd>
   <dt><code>XML_ERROR_FINISHED</code></dt>

+ 1 - 1
libs/expat/doc/xmlwf.1

@@ -5,7 +5,7 @@
 \\$2 \(la\\$1\(ra\\$3
 ..
 .if \n(.g .mso www.tmac
-.TH XMLWF 1 "November 6, 2024" "" ""
+.TH XMLWF 1 "March 13, 2025" "" ""
 .SH NAME
 xmlwf \- Determines if an XML document is well-formed
 .SH SYNOPSIS

+ 2 - 2
libs/expat/doc/xmlwf.xml

@@ -9,7 +9,7 @@
    Copyright (c) 2001      Scott Bronson <[email protected]>
    Copyright (c) 2002-2003 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2009      Karl Waclawek <[email protected]>
-   Copyright (c) 2016-2024 Sebastian Pipping <[email protected]>
+   Copyright (c) 2016-2025 Sebastian Pipping <[email protected]>
    Copyright (c) 2016      Ardo van Rangelrooij <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
    Copyright (c) 2020      Joe Orton <[email protected]>
@@ -21,7 +21,7 @@
           "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
   <!ENTITY dhfirstname "<firstname>Scott</firstname>">
   <!ENTITY dhsurname   "<surname>Bronson</surname>">
-  <!ENTITY dhdate      "<date>November 6, 2024</date>">
+  <!ENTITY dhdate      "<date>March 13, 2025</date>">
   <!-- Please adjust this^^ date whenever cutting a new release. -->
   <!ENTITY dhsection   "<manvolnum>1</manvolnum>">
   <!ENTITY dhemail     "<email>[email protected]</email>">

+ 3 - 3
libs/expat/expat_config.h

@@ -83,7 +83,7 @@
 #define PACKAGE_NAME "expat"
 
 /* Define to the full name and version of this package. */
-#define PACKAGE_STRING "expat 2.6.4"
+#define PACKAGE_STRING "expat 2.7.0"
 
 /* Define to the one symbol short name of this package. */
 #define PACKAGE_TARNAME "expat"
@@ -92,7 +92,7 @@
 #define PACKAGE_URL ""
 
 /* Define to the version of this package. */
-#define PACKAGE_VERSION "2.6.4"
+#define PACKAGE_VERSION "2.7.0"
 
 /* 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
@@ -100,7 +100,7 @@
 #define STDC_HEADERS 1
 
 /* Version number of package */
-#define VERSION "2.6.4"
+#define VERSION "2.7.0"
 
 /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
    significant byte first (like Motorola and SPARC, unlike Intel). */

+ 1 - 1
libs/expat/fuzz/xml_parse_fuzzer.c

@@ -5,7 +5,7 @@
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *      https://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,

+ 1 - 1
libs/expat/fuzz/xml_parsebuffer_fuzzer.c

@@ -5,7 +5,7 @@
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *      https://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,

+ 3 - 3
libs/expat/lib/expat.h

@@ -11,7 +11,7 @@
    Copyright (c) 2000-2005 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2001-2002 Greg Stein <[email protected]>
    Copyright (c) 2002-2016 Karl Waclawek <[email protected]>
-   Copyright (c) 2016-2024 Sebastian Pipping <[email protected]>
+   Copyright (c) 2016-2025 Sebastian Pipping <[email protected]>
    Copyright (c) 2016      Cristian Rodríguez <[email protected]>
    Copyright (c) 2016      Thomas Beutlich <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
@@ -1067,8 +1067,8 @@ XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled);
    See https://semver.org
 */
 #define XML_MAJOR_VERSION 2
-#define XML_MINOR_VERSION 6
-#define XML_MICRO_VERSION 4
+#define XML_MINOR_VERSION 7
+#define XML_MICRO_VERSION 0
 
 #ifdef __cplusplus
 }

+ 418 - 154
libs/expat/lib/xmlparse.c

@@ -1,4 +1,4 @@
-/* c5625880f4bf417c1463deee4eb92d86ff413f802048621c57e25fe483eb59e4 (2.6.4+)
+/* 7d6840a33c250b74adb0ba295d6ec818dccebebaffc8c3ed27d0b29c28adbeb3 (2.7.0+)
                             __  __            _
                          ___\ \/ /_ __   __ _| |_
                         / _ \\  /| '_ \ / _` | __|
@@ -13,7 +13,7 @@
    Copyright (c) 2002-2016 Karl Waclawek <[email protected]>
    Copyright (c) 2005-2009 Steven Solie <[email protected]>
    Copyright (c) 2016      Eric Rahm <[email protected]>
-   Copyright (c) 2016-2024 Sebastian Pipping <[email protected]>
+   Copyright (c) 2016-2025 Sebastian Pipping <[email protected]>
    Copyright (c) 2016      Gaurav <[email protected]>
    Copyright (c) 2016      Thomas Beutlich <[email protected]>
    Copyright (c) 2016      Gustavo Grieco <[email protected]>
@@ -39,7 +39,7 @@
    Copyright (c) 2022      Sean McBride <[email protected]>
    Copyright (c) 2023      Owain Davies <[email protected]>
    Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <[email protected]>
-   Copyright (c) 2024      Berkay Eren Ürün <[email protected]>
+   Copyright (c) 2024-2025 Berkay Eren Ürün <[email protected]>
    Copyright (c) 2024      Hanno Böck <[email protected]>
    Licensed under the MIT license:
 
@@ -325,6 +325,10 @@ typedef struct {
   const XML_Char *publicId;
   const XML_Char *notation;
   XML_Bool open;
+  XML_Bool hasMore; /* true if entity has not been completely processed */
+  /* An entity can be open while being already completely processed (hasMore ==
+    XML_FALSE). The reason is the delayed closing of entities until their inner
+    entities are processed and closed */
   XML_Bool is_param;
   XML_Bool is_internal; /* true if declared in internal subset outside PE */
 } ENTITY;
@@ -415,6 +419,12 @@ typedef struct {
   int *scaffIndex;
 } DTD;
 
+enum EntityType {
+  ENTITY_INTERNAL,
+  ENTITY_ATTRIBUTE,
+  ENTITY_VALUE,
+};
+
 typedef struct open_internal_entity {
   const char *internalEventPtr;
   const char *internalEventEndPtr;
@@ -422,6 +432,7 @@ typedef struct open_internal_entity {
   ENTITY *entity;
   int startTagLevel;
   XML_Bool betweenDecl; /* WFC: PE Between Declarations */
+  enum EntityType type;
 } OPEN_INTERNAL_ENTITY;
 
 enum XML_Account {
@@ -481,8 +492,8 @@ static enum XML_Error doProlog(XML_Parser parser, const ENCODING *enc,
                                const char *next, const char **nextPtr,
                                XML_Bool haveMore, XML_Bool allowClosingDoctype,
                                enum XML_Account account);
-static enum XML_Error processInternalEntity(XML_Parser parser, ENTITY *entity,
-                                            XML_Bool betweenDecl);
+static enum XML_Error processEntity(XML_Parser parser, ENTITY *entity,
+                                    XML_Bool betweenDecl, enum EntityType type);
 static enum XML_Error doContent(XML_Parser parser, int startTagLevel,
                                 const ENCODING *enc, const char *start,
                                 const char *end, const char **endPtr,
@@ -513,18 +524,22 @@ static enum XML_Error storeAttributeValue(XML_Parser parser,
                                           const char *ptr, const char *end,
                                           STRING_POOL *pool,
                                           enum XML_Account account);
-static enum XML_Error appendAttributeValue(XML_Parser parser,
-                                           const ENCODING *enc,
-                                           XML_Bool isCdata, const char *ptr,
-                                           const char *end, STRING_POOL *pool,
-                                           enum XML_Account account);
+static enum XML_Error
+appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
+                     const char *ptr, const char *end, STRING_POOL *pool,
+                     enum XML_Account account, const char **nextPtr);
 static ATTRIBUTE_ID *getAttributeId(XML_Parser parser, const ENCODING *enc,
                                     const char *start, const char *end);
 static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType);
 #if XML_GE == 1
 static enum XML_Error storeEntityValue(XML_Parser parser, const ENCODING *enc,
                                        const char *start, const char *end,
-                                       enum XML_Account account);
+                                       enum XML_Account account,
+                                       const char **nextPtr);
+static enum XML_Error callStoreEntityValue(XML_Parser parser,
+                                           const ENCODING *enc,
+                                           const char *start, const char *end,
+                                           enum XML_Account account);
 #else
 static enum XML_Error storeSelfEntityValue(XML_Parser parser, ENTITY *entity);
 #endif
@@ -709,6 +724,10 @@ struct XML_ParserStruct {
   const char *m_positionPtr;
   OPEN_INTERNAL_ENTITY *m_openInternalEntities;
   OPEN_INTERNAL_ENTITY *m_freeInternalEntities;
+  OPEN_INTERNAL_ENTITY *m_openAttributeEntities;
+  OPEN_INTERNAL_ENTITY *m_freeAttributeEntities;
+  OPEN_INTERNAL_ENTITY *m_openValueEntities;
+  OPEN_INTERNAL_ENTITY *m_freeValueEntities;
   XML_Bool m_defaultExpandInternalEntities;
   int m_tagLevel;
   ENTITY *m_declEntity;
@@ -756,6 +775,7 @@ struct XML_ParserStruct {
   ACCOUNTING m_accounting;
   ENTITY_STATS m_entity_stats;
 #endif
+  XML_Bool m_reenter;
 };
 
 #define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s)))
@@ -1028,7 +1048,29 @@ callProcessor(XML_Parser parser, const char *start, const char *end,
 #if defined(XML_TESTING)
   g_bytesScanned += (unsigned)have_now;
 #endif
-  const enum XML_Error ret = parser->m_processor(parser, start, end, endPtr);
+  // Run in a loop to eliminate dangerous recursion depths
+  enum XML_Error ret;
+  *endPtr = start;
+  while (1) {
+    // Use endPtr as the new start in each iteration, since it will
+    // be set to the next start point by m_processor.
+    ret = parser->m_processor(parser, *endPtr, end, endPtr);
+
+    // Make parsing status (and in particular XML_SUSPENDED) take
+    // precedence over re-enter flag when they disagree
+    if (parser->m_parsingStatus.parsing != XML_PARSING) {
+      parser->m_reenter = XML_FALSE;
+    }
+
+    if (! parser->m_reenter) {
+      break;
+    }
+
+    parser->m_reenter = XML_FALSE;
+    if (ret != XML_ERROR_NONE)
+      return ret;
+  }
+
   if (ret == XML_ERROR_NONE) {
     // if we consumed nothing, remember what we had on this parse attempt.
     if (*endPtr == start) {
@@ -1139,6 +1181,8 @@ parserCreate(const XML_Char *encodingName,
   parser->m_freeBindingList = NULL;
   parser->m_freeTagList = NULL;
   parser->m_freeInternalEntities = NULL;
+  parser->m_freeAttributeEntities = NULL;
+  parser->m_freeValueEntities = NULL;
 
   parser->m_groupSize = 0;
   parser->m_groupConnector = NULL;
@@ -1241,6 +1285,8 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
   parser->m_eventEndPtr = NULL;
   parser->m_positionPtr = NULL;
   parser->m_openInternalEntities = NULL;
+  parser->m_openAttributeEntities = NULL;
+  parser->m_openValueEntities = NULL;
   parser->m_defaultExpandInternalEntities = XML_TRUE;
   parser->m_tagLevel = 0;
   parser->m_tagStack = NULL;
@@ -1251,6 +1297,8 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
   parser->m_unknownEncodingData = NULL;
   parser->m_parentParser = NULL;
   parser->m_parsingStatus.parsing = XML_INITIALIZED;
+  // Reentry can only be triggered inside m_processor calls
+  parser->m_reenter = XML_FALSE;
 #ifdef XML_DTD
   parser->m_isParamEntity = XML_FALSE;
   parser->m_useForeignDTD = XML_FALSE;
@@ -1310,6 +1358,24 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) {
     openEntity->next = parser->m_freeInternalEntities;
     parser->m_freeInternalEntities = openEntity;
   }
+  /* move m_openAttributeEntities to m_freeAttributeEntities (i.e. same task but
+   * for attributes) */
+  openEntityList = parser->m_openAttributeEntities;
+  while (openEntityList) {
+    OPEN_INTERNAL_ENTITY *openEntity = openEntityList;
+    openEntityList = openEntity->next;
+    openEntity->next = parser->m_freeAttributeEntities;
+    parser->m_freeAttributeEntities = openEntity;
+  }
+  /* move m_openValueEntities to m_freeValueEntities (i.e. same task but
+   * for value entities) */
+  openEntityList = parser->m_openValueEntities;
+  while (openEntityList) {
+    OPEN_INTERNAL_ENTITY *openEntity = openEntityList;
+    openEntityList = openEntity->next;
+    openEntity->next = parser->m_freeValueEntities;
+    parser->m_freeValueEntities = openEntity;
+  }
   moveToFreeBindingList(parser, parser->m_inheritedBindings);
   FREE(parser, parser->m_unknownEncodingMem);
   if (parser->m_unknownEncodingRelease)
@@ -1323,6 +1389,19 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) {
   return XML_TRUE;
 }
 
+static XML_Bool
+parserBusy(XML_Parser parser) {
+  switch (parser->m_parsingStatus.parsing) {
+  case XML_PARSING:
+  case XML_SUSPENDED:
+    return XML_TRUE;
+  case XML_INITIALIZED:
+  case XML_FINISHED:
+  default:
+    return XML_FALSE;
+  }
+}
+
 enum XML_Status XMLCALL
 XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) {
   if (parser == NULL)
@@ -1331,8 +1410,7 @@ XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) {
      XXX There's no way for the caller to determine which of the
      XXX possible error cases caused the XML_STATUS_ERROR return.
   */
-  if (parser->m_parsingStatus.parsing == XML_PARSING
-      || parser->m_parsingStatus.parsing == XML_SUSPENDED)
+  if (parserBusy(parser))
     return XML_STATUS_ERROR;
 
   /* Get rid of any previous encoding name */
@@ -1569,7 +1647,34 @@ XML_ParserFree(XML_Parser parser) {
     entityList = entityList->next;
     FREE(parser, openEntity);
   }
-
+  /* free m_openAttributeEntities and m_freeAttributeEntities */
+  entityList = parser->m_openAttributeEntities;
+  for (;;) {
+    OPEN_INTERNAL_ENTITY *openEntity;
+    if (entityList == NULL) {
+      if (parser->m_freeAttributeEntities == NULL)
+        break;
+      entityList = parser->m_freeAttributeEntities;
+      parser->m_freeAttributeEntities = NULL;
+    }
+    openEntity = entityList;
+    entityList = entityList->next;
+    FREE(parser, openEntity);
+  }
+  /* free m_openValueEntities and m_freeValueEntities */
+  entityList = parser->m_openValueEntities;
+  for (;;) {
+    OPEN_INTERNAL_ENTITY *openEntity;
+    if (entityList == NULL) {
+      if (parser->m_freeValueEntities == NULL)
+        break;
+      entityList = parser->m_freeValueEntities;
+      parser->m_freeValueEntities = NULL;
+    }
+    openEntity = entityList;
+    entityList = entityList->next;
+    FREE(parser, openEntity);
+  }
   destroyBindings(parser->m_freeBindingList, parser);
   destroyBindings(parser->m_inheritedBindings, parser);
   poolDestroy(&parser->m_tempPool);
@@ -1611,8 +1716,7 @@ XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD) {
     return XML_ERROR_INVALID_ARGUMENT;
 #ifdef XML_DTD
   /* block after XML_Parse()/XML_ParseBuffer() has been called */
-  if (parser->m_parsingStatus.parsing == XML_PARSING
-      || parser->m_parsingStatus.parsing == XML_SUSPENDED)
+  if (parserBusy(parser))
     return XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING;
   parser->m_useForeignDTD = useDTD;
   return XML_ERROR_NONE;
@@ -1627,8 +1731,7 @@ XML_SetReturnNSTriplet(XML_Parser parser, int do_nst) {
   if (parser == NULL)
     return;
   /* block after XML_Parse()/XML_ParseBuffer() has been called */
-  if (parser->m_parsingStatus.parsing == XML_PARSING
-      || parser->m_parsingStatus.parsing == XML_SUSPENDED)
+  if (parserBusy(parser))
     return;
   parser->m_ns_triplets = do_nst ? XML_TRUE : XML_FALSE;
 }
@@ -1897,8 +2000,7 @@ XML_SetParamEntityParsing(XML_Parser parser,
   if (parser == NULL)
     return 0;
   /* block after XML_Parse()/XML_ParseBuffer() has been called */
-  if (parser->m_parsingStatus.parsing == XML_PARSING
-      || parser->m_parsingStatus.parsing == XML_SUSPENDED)
+  if (parserBusy(parser))
     return 0;
 #ifdef XML_DTD
   parser->m_paramEntityParsing = peParsing;
@@ -1915,8 +2017,7 @@ XML_SetHashSalt(XML_Parser parser, unsigned long hash_salt) {
   if (parser->m_parentParser)
     return XML_SetHashSalt(parser->m_parentParser, hash_salt);
   /* block after XML_Parse()/XML_ParseBuffer() has been called */
-  if (parser->m_parsingStatus.parsing == XML_PARSING
-      || parser->m_parsingStatus.parsing == XML_SUSPENDED)
+  if (parserBusy(parser))
     return 0;
   parser->m_hash_secret_salt = hash_salt;
   return 1;
@@ -2230,6 +2331,11 @@ XML_GetBuffer(XML_Parser parser, int len) {
   return parser->m_bufferEnd;
 }
 
+static void
+triggerReenter(XML_Parser parser) {
+  parser->m_reenter = XML_TRUE;
+}
+
 enum XML_Status XMLCALL
 XML_StopParser(XML_Parser parser, XML_Bool resumable) {
   if (parser == NULL)
@@ -2704,8 +2810,9 @@ static enum XML_Error PTRCALL
 contentProcessor(XML_Parser parser, const char *start, const char *end,
                  const char **endPtr) {
   enum XML_Error result = doContent(
-      parser, 0, parser->m_encoding, start, end, endPtr,
-      (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT);
+      parser, parser->m_parentParser ? 1 : 0, parser->m_encoding, start, end,
+      endPtr, (XML_Bool)! parser->m_parsingStatus.finalBuffer,
+      XML_ACCOUNT_DIRECT);
   if (result == XML_ERROR_NONE) {
     if (! storeRawNames(parser))
       return XML_ERROR_NO_MEMORY;
@@ -2793,6 +2900,11 @@ externalEntityInitProcessor3(XML_Parser parser, const char *start,
       return XML_ERROR_NONE;
     case XML_FINISHED:
       return XML_ERROR_ABORTED;
+    case XML_PARSING:
+      if (parser->m_reenter) {
+        return XML_ERROR_UNEXPECTED_STATE; // LCOV_EXCL_LINE
+      }
+      /* Fall through */
     default:
       start = next;
     }
@@ -2966,7 +3078,7 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
             reportDefault(parser, enc, s, next);
           break;
         }
-        result = processInternalEntity(parser, entity, XML_FALSE);
+        result = processEntity(parser, entity, XML_FALSE, ENTITY_INTERNAL);
         if (result != XML_ERROR_NONE)
           return result;
       } else if (parser->m_externalEntityRefHandler) {
@@ -3092,7 +3204,9 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
     }
       if ((parser->m_tagLevel == 0)
           && (parser->m_parsingStatus.parsing != XML_FINISHED)) {
-        if (parser->m_parsingStatus.parsing == XML_SUSPENDED)
+        if (parser->m_parsingStatus.parsing == XML_SUSPENDED
+            || (parser->m_parsingStatus.parsing == XML_PARSING
+                && parser->m_reenter))
           parser->m_processor = epilogProcessor;
         else
           return epilogProcessor(parser, next, end, nextPtr);
@@ -3153,7 +3267,9 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
         }
         if ((parser->m_tagLevel == 0)
             && (parser->m_parsingStatus.parsing != XML_FINISHED)) {
-          if (parser->m_parsingStatus.parsing == XML_SUSPENDED)
+          if (parser->m_parsingStatus.parsing == XML_SUSPENDED
+              || (parser->m_parsingStatus.parsing == XML_PARSING
+                  && parser->m_reenter))
             parser->m_processor = epilogProcessor;
           else
             return epilogProcessor(parser, next, end, nextPtr);
@@ -3293,6 +3409,12 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
       return XML_ERROR_NONE;
     case XML_FINISHED:
       return XML_ERROR_ABORTED;
+    case XML_PARSING:
+      if (parser->m_reenter) {
+        *nextPtr = next;
+        return XML_ERROR_NONE;
+      }
+      /* Fall through */
     default:;
     }
   }
@@ -4217,6 +4339,11 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
       return XML_ERROR_NONE;
     case XML_FINISHED:
       return XML_ERROR_ABORTED;
+    case XML_PARSING:
+      if (parser->m_reenter) {
+        return XML_ERROR_UNEXPECTED_STATE; // LCOV_EXCL_LINE
+      }
+      /* Fall through */
     default:;
     }
   }
@@ -4549,7 +4676,7 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end,
       }
       /* found end of entity value - can store it now */
       return storeEntityValue(parser, parser->m_encoding, s, end,
-                              XML_ACCOUNT_DIRECT);
+                              XML_ACCOUNT_DIRECT, NULL);
     } else if (tok == XML_TOK_XML_DECL) {
       enum XML_Error result;
       result = processXmlDecl(parser, 0, start, next);
@@ -4676,7 +4803,7 @@ entityValueProcessor(XML_Parser parser, const char *s, const char *end,
         break;
       }
       /* found end of entity value - can store it now */
-      return storeEntityValue(parser, enc, s, end, XML_ACCOUNT_DIRECT);
+      return storeEntityValue(parser, enc, s, end, XML_ACCOUNT_DIRECT, NULL);
     }
     start = next;
   }
@@ -5119,9 +5246,9 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
 #if XML_GE == 1
         // This will store the given replacement text in
         // parser->m_declEntity->textPtr.
-        enum XML_Error result
-            = storeEntityValue(parser, enc, s + enc->minBytesPerChar,
-                               next - enc->minBytesPerChar, XML_ACCOUNT_NONE);
+        enum XML_Error result = callStoreEntityValue(
+            parser, enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar,
+            XML_ACCOUNT_NONE);
         if (parser->m_declEntity) {
           parser->m_declEntity->textPtr = poolStart(&dtd->entityValuePool);
           parser->m_declEntity->textLen
@@ -5546,7 +5673,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
           enum XML_Error result;
           XML_Bool betweenDecl
               = (role == XML_ROLE_PARAM_ENTITY_REF ? XML_TRUE : XML_FALSE);
-          result = processInternalEntity(parser, entity, betweenDecl);
+          result = processEntity(parser, entity, betweenDecl, ENTITY_INTERNAL);
           if (result != XML_ERROR_NONE)
             return result;
           handleDefault = XML_FALSE;
@@ -5751,6 +5878,12 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
       return XML_ERROR_NONE;
     case XML_FINISHED:
       return XML_ERROR_ABORTED;
+    case XML_PARSING:
+      if (parser->m_reenter) {
+        *nextPtr = next;
+        return XML_ERROR_NONE;
+      }
+    /* Fall through */
     default:
       s = next;
       tok = XmlPrologTok(enc, s, end, &next);
@@ -5825,21 +5958,49 @@ epilogProcessor(XML_Parser parser, const char *s, const char *end,
       return XML_ERROR_NONE;
     case XML_FINISHED:
       return XML_ERROR_ABORTED;
+    case XML_PARSING:
+      if (parser->m_reenter) {
+        return XML_ERROR_UNEXPECTED_STATE; // LCOV_EXCL_LINE
+      }
+    /* Fall through */
     default:;
     }
   }
 }
 
 static enum XML_Error
-processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) {
-  const char *textStart, *textEnd;
-  const char *next;
-  enum XML_Error result;
-  OPEN_INTERNAL_ENTITY *openEntity;
+processEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl,
+              enum EntityType type) {
+  OPEN_INTERNAL_ENTITY *openEntity, **openEntityList, **freeEntityList;
+  switch (type) {
+  case ENTITY_INTERNAL:
+    parser->m_processor = internalEntityProcessor;
+    openEntityList = &parser->m_openInternalEntities;
+    freeEntityList = &parser->m_freeInternalEntities;
+    break;
+  case ENTITY_ATTRIBUTE:
+    openEntityList = &parser->m_openAttributeEntities;
+    freeEntityList = &parser->m_freeAttributeEntities;
+    break;
+  case ENTITY_VALUE:
+    openEntityList = &parser->m_openValueEntities;
+    freeEntityList = &parser->m_freeValueEntities;
+    break;
+    /* default case serves merely as a safety net in case of a
+     * wrong entityType. Therefore we exclude the following lines
+     * from the test coverage.
+     *
+     * LCOV_EXCL_START
+     */
+  default:
+    // Should not reach here
+    assert(0);
+    /* LCOV_EXCL_STOP */
+  }
 
-  if (parser->m_freeInternalEntities) {
-    openEntity = parser->m_freeInternalEntities;
-    parser->m_freeInternalEntities = openEntity->next;
+  if (*freeEntityList) {
+    openEntity = *freeEntityList;
+    *freeEntityList = openEntity->next;
   } else {
     openEntity
         = (OPEN_INTERNAL_ENTITY *)MALLOC(parser, sizeof(OPEN_INTERNAL_ENTITY));
@@ -5847,55 +6008,34 @@ processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) {
       return XML_ERROR_NO_MEMORY;
   }
   entity->open = XML_TRUE;
+  entity->hasMore = XML_TRUE;
 #if XML_GE == 1
   entityTrackingOnOpen(parser, entity, __LINE__);
 #endif
   entity->processed = 0;
-  openEntity->next = parser->m_openInternalEntities;
-  parser->m_openInternalEntities = openEntity;
+  openEntity->next = *openEntityList;
+  *openEntityList = openEntity;
   openEntity->entity = entity;
+  openEntity->type = type;
   openEntity->startTagLevel = parser->m_tagLevel;
   openEntity->betweenDecl = betweenDecl;
   openEntity->internalEventPtr = NULL;
   openEntity->internalEventEndPtr = NULL;
-  textStart = (const char *)entity->textPtr;
-  textEnd = (const char *)(entity->textPtr + entity->textLen);
-  /* Set a safe default value in case 'next' does not get set */
-  next = textStart;
-
-  if (entity->is_param) {
-    int tok
-        = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
-    result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,
-                      tok, next, &next, XML_FALSE, XML_FALSE,
-                      XML_ACCOUNT_ENTITY_EXPANSION);
-  } else {
-    result = doContent(parser, parser->m_tagLevel, parser->m_internalEncoding,
-                       textStart, textEnd, &next, XML_FALSE,
-                       XML_ACCOUNT_ENTITY_EXPANSION);
-  }
 
-  if (result == XML_ERROR_NONE) {
-    if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) {
-      entity->processed = (int)(next - textStart);
-      parser->m_processor = internalEntityProcessor;
-    } else if (parser->m_openInternalEntities->entity == entity) {
-#if XML_GE == 1
-      entityTrackingOnClose(parser, entity, __LINE__);
-#endif /* XML_GE == 1 */
-      entity->open = XML_FALSE;
-      parser->m_openInternalEntities = openEntity->next;
-      /* put openEntity back in list of free instances */
-      openEntity->next = parser->m_freeInternalEntities;
-      parser->m_freeInternalEntities = openEntity;
-    }
+  // Only internal entities make use of the reenter flag
+  // therefore no need to set it for other entity types
+  if (type == ENTITY_INTERNAL) {
+    triggerReenter(parser);
   }
-  return result;
+  return XML_ERROR_NONE;
 }
 
 static enum XML_Error PTRCALL
 internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
                         const char **nextPtr) {
+  UNUSED_P(s);
+  UNUSED_P(end);
+  UNUSED_P(nextPtr);
   ENTITY *entity;
   const char *textStart, *textEnd;
   const char *next;
@@ -5905,68 +6045,67 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
     return XML_ERROR_UNEXPECTED_STATE;
 
   entity = openEntity->entity;
-  textStart = ((const char *)entity->textPtr) + entity->processed;
-  textEnd = (const char *)(entity->textPtr + entity->textLen);
-  /* Set a safe default value in case 'next' does not get set */
-  next = textStart;
-
-  if (entity->is_param) {
-    int tok
-        = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
-    result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,
-                      tok, next, &next, XML_FALSE, XML_TRUE,
-                      XML_ACCOUNT_ENTITY_EXPANSION);
-  } else {
-    result = doContent(parser, openEntity->startTagLevel,
-                       parser->m_internalEncoding, textStart, textEnd, &next,
-                       XML_FALSE, XML_ACCOUNT_ENTITY_EXPANSION);
-  }
 
-  if (result != XML_ERROR_NONE)
-    return result;
+  // This will return early
+  if (entity->hasMore) {
+    textStart = ((const char *)entity->textPtr) + entity->processed;
+    textEnd = (const char *)(entity->textPtr + entity->textLen);
+    /* Set a safe default value in case 'next' does not get set */
+    next = textStart;
+
+    if (entity->is_param) {
+      int tok
+          = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
+      result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,
+                        tok, next, &next, XML_FALSE, XML_FALSE,
+                        XML_ACCOUNT_ENTITY_EXPANSION);
+    } else {
+      result = doContent(parser, openEntity->startTagLevel,
+                         parser->m_internalEncoding, textStart, textEnd, &next,
+                         XML_FALSE, XML_ACCOUNT_ENTITY_EXPANSION);
+    }
+
+    if (result != XML_ERROR_NONE)
+      return result;
+    // Check if entity is complete, if not, mark down how much of it is
+    // processed
+    if (textEnd != next
+        && (parser->m_parsingStatus.parsing == XML_SUSPENDED
+            || (parser->m_parsingStatus.parsing == XML_PARSING
+                && parser->m_reenter))) {
+      entity->processed = (int)(next - (const char *)entity->textPtr);
+      return result;
+    }
 
-  if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) {
-    entity->processed = (int)(next - (const char *)entity->textPtr);
+    // Entity is complete. We cannot close it here since we need to first
+    // process its possible inner entities (which are added to the
+    // m_openInternalEntities during doProlog or doContent calls above)
+    entity->hasMore = XML_FALSE;
+    triggerReenter(parser);
     return result;
-  }
+  } // End of entity processing, "if" block will return here
 
+  // Remove fully processed openEntity from open entity list.
 #if XML_GE == 1
   entityTrackingOnClose(parser, entity, __LINE__);
 #endif
+  // openEntity is m_openInternalEntities' head, as we set it at the start of
+  // this function and we skipped doProlog and doContent calls with hasMore set
+  // to false. This means we can directly remove the head of
+  // m_openInternalEntities
+  assert(parser->m_openInternalEntities == openEntity);
   entity->open = XML_FALSE;
-  parser->m_openInternalEntities = openEntity->next;
+  parser->m_openInternalEntities = parser->m_openInternalEntities->next;
+
   /* put openEntity back in list of free instances */
   openEntity->next = parser->m_freeInternalEntities;
   parser->m_freeInternalEntities = openEntity;
 
-  // If there are more open entities we want to stop right here and have the
-  // upcoming call to XML_ResumeParser continue with entity content, or it would
-  // be ignored altogether.
-  if (parser->m_openInternalEntities != NULL
-      && parser->m_parsingStatus.parsing == XML_SUSPENDED) {
-    return XML_ERROR_NONE;
-  }
-
-  if (entity->is_param) {
-    int tok;
-    parser->m_processor = prologProcessor;
-    tok = XmlPrologTok(parser->m_encoding, s, end, &next);
-    return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
-                    (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE,
-                    XML_ACCOUNT_DIRECT);
-  } else {
-    parser->m_processor = contentProcessor;
-    /* see externalEntityContentProcessor vs contentProcessor */
-    result = doContent(parser, parser->m_parentParser ? 1 : 0,
-                       parser->m_encoding, s, end, nextPtr,
-                       (XML_Bool)! parser->m_parsingStatus.finalBuffer,
-                       XML_ACCOUNT_DIRECT);
-    if (result == XML_ERROR_NONE) {
-      if (! storeRawNames(parser))
-        return XML_ERROR_NO_MEMORY;
-    }
-    return result;
+  if (parser->m_openInternalEntities == NULL) {
+    parser->m_processor = entity->is_param ? prologProcessor : contentProcessor;
   }
+  triggerReenter(parser);
+  return XML_ERROR_NONE;
 }
 
 static enum XML_Error PTRCALL
@@ -5982,8 +6121,70 @@ static enum XML_Error
 storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
                     const char *ptr, const char *end, STRING_POOL *pool,
                     enum XML_Account account) {
-  enum XML_Error result
-      = appendAttributeValue(parser, enc, isCdata, ptr, end, pool, account);
+  const char *next = ptr;
+  enum XML_Error result = XML_ERROR_NONE;
+
+  while (1) {
+    if (! parser->m_openAttributeEntities) {
+      result = appendAttributeValue(parser, enc, isCdata, next, end, pool,
+                                    account, &next);
+    } else {
+      OPEN_INTERNAL_ENTITY *const openEntity = parser->m_openAttributeEntities;
+      if (! openEntity)
+        return XML_ERROR_UNEXPECTED_STATE;
+
+      ENTITY *const entity = openEntity->entity;
+      const char *const textStart
+          = ((const char *)entity->textPtr) + entity->processed;
+      const char *const textEnd
+          = (const char *)(entity->textPtr + entity->textLen);
+      /* Set a safe default value in case 'next' does not get set */
+      const char *nextInEntity = textStart;
+      if (entity->hasMore) {
+        result = appendAttributeValue(
+            parser, parser->m_internalEncoding, isCdata, textStart, textEnd,
+            pool, XML_ACCOUNT_ENTITY_EXPANSION, &nextInEntity);
+        if (result != XML_ERROR_NONE)
+          break;
+        // Check if entity is complete, if not, mark down how much of it is
+        // processed. A XML_SUSPENDED check here is not required as
+        // appendAttributeValue will never suspend the parser.
+        if (textEnd != nextInEntity) {
+          entity->processed
+              = (int)(nextInEntity - (const char *)entity->textPtr);
+          continue;
+        }
+
+        // Entity is complete. We cannot close it here since we need to first
+        // process its possible inner entities (which are added to the
+        // m_openAttributeEntities during appendAttributeValue)
+        entity->hasMore = XML_FALSE;
+        continue;
+      } // End of entity processing, "if" block skips the rest
+
+      // Remove fully processed openEntity from open entity list.
+#if XML_GE == 1
+      entityTrackingOnClose(parser, entity, __LINE__);
+#endif
+      // openEntity is m_openAttributeEntities' head, since we set it at the
+      // start of this function and because we skipped appendAttributeValue call
+      // with hasMore set to false. This means we can directly remove the head
+      // of m_openAttributeEntities
+      assert(parser->m_openAttributeEntities == openEntity);
+      entity->open = XML_FALSE;
+      parser->m_openAttributeEntities = parser->m_openAttributeEntities->next;
+
+      /* put openEntity back in list of free instances */
+      openEntity->next = parser->m_freeAttributeEntities;
+      parser->m_freeAttributeEntities = openEntity;
+    }
+
+    // Break if an error occurred or there is nothing left to process
+    if (result || (parser->m_openAttributeEntities == NULL && end == next)) {
+      break;
+    }
+  }
+
   if (result)
     return result;
   if (! isCdata && poolLength(pool) && poolLastChar(pool) == 0x20)
@@ -5996,7 +6197,7 @@ storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
 static enum XML_Error
 appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
                      const char *ptr, const char *end, STRING_POOL *pool,
-                     enum XML_Account account) {
+                     enum XML_Account account, const char **nextPtr) {
   DTD *const dtd = parser->m_dtd; /* save one level of indirection */
 #ifndef XML_DTD
   UNUSED_P(account);
@@ -6014,6 +6215,9 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
 #endif
     switch (tok) {
     case XML_TOK_NONE:
+      if (nextPtr) {
+        *nextPtr = next;
+      }
       return XML_ERROR_NONE;
     case XML_TOK_INVALID:
       if (enc == parser->m_encoding)
@@ -6154,21 +6358,11 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
         return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF;
       } else {
         enum XML_Error result;
-        const XML_Char *textEnd = entity->textPtr + entity->textLen;
-        entity->open = XML_TRUE;
-#if XML_GE == 1
-        entityTrackingOnOpen(parser, entity, __LINE__);
-#endif
-        result = appendAttributeValue(parser, parser->m_internalEncoding,
-                                      isCdata, (const char *)entity->textPtr,
-                                      (const char *)textEnd, pool,
-                                      XML_ACCOUNT_ENTITY_EXPANSION);
-#if XML_GE == 1
-        entityTrackingOnClose(parser, entity, __LINE__);
-#endif
-        entity->open = XML_FALSE;
-        if (result)
-          return result;
+        result = processEntity(parser, entity, XML_FALSE, ENTITY_ATTRIBUTE);
+        if ((result == XML_ERROR_NONE) && (nextPtr != NULL)) {
+          *nextPtr = next;
+        }
+        return result;
       }
     } break;
     default:
@@ -6197,7 +6391,7 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
 static enum XML_Error
 storeEntityValue(XML_Parser parser, const ENCODING *enc,
                  const char *entityTextPtr, const char *entityTextEnd,
-                 enum XML_Account account) {
+                 enum XML_Account account, const char **nextPtr) {
   DTD *const dtd = parser->m_dtd; /* save one level of indirection */
   STRING_POOL *pool = &(dtd->entityValuePool);
   enum XML_Error result = XML_ERROR_NONE;
@@ -6215,8 +6409,9 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
       return XML_ERROR_NO_MEMORY;
   }
 
+  const char *next;
   for (;;) {
-    const char *next
+    next
         = entityTextPtr; /* XmlEntityValueTok doesn't always set the last arg */
     int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next);
 
@@ -6278,16 +6473,8 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
           } else
             dtd->keepProcessing = dtd->standalone;
         } else {
-          entity->open = XML_TRUE;
-          entityTrackingOnOpen(parser, entity, __LINE__);
-          result = storeEntityValue(
-              parser, parser->m_internalEncoding, (const char *)entity->textPtr,
-              (const char *)(entity->textPtr + entity->textLen),
-              XML_ACCOUNT_ENTITY_EXPANSION);
-          entityTrackingOnClose(parser, entity, __LINE__);
-          entity->open = XML_FALSE;
-          if (result)
-            goto endEntityValue;
+          result = processEntity(parser, entity, XML_FALSE, ENTITY_VALUE);
+          goto endEntityValue;
         }
         break;
       }
@@ -6375,6 +6562,81 @@ endEntityValue:
 #  ifdef XML_DTD
   parser->m_prologState.inEntityValue = oldInEntityValue;
 #  endif /* XML_DTD */
+  // If 'nextPtr' is given, it should be updated during the processing
+  if (nextPtr != NULL) {
+    *nextPtr = next;
+  }
+  return result;
+}
+
+static enum XML_Error
+callStoreEntityValue(XML_Parser parser, const ENCODING *enc,
+                     const char *entityTextPtr, const char *entityTextEnd,
+                     enum XML_Account account) {
+  const char *next = entityTextPtr;
+  enum XML_Error result = XML_ERROR_NONE;
+  while (1) {
+    if (! parser->m_openValueEntities) {
+      result
+          = storeEntityValue(parser, enc, next, entityTextEnd, account, &next);
+    } else {
+      OPEN_INTERNAL_ENTITY *const openEntity = parser->m_openValueEntities;
+      if (! openEntity)
+        return XML_ERROR_UNEXPECTED_STATE;
+
+      ENTITY *const entity = openEntity->entity;
+      const char *const textStart
+          = ((const char *)entity->textPtr) + entity->processed;
+      const char *const textEnd
+          = (const char *)(entity->textPtr + entity->textLen);
+      /* Set a safe default value in case 'next' does not get set */
+      const char *nextInEntity = textStart;
+      if (entity->hasMore) {
+        result = storeEntityValue(parser, parser->m_internalEncoding, textStart,
+                                  textEnd, XML_ACCOUNT_ENTITY_EXPANSION,
+                                  &nextInEntity);
+        if (result != XML_ERROR_NONE)
+          break;
+        // Check if entity is complete, if not, mark down how much of it is
+        // processed. A XML_SUSPENDED check here is not required as
+        // appendAttributeValue will never suspend the parser.
+        if (textEnd != nextInEntity) {
+          entity->processed
+              = (int)(nextInEntity - (const char *)entity->textPtr);
+          continue;
+        }
+
+        // Entity is complete. We cannot close it here since we need to first
+        // process its possible inner entities (which are added to the
+        // m_openValueEntities during storeEntityValue)
+        entity->hasMore = XML_FALSE;
+        continue;
+      } // End of entity processing, "if" block skips the rest
+
+      // Remove fully processed openEntity from open entity list.
+#  if XML_GE == 1
+      entityTrackingOnClose(parser, entity, __LINE__);
+#  endif
+      // openEntity is m_openValueEntities' head, since we set it at the
+      // start of this function and because we skipped storeEntityValue call
+      // with hasMore set to false. This means we can directly remove the head
+      // of m_openValueEntities
+      assert(parser->m_openValueEntities == openEntity);
+      entity->open = XML_FALSE;
+      parser->m_openValueEntities = parser->m_openValueEntities->next;
+
+      /* put openEntity back in list of free instances */
+      openEntity->next = parser->m_freeValueEntities;
+      parser->m_freeValueEntities = openEntity;
+    }
+
+    // Break if an error occurred or there is nothing left to process
+    if (result
+        || (parser->m_openValueEntities == NULL && entityTextEnd == next)) {
+      break;
+    }
+  }
+
   return result;
 }
 
@@ -8542,11 +8804,13 @@ unsignedCharToPrintable(unsigned char c) {
     return "\\xFE";
   case 255:
     return "\\xFF";
+  // LCOV_EXCL_START
   default:
     assert(0); /* never gets here */
     return "dead code";
   }
   assert(0); /* never gets here */
+  // LCOV_EXCL_STOP
 }
 
 #endif /* XML_GE == 1 */

+ 151 - 44
libs/expat/m4/libtool.m4

@@ -32,7 +32,7 @@ m4_define([_LT_COPYING], [dnl
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ])
 
-# serial 62 LT_INIT
+# serial 63 LT_INIT
 
 
 # LT_PREREQ(VERSION)
@@ -1100,6 +1100,21 @@ _LT_EOF
     if test yes = "$lt_cv_apple_cc_single_mod"; then
       _lt_dar_single_mod='$single_module'
     fi
+    _lt_dar_needs_single_mod=no
+    case $host_os in
+    rhapsody* | darwin1.*)
+      _lt_dar_needs_single_mod=yes ;;
+    darwin*)
+      # When targeting Mac OS X 10.4 (darwin 8) or later,
+      # -single_module is the default and -multi_module is unsupported.
+      # The toolchain on macOS 10.14 (darwin 18) and later cannot
+      # target any OS version that needs -single_module.
+      case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+      10.0,*-darwin[[567]].*|10.[[0-3]],*-darwin[[5-9]].*|10.[[0-3]],*-darwin1[[0-7]].*)
+        _lt_dar_needs_single_mod=yes ;;
+      esac
+    ;;
+    esac
     if test yes = "$lt_cv_ld_exported_symbols_list"; then
       _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym'
     else
@@ -1145,7 +1160,7 @@ m4_defun([_LT_DARWIN_LINKER_FEATURES],
     _LT_TAGVAR(archive_expsym_cmds, $1)="$SED 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil"
     _LT_TAGVAR(module_expsym_cmds, $1)="$SED -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil"
     m4_if([$1], [CXX],
-[   if test yes != "$lt_cv_apple_cc_single_mod"; then
+[   if test yes = "$_lt_dar_needs_single_mod" -a yes != "$lt_cv_apple_cc_single_mod"; then
       _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil"
       _LT_TAGVAR(archive_expsym_cmds, $1)="$SED 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil"
     fi
@@ -1710,9 +1725,9 @@ AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
     lt_cv_sys_max_cmd_len=12288;    # 12K is about right
     ;;
 
-  gnu*)
-    # Under GNU Hurd, this test is not required because there is
-    # no limit to the length of command line arguments.
+  gnu* | ironclad*)
+    # Under GNU Hurd and Ironclad, this test is not required because there
+    # is no limit to the length of command line arguments.
     # Libtool will interpret -1 as no limit whatsoever
     lt_cv_sys_max_cmd_len=-1;
     ;;
@@ -2566,28 +2581,28 @@ cygwin* | mingw* | windows* | pw32* | cegcc*)
     # gcc
     library_names_spec='$libname.dll.a'
     # DLL is installed to $(libdir)/../bin by postinstall_cmds
-    # If user builds GCC with mulitlibs enabled,
+    # If user builds GCC with multilib enabled,
     # it should just install on $(libdir)
     # not on $(libdir)/../bin or 32 bits dlls would override 64 bit ones.
-    if test yes = $multilib; then
-    postinstall_cmds='base_file=`basename \$file`~
-      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
-      dldir=$destdir/`dirname \$dlpath`~
-      $install_prog $dir/$dlname $destdir/$dlname~
-      chmod a+x $destdir/$dlname~
-      if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
-        eval '\''$striplib $destdir/$dlname'\'' || exit \$?;
-      fi'
+    if test xyes = x"$multilib"; then
+      postinstall_cmds='base_file=`basename \$file`~
+        dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
+        dldir=$destdir/`dirname \$dlpath`~
+        $install_prog $dir/$dlname $destdir/$dlname~
+        chmod a+x $destdir/$dlname~
+        if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+          eval '\''$striplib $destdir/$dlname'\'' || exit \$?;
+        fi'
     else
-    postinstall_cmds='base_file=`basename \$file`~
-      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
-      dldir=$destdir/`dirname \$dlpath`~
-      test -d \$dldir || mkdir -p \$dldir~
-      $install_prog $dir/$dlname \$dldir/$dlname~
-      chmod a+x \$dldir/$dlname~
-      if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
-        eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
-      fi'
+      postinstall_cmds='base_file=`basename \$file`~
+        dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
+        dldir=$destdir/`dirname \$dlpath`~
+        test -d \$dldir || mkdir -p \$dldir~
+        $install_prog $dir/$dlname \$dldir/$dlname~
+        chmod a+x \$dldir/$dlname~
+        if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+          eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+        fi'
     fi
     postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
       dlpath=$dir/\$dldll~
@@ -2784,8 +2799,9 @@ haiku*)
   soname_spec='$libname$release$shared_ext$major'
   shlibpath_var=LIBRARY_PATH
   shlibpath_overrides_runpath=no
-  sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'
-  hardcode_into_libs=yes
+  sys_lib_search_path_spec='/boot/system/non-packaged/develop/lib /boot/system/develop/lib'
+  sys_lib_dlsearch_path_spec='/boot/home/config/non-packaged/lib /boot/home/config/lib /boot/system/non-packaged/lib /boot/system/lib'
+  hardcode_into_libs=no
   ;;
 
 hpux9* | hpux10* | hpux11*)
@@ -2963,6 +2979,18 @@ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
   dynamic_linker='GNU/Linux ld.so'
   ;;
 
+netbsdelf*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='NetBSD ld.elf_so'
+  ;;
+
 netbsd*)
   version_type=sunos
   need_lib_prefix=no
@@ -2981,6 +3009,18 @@ netbsd*)
   hardcode_into_libs=yes
   ;;
 
+*-mlibc)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  dynamic_linker='mlibc ld.so'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
 newsos6)
   version_type=linux # correct to gnu/linux during the next big refactor
   library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
@@ -3060,6 +3100,17 @@ rdos*)
   dynamic_linker=no
   ;;
 
+serenity*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  dynamic_linker='SerenityOS LibELF'
+  ;;
+
 solaris*)
   version_type=linux # correct to gnu/linux during the next big refactor
   need_lib_prefix=no
@@ -3157,6 +3208,21 @@ uts4*)
   shlibpath_var=LD_LIBRARY_PATH
   ;;
 
+emscripten*)
+  version_type=none
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='$libname$release$shared_ext'
+  soname_spec='$libname$release$shared_ext'
+  finish_cmds=
+  dynamic_linker="Emscripten linker"
+  _LT_COMPILER_PIC($1)='-fPIC'
+  _LT_TAGVAR(archive_cmds, $1)='$CC -sSIDE_MODULE=2 -shared $libobjs $deplibs $compiler_flags -o $lib'
+  _LT_TAGVAR(archive_expsym_cmds, $1)='$SED "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -sSIDE_MODULE=2 -shared $libobjs $deplibs $compiler_flags -o $lib -s EXPORTED_FUNCTIONS=@$output_objdir/$soname.expsym'
+  _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+  _LT_TAGVAR(no_undefined_flag, $1)=
+  ;;
+
 *)
   dynamic_linker=no
   ;;
@@ -3621,7 +3687,11 @@ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
   lt_cv_deplibs_check_method=pass_all
   ;;
 
-netbsd*)
+*-mlibc)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+netbsd* | netbsdelf*-gnu)
   if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
     lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
   else
@@ -3655,6 +3725,10 @@ rdos*)
   lt_cv_deplibs_check_method=pass_all
   ;;
 
+serenity*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
 solaris*)
   lt_cv_deplibs_check_method=pass_all
   ;;
@@ -4127,7 +4201,8 @@ _LT_EOF
   if AC_TRY_EVAL(ac_compile); then
     # Now try to grab the symbols.
     nlist=conftest.nm
-    if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then
+    $ECHO "$as_me:$LINENO: $NM conftest.$ac_objext | $lt_cv_sys_global_symbol_pipe > $nlist" >&AS_MESSAGE_LOG_FD
+    if eval "$NM" conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist 2>&AS_MESSAGE_LOG_FD && test -s "$nlist"; then
       # Try sorting and uniquifying the output.
       if sort "$nlist" | uniq > "$nlist"T; then
 	mv -f "$nlist"T "$nlist"
@@ -4499,7 +4574,9 @@ m4_if([$1], [CXX], [
 	    ;;
 	esac
 	;;
-      netbsd*)
+      netbsd* | netbsdelf*-gnu)
+	;;
+      *-mlibc)
 	;;
       *qnx* | *nto*)
         # QNX uses GNU C++, but need to define -shared option too, otherwise
@@ -4529,6 +4606,8 @@ m4_if([$1], [CXX], [
 	;;
       psos*)
 	;;
+      serenity*)
+        ;;
       solaris*)
 	case $cc_basename in
 	  CC* | sunCC*)
@@ -4767,7 +4846,7 @@ m4_if([$1], [CXX], [
 	_LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
 	_LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
         ;;
-      *flang* | ftn)
+      *flang* | ftn | f18* | f95*)
         # Flang compiler.
 	_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
 	_LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
@@ -4855,6 +4934,12 @@ m4_if([$1], [CXX], [
       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
       ;;
 
+    *-mlibc)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+      ;;
+
     *nto* | *qnx*)
       # QNX uses GNU C++, but need to define -shared option too, otherwise
       # it will coredump.
@@ -4871,6 +4956,9 @@ m4_if([$1], [CXX], [
       _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
       ;;
 
+    serenity*)
+      ;;
+
     solaris*)
       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
@@ -5076,9 +5164,6 @@ dnl Note also adjust exclude_expsyms for C++ above.
     # we just hope/assume this is gcc and not c89 (= MSVC++ or ICC)
     with_gnu_ld=yes
     ;;
-  openbsd*)
-    with_gnu_ld=no
-    ;;
   esac
 
   _LT_TAGVAR(ld_shlibs, $1)=yes
@@ -5189,6 +5274,7 @@ _LT_EOF
       _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
       _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols'
       _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname']
+      _LT_TAGVAR(file_list_spec, $1)='@'
 
       if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
         _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
@@ -5208,7 +5294,7 @@ _LT_EOF
 
     haiku*)
       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
-      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      _LT_TAGVAR(link_all_deplibs, $1)=no
       ;;
 
     os2*)
@@ -5314,6 +5400,7 @@ _LT_EOF
 
 	case $cc_basename in
 	tcc*)
+	  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
 	  _LT_TAGVAR(export_dynamic_flag_spec, $1)='-rdynamic'
 	  ;;
 	xlf* | bgf* | bgxlf* | mpixlf*)
@@ -5334,7 +5421,12 @@ _LT_EOF
       fi
       ;;
 
-    netbsd*)
+    *-mlibc)
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+      ;;
+
+    netbsd* | netbsdelf*-gnu)
       if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
 	_LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
 	wlarc=
@@ -5653,14 +5745,14 @@ _LT_EOF
 	# Tell ltmain to make .dll files, not .so files.
 	shrext_cmds=.dll
 	# FIXME: Setting linknames here is a bad hack.
-	_LT_TAGVAR(archive_cmds, $1)='$CC -Fe $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames='
+	_LT_TAGVAR(archive_cmds, $1)='$CC -Fe$output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames='
 	_LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then
             cp "$export_symbols" "$output_objdir/$soname.def";
             echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp";
           else
             $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp;
           fi~
-          $CC -Fe $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+          $CC -Fe$tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
           linknames='
 	# The linker will not automatically build a static lib if we build a DLL.
 	# _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
@@ -5872,11 +5964,15 @@ _LT_EOF
 	# Fabrice Bellard et al's Tiny C Compiler
 	_LT_TAGVAR(ld_shlibs, $1)=yes
 	_LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+	_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
 	;;
       esac
       ;;
 
-    netbsd*)
+    *-mlibc)
+      ;;
+
+    netbsd* | netbsdelf*-gnu)
       if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
 	_LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'  # a.out
       else
@@ -5977,6 +6073,9 @@ _LT_EOF
       _LT_TAGVAR(hardcode_libdir_separator, $1)=:
       ;;
 
+    serenity*)
+      ;;
+
     solaris*)
       _LT_TAGVAR(no_undefined_flag, $1)=' -z defs'
       if test yes = "$GCC"; then
@@ -6502,7 +6601,7 @@ if test yes != "$_lt_caught_CXX_error"; then
       # Commands to make compiler produce verbose output that lists
       # what "hidden" libraries, object files and flags are used when
       # linking a shared library.
-      output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "[[-]]L"'
+      output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " [[-]]L"'
 
     else
       GXX=no
@@ -6764,6 +6863,7 @@ if test yes != "$_lt_caught_CXX_error"; then
 	  _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
 	  _LT_TAGVAR(always_export_symbols, $1)=no
 	  _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+	  _LT_TAGVAR(file_list_spec, $1)='@'
 
 	  if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
 	    _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
@@ -6851,7 +6951,7 @@ if test yes != "$_lt_caught_CXX_error"; then
 
       haiku*)
         _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
-        _LT_TAGVAR(link_all_deplibs, $1)=yes
+        _LT_TAGVAR(link_all_deplibs, $1)=no
         ;;
 
       hpux9*)
@@ -6943,7 +7043,7 @@ if test yes != "$_lt_caught_CXX_error"; then
 	    # explicitly linking system object files so we need to strip them
 	    # from the output so that they don't get included in the library
 	    # dependencies.
-	    output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "[[-]]L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+	    output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP " [[-]]L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
 	    ;;
           *)
 	    if test yes = "$GXX"; then
@@ -7175,6 +7275,10 @@ if test yes != "$_lt_caught_CXX_error"; then
 	esac
 	;;
 
+      *-mlibc)
+        _LT_TAGVAR(ld_shlibs, $1)=yes
+	;;
+
       netbsd*)
         if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
 	  _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable  -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags'
@@ -7282,7 +7386,7 @@ if test yes != "$_lt_caught_CXX_error"; then
 	      # Commands to make compiler produce verbose output that lists
 	      # what "hidden" libraries, object files and flags are used when
 	      # linking a shared library.
-	      output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "[[-]]L"'
+	      output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " [[-]]L"'
 
 	    else
 	      # FIXME: insert proper C++ library support
@@ -7297,6 +7401,9 @@ if test yes != "$_lt_caught_CXX_error"; then
         _LT_TAGVAR(ld_shlibs, $1)=no
         ;;
 
+      serenity*)
+        ;;
+
       sunos4*)
         case $cc_basename in
           CC*)
@@ -7366,7 +7473,7 @@ if test yes != "$_lt_caught_CXX_error"; then
 	        # Commands to make compiler produce verbose output that lists
 	        # what "hidden" libraries, object files and flags are used when
 	        # linking a shared library.
-	        output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "[[-]]L"'
+	        output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " [[-]]L"'
 	      else
 	        # g++ 2.7 appears to require '-G' NOT '-shared' on this
 	        # platform.
@@ -7377,7 +7484,7 @@ if test yes != "$_lt_caught_CXX_error"; then
 	        # Commands to make compiler produce verbose output that lists
 	        # what "hidden" libraries, object files and flags are used when
 	        # linking a shared library.
-	        output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "[[-]]L"'
+	        output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " [[-]]L"'
 	      fi
 
 	      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir'

+ 5 - 5
libs/expat/m4/ltversion.m4

@@ -10,15 +10,15 @@
 
 # @configure_input@
 
-# serial 4392 ltversion.m4
+# serial 4441 ltversion.m4
 # This file is part of GNU Libtool
 
-m4_define([LT_PACKAGE_VERSION], [2.5.3])
-m4_define([LT_PACKAGE_REVISION], [2.5.3])
+m4_define([LT_PACKAGE_VERSION], [2.5.4])
+m4_define([LT_PACKAGE_REVISION], [2.5.4])
 
 AC_DEFUN([LTVERSION_VERSION],
-[macro_version='2.5.3'
-macro_revision='2.5.3'
+[macro_version='2.5.4'
+macro_revision='2.5.4'
 _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
 _LT_DECL(, macro_revision, 0)
 ])

+ 4 - 1
libs/expat/tests/acc_tests.c

@@ -360,13 +360,16 @@ END_TEST
 START_TEST(test_helper_unsigned_char_to_printable) {
   // Smoke test
   unsigned char uc = 0;
-  for (; uc < (unsigned char)-1; uc++) {
+  for (;; uc++) {
     set_subtest("char %u", (unsigned)uc);
     const char *const printable = unsignedCharToPrintable(uc);
     if (printable == NULL)
       fail("unsignedCharToPrintable returned NULL");
     else if (strlen(printable) < (size_t)1)
       fail("unsignedCharToPrintable returned empty string");
+    if (uc == (unsigned char)-1) {
+      break;
+    }
   }
 
   // Two concrete samples

+ 27 - 0
libs/expat/tests/alloc_tests.c

@@ -19,6 +19,7 @@
    Copyright (c) 2020      Tim Gates <[email protected]>
    Copyright (c) 2021      Donghee Na <[email protected]>
    Copyright (c) 2023      Sony Corporation / Snild Dolkow <[email protected]>
+   Copyright (c) 2025      Berkay Eren Ürün <[email protected]>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -450,6 +451,31 @@ START_TEST(test_alloc_internal_entity) {
 }
 END_TEST
 
+START_TEST(test_alloc_parameter_entity) {
+  const char *text = "<!DOCTYPE foo ["
+                     "<!ENTITY % param1 \"<!ENTITY internal 'some_text'>\">"
+                     "%param1;"
+                     "]> <foo>&internal;content</foo>";
+  int i;
+  const int alloc_test_max_repeats = 30;
+
+  for (i = 0; i < alloc_test_max_repeats; i++) {
+    g_allocation_count = i;
+    XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+    if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+        != XML_STATUS_ERROR)
+      break;
+    alloc_teardown();
+    alloc_setup();
+  }
+  g_allocation_count = -1;
+  if (i == 0)
+    fail("Parameter entity processed despite duff allocator");
+  if (i == alloc_test_max_repeats)
+    fail("Parameter entity not processed at max allocation count");
+}
+END_TEST
+
 /* Test the robustness against allocation failure of element handling
  * Based on test_dtd_default_handling().
  */
@@ -2079,6 +2105,7 @@ make_alloc_test_case(Suite *s) {
   tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_external_entity);
   tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_ext_entity_set_encoding);
   tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_internal_entity);
+  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_parameter_entity);
   tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_dtd_default_handling);
   tcase_add_test(tc_alloc, test_alloc_explicit_encoding);
   tcase_add_test(tc_alloc, test_alloc_set_base);

+ 322 - 9
libs/expat/tests/basic_tests.c

@@ -10,7 +10,7 @@
    Copyright (c) 2003      Greg Stein <[email protected]>
    Copyright (c) 2005-2007 Steven Solie <[email protected]>
    Copyright (c) 2005-2012 Karl Waclawek <[email protected]>
-   Copyright (c) 2016-2024 Sebastian Pipping <[email protected]>
+   Copyright (c) 2016-2025 Sebastian Pipping <[email protected]>
    Copyright (c) 2017-2022 Rhodri James <[email protected]>
    Copyright (c) 2017      Joe Orton <[email protected]>
    Copyright (c) 2017      José Gutiérrez de la Concha <[email protected]>
@@ -19,6 +19,7 @@
    Copyright (c) 2020      Tim Gates <[email protected]>
    Copyright (c) 2021      Donghee Na <[email protected]>
    Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <[email protected]>
+   Copyright (c) 2024-2025 Berkay Eren Ürün <[email protected]>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -1191,6 +1192,22 @@ START_TEST(test_not_standalone_handler_accept) {
 }
 END_TEST
 
+START_TEST(test_entity_start_tag_level_greater_than_one) {
+  const char *const text = "<!DOCTYPE t1 [\n"
+                           "  <!ENTITY e1 'hello'>\n"
+                           "]>\n"
+                           "<t1>\n"
+                           "  <t2>&e1;</t2>\n"
+                           "</t1>\n";
+
+  XML_Parser parser = XML_ParserCreate(NULL);
+  assert_true(_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text),
+                                      /*isFinal*/ XML_TRUE)
+              == XML_STATUS_OK);
+  XML_ParserFree(parser);
+}
+END_TEST
+
 START_TEST(test_wfc_no_recursive_entity_refs) {
   const char *text = "<!DOCTYPE doc [\n"
                      "  <!ENTITY entity '&#38;entity;'>\n"
@@ -1202,6 +1219,93 @@ START_TEST(test_wfc_no_recursive_entity_refs) {
 }
 END_TEST
 
+START_TEST(test_no_indirectly_recursive_entity_refs) {
+  struct TestCase {
+    const char *doc;
+    bool usesParameterEntities;
+  };
+
+  const struct TestCase cases[] = {
+      // general entity + character data
+      {"<!DOCTYPE a [\n"
+       "  <!ENTITY e1 '&e2;'>\n"
+       "  <!ENTITY e2 '&e1;'>\n"
+       "]><a>&e2;</a>\n",
+       false},
+
+      // general entity + attribute value
+      {"<!DOCTYPE a [\n"
+       "  <!ENTITY e1 '&e2;'>\n"
+       "  <!ENTITY e2 '&e1;'>\n"
+       "]><a k1='&e2;' />\n",
+       false},
+
+      // parameter entity
+      {"<!DOCTYPE doc [\n"
+       "  <!ENTITY % p1 '&#37;p2;'>\n"
+       "  <!ENTITY % p2 '&#37;p1;'>\n"
+       "  <!ENTITY % define_g \"<!ENTITY g '&#37;p2;'>\">\n"
+       "  %define_g;\n"
+       "]>\n"
+       "<doc/>\n",
+       true},
+  };
+  const XML_Bool reset_or_not[] = {XML_TRUE, XML_FALSE};
+
+  for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
+    for (size_t j = 0; j < sizeof(reset_or_not) / sizeof(reset_or_not[0]);
+         j++) {
+      const XML_Bool reset_wanted = reset_or_not[j];
+      const char *const doc = cases[i].doc;
+      const bool usesParameterEntities = cases[i].usesParameterEntities;
+
+      set_subtest("[%i,reset=%i] %s", (int)i, (int)j, doc);
+
+#ifdef XML_DTD // both GE and DTD
+      const bool rejection_expected = true;
+#elif XML_GE == 1 // GE but not DTD
+      const bool rejection_expected = ! usesParameterEntities;
+#else             // neither DTD nor GE
+      const bool rejection_expected = false;
+#endif
+
+      XML_Parser parser = XML_ParserCreate(NULL);
+
+#ifdef XML_DTD
+      if (usesParameterEntities) {
+        assert_true(
+            XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS)
+            == 1);
+      }
+#else
+      UNUSED_P(usesParameterEntities);
+#endif // XML_DTD
+
+      const enum XML_Status status
+          = _XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc),
+                                    /*isFinal*/ XML_TRUE);
+
+      if (rejection_expected) {
+        assert_true(status == XML_STATUS_ERROR);
+        assert_true(XML_GetErrorCode(parser) == XML_ERROR_RECURSIVE_ENTITY_REF);
+      } else {
+        assert_true(status == XML_STATUS_OK);
+      }
+
+      if (reset_wanted) {
+        // This covers free'ing of (eventually) all three open entity lists by
+        // XML_ParserReset.
+        XML_ParserReset(parser, NULL);
+      }
+
+      // This covers free'ing of (eventually) all three open entity lists by
+      // XML_ParserFree (unless XML_ParserReset has already done that above).
+      XML_ParserFree(parser);
+    }
+  }
+}
+END_TEST
+
 START_TEST(test_recursive_external_parameter_entity_2) {
   struct TestCase {
     const char *doc;
@@ -1417,7 +1521,9 @@ START_TEST(test_suspend_parser_between_char_data_calls) {
 
   XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler);
   g_resumable = XML_TRUE;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+  // can't use SINGLE_BYTES here, because it'll return early on suspension, and
+  // we won't know exactly how much input we actually managed to give Expat.
+  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
       != XML_STATUS_SUSPENDED)
     xml_failure(g_parser);
   if (XML_GetErrorCode(g_parser) != XML_ERROR_NONE)
@@ -1446,7 +1552,9 @@ START_TEST(test_repeated_stop_parser_between_char_data_calls) {
   XML_SetCharacterDataHandler(g_parser, parser_stop_character_handler);
   g_resumable = XML_TRUE;
   g_abortable = XML_FALSE;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+  // can't use SINGLE_BYTES here, because it'll return early on suspension, and
+  // we won't know exactly how much input we actually managed to give Expat.
+  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
       != XML_STATUS_SUSPENDED)
     fail("Failed to double-suspend parser");
 
@@ -1830,12 +1938,19 @@ END_TEST
 
 /* Test suspending the parser in cdata handler */
 START_TEST(test_suspend_parser_between_cdata_calls) {
+  if (g_chunkSize != 0) {
+    // this test does not use SINGLE_BYTES, because of suspension
+    return;
+  }
+
   const char *text = long_cdata_text;
   enum XML_Status result;
 
   XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler);
   g_resumable = XML_TRUE;
-  result = _XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE);
+  // can't use SINGLE_BYTES here, because it'll return early on suspension, and
+  // we won't know exactly how much input we actually managed to give Expat.
+  result = XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE);
   if (result != XML_STATUS_SUSPENDED) {
     if (result == XML_STATUS_ERROR)
       xml_failure(g_parser);
@@ -2378,6 +2493,11 @@ END_TEST
  * entity.  Exercises some obscure code in XML_ParserReset().
  */
 START_TEST(test_reset_in_entity) {
+  if (g_chunkSize != 0) {
+    // this test does not use SINGLE_BYTES, because of suspension
+    return;
+  }
+
   const char *text = "<!DOCTYPE doc [\n"
                      "<!ENTITY wombat 'wom'>\n"
                      "<!ENTITY entity 'hi &wom; there'>\n"
@@ -2387,7 +2507,9 @@ START_TEST(test_reset_in_entity) {
 
   g_resumable = XML_TRUE;
   XML_SetCharacterDataHandler(g_parser, clearing_aborting_character_handler);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+  // can't use SINGLE_BYTES here, because it'll return early on suspension, and
+  // we won't know exactly how much input we actually managed to give Expat.
+  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
       == XML_STATUS_ERROR)
     xml_failure(g_parser);
   XML_GetParsingStatus(g_parser, &status);
@@ -3634,7 +3756,9 @@ START_TEST(test_suspend_xdecl) {
   XML_SetXmlDeclHandler(g_parser, entity_suspending_xdecl_handler);
   XML_SetUserData(g_parser, g_parser);
   g_resumable = XML_TRUE;
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+  // can't use SINGLE_BYTES here, because it'll return early on suspension, and
+  // we won't know exactly how much input we actually managed to give Expat.
+  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
       != XML_STATUS_SUSPENDED)
     xml_failure(g_parser);
   if (XML_GetErrorCode(g_parser) != XML_ERROR_NONE)
@@ -3830,13 +3954,20 @@ END_TEST
 
 /* Test syntax error is caught at parse resumption */
 START_TEST(test_resume_entity_with_syntax_error) {
+  if (g_chunkSize != 0) {
+    // this test does not use SINGLE_BYTES, because of suspension
+    return;
+  }
+
   const char *text = "<!DOCTYPE doc [\n"
                      "<!ENTITY foo '<suspend>Hi</wombat>'>\n"
                      "]>\n"
                      "<doc>&foo;</doc>\n";
 
   XML_SetStartElementHandler(g_parser, start_element_suspender);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+  // can't use SINGLE_BYTES here, because it'll return early on suspension, and
+  // we won't know exactly how much input we actually managed to give Expat.
+  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
       != XML_STATUS_SUSPENDED)
     xml_failure(g_parser);
   if (XML_ResumeParser(g_parser) != XML_STATUS_ERROR)
@@ -3960,7 +4091,7 @@ START_TEST(test_skipped_null_loaded_ext_entity) {
       = {"<!ENTITY % pe1 SYSTEM 'http://example.org/two.ent'>\n"
          "<!ENTITY % pe2 '%pe1;'>\n"
          "%pe2;\n",
-         external_entity_null_loader};
+         external_entity_null_loader, NULL};
 
   XML_SetUserData(g_parser, &test_data);
   XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
@@ -3978,7 +4109,7 @@ START_TEST(test_skipped_unloaded_ext_entity) {
       = {"<!ENTITY % pe1 SYSTEM 'http://example.org/two.ent'>\n"
          "<!ENTITY % pe2 '%pe1;'>\n"
          "%pe2;\n",
-         NULL};
+         NULL, NULL};
 
   XML_SetUserData(g_parser, &test_data);
   XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
@@ -5278,6 +5409,151 @@ START_TEST(test_pool_integrity_with_unfinished_attr) {
 }
 END_TEST
 
+/* Test a possible early return location in internalEntityProcessor */
+START_TEST(test_entity_ref_no_elements) {
+  const char *const text = "<!DOCTYPE foo [\n"
+                           "<!ENTITY e1 \"test\">\n"
+                           "]> <foo>&e1;"; // intentionally missing newline
+
+  XML_Parser parser = XML_ParserCreate(NULL);
+  assert_true(_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
+              == XML_STATUS_ERROR);
+  assert_true(XML_GetErrorCode(parser) == XML_ERROR_NO_ELEMENTS);
+  XML_ParserFree(parser);
+}
+END_TEST
+
+/* Tests if chained entity references lead to unbounded recursion */
+START_TEST(test_deep_nested_entity) {
+  const size_t N_LINES = 60000;
+  const size_t SIZE_PER_LINE = 50;
+
+  char *const text = (char *)malloc((N_LINES + 4) * SIZE_PER_LINE);
+  if (text == NULL) {
+    fail("malloc failed");
+  }
+
+  char *textPtr = text;
+
+  // Create the XML
+  textPtr += snprintf(textPtr, SIZE_PER_LINE,
+                      "<!DOCTYPE foo [\n"
+                      "	<!ENTITY s0 'deepText'>\n");
+
+  for (size_t i = 1; i < N_LINES; ++i) {
+    textPtr += snprintf(textPtr, SIZE_PER_LINE, "  <!ENTITY s%lu '&s%lu;'>\n",
+                        (long unsigned)i, (long unsigned)(i - 1));
+  }
+
+  snprintf(textPtr, SIZE_PER_LINE, "]> <foo>&s%lu;</foo>\n",
+           (long unsigned)(N_LINES - 1));
+
+  const XML_Char *const expected = XCS("deepText");
+
+  CharData storage;
+  CharData_Init(&storage);
+
+  XML_Parser parser = XML_ParserCreate(NULL);
+
+  XML_SetCharacterDataHandler(parser, accumulate_characters);
+  XML_SetUserData(parser, &storage);
+
+  if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(parser);
+
+  CharData_CheckXMLChars(&storage, expected);
+  XML_ParserFree(parser);
+  free(text);
+}
+END_TEST
+
+/* Tests if chained entity references in attributes
+lead to unbounded recursion */
+START_TEST(test_deep_nested_attribute_entity) {
+  const size_t N_LINES = 60000;
+  const size_t SIZE_PER_LINE = 100;
+
+  char *const text = (char *)malloc((N_LINES + 4) * SIZE_PER_LINE);
+  if (text == NULL) {
+    fail("malloc failed");
+  }
+
+  char *textPtr = text;
+
+  // Create the XML
+  textPtr += snprintf(textPtr, SIZE_PER_LINE,
+                      "<!DOCTYPE foo [\n"
+                      "	<!ENTITY s0 'deepText'>\n");
+
+  for (size_t i = 1; i < N_LINES; ++i) {
+    textPtr += snprintf(textPtr, SIZE_PER_LINE, "  <!ENTITY s%lu '&s%lu;'>\n",
+                        (long unsigned)i, (long unsigned)(i - 1));
+  }
+
+  snprintf(textPtr, SIZE_PER_LINE, "]> <foo name='&s%lu;'>mainText</foo>\n",
+           (long unsigned)(N_LINES - 1));
+
+  AttrInfo doc_info[] = {{XCS("name"), XCS("deepText")}, {NULL, NULL}};
+  ElementInfo info[] = {{XCS("foo"), 1, NULL, NULL}, {NULL, 0, NULL, NULL}};
+  info[0].attributes = doc_info;
+
+  XML_Parser parser = XML_ParserCreate(NULL);
+  ParserAndElementInfo parserPlusElemenInfo = {parser, info};
+
+  XML_SetStartElementHandler(parser, counting_start_element_handler);
+  XML_SetUserData(parser, &parserPlusElemenInfo);
+
+  if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(parser);
+
+  XML_ParserFree(parser);
+  free(text);
+}
+END_TEST
+
+START_TEST(test_deep_nested_entity_delayed_interpretation) {
+  const size_t N_LINES = 70000;
+  const size_t SIZE_PER_LINE = 100;
+
+  char *const text = (char *)malloc((N_LINES + 4) * SIZE_PER_LINE);
+  if (text == NULL) {
+    fail("malloc failed");
+  }
+
+  char *textPtr = text;
+
+  // Create the XML
+  textPtr += snprintf(textPtr, SIZE_PER_LINE,
+                      "<!DOCTYPE foo [\n"
+                      "	<!ENTITY %% s0 'deepText'>\n");
+
+  for (size_t i = 1; i < N_LINES; ++i) {
+    textPtr += snprintf(textPtr, SIZE_PER_LINE,
+                        "  <!ENTITY %% s%lu '&#37;s%lu;'>\n", (long unsigned)i,
+                        (long unsigned)(i - 1));
+  }
+
+  snprintf(textPtr, SIZE_PER_LINE,
+           "  <!ENTITY %% define_g \"<!ENTITY g '&#37;s%lu;'>\">\n"
+           "  %%define_g;\n"
+           "]>\n"
+           "<foo/>\n",
+           (long unsigned)(N_LINES - 1));
+
+  XML_Parser parser = XML_ParserCreate(NULL);
+
+  XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+  if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(parser);
+
+  XML_ParserFree(parser);
+  free(text);
+}
+END_TEST
+
 START_TEST(test_nested_entity_suspend) {
   const char *const text = "<!DOCTYPE a [\n"
                            "  <!ENTITY e1 '<!--e1-->'>\n"
@@ -5308,6 +5584,35 @@ START_TEST(test_nested_entity_suspend) {
 }
 END_TEST
 
+START_TEST(test_nested_entity_suspend_2) {
+  const char *const text = "<!DOCTYPE doc [\n"
+                           "  <!ENTITY ge1 'head1Ztail1'>\n"
+                           "  <!ENTITY ge2 'head2&ge1;tail2'>\n"
+                           "  <!ENTITY ge3 'head3&ge2;tail3'>\n"
+                           "]>\n"
+                           "<doc>&ge3;</doc>";
+  const XML_Char *const expected = XCS("head3") XCS("head2") XCS("head1")
+      XCS("Z") XCS("tail1") XCS("tail2") XCS("tail3");
+  CharData storage;
+  CharData_Init(&storage);
+  XML_Parser parser = XML_ParserCreate(NULL);
+  ParserPlusStorage parserPlusStorage = {parser, &storage};
+
+  XML_SetCharacterDataHandler(parser, accumulate_char_data_and_suspend);
+  XML_SetUserData(parser, &parserPlusStorage);
+
+  enum XML_Status status = XML_Parse(parser, text, (int)strlen(text), XML_TRUE);
+  while (status == XML_STATUS_SUSPENDED) {
+    status = XML_ResumeParser(parser);
+  }
+  if (status != XML_STATUS_OK)
+    xml_failure(parser);
+
+  CharData_CheckXMLChars(&storage, expected);
+  XML_ParserFree(parser);
+}
+END_TEST
+
 /* Regression test for quadratic parsing on large tokens */
 START_TEST(test_big_tokens_scale_linearly) {
   const struct {
@@ -5968,7 +6273,9 @@ make_basic_test_case(Suite *s) {
   tcase_add_test(tc_basic, test_wfc_undeclared_entity_with_external_subset);
   tcase_add_test(tc_basic, test_not_standalone_handler_reject);
   tcase_add_test(tc_basic, test_not_standalone_handler_accept);
+  tcase_add_test(tc_basic, test_entity_start_tag_level_greater_than_one);
   tcase_add_test__if_xml_ge(tc_basic, test_wfc_no_recursive_entity_refs);
+  tcase_add_test(tc_basic, test_no_indirectly_recursive_entity_refs);
   tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_invalid_parse);
   tcase_add_test__if_xml_ge(tc_basic, test_dtd_default_handling);
   tcase_add_test(tc_basic, test_dtd_attr_handling);
@@ -6147,7 +6454,13 @@ make_basic_test_case(Suite *s) {
   tcase_add_test(tc_basic, test_empty_element_abort);
   tcase_add_test__ifdef_xml_dtd(tc_basic,
                                 test_pool_integrity_with_unfinished_attr);
+  tcase_add_test__if_xml_ge(tc_basic, test_entity_ref_no_elements);
+  tcase_add_test__if_xml_ge(tc_basic, test_deep_nested_entity);
+  tcase_add_test__if_xml_ge(tc_basic, test_deep_nested_attribute_entity);
+  tcase_add_test__if_xml_ge(tc_basic,
+                            test_deep_nested_entity_delayed_interpretation);
   tcase_add_test__if_xml_ge(tc_basic, test_nested_entity_suspend);
+  tcase_add_test__if_xml_ge(tc_basic, test_nested_entity_suspend_2);
   tcase_add_test(tc_basic, test_big_tokens_scale_linearly);
   tcase_add_test(tc_basic, test_set_reparse_deferral);
   tcase_add_test(tc_basic, test_reparse_deferral_is_inherited);

+ 43 - 17
libs/expat/tests/benchmark/benchmark.c

@@ -8,7 +8,7 @@
 
    Copyright (c) 2003-2006 Karl Waclawek <[email protected]>
    Copyright (c) 2005-2007 Steven Solie <[email protected]>
-   Copyright (c) 2017-2023 Sebastian Pipping <[email protected]>
+   Copyright (c) 2017-2025 Sebastian Pipping <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
    Licensed under the MIT license:
 
@@ -32,10 +32,18 @@
    USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
+#define _POSIX_C_SOURCE 1 // fdopen
+
+#if defined(_MSC_VER)
+#  include <io.h> // _open, _close
+#else
+#  include <unistd.h> // close
+#endif
+
+#include <fcntl.h> // open
 #include <sys/stat.h>
 #include <assert.h>
 #include <stddef.h> // ptrdiff_t
-#include <stdlib.h>
 #include <stdio.h>
 #include <time.h>
 #include "expat.h"
@@ -52,17 +60,18 @@
 #  define XML_FMT_STR "s"
 #endif
 
-static void
+static int
 usage(const char *prog, int rc) {
   fprintf(stderr, "usage: %s [-n] filename bufferSize nr_of_loops\n", prog);
-  exit(rc);
+  return rc;
 }
 
 int
 main(int argc, char *argv[]) {
   XML_Parser parser;
   char *XMLBuf, *XMLBufEnd, *XMLBufPtr;
-  FILE *fd;
+  int fd;
+  FILE *file;
   struct stat fileAttr;
   int nrOfLoops, bufferSize, i, isFinal;
   size_t fileSize;
@@ -76,34 +85,51 @@ main(int argc, char *argv[]) {
         ns = 1;
         j = 1;
       } else
-        usage(argv[0], 1);
+        return usage(argv[0], 1);
     }
   }
 
   if (argc != j + 4)
-    usage(argv[0], 1);
+    return usage(argv[0], 1);
 
-  if (stat(argv[j + 1], &fileAttr) != 0) {
-    fprintf(stderr, "could not access file '%s'\n", argv[j + 1]);
+  fd = open(argv[j + 1], O_RDONLY);
+  if (fd == -1) {
+    fprintf(stderr, "could not open file '%s'\n", argv[j + 1]);
     return 2;
   }
 
-  fd = fopen(argv[j + 1], "r");
-  if (! fd) {
-    fprintf(stderr, "could not open file '%s'\n", argv[j + 1]);
-    exit(2);
+  if (fstat(fd, &fileAttr) != 0) {
+    close(fd);
+    fprintf(stderr, "could not fstat file '%s'\n", argv[j + 1]);
+    return 2;
+  }
+
+  file = fdopen(fd, "r");
+  if (! file) {
+    close(fd);
+    fprintf(stderr, "could not fdopen file '%s'\n", argv[j + 1]);
+    return 2;
   }
 
   bufferSize = atoi(argv[j + 2]);
   nrOfLoops = atoi(argv[j + 3]);
   if (bufferSize <= 0 || nrOfLoops <= 0) {
+    fclose(file);
+    close(fd);
     fprintf(stderr, "buffer size and nr of loops must be greater than zero.\n");
-    exit(3);
+    return 3;
   }
 
   XMLBuf = malloc(fileAttr.st_size);
-  fileSize = fread(XMLBuf, sizeof(char), fileAttr.st_size, fd);
-  fclose(fd);
+  if (XMLBuf == NULL) {
+    fclose(file);
+    close(fd);
+    fprintf(stderr, "ouf of memory.\n");
+    return 5;
+  }
+  fileSize = fread(XMLBuf, sizeof(char), fileAttr.st_size, file);
+  fclose(file);
+  close(fd);
 
   if (ns)
     parser = XML_ParserCreateNS(NULL, '!');
@@ -132,7 +158,7 @@ main(int argc, char *argv[]) {
                 XML_GetCurrentColumnNumber(parser));
         free(XMLBuf);
         XML_ParserFree(parser);
-        exit(4);
+        return 4;
       }
       XMLBufPtr += bufferSize;
     } while (! isFinal);

+ 6 - 0
libs/expat/tests/common.c

@@ -202,6 +202,12 @@ _XML_Parse_SINGLE_BYTES(XML_Parser parser, const char *s, int len,
     for (; len > chunksize; len -= chunksize, s += chunksize) {
       enum XML_Status res = XML_Parse(parser, s, chunksize, XML_FALSE);
       if (res != XML_STATUS_OK) {
+        if ((res == XML_STATUS_SUSPENDED) && (len > chunksize)) {
+          fail("Use of function _XML_Parse_SINGLE_BYTES with a chunk size "
+               "greater than 0 (from g_chunkSize) does not work well with "
+               "suspension. Please consider use of plain XML_Parse at this "
+               "place in your test, instead.");
+        }
         return res;
       }
     }

+ 23 - 0
libs/expat/tests/handlers.c

@@ -1842,6 +1842,15 @@ element_decl_suspender(void *userData, const XML_Char *name,
   XML_FreeContentModel(g_parser, model);
 }
 
+void XMLCALL
+suspend_after_element_declaration(void *userData, const XML_Char *name,
+                                  XML_Content *model) {
+  UNUSED_P(name);
+  XML_Parser parser = (XML_Parser)userData;
+  assert_true(XML_StopParser(parser, /*resumable*/ XML_TRUE) == XML_STATUS_OK);
+  XML_FreeContentModel(parser, model);
+}
+
 void XMLCALL
 accumulate_pi_characters(void *userData, const XML_Char *target,
                          const XML_Char *data) {
@@ -1882,6 +1891,20 @@ accumulate_entity_decl(void *userData, const XML_Char *entityName,
   CharData_AppendXMLChars(storage, XCS("\n"), 1);
 }
 
+void XMLCALL
+accumulate_char_data_and_suspend(void *userData, const XML_Char *s, int len) {
+  ParserPlusStorage *const parserPlusStorage = (ParserPlusStorage *)userData;
+
+  CharData_AppendXMLChars(parserPlusStorage->storage, s, len);
+
+  for (int i = 0; i < len; i++) {
+    if (s[i] == 'Z') {
+      XML_StopParser(parserPlusStorage->parser, /*resumable=*/XML_TRUE);
+      break;
+    }
+  }
+}
+
 void XMLCALL
 accumulate_start_element(void *userData, const XML_Char *name,
                          const XML_Char **atts) {

+ 9 - 0
libs/expat/tests/handlers.h

@@ -325,6 +325,7 @@ extern int XMLCALL external_entity_devaluer(XML_Parser parser,
 typedef struct ext_hdlr_data {
   const char *parse_text;
   XML_ExternalEntityRefHandler handler;
+  CharData *storage;
 } ExtHdlrData;
 
 extern int XMLCALL external_entity_oneshot_loader(XML_Parser parser,
@@ -557,6 +558,10 @@ extern void XMLCALL suspending_comment_handler(void *userData,
 extern void XMLCALL element_decl_suspender(void *userData, const XML_Char *name,
                                            XML_Content *model);
 
+extern void XMLCALL suspend_after_element_declaration(void *userData,
+                                                      const XML_Char *name,
+                                                      XML_Content *model);
+
 extern void XMLCALL accumulate_pi_characters(void *userData,
                                              const XML_Char *target,
                                              const XML_Char *data);
@@ -569,6 +574,10 @@ extern void XMLCALL accumulate_entity_decl(
     const XML_Char *systemId, const XML_Char *publicId,
     const XML_Char *notationName);
 
+extern void XMLCALL accumulate_char_data_and_suspend(void *userData,
+                                                     const XML_Char *s,
+                                                     int len);
+
 extern void XMLCALL accumulate_start_element(void *userData,
                                              const XML_Char *name,
                                              const XML_Char **atts);

+ 4 - 2
libs/expat/tests/minicheck.h

@@ -14,7 +14,7 @@
 
    Copyright (c) 2004-2006 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2006-2012 Karl Waclawek <[email protected]>
-   Copyright (c) 2016-2024 Sebastian Pipping <[email protected]>
+   Copyright (c) 2016-2025 Sebastian Pipping <[email protected]>
    Copyright (c) 2022      Rhodri James <[email protected]>
    Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <[email protected]>
    Licensed under the MIT license:
@@ -129,8 +129,10 @@ void _check_set_test_info(char const *function, char const *filename,
  * Prototypes for the actual implementation.
  */
 
-#  if defined(__GNUC__)
+#  if defined(__has_attribute)
+#    if __has_attribute(noreturn)
 __attribute__((noreturn))
+#    endif
 #  endif
 void
 _fail(const char *file, int line, const char *msg);

+ 143 - 43
libs/expat/tests/misc_tests.c

@@ -10,7 +10,7 @@
    Copyright (c) 2003      Greg Stein <[email protected]>
    Copyright (c) 2005-2007 Steven Solie <[email protected]>
    Copyright (c) 2005-2012 Karl Waclawek <[email protected]>
-   Copyright (c) 2016-2024 Sebastian Pipping <[email protected]>
+   Copyright (c) 2016-2025 Sebastian Pipping <[email protected]>
    Copyright (c) 2017-2022 Rhodri James <[email protected]>
    Copyright (c) 2017      Joe Orton <[email protected]>
    Copyright (c) 2017      José Gutiérrez de la Concha <[email protected]>
@@ -59,6 +59,9 @@
 #include "handlers.h"
 #include "misc_tests.h"
 
+void XMLCALL accumulate_characters_ext_handler(void *userData,
+                                               const XML_Char *s, int len);
+
 /* Test that a failure to allocate the parser structure fails gracefully */
 START_TEST(test_misc_alloc_create_parser) {
   XML_Memory_Handling_Suite memsuite = {duff_allocator, realloc, free};
@@ -208,7 +211,7 @@ START_TEST(test_misc_version) {
   if (! versions_equal(&read_version, &parsed_version))
     fail("Version mismatch");
 
-  if (xcstrcmp(version_text, XCS("expat_2.6.4"))) /* needs bump on releases */
+  if (xcstrcmp(version_text, XCS("expat_2.7.0"))) /* needs bump on releases */
     fail("XML_*_VERSION in expat.h out of sync?\n");
 }
 END_TEST
@@ -294,6 +297,7 @@ START_TEST(test_misc_stop_during_end_handler_issue_240_1) {
   parser = XML_ParserCreate(NULL);
   XML_SetElementHandler(parser, start_element_issue_240, end_element_issue_240);
   mydata = (DataIssue240 *)malloc(sizeof(DataIssue240));
+  assert_true(mydata != NULL);
   mydata->parser = parser;
   mydata->deep = 0;
   XML_SetUserData(parser, mydata);
@@ -315,6 +319,7 @@ START_TEST(test_misc_stop_during_end_handler_issue_240_2) {
   parser = XML_ParserCreate(NULL);
   XML_SetElementHandler(parser, start_element_issue_240, end_element_issue_240);
   mydata = (DataIssue240 *)malloc(sizeof(DataIssue240));
+  assert_true(mydata != NULL);
   mydata->parser = parser;
   mydata->deep = 0;
   XML_SetUserData(parser, mydata);
@@ -328,64 +333,119 @@ START_TEST(test_misc_stop_during_end_handler_issue_240_2) {
 END_TEST
 
 START_TEST(test_misc_deny_internal_entity_closing_doctype_issue_317) {
-  const char *const inputOne = "<!DOCTYPE d [\n"
-                               "<!ENTITY % e ']><d/>'>\n"
-                               "\n"
-                               "%e;";
+  const char *const inputOne
+      = "<!DOCTYPE d [\n"
+        "<!ENTITY % element_d '<!ELEMENT d (#PCDATA)*>'>\n"
+        "%element_d;\n"
+        "<!ENTITY % e ']><d/>'>\n"
+        "\n"
+        "%e;";
   const char *const inputTwo
       = "<!DOCTYPE d [\n"
+        "<!ENTITY % element_d '<!ELEMENT d (#PCDATA)*>'>\n"
+        "%element_d;\n"
         "<!ENTITY % e1 ']><d/>'><!ENTITY % e2 '&#37;e1;'>\n"
         "\n"
         "%e2;";
-  const char *const inputThree = "<!DOCTYPE d [\n"
-                                 "<!ENTITY % e ']><d'>\n"
-                                 "\n"
-                                 "%e;/>";
-  const char *const inputIssue317 = "<!DOCTYPE doc [\n"
-                                    "<!ENTITY % foo ']>\n"
-                                    "<doc>Hell<oc (#PCDATA)*>'>\n"
-                                    "%foo;\n"
-                                    "]>\n"
-                                    "<doc>Hello, world</dVc>";
+  const char *const inputThree
+      = "<!DOCTYPE d [\n"
+        "<!ENTITY % element_d '<!ELEMENT d (#PCDATA)*>'>\n"
+        "%element_d;\n"
+        "<!ENTITY % e ']><d'>\n"
+        "\n"
+        "%e;/>";
+  const char *const inputIssue317
+      = "<!DOCTYPE doc [\n"
+        "<!ENTITY % element_doc '<!ELEMENT doc (#PCDATA)*>'>\n"
+        "%element_doc;\n"
+        "<!ENTITY % foo ']>\n"
+        "<doc>Hell<oc (#PCDATA)*>'>\n"
+        "%foo;\n"
+        "]>\n"
+        "<doc>Hello, world</dVc>";
 
   const char *const inputs[] = {inputOne, inputTwo, inputThree, inputIssue317};
+  const XML_Bool suspendOrNot[] = {XML_FALSE, XML_TRUE};
   size_t inputIndex = 0;
 
   for (; inputIndex < sizeof(inputs) / sizeof(inputs[0]); inputIndex++) {
-    set_subtest("%s", inputs[inputIndex]);
-    XML_Parser parser;
-    enum XML_Status parseResult;
-    int setParamEntityResult;
-    XML_Size lineNumber;
-    XML_Size columnNumber;
-    const char *const input = inputs[inputIndex];
-
-    parser = XML_ParserCreate(NULL);
-    setParamEntityResult
-        = XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-    if (setParamEntityResult != 1)
-      fail("Failed to set XML_PARAM_ENTITY_PARSING_ALWAYS.");
-
-    parseResult = _XML_Parse_SINGLE_BYTES(parser, input, (int)strlen(input), 0);
-    if (parseResult != XML_STATUS_ERROR) {
-      parseResult = _XML_Parse_SINGLE_BYTES(parser, "", 0, 1);
+    for (size_t suspendOrNotIndex = 0;
+         suspendOrNotIndex < sizeof(suspendOrNot) / sizeof(suspendOrNot[0]);
+         suspendOrNotIndex++) {
+      const char *const input = inputs[inputIndex];
+      const XML_Bool suspend = suspendOrNot[suspendOrNotIndex];
+      if (suspend && (g_chunkSize > 0)) {
+        // We cannot use _XML_Parse_SINGLE_BYTES below due to suspension, and
+        // so chunk sizes >0 would only repeat the very same test
+        // due to use of plain XML_Parse; we are saving upon that runtime:
+        return;
+      }
+
+      set_subtest("[input=%d suspend=%s] %s", (int)inputIndex,
+                  suspend ? "true" : "false", input);
+      XML_Parser parser;
+      enum XML_Status parseResult;
+      int setParamEntityResult;
+      XML_Size lineNumber;
+      XML_Size columnNumber;
+
+      parser = XML_ParserCreate(NULL);
+      setParamEntityResult
+          = XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+      if (setParamEntityResult != 1)
+        fail("Failed to set XML_PARAM_ENTITY_PARSING_ALWAYS.");
+
+      if (suspend) {
+        XML_SetUserData(parser, parser);
+        XML_SetElementDeclHandler(parser, suspend_after_element_declaration);
+      }
+
+      if (suspend) {
+        // can't use SINGLE_BYTES here, because it'll return early on
+        // suspension, and we won't know exactly how much input we actually
+        // managed to give Expat.
+        parseResult = XML_Parse(parser, input, (int)strlen(input), 0);
+
+        while (parseResult == XML_STATUS_SUSPENDED) {
+          parseResult = XML_ResumeParser(parser);
+        }
+
+        if (parseResult != XML_STATUS_ERROR) {
+          // can't use SINGLE_BYTES here, because it'll return early on
+          // suspension, and we won't know exactly how much input we actually
+          // managed to give Expat.
+          parseResult = XML_Parse(parser, "", 0, 1);
+        }
+
+        while (parseResult == XML_STATUS_SUSPENDED) {
+          parseResult = XML_ResumeParser(parser);
+        }
+      } else {
+        parseResult
+            = _XML_Parse_SINGLE_BYTES(parser, input, (int)strlen(input), 0);
+
+        if (parseResult != XML_STATUS_ERROR) {
+          parseResult = _XML_Parse_SINGLE_BYTES(parser, "", 0, 1);
+        }
+      }
+
       if (parseResult != XML_STATUS_ERROR) {
         fail("Parsing was expected to fail but succeeded.");
       }
-    }
 
-    if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN)
-      fail("Error code does not match XML_ERROR_INVALID_TOKEN");
+      if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN)
+        fail("Error code does not match XML_ERROR_INVALID_TOKEN");
 
-    lineNumber = XML_GetCurrentLineNumber(parser);
-    if (lineNumber != 4)
-      fail("XML_GetCurrentLineNumber does not work as expected.");
+      lineNumber = XML_GetCurrentLineNumber(parser);
+      if (lineNumber != 6)
+        fail("XML_GetCurrentLineNumber does not work as expected.");
 
-    columnNumber = XML_GetCurrentColumnNumber(parser);
-    if (columnNumber != 0)
-      fail("XML_GetCurrentColumnNumber does not work as expected.");
+      columnNumber = XML_GetCurrentColumnNumber(parser);
+      if (columnNumber != 0)
+        fail("XML_GetCurrentColumnNumber does not work as expected.");
 
-    XML_ParserFree(parser);
+      XML_ParserFree(parser);
+    }
   }
 }
 END_TEST
@@ -519,6 +579,45 @@ START_TEST(test_misc_stopparser_rejects_unstarted_parser) {
 }
 END_TEST
 
+/* Adaptation of accumulate_characters that takes ExtHdlrData input to work with
+ * test_renter_loop_finite_content below */
+void XMLCALL
+accumulate_characters_ext_handler(void *userData, const XML_Char *s, int len) {
+  ExtHdlrData *const test_data = (ExtHdlrData *)userData;
+  CharData_AppendXMLChars(test_data->storage, s, len);
+}
+
+/* Test that internalEntityProcessor does not re-enter forever;
+ * based on files tests/xmlconf/xmltest/valid/ext-sa/012.{xml,ent} */
+START_TEST(test_renter_loop_finite_content) {
+  CharData storage;
+  CharData_Init(&storage);
+  const char *const text = "<!DOCTYPE doc [\n"
+                           "<!ENTITY e1 '&e2;'>\n"
+                           "<!ENTITY e2 '&e3;'>\n"
+                           "<!ENTITY e3 SYSTEM '012.ent'>\n"
+                           "<!ENTITY e4 '&e5;'>\n"
+                           "<!ENTITY e5 '(e5)'>\n"
+                           "<!ELEMENT doc (#PCDATA)>\n"
+                           "]>\n"
+                           "<doc>&e1;</doc>\n";
+  ExtHdlrData test_data = {"&e4;\n", external_entity_null_loader, &storage};
+  const XML_Char *const expected = XCS("(e5)\n");
+
+  XML_Parser parser = XML_ParserCreate(NULL);
+  assert_true(parser != NULL);
+  XML_SetUserData(parser, &test_data);
+  XML_SetExternalEntityRefHandler(parser, external_entity_oneshot_loader);
+  XML_SetCharacterDataHandler(parser, accumulate_characters_ext_handler);
+  if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(parser);
+
+  CharData_CheckXMLChars(&storage, expected);
+  XML_ParserFree(parser);
+}
+END_TEST
+
 void
 make_miscellaneous_test_case(Suite *s) {
   TCase *tc_misc = tcase_create("miscellaneous tests");
@@ -545,4 +644,5 @@ make_miscellaneous_test_case(Suite *s) {
   tcase_add_test(tc_misc, test_misc_char_handler_stop_without_leak);
   tcase_add_test(tc_misc, test_misc_resumeparser_not_crashing);
   tcase_add_test(tc_misc, test_misc_stopparser_rejects_unstarted_parser);
+  tcase_add_test__if_xml_ge(tc_misc, test_renter_loop_finite_content);
 }

+ 3 - 2
libs/expat/tests/xmltest.sh

@@ -2,8 +2,8 @@
 # EXPAT TEST SCRIPT FOR W3C XML TEST SUITE
 #
 # This script can be used to exercise Expat against the
-# w3c.org xml test suite, available from
-# http://www.w3.org/XML/Test/xmlts20020606.zip.
+# w3c.org xml test suite, available from:
+# https://www.w3.org/XML/Test/xmlts20020606.zip
 #
 # To run this script, first set XMLWF below so that xmlwf can be
 # found, then set the output directory with OUTPUT.
@@ -30,6 +30,7 @@
 # Copyright (c) 2002      Karl Waclawek <[email protected]>
 # Copyright (c) 2008-2019 Sebastian Pipping <[email protected]>
 # Copyright (c) 2017      Rhodri James <[email protected]>
+# Copyright (c) 2025      Hanno Böck <[email protected]>
 # Licensed under the MIT license:
 #
 # Permission is  hereby granted,  free of charge,  to any  person obtaining

+ 4 - 4
libs/expat/win32/expat.iss

@@ -14,7 +14,7 @@
 ; Copyright (c) 2001      Tim Peters <[email protected]>
 ; Copyright (c) 2001-2005 Fred L. Drake, Jr. <[email protected]>
 ; Copyright (c) 2006-2017 Karl Waclawek <[email protected]>
-; Copyright (c) 2007-2024 Sebastian Pipping <[email protected]>
+; Copyright (c) 2007-2025 Sebastian Pipping <[email protected]>
 ; Copyright (c) 2022      Johnny Jazeix <[email protected]>
 ; Copyright (c) 2024      Dag-Erling Smørgrav <[email protected]>
 ; Licensed under the MIT license:
@@ -38,14 +38,14 @@
 ; OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 ; USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-#define expatVer "2.6.4"
+#define expatVer "2.7.0"
 
 [Setup]
 AppName=Expat
 AppId=expat
 AppVersion={#expatVer}
 AppVerName=Expat {#expatVer}
-AppCopyright=Copyright © 1997-2022 Thai Open Source Software Center, Clark Cooper, and the Expat maintainers
+AppCopyright=Copyright © 1997-2025 Thai Open Source Software Center, Clark Cooper, and the Expat maintainers
 AppPublisher=The Expat Developers
 AppPublisherURL=https://libexpat.github.io/
 AppSupportURL=https://libexpat.github.io/
@@ -75,7 +75,7 @@ Flags: ignoreversion; Source: COPYING;                      DestDir: "{app}"; De
 Flags: ignoreversion; Source: README.md;                    DestDir: "{app}"; DestName: README.txt
 Flags: ignoreversion; Source: doc\*.html;                   DestDir: "{app}\Doc"
 Flags: ignoreversion; Source: doc\*.css;                    DestDir: "{app}\Doc"
-Flags: ignoreversion; Source: doc\*.xml;                    DestDir: "{app}\Doc"
+Flags: ignoreversion; Source: doc\*.xml;                    DestDir: "{app}\Source\doc"
 Flags: ignoreversion; Source: win32\bin\Release\*.dll;      DestDir: "{app}\Bin"
 Flags: ignoreversion; Source: win32\bin\Release\*.lib;      DestDir: "{app}\Bin"
 Flags: ignoreversion; Source: win32\version.rc.cmake;       DestDir: "{app}\Source\win32"

+ 2 - 1
libs/expat/xmlwf/readfilemap.c

@@ -14,6 +14,7 @@
    Copyright (c) 2017      Rhodri James <[email protected]>
    Copyright (c) 2017      Franek Korta <[email protected]>
    Copyright (c) 2022      Sean McBride <[email protected]>
+   Copyright (c) 2025      Hanno Böck <[email protected]>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -55,7 +56,7 @@
 #  define EXPAT_read_count_t int
 #  define EXPAT_read_req_t unsigned int
 #else /* POSIX */
-/* http://pubs.opengroup.org/onlinepubs/009695399/functions/read.html */
+/* https://pubs.opengroup.org/onlinepubs/009695399/functions/read.html */
 #  define EXPAT_read read
 #  define EXPAT_read_count_t ssize_t
 #  define EXPAT_read_req_t size_t

Некоторые файлы не были показаны из-за большого количества измененных файлов