Browse Source

Merge branch 'master' into dev

Source commit: c98fed282021b1b0e68d1115d843548ac9aceb58
Martin Prikryl 6 months ago
parent
commit
3b698a0ed2
65 changed files with 951 additions and 137 deletions
  1. 6 6
      libs/expat/CMake.README
  2. 2 2
      libs/expat/CMakeLists.txt
  3. 1 1
      libs/expat/COPYING
  4. 38 0
      libs/expat/Changes
  5. 3 1
      libs/expat/Makefile.am
  6. 3 1
      libs/expat/Makefile.in
  7. 2 1
      libs/expat/README.md
  8. 11 11
      libs/expat/configure
  9. 1 1
      libs/expat/configure.ac
  10. 1 1
      libs/expat/doc/reference.html
  11. 1 1
      libs/expat/doc/xmlwf.1
  12. 1 1
      libs/expat/doc/xmlwf.xml
  13. 3 3
      libs/expat/expat_config.h
  14. 464 0
      libs/expat/fuzz/xml_lpm_fuzzer.cpp
  15. 58 0
      libs/expat/fuzz/xml_lpm_fuzzer.proto
  16. 1 1
      libs/expat/lib/expat.h
  17. 4 1
      libs/expat/lib/internal.h
  18. 11 5
      libs/expat/lib/xmlparse.c
  19. 3 6
      libs/expat/tests/benchmark/benchmark.c
  20. 26 1
      libs/expat/tests/common.c
  21. 3 1
      libs/expat/tests/common.h
  22. 62 1
      libs/expat/tests/misc_tests.c
  23. 1 1
      libs/expat/win32/expat.iss
  24. 5 1
      source/forms/About.dfm
  25. 5 1
      source/forms/Authenticate.dfm
  26. 5 1
      source/forms/Cleanup.dfm
  27. 5 1
      source/forms/Console.dfm
  28. 5 1
      source/forms/Copy.dfm
  29. 5 1
      source/forms/CopyLocal.dfm
  30. 5 1
      source/forms/CopyParamCustom.dfm
  31. 5 1
      source/forms/CopyParamPreset.dfm
  32. 6 0
      source/forms/CopyParams.dfm
  33. 5 1
      source/forms/CreateDirectory.dfm
  34. 5 1
      source/forms/Custom.dfm
  35. 5 1
      source/forms/CustomCommand.dfm
  36. 5 1
      source/forms/CustomScpExplorer.dfm
  37. 5 1
      source/forms/EditMask.dfm
  38. 5 1
      source/forms/Editor.dfm
  39. 5 1
      source/forms/EditorPreferences.dfm
  40. 5 1
      source/forms/FileFind.dfm
  41. 5 1
      source/forms/FileSystemInfo.dfm
  42. 5 1
      source/forms/FullSynchronize.dfm
  43. 5 1
      source/forms/GenerateUrl.dfm
  44. 5 1
      source/forms/ImportSessions.dfm
  45. 5 1
      source/forms/License.dfm
  46. 5 1
      source/forms/LocationProfiles.dfm
  47. 5 1
      source/forms/Login.dfm
  48. 1 1
      source/forms/MessageDlg.cpp
  49. 5 1
      source/forms/MessageDlg.dfm
  50. 5 1
      source/forms/OpenDirectory.dfm
  51. 5 1
      source/forms/Preferences.dfm
  52. 5 1
      source/forms/Progress.dfm
  53. 5 1
      source/forms/Properties.dfm
  54. 5 1
      source/forms/RemoteTransfer.dfm
  55. 6 0
      source/forms/Rights.dfm
  56. 5 1
      source/forms/SelectMask.dfm
  57. 5 1
      source/forms/SiteAdvanced.dfm
  58. 5 1
      source/forms/Symlink.dfm
  59. 5 1
      source/forms/Synchronize.dfm
  60. 5 1
      source/forms/SynchronizeChecklist.dfm
  61. 5 1
      source/forms/SynchronizeProgress.dfm
  62. 48 48
      source/packages/my/ListViewColProperties.pas
  63. 1 0
      source/packages/my/NortonLikeListView.pas
  64. 1 1
      source/resource/LicenseExpat.txt
  65. 7 5
      source/windows/VCLCommon.cpp

+ 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.7.0 is the source directory of expat, add a subdirectory
+Assuming ~/expat-2.7.1 is the source directory of expat, add a subdirectory
 build and change into that directory:
-~/expat-2.7.0$ mkdir build && cd build
-~/expat-2.7.0/build$
+~/expat-2.7.1$ mkdir build && cd build
+~/expat-2.7.1/build$
 
 From that directory, call cmake first, then call make, make test and
 make install in the usual way:
-~/expat-2.7.0/build$ cmake ..
+~/expat-2.7.1/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.7.0/build
+-- Build files have been written to: /home/patrick/expat-2.7.1/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.7.0/build$ make && make test && make install
+~/expat-2.7.1/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

+ 2 - 2
libs/expat/CMakeLists.txt

@@ -39,7 +39,7 @@ cmake_minimum_required(VERSION 3.13.0)
 
 project(expat
     VERSION
-        2.7.0
+        2.7.1
     LANGUAGES
         C
 )
@@ -472,7 +472,7 @@ foreach(build_type_upper
 endforeach()
 
 set(LIBCURRENT 11)  # sync
-set(LIBREVISION 1)  # with
+set(LIBREVISION 2)  # with
 set(LIBAGE 10)      # configure.ac!
 math(EXPR LIBCURRENT_MINUS_AGE "${LIBCURRENT} - ${LIBAGE}")
 

+ 1 - 1
libs/expat/COPYING

@@ -1,5 +1,5 @@
 Copyright (c) 1998-2000 Thai Open Source Software Center Ltd and Clark Cooper
-Copyright (c) 2001-2022 Expat maintainers
+Copyright (c) 2001-2025 Expat maintainers
 
 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the

+ 38 - 0
libs/expat/Changes

@@ -37,6 +37,44 @@
 !! THANK YOU!                        Sebastian Pipping -- Berlin, 2024-03-09 !!
 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
+Release 2.7.1 Thu March 27 2025
+        Bug fixes:
+       #980 #989  Restore event pointer behavior from Expat 2.6.4
+                    (that the fix to CVE-2024-8176 changed in 2.7.0);
+                    affected API functions are:
+                    - XML_GetCurrentByteCount
+                    - XML_GetCurrentByteIndex
+                    - XML_GetCurrentColumnNumber
+                    - XML_GetCurrentLineNumber
+                    - XML_GetInputContext
+
+        Other changes:
+       #976 #977  Autotools: Integrate files "fuzz/xml_lpm_fuzzer.{cpp,proto}"
+                    with Automake that were missing from 2.7.0 release tarballs
+       #983 #984  Fix printf format specifiers for 32bit Emscripten
+            #992  docs: Promote OpenSSF Best Practices self-certification
+            #978  tests/benchmark: Resolve mistaken double close
+            #986  Address compiler warnings
+       #990 #993  Version info bumped from 11:1:10 (libexpat*.so.1.10.1)
+                    to 11:2:10 (libexpat*.so.1.10.2); see https://verbump.de/
+                    for what these numbers do
+
+        Infrastructure:
+            #982  CI: Start running Perl XML::Parser integration tests
+            #987  CI: Enforce Clang Static Analyzer clean code
+            #991  CI: Re-enable warning clang-analyzer-valist.Uninitialized
+                    for clang-tidy
+            #981  CI: Cover compilation with musl
+       #983 #984  CI: Cover compilation with 32bit Emscripten
+       #976 #977  CI: Protect against fuzzer files missing from future
+                    release archives
+
+        Special thanks to:
+            Berkay Eren Ürün
+            Matthew Fernandez
+                 and
+            Perl XML::Parser
+
 Release 2.7.0 Thu March 13 2025
         Security fixes:
        #893 #973  CVE-2024-8176 -- Fix crash from chaining a large number

+ 3 - 1
libs/expat/Makefile.am

@@ -6,7 +6,7 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017-2023 Sebastian Pipping <[email protected]>
+# Copyright (c) 2017-2025 Sebastian Pipping <[email protected]>
 # Copyright (c) 2018      KangLin <[email protected]>
 # Copyright (c) 2022      Johnny Jazeix <[email protected]>
 # Copyright (c) 2023      Sony Corporation / Snild Dolkow <[email protected]>
@@ -96,6 +96,8 @@ EXTRA_DIST = \
     conftools/expat.m4 \
     conftools/get-version.sh \
     \
+    fuzz/xml_lpm_fuzzer.cpp \
+    fuzz/xml_lpm_fuzzer.proto \
     fuzz/xml_parsebuffer_fuzzer.c \
     fuzz/xml_parse_fuzzer.c \
     \

+ 3 - 1
libs/expat/Makefile.in

@@ -22,7 +22,7 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017-2023 Sebastian Pipping <[email protected]>
+# Copyright (c) 2017-2025 Sebastian Pipping <[email protected]>
 # Copyright (c) 2018      KangLin <[email protected]>
 # Copyright (c) 2022      Johnny Jazeix <[email protected]>
 # Copyright (c) 2023      Sony Corporation / Snild Dolkow <[email protected]>
@@ -494,6 +494,8 @@ EXTRA_DIST = \
     conftools/expat.m4 \
     conftools/get-version.sh \
     \
+    fuzz/xml_lpm_fuzzer.cpp \
+    fuzz/xml_lpm_fuzzer.proto \
     fuzz/xml_parsebuffer_fuzzer.c \
     fuzz/xml_parse_fuzzer.c \
     \

+ 2 - 1
libs/expat/README.md

@@ -3,6 +3,7 @@
 [![Packaging status](https://repology.org/badge/tiny-repos/expat.svg)](https://repology.org/metapackage/expat/versions)
 [![Downloads SourceForge](https://img.shields.io/sourceforge/dt/expat?label=Downloads%20SourceForge)](https://sourceforge.net/projects/expat/files/)
 [![Downloads GitHub](https://img.shields.io/github/downloads/libexpat/libexpat/total?label=Downloads%20GitHub)](https://github.com/libexpat/libexpat/releases)
+[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/10205/badge)](https://www.bestpractices.dev/projects/10205)
 
 > [!CAUTION]
 >
@@ -11,7 +12,7 @@
 > at the top of the `Changes` file.
 
 
-# Expat, Release 2.7.0
+# Expat, Release 2.7.1
 
 This is Expat, a C99 library for parsing
 [XML 1.0 Fourth Edition](https://www.w3.org/TR/2006/REC-xml-20060816/), started by

+ 11 - 11
libs/expat/configure

@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.71 for expat 2.7.0.
+# Generated by GNU Autoconf 2.71 for expat 2.7.1.
 #
 # Report bugs to <https://github.com/libexpat/libexpat/issues>.
 #
@@ -621,8 +621,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='expat'
 PACKAGE_TARNAME='expat'
-PACKAGE_VERSION='2.7.0'
-PACKAGE_STRING='expat 2.7.0'
+PACKAGE_VERSION='2.7.1'
+PACKAGE_STRING='expat 2.7.1'
 PACKAGE_BUGREPORT='https://github.com/libexpat/libexpat/issues'
 PACKAGE_URL=''
 
@@ -1426,7 +1426,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures expat 2.7.0 to adapt to many kinds of systems.
+\`configure' configures expat 2.7.1 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1497,7 +1497,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of expat 2.7.0:";;
+     short | recursive ) echo "Configuration of expat 2.7.1:";;
    esac
   cat <<\_ACEOF
 
@@ -1634,7 +1634,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-expat configure 2.7.0
+expat configure 2.7.1
 generated by GNU Autoconf 2.71
 
 Copyright (C) 2021 Free Software Foundation, Inc.
@@ -2265,7 +2265,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by expat $as_me 2.7.0, which was
+It was created by expat $as_me 2.7.1, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -3831,7 +3831,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='expat'
- VERSION='2.7.0'
+ VERSION='2.7.1'
 
 
 printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -3962,7 +3962,7 @@ fi
 
 
 LIBCURRENT=11  # sync
-LIBREVISION=1  # with
+LIBREVISION=2  # with
 LIBAGE=10      # CMakeLists.txt!
 
 ac_config_headers="$ac_config_headers expat_config.h"
@@ -22099,7 +22099,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by expat $as_me 2.7.0, which was
+This file was extended by expat $as_me 2.7.1, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -22167,7 +22167,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config='$ac_cs_config_escaped'
 ac_cs_version="\\
-expat config.status 2.7.0
+expat config.status 2.7.1
 configured by $0, generated by GNU Autoconf 2.71,
   with options \\"\$ac_cs_config\\"
 

+ 1 - 1
libs/expat/configure.ac

@@ -85,7 +85,7 @@ dnl If the API changes incompatibly set LIBAGE back to 0
 dnl
 
 LIBCURRENT=11  # sync
-LIBREVISION=1  # with
+LIBREVISION=2  # with
 LIBAGE=10      # CMakeLists.txt!
 
 AC_CONFIG_HEADERS([expat_config.h])

+ 1 - 1
libs/expat/doc/reference.html

@@ -52,7 +52,7 @@
   <div>
     <h1>
       The Expat XML Parser
-      <small>Release 2.7.0</small>
+      <small>Release 2.7.1</small>
     </h1>
   </div>
 <div class="content">

+ 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 "March 13, 2025" "" ""
+.TH XMLWF 1 "March 27, 2025" "" ""
 .SH NAME
 xmlwf \- Determines if an XML document is well-formed
 .SH SYNOPSIS

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

@@ -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>March 13, 2025</date>">
+  <!ENTITY dhdate      "<date>March 27, 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

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

+ 464 - 0
libs/expat/fuzz/xml_lpm_fuzzer.cpp

@@ -0,0 +1,464 @@
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2022 Mark Brand <[email protected]>
+   Copyright (c) 2025 Sebastian Pipping <[email protected]>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#if defined(NDEBUG)
+#  undef NDEBUG // because checks below rely on assert(...)
+#endif
+
+#include <assert.h>
+#include <stdint.h>
+#include <vector>
+
+#include "expat.h"
+#include "xml_lpm_fuzzer.pb.h"
+#include "src/libfuzzer/libfuzzer_macro.h"
+
+static const char *g_encoding = nullptr;
+static const char *g_external_entity = nullptr;
+static size_t g_external_entity_size = 0;
+
+void
+SetEncoding(const xml_lpm_fuzzer::Encoding &e) {
+  switch (e) {
+  case xml_lpm_fuzzer::Encoding::UTF8:
+    g_encoding = "UTF-8";
+    break;
+
+  case xml_lpm_fuzzer::Encoding::UTF16:
+    g_encoding = "UTF-16";
+    break;
+
+  case xml_lpm_fuzzer::Encoding::ISO88591:
+    g_encoding = "ISO-8859-1";
+    break;
+
+  case xml_lpm_fuzzer::Encoding::ASCII:
+    g_encoding = "US-ASCII";
+    break;
+
+  case xml_lpm_fuzzer::Encoding::NONE:
+    g_encoding = NULL;
+    break;
+
+  default:
+    g_encoding = "UNKNOWN";
+    break;
+  }
+}
+
+static int g_allocation_count = 0;
+static std::vector<int> g_fail_allocations = {};
+
+void *
+MallocHook(size_t size) {
+  g_allocation_count += 1;
+  for (auto index : g_fail_allocations) {
+    if (index == g_allocation_count) {
+      return NULL;
+    }
+  }
+  return malloc(size);
+}
+
+void *
+ReallocHook(void *ptr, size_t size) {
+  g_allocation_count += 1;
+  for (auto index : g_fail_allocations) {
+    if (index == g_allocation_count) {
+      return NULL;
+    }
+  }
+  return realloc(ptr, size);
+}
+
+void
+FreeHook(void *ptr) {
+  free(ptr);
+}
+
+XML_Memory_Handling_Suite memory_handling_suite
+    = {MallocHook, ReallocHook, FreeHook};
+
+void InitializeParser(XML_Parser parser);
+
+// We want a parse function that supports resumption, so that we can cover the
+// suspend/resume code.
+enum XML_Status
+Parse(XML_Parser parser, const char *input, int input_len, int is_final) {
+  enum XML_Status status = XML_Parse(parser, input, input_len, is_final);
+  while (status == XML_STATUS_SUSPENDED) {
+    status = XML_ResumeParser(parser);
+  }
+  return status;
+}
+
+// When the fuzzer is compiled with instrumentation such as ASan, then the
+// accesses in TouchString will fault if they access invalid memory (ie. detect
+// either a use-after-free or buffer-overflow). By calling TouchString in each
+// of the callbacks, we can check that the arguments meet the API specifications
+// in terms of length/null-termination. no_optimize is used to ensure that the
+// compiler has to emit actual memory reads, instead of removing them.
+static volatile size_t no_optimize = 0;
+static void
+TouchString(const XML_Char *ptr, int len = -1) {
+  if (! ptr) {
+    return;
+  }
+
+  if (len == -1) {
+    for (XML_Char value = *ptr++; value; value = *ptr++) {
+      no_optimize += value;
+    }
+  } else {
+    for (int i = 0; i < len; ++i) {
+      no_optimize += ptr[i];
+    }
+  }
+}
+
+static void
+TouchNodeAndRecurse(XML_Content *content) {
+  switch (content->type) {
+  case XML_CTYPE_EMPTY:
+  case XML_CTYPE_ANY:
+    assert(content->quant == XML_CQUANT_NONE);
+    assert(content->name == NULL);
+    assert(content->numchildren == 0);
+    assert(content->children == NULL);
+    break;
+
+  case XML_CTYPE_MIXED:
+    assert(content->quant == XML_CQUANT_NONE
+           || content->quant == XML_CQUANT_REP);
+    assert(content->name == NULL);
+    for (unsigned int i = 0; i < content->numchildren; ++i) {
+      assert(content->children[i].type == XML_CTYPE_NAME);
+      assert(content->children[i].quant == XML_CQUANT_NONE);
+      assert(content->children[i].numchildren == 0);
+      assert(content->children[i].children == NULL);
+      TouchString(content->children[i].name);
+    }
+    break;
+
+  case XML_CTYPE_NAME:
+    assert((content->quant == XML_CQUANT_NONE)
+           || (content->quant == XML_CQUANT_OPT)
+           || (content->quant == XML_CQUANT_REP)
+           || (content->quant == XML_CQUANT_PLUS));
+    assert(content->numchildren == 0);
+    assert(content->children == NULL);
+    TouchString(content->name);
+    break;
+
+  case XML_CTYPE_CHOICE:
+  case XML_CTYPE_SEQ:
+    assert((content->quant == XML_CQUANT_NONE)
+           || (content->quant == XML_CQUANT_OPT)
+           || (content->quant == XML_CQUANT_REP)
+           || (content->quant == XML_CQUANT_PLUS));
+    assert(content->name == NULL);
+    for (unsigned int i = 0; i < content->numchildren; ++i) {
+      TouchNodeAndRecurse(&content->children[i]);
+    }
+    break;
+
+  default:
+    assert(false);
+  }
+}
+
+static void XMLCALL
+ElementDeclHandler(void *userData, const XML_Char *name, XML_Content *model) {
+  TouchString(name);
+  TouchNodeAndRecurse(model);
+  XML_FreeContentModel((XML_Parser)userData, model);
+}
+
+static void XMLCALL
+AttlistDeclHandler(void *userData, const XML_Char *elname,
+                   const XML_Char *attname, const XML_Char *atttype,
+                   const XML_Char *dflt, int isrequired) {
+  (void)userData;
+  TouchString(elname);
+  TouchString(attname);
+  TouchString(atttype);
+  TouchString(dflt);
+  (void)isrequired;
+}
+
+static void XMLCALL
+XmlDeclHandler(void *userData, const XML_Char *version,
+               const XML_Char *encoding, int standalone) {
+  (void)userData;
+  TouchString(version);
+  TouchString(encoding);
+  (void)standalone;
+}
+
+static void XMLCALL
+StartElementHandler(void *userData, const XML_Char *name,
+                    const XML_Char **atts) {
+  (void)userData;
+  TouchString(name);
+  for (size_t i = 0; atts[i] != NULL; ++i) {
+    TouchString(atts[i]);
+  }
+}
+
+static void XMLCALL
+EndElementHandler(void *userData, const XML_Char *name) {
+  (void)userData;
+  TouchString(name);
+}
+
+static void XMLCALL
+CharacterDataHandler(void *userData, const XML_Char *s, int len) {
+  (void)userData;
+  TouchString(s, len);
+}
+
+static void XMLCALL
+ProcessingInstructionHandler(void *userData, const XML_Char *target,
+                             const XML_Char *data) {
+  (void)userData;
+  TouchString(target);
+  TouchString(data);
+}
+
+static void XMLCALL
+CommentHandler(void *userData, const XML_Char *data) {
+  TouchString(data);
+  // Use the comment handler to trigger parser suspend, so that we can get
+  // coverage of that code.
+  XML_StopParser((XML_Parser)userData, XML_TRUE);
+}
+
+static void XMLCALL
+StartCdataSectionHandler(void *userData) {
+  (void)userData;
+}
+
+static void XMLCALL
+EndCdataSectionHandler(void *userData) {
+  (void)userData;
+}
+
+static void XMLCALL
+DefaultHandler(void *userData, const XML_Char *s, int len) {
+  (void)userData;
+  TouchString(s, len);
+}
+
+static void XMLCALL
+StartDoctypeDeclHandler(void *userData, const XML_Char *doctypeName,
+                        const XML_Char *sysid, const XML_Char *pubid,
+                        int has_internal_subset) {
+  (void)userData;
+  TouchString(doctypeName);
+  TouchString(sysid);
+  TouchString(pubid);
+  (void)has_internal_subset;
+}
+
+static void XMLCALL
+EndDoctypeDeclHandler(void *userData) {
+  (void)userData;
+}
+
+static void XMLCALL
+EntityDeclHandler(void *userData, const XML_Char *entityName,
+                  int is_parameter_entity, const XML_Char *value,
+                  int value_length, const XML_Char *base,
+                  const XML_Char *systemId, const XML_Char *publicId,
+                  const XML_Char *notationName) {
+  (void)userData;
+  TouchString(entityName);
+  (void)is_parameter_entity;
+  TouchString(value, value_length);
+  TouchString(base);
+  TouchString(systemId);
+  TouchString(publicId);
+  TouchString(notationName);
+}
+
+static void XMLCALL
+NotationDeclHandler(void *userData, const XML_Char *notationName,
+                    const XML_Char *base, const XML_Char *systemId,
+                    const XML_Char *publicId) {
+  (void)userData;
+  TouchString(notationName);
+  TouchString(base);
+  TouchString(systemId);
+  TouchString(publicId);
+}
+
+static void XMLCALL
+StartNamespaceDeclHandler(void *userData, const XML_Char *prefix,
+                          const XML_Char *uri) {
+  (void)userData;
+  TouchString(prefix);
+  TouchString(uri);
+}
+
+static void XMLCALL
+EndNamespaceDeclHandler(void *userData, const XML_Char *prefix) {
+  (void)userData;
+  TouchString(prefix);
+}
+
+static int XMLCALL
+NotStandaloneHandler(void *userData) {
+  (void)userData;
+  return XML_STATUS_OK;
+}
+
+static int XMLCALL
+ExternalEntityRefHandler(XML_Parser parser, const XML_Char *context,
+                         const XML_Char *base, const XML_Char *systemId,
+                         const XML_Char *publicId) {
+  int rc = XML_STATUS_ERROR;
+  TouchString(context);
+  TouchString(base);
+  TouchString(systemId);
+  TouchString(publicId);
+
+  if (g_external_entity) {
+    XML_Parser ext_parser
+        = XML_ExternalEntityParserCreate(parser, context, g_encoding);
+    rc = Parse(ext_parser, g_external_entity, g_external_entity_size, 1);
+    XML_ParserFree(ext_parser);
+  }
+
+  return rc;
+}
+
+static void XMLCALL
+SkippedEntityHandler(void *userData, const XML_Char *entityName,
+                     int is_parameter_entity) {
+  (void)userData;
+  TouchString(entityName);
+  (void)is_parameter_entity;
+}
+
+static int XMLCALL
+UnknownEncodingHandler(void *encodingHandlerData, const XML_Char *name,
+                       XML_Encoding *info) {
+  (void)encodingHandlerData;
+  TouchString(name);
+  (void)info;
+  return XML_STATUS_ERROR;
+}
+
+void
+InitializeParser(XML_Parser parser) {
+  XML_SetUserData(parser, (void *)parser);
+  XML_SetHashSalt(parser, 0x41414141);
+  XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+
+  XML_SetElementDeclHandler(parser, ElementDeclHandler);
+  XML_SetAttlistDeclHandler(parser, AttlistDeclHandler);
+  XML_SetXmlDeclHandler(parser, XmlDeclHandler);
+  XML_SetElementHandler(parser, StartElementHandler, EndElementHandler);
+  XML_SetCharacterDataHandler(parser, CharacterDataHandler);
+  XML_SetProcessingInstructionHandler(parser, ProcessingInstructionHandler);
+  XML_SetCommentHandler(parser, CommentHandler);
+  XML_SetCdataSectionHandler(parser, StartCdataSectionHandler,
+                             EndCdataSectionHandler);
+  // XML_SetDefaultHandler disables entity expansion
+  XML_SetDefaultHandlerExpand(parser, DefaultHandler);
+  XML_SetDoctypeDeclHandler(parser, StartDoctypeDeclHandler,
+                            EndDoctypeDeclHandler);
+  // Note: This is mutually exclusive with XML_SetUnparsedEntityDeclHandler,
+  //       and there isn't any significant code change between the two.
+  XML_SetEntityDeclHandler(parser, EntityDeclHandler);
+  XML_SetNotationDeclHandler(parser, NotationDeclHandler);
+  XML_SetNamespaceDeclHandler(parser, StartNamespaceDeclHandler,
+                              EndNamespaceDeclHandler);
+  XML_SetNotStandaloneHandler(parser, NotStandaloneHandler);
+  XML_SetExternalEntityRefHandler(parser, ExternalEntityRefHandler);
+  XML_SetSkippedEntityHandler(parser, SkippedEntityHandler);
+  XML_SetUnknownEncodingHandler(parser, UnknownEncodingHandler, (void *)parser);
+}
+
+DEFINE_TEXT_PROTO_FUZZER(const xml_lpm_fuzzer::Testcase &testcase) {
+  g_external_entity = nullptr;
+
+  if (! testcase.actions_size()) {
+    return;
+  }
+
+  g_allocation_count = 0;
+  g_fail_allocations.clear();
+  for (int i = 0; i < testcase.fail_allocations_size(); ++i) {
+    g_fail_allocations.push_back(testcase.fail_allocations(i));
+  }
+
+  SetEncoding(testcase.encoding());
+  XML_Parser parser
+      = XML_ParserCreate_MM(g_encoding, &memory_handling_suite, "|");
+  InitializeParser(parser);
+
+  for (int i = 0; i < testcase.actions_size(); ++i) {
+    const auto &action = testcase.actions(i);
+    switch (action.action_case()) {
+    case xml_lpm_fuzzer::Action::kChunk:
+      if (XML_STATUS_ERROR
+          == Parse(parser, action.chunk().data(), action.chunk().size(), 0)) {
+        // Force a reset after parse error.
+        XML_ParserReset(parser, g_encoding);
+        InitializeParser(parser);
+      }
+      break;
+
+    case xml_lpm_fuzzer::Action::kLastChunk:
+      Parse(parser, action.last_chunk().data(), action.last_chunk().size(), 1);
+      XML_ParserReset(parser, g_encoding);
+      InitializeParser(parser);
+      break;
+
+    case xml_lpm_fuzzer::Action::kReset:
+      XML_ParserReset(parser, g_encoding);
+      InitializeParser(parser);
+      break;
+
+    case xml_lpm_fuzzer::Action::kExternalEntity:
+      g_external_entity = action.external_entity().data();
+      g_external_entity_size = action.external_entity().size();
+      break;
+
+    default:
+      break;
+    }
+  }
+
+  XML_ParserFree(parser);
+}

+ 58 - 0
libs/expat/fuzz/xml_lpm_fuzzer.proto

@@ -0,0 +1,58 @@
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2022 Mark Brand <[email protected]>
+   Copyright (c) 2025 Sebastian Pipping <[email protected]>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+syntax = "proto2";
+package xml_lpm_fuzzer;
+
+enum Encoding {
+  UTF8 = 0;
+  UTF16 = 1;
+  ISO88591 = 2;
+  ASCII = 3;
+  UNKNOWN = 4;
+  NONE = 5;
+}
+
+message Action {
+  oneof action {
+    string chunk = 1;
+    string last_chunk = 2;
+    bool reset = 3;
+    string external_entity = 4;
+  }
+}
+
+message Testcase {
+  required Encoding encoding = 1;
+  repeated Action actions = 2;
+  repeated int32 fail_allocations = 3;
+}

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

@@ -1068,7 +1068,7 @@ XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled);
 */
 #define XML_MAJOR_VERSION 2
 #define XML_MINOR_VERSION 7
-#define XML_MICRO_VERSION 0
+#define XML_MICRO_VERSION 1
 
 #ifdef __cplusplus
 }

+ 4 - 1
libs/expat/lib/internal.h

@@ -28,7 +28,7 @@
    Copyright (c) 2002-2003 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2002-2006 Karl Waclawek <[email protected]>
    Copyright (c) 2003      Greg Stein <[email protected]>
-   Copyright (c) 2016-2024 Sebastian Pipping <[email protected]>
+   Copyright (c) 2016-2025 Sebastian Pipping <[email protected]>
    Copyright (c) 2018      Yury Gribov <[email protected]>
    Copyright (c) 2019      David Loffredo <[email protected]>
    Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <[email protected]>
@@ -127,6 +127,9 @@
 #  elif ULONG_MAX == 18446744073709551615u // 2^64-1
 #    define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "ld"
 #    define EXPAT_FMT_SIZE_T(midpart) "%" midpart "lu"
+#  elif defined(EMSCRIPTEN) // 32bit mode Emscripten
+#    define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "ld"
+#    define EXPAT_FMT_SIZE_T(midpart) "%" midpart "zu"
 #  else
 #    define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d"
 #    define EXPAT_FMT_SIZE_T(midpart) "%" midpart "u"

+ 11 - 5
libs/expat/lib/xmlparse.c

@@ -1,4 +1,4 @@
-/* 7d6840a33c250b74adb0ba295d6ec818dccebebaffc8c3ed27d0b29c28adbeb3 (2.7.0+)
+/* d19ae032c224863c1527ba44d228cc34b99192c3a4c5a27af1f4e054d45ee031 (2.7.1+)
                             __  __            _
                          ___\ \/ /_ __   __ _| |_
                         / _ \\  /| '_ \ / _` | __|
@@ -3418,12 +3418,13 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
       break;
       /* LCOV_EXCL_STOP */
     }
-    *eventPP = s = next;
     switch (parser->m_parsingStatus.parsing) {
     case XML_SUSPENDED:
+      *eventPP = next;
       *nextPtr = next;
       return XML_ERROR_NONE;
     case XML_FINISHED:
+      *eventPP = next;
       return XML_ERROR_ABORTED;
     case XML_PARSING:
       if (parser->m_reenter) {
@@ -3432,6 +3433,7 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
       }
       /* Fall through */
     default:;
+      *eventPP = s = next;
     }
   }
   /* not reached */
@@ -4352,12 +4354,13 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
       /* LCOV_EXCL_STOP */
     }
 
-    *eventPP = s = next;
     switch (parser->m_parsingStatus.parsing) {
     case XML_SUSPENDED:
+      *eventPP = next;
       *nextPtr = next;
       return XML_ERROR_NONE;
     case XML_FINISHED:
+      *eventPP = next;
       return XML_ERROR_ABORTED;
     case XML_PARSING:
       if (parser->m_reenter) {
@@ -4365,6 +4368,7 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
       }
       /* Fall through */
     default:;
+      *eventPP = s = next;
     }
   }
   /* not reached */
@@ -5977,12 +5981,13 @@ epilogProcessor(XML_Parser parser, const char *s, const char *end,
     default:
       return XML_ERROR_JUNK_AFTER_DOC_ELEMENT;
     }
-    parser->m_eventPtr = s = next;
     switch (parser->m_parsingStatus.parsing) {
     case XML_SUSPENDED:
+      parser->m_eventPtr = next;
       *nextPtr = next;
       return XML_ERROR_NONE;
     case XML_FINISHED:
+      parser->m_eventPtr = next;
       return XML_ERROR_ABORTED;
     case XML_PARSING:
       if (parser->m_reenter) {
@@ -5990,6 +5995,7 @@ epilogProcessor(XML_Parser parser, const char *s, const char *end,
       }
     /* Fall through */
     default:;
+      parser->m_eventPtr = s = next;
     }
   }
 }
@@ -8304,7 +8310,7 @@ entityTrackingReportStats(XML_Parser rootParser, ENTITY *entity,
       (void *)rootParser, rootParser->m_entity_stats.countEverOpened,
       rootParser->m_entity_stats.currentDepth,
       rootParser->m_entity_stats.maximumDepthSeen,
-      (rootParser->m_entity_stats.currentDepth - 1) * 2, "",
+      ((int)rootParser->m_entity_stats.currentDepth - 1) * 2, "",
       entity->is_param ? "%" : "&", entityName, action, entity->textLen,
       sourceLine);
   } // WINSCP

+ 3 - 6
libs/expat/tests/benchmark/benchmark.c

@@ -114,22 +114,19 @@ main(int argc, char *argv[]) {
   bufferSize = atoi(argv[j + 2]);
   nrOfLoops = atoi(argv[j + 3]);
   if (bufferSize <= 0 || nrOfLoops <= 0) {
-    fclose(file);
-    close(fd);
+    fclose(file); // NOTE: this closes fd as well
     fprintf(stderr, "buffer size and nr of loops must be greater than zero.\n");
     return 3;
   }
 
   XMLBuf = malloc(fileAttr.st_size);
   if (XMLBuf == NULL) {
-    fclose(file);
-    close(fd);
+    fclose(file); // NOTE: this closes fd as well
     fprintf(stderr, "ouf of memory.\n");
     return 5;
   }
   fileSize = fread(XMLBuf, sizeof(char), fileAttr.st_size, file);
-  fclose(file);
-  close(fd);
+  fclose(file); // NOTE: this closes fd as well
 
   if (ns)
     parser = XML_ParserCreateNS(NULL, '!');

+ 26 - 1
libs/expat/tests/common.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]>
@@ -42,6 +42,8 @@
 */
 
 #include <assert.h>
+#include <errno.h>
+#include <stdint.h> // for SIZE_MAX
 #include <stdio.h>
 #include <string.h>
 
@@ -300,3 +302,26 @@ duff_reallocator(void *ptr, size_t size) {
     g_reallocation_count--;
   return realloc(ptr, size);
 }
+
+// Portable remake of strndup(3) for C99; does not care about space efficiency
+char *
+portable_strndup(const char *s, size_t n) {
+  if ((s == NULL) || (n == SIZE_MAX)) {
+    errno = EINVAL;
+    return NULL;
+  }
+
+  char *const buffer = (char *)malloc(n + 1);
+  if (buffer == NULL) {
+    errno = ENOMEM;
+    return NULL;
+  }
+
+  errno = 0;
+
+  memcpy(buffer, s, n);
+
+  buffer[n] = '\0';
+
+  return buffer;
+}

+ 3 - 1
libs/expat/tests/common.h

@@ -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]>
@@ -146,6 +146,8 @@ extern void *duff_allocator(size_t size);
 
 extern void *duff_reallocator(void *ptr, size_t size);
 
+extern char *portable_strndup(const char *s, size_t n);
+
 #endif /* XML_COMMON_H */
 
 #ifdef __cplusplus

+ 62 - 1
libs/expat/tests/misc_tests.c

@@ -211,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.7.0"))) /* needs bump on releases */
+  if (xcstrcmp(version_text, XCS("expat_2.7.1"))) /* needs bump on releases */
     fail("XML_*_VERSION in expat.h out of sync?\n");
 }
 END_TEST
@@ -618,6 +618,66 @@ START_TEST(test_renter_loop_finite_content) {
 }
 END_TEST
 
+// Inspired by function XML_OriginalString of Perl's XML::Parser
+static char *
+dup_original_string(XML_Parser parser) {
+  const int byte_count = XML_GetCurrentByteCount(parser);
+
+  assert_true(byte_count >= 0);
+
+  int offset = -1;
+  int size = -1;
+
+  const char *const context = XML_GetInputContext(parser, &offset, &size);
+
+#if XML_CONTEXT_BYTES > 0
+  assert_true(context != NULL);
+  assert_true(offset >= 0);
+  assert_true(size >= 0);
+  return portable_strndup(context + offset, byte_count);
+#else
+  assert_true(context == NULL);
+  return NULL;
+#endif
+}
+
+static void
+on_characters_issue_980(void *userData, const XML_Char *s, int len) {
+  (void)s;
+  (void)len;
+  XML_Parser parser = (XML_Parser)userData;
+
+  char *const original_string = dup_original_string(parser);
+
+#if XML_CONTEXT_BYTES > 0
+  assert_true(original_string != NULL);
+  assert_true(strcmp(original_string, "&draft.day;") == 0);
+  free(original_string);
+#else
+  assert_true(original_string == NULL);
+#endif
+}
+
+START_TEST(test_misc_expected_event_ptr_issue_980) {
+  // NOTE: This is a tiny subset of sample "REC-xml-19980210.xml"
+  //       from Perl's XML::Parser
+  const char *const doc = "<!DOCTYPE day [\n"
+                          "  <!ENTITY draft.day '10'>\n"
+                          "]>\n"
+                          "<day>&draft.day;</day>\n";
+
+  XML_Parser parser = XML_ParserCreate(NULL);
+  XML_SetUserData(parser, parser);
+  XML_SetCharacterDataHandler(parser, on_characters_issue_980);
+
+  assert_true(_XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc),
+                                      /*isFinal=*/XML_TRUE)
+              == XML_STATUS_OK);
+
+  XML_ParserFree(parser);
+}
+END_TEST
+
 void
 make_miscellaneous_test_case(Suite *s) {
   TCase *tc_misc = tcase_create("miscellaneous tests");
@@ -645,4 +705,5 @@ make_miscellaneous_test_case(Suite *s) {
   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);
+  tcase_add_test(tc_misc, test_misc_expected_event_ptr_issue_980);
 }

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

@@ -38,7 +38,7 @@
 ; OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 ; USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-#define expatVer "2.7.0"
+#define expatVer "2.7.1"
 
 [Setup]
 AppName=Expat

+ 5 - 1
source/forms/About.dfm

@@ -9,7 +9,11 @@ object AboutDialog: TAboutDialog
   ClientHeight = 532
   ClientWidth = 455
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   KeyPreview = True
   Position = poOwnerFormCenter
   OnAfterMonitorDpiChanged = FormAfterMonitorDpiChanged

+ 5 - 1
source/forms/Authenticate.dfm

@@ -11,7 +11,11 @@ object AuthenticateForm: TAuthenticateForm
   Color = clBtnFace
   Constraints.MinHeight = 200
   Constraints.MinWidth = 280
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   OnAfterMonitorDpiChanged = FormAfterMonitorDpiChanged
   OnResize = FormResize

+ 5 - 1
source/forms/Cleanup.dfm

@@ -9,7 +9,11 @@ object CleanupDialog: TCleanupDialog
   ClientHeight = 323
   ClientWidth = 489
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   OnShow = FormShow
   DesignSize = (

+ 5 - 1
source/forms/Console.dfm

@@ -10,7 +10,11 @@ object ConsoleDialog: TConsoleDialog
   Color = clBtnFace
   Constraints.MinHeight = 250
   Constraints.MinWidth = 420
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Icon.Data = {
     000001000700404000000100200028420000760000003030000001002000A825
     00009E4200002828000001002000681A0000466800002020000001002000A810

+ 5 - 1
source/forms/Copy.dfm

@@ -9,7 +9,11 @@ object CopyDialog: TCopyDialog
   ClientHeight = 235
   ClientWidth = 567
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   OnAfterMonitorDpiChanged = FormAfterMonitorDpiChanged
   OnCloseQuery = FormCloseQuery

+ 5 - 1
source/forms/CopyLocal.dfm

@@ -9,7 +9,11 @@ object CopyLocalDialog: TCopyLocalDialog
   ClientHeight = 126
   ClientWidth = 511
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   OnAfterMonitorDpiChanged = FormAfterMonitorDpiChanged
   OnCloseQuery = FormCloseQuery

+ 5 - 1
source/forms/CopyParamCustom.dfm

@@ -9,7 +9,11 @@ object CopyParamCustomDialog: TCopyParamCustomDialog
   ClientHeight = 511
   ClientWidth = 466
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   OnCloseQuery = FormCloseQuery
   DesignSize = (

+ 5 - 1
source/forms/CopyParamPreset.dfm

@@ -9,7 +9,11 @@ object CopyParamPresetDialog: TCopyParamPresetDialog
   ClientHeight = 556
   ClientWidth = 744
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   OnCloseQuery = FormCloseQuery
   OnShow = FormShow

+ 6 - 0
source/forms/CopyParams.dfm

@@ -4,6 +4,12 @@ object CopyParamsFrame: TCopyParamsFrame
   Width = 456
   Height = 471
   HelpType = htKeyword
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
+  ParentFont = False
   TabOrder = 0
   object CommonPropertiesGroup: TGroupBox
     Left = 232

+ 5 - 1
source/forms/CreateDirectory.dfm

@@ -9,7 +9,11 @@ object CreateDirectoryDialog: TCreateDirectoryDialog
   ClientHeight = 263
   ClientWidth = 337
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   OnCloseQuery = FormCloseQuery
   OnShow = FormShow

+ 5 - 1
source/forms/Custom.dfm

@@ -7,7 +7,11 @@ object CustomDialog: TCustomDialog
   ClientHeight = 42
   ClientWidth = 362
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   DesignSize = (
     362

+ 5 - 1
source/forms/CustomCommand.dfm

@@ -9,7 +9,11 @@ object CustomCommandDialog: TCustomCommandDialog
   ClientHeight = 287
   ClientWidth = 464
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   OnCloseQuery = FormCloseQuery
   OnShow = FormShow

+ 5 - 1
source/forms/CustomScpExplorer.dfm

@@ -5,7 +5,11 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
   ClientHeight = 429
   ClientWidth = 608
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   KeyPreview = True
   OnAfterMonitorDpiChanged = FormAfterMonitorDpiChanged
   OnClose = FormClose

+ 5 - 1
source/forms/EditMask.dfm

@@ -9,7 +9,11 @@ object EditMaskDialog: TEditMaskDialog
   ClientHeight = 537
   ClientWidth = 474
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   KeyPreview = True
   Position = poOwnerFormCenter
   OnCloseQuery = FormCloseQuery

+ 5 - 1
source/forms/Editor.dfm

@@ -8,7 +8,11 @@ object EditorForm: TEditorForm
   ClientHeight = 381
   ClientWidth = 609
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Icon.Data = {
     000001000700404000000100200028420000760000003030000001002000A825
     00009E4200002828000001002000681A0000466800002020000001002000A810

+ 5 - 1
source/forms/EditorPreferences.dfm

@@ -9,7 +9,11 @@ object EditorPreferencesDialog: TEditorPreferencesDialog
   ClientHeight = 382
   ClientWidth = 447
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   OnCloseQuery = FormCloseQuery
   OnShow = FormShow

+ 5 - 1
source/forms/FileFind.dfm

@@ -10,7 +10,11 @@ object FileFindDialog: TFileFindDialog
   Color = clBtnFace
   Constraints.MinHeight = 240
   Constraints.MinWidth = 400
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Icon.Data = {
     000001000700404000000100200028420000760000003030000001002000A825
     00009E4200002828000001002000681A0000466800002020000001002000A810

+ 5 - 1
source/forms/FileSystemInfo.dfm

@@ -9,7 +9,11 @@ object FileSystemInfoDialog: TFileSystemInfoDialog
   ClientHeight = 444
   ClientWidth = 432
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   OnShow = FormShow
   DesignSize = (

+ 5 - 1
source/forms/FullSynchronize.dfm

@@ -9,7 +9,11 @@ object FullSynchronizeDialog: TFullSynchronizeDialog
   ClientHeight = 492
   ClientWidth = 534
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   OnCloseQuery = FormCloseQuery
   OnShow = FormShow

+ 5 - 1
source/forms/GenerateUrl.dfm

@@ -11,7 +11,11 @@ object GenerateUrlDialog: TGenerateUrlDialog
   Color = clBtnFace
   Constraints.MinHeight = 300
   Constraints.MinWidth = 500
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   OnShow = FormShow
   DesignSize = (

+ 5 - 1
source/forms/ImportSessions.dfm

@@ -9,7 +9,11 @@ object ImportSessionsDialog: TImportSessionsDialog
   ClientHeight = 307
   ClientWidth = 418
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   OnShow = FormShow
   DesignSize = (

+ 5 - 1
source/forms/License.dfm

@@ -8,7 +8,11 @@ object LicenseDialog: TLicenseDialog
   ClientHeight = 355
   ClientWidth = 559
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   DesignSize = (
     559

+ 5 - 1
source/forms/LocationProfiles.dfm

@@ -9,7 +9,11 @@ object LocationProfilesDialog: TLocationProfilesDialog
   ClientHeight = 448
   ClientWidth = 601
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   OnShow = FormShow
   DesignSize = (

+ 5 - 1
source/forms/Login.dfm

@@ -10,7 +10,11 @@ object LoginDialog: TLoginDialog
   Color = clBtnFace
   Constraints.MinHeight = 399
   Constraints.MinWidth = 660
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   KeyPreview = True
   Position = poOwnerFormCenter
   OnAfterMonitorDpiChanged = FormAfterMonitorDpiChanged

+ 1 - 1
source/forms/MessageDlg.cpp

@@ -869,7 +869,6 @@ TForm * __fastcall TMessageForm::Create(const UnicodeString & Msg,
         LinkControl->Caption = ActionAlias;
         LinkControl->Alignment = taRightJustify;
         LinkControl->Anchors = TAnchors() << akRight << akTop;
-        LinkActionLabel(LinkControl);
         LinkControl->OnClick = Result->ButtonSubmit;
         Result->FButtonSubmitEvents[LinkControl] = OnSubmit;
       }
@@ -1103,6 +1102,7 @@ TForm * __fastcall TMessageForm::Create(const UnicodeString & Msg,
   if (LinkControl != NULL)
   {
     LinkControl->Parent = Panel;
+    LinkActionLabel(LinkControl);
     LinkControl->Left = Panel->ClientWidth - HorzMargin - LinkControl->Width;
     LinkControl->Top = VertMargin + IconTextHeight + VertMargin;
     IconTextHeight += VertMargin + LinkControl->Height;

+ 5 - 1
source/forms/MessageDlg.dfm

@@ -7,7 +7,11 @@ object MessageForm: TMessageForm
   ClientHeight = 41
   ClientWidth = 326
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   OnAfterMonitorDpiChanged = FormAfterMonitorDpiChanged
   TextHeight = 15

+ 5 - 1
source/forms/OpenDirectory.dfm

@@ -9,7 +9,11 @@ object OpenDirectoryDialog: TOpenDirectoryDialog
   ClientHeight = 342
   ClientWidth = 450
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   OnShow = FormShow
   DesignSize = (

+ 5 - 1
source/forms/Preferences.dfm

@@ -9,7 +9,11 @@ object PreferencesDialog: TPreferencesDialog
   ClientHeight = 569
   ClientWidth = 605
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   OnAfterMonitorDpiChanged = FormAfterMonitorDpiChanged
   OnCloseQuery = FormCloseQuery

+ 5 - 1
source/forms/Progress.dfm

@@ -9,7 +9,11 @@ object ProgressForm: TProgressForm
   ClientHeight = 294
   ClientWidth = 398
   Color = clWindow
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   PopupMode = pmAuto
   Position = poOwnerFormCenter
   OnHide = FormHide

+ 5 - 1
source/forms/Properties.dfm

@@ -9,7 +9,11 @@ object PropertiesDialog: TPropertiesDialog
   ClientHeight = 424
   ClientWidth = 398
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   OnCloseQuery = FormCloseQuery
   OnShow = FormShow

+ 5 - 1
source/forms/RemoteTransfer.dfm

@@ -9,7 +9,11 @@ object RemoteTransferDialog: TRemoteTransferDialog
   ClientHeight = 181
   ClientWidth = 464
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   OnCloseQuery = FormCloseQuery
   OnShow = FormShow

+ 6 - 0
source/forms/Rights.dfm

@@ -3,6 +3,12 @@ object RightsFrame: TRightsFrame
   Top = 0
   Width = 258
   Height = 130
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
+  ParentFont = False
   TabOrder = 0
   OnContextPopup = FrameContextPopup
   object OthersButton: TSpeedButton

+ 5 - 1
source/forms/SelectMask.dfm

@@ -9,7 +9,11 @@ object SelectMaskDialog: TSelectMaskDialog
   ClientHeight = 181
   ClientWidth = 460
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poDesigned
   OnCloseQuery = FormCloseQuery
   OnShow = FormShow

+ 5 - 1
source/forms/SiteAdvanced.dfm

@@ -9,7 +9,11 @@ object SiteAdvancedDialog: TSiteAdvancedDialog
   ClientHeight = 429
   ClientWidth = 619
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   OnClose = FormClose
   OnCloseQuery = FormCloseQuery

+ 5 - 1
source/forms/Symlink.dfm

@@ -9,7 +9,11 @@ object SymlinkDialog: TSymlinkDialog
   ClientHeight = 177
   ClientWidth = 417
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   OnShow = FormShow
   DesignSize = (

+ 5 - 1
source/forms/Synchronize.dfm

@@ -9,7 +9,11 @@ object SynchronizeDialog: TSynchronizeDialog
   ClientHeight = 466
   ClientWidth = 518
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   KeyPreview = True
   Position = poOwnerFormCenter
   OnCloseQuery = FormCloseQuery

+ 5 - 1
source/forms/SynchronizeChecklist.dfm

@@ -8,7 +8,11 @@ object SynchronizeChecklistDialog: TSynchronizeChecklistDialog
   ClientHeight = 521
   ClientWidth = 695
   Color = clBtnFace
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Icon.Data = {
     000001000700404000000100200028420000760000003030000001002000A825
     00009E4200002828000001002000681A0000466800002020000001002000A810

+ 5 - 1
source/forms/SynchronizeProgress.dfm

@@ -7,7 +7,11 @@ object SynchronizeProgressForm: TSynchronizeProgressForm
   ClientHeight = 219
   ClientWidth = 424
   Color = clWindow
-  ParentFont = True
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Segoe UI'
+  Font.Style = []
   Position = poOwnerFormCenter
   DesignSize = (
     424

+ 48 - 48
source/packages/my/ListViewColProperties.pas

@@ -53,13 +53,13 @@ type
     procedure UpdateFromListView;
     procedure UpdateOrderFromListView;
     procedure UpdateListViewOrder;
-    procedure UpdateListViewMaxMinWidth;
     function GetProperties(Index: Integer): TCustomListViewColProperty;
     function GetIndexByOrder(Order: Integer): Integer;
     function ColumnsExists: Boolean;
     procedure SetRuntimeVisible(Index: Integer; Value: Boolean; SaveWidth: Boolean);
     function GetColumn(Index: Integer): TListColumn;
     procedure CreateProperties(ACount: Integer);
+    function DefaultConstraint(Value: Integer; Visible: Boolean; Def: Integer): Integer;
 
     property Columns: TListColumns read GetColumns stored False;
   public
@@ -70,6 +70,7 @@ type
     procedure ListViewWndCreated;
     procedure ListViewWndDestroying;
     procedure ListViewWndDestroyed;
+    procedure ChangeScale(M, D: Integer);
     property Count: Integer read GetCount stored False;
     property Alignments[Index: Integer]: TAlignment read GetAlignments write SetAlignments;
     property Captions[Index: Integer]: string read GetCaptions write SetCaptions;
@@ -568,12 +569,17 @@ begin
   end;
 end;
 
+function TCustomListViewColProperties.DefaultConstraint(Value: Integer; Visible: Boolean; Def: Integer): Integer;
+begin
+  if (Value > 0) and Visible then Result := Value
+    else Result := Def;
+end;
+
 procedure TCustomListViewColProperties.ListViewWndCreated;
 var
   Index: Integer;
   Properties: TCustomListViewColProperty;
   Column: TListColumn;
-  W: Integer;
 begin
   if FListViewManaged then
   begin
@@ -587,36 +593,29 @@ begin
     UpdateListView;
   end;
 
-  if not FConstraintsInitialized then
+  Assert(ColumnsExists);
+  for Index := 0 to Count - 1 do
   begin
-    FConstraintsInitialized := True;
+    Column := GetColumn(Index);
+    Properties := GetProperties(Index);
 
-    for Index := 0 to Count - 1 do
+    if not FConstraintsInitialized then
     begin
-      Column := GetColumn(Index);
-      Properties := GetProperties(Index);
+      Properties.MaxWidth := DefaultConstraint(Column.MaxWidth, Properties.Visible, DefaultListViewMaxWidth);
+      Properties.MinWidth := DefaultConstraint(Column.MinWidth, Properties.Visible, DefaultListViewMinWidth);
+    end;
 
-      // Is this branching needed?
-      if Properties.Visible then
-      begin
-        W := Column.MaxWidth;
-        if W = 0 then W := DefaultListViewMaxWidth;
-        Properties.MaxWidth := ScaleByTextHeight(FListView, W);
-
-        W := Column.MinWidth;
-        if W = 0 then W := DefaultListViewMinWidth;
-        Properties.MinWidth := ScaleByTextHeight(FListView, W);
-      end
-        else
-      begin
-        Column.MaxWidth := ScaleByTextHeight(FListView, Column.MaxWidth);
-        Column.MinWidth := ScaleByTextHeight(FListView, Column.MinWidth);
-      end;
+    // To apply the default constraints to columns that do not have their own
+    if Properties.Visible then
+    begin
+      Column.MaxWidth := Properties.MaxWidth;
+      if Column.Width > Column.MaxWidth then Column.Width := Column.MaxWidth;
+      Column.MinWidth := Properties.MinWidth;
+      if Column.Width < Column.MinWidth then Column.Width := Column.MinWidth;
     end;
   end;
 
-  // To apply the default constraints to columns that do not have their own
-  UpdateListViewMaxMinWidth;
+  FConstraintsInitialized := True;
 end;
 
 procedure TCustomListViewColProperties.ListViewWndDestroying;
@@ -630,46 +629,47 @@ begin
     FCreated := False;
 end;
 
-procedure TCustomListViewColProperties.UpdateListViewOrder;
+procedure TCustomListViewColProperties.ChangeScale(M, D: Integer);
 var
   Index: Integer;
   Properties: TCustomListViewColProperty;
-  Temp: array of Integer;
+  Column: TListColumn;
 begin
-  SetLength(Temp, Count);
-  // Seemingly useless,
-  // but probably only because we swallow HDN_ENDDRAG in TCustomIEListView.WMNotify,
-  // what prevents VLC from actually reordering columns collection
-  ListView_GetColumnOrderArray(FListView.Handle, Count, PInteger(Temp));
   for Index := 0 to Count - 1 do
   begin
     Properties := GetProperties(Index);
-    Temp[Properties.Order] := Index;
+    // This is not perfect, as the constraints apply on re-scaled width before they are re-scaled themselves
+    if Properties.Visible and ColumnsExists then
+    begin
+      Column := GetColumn(Index);
+      Column.MaxWidth := MulDiv(Column.MaxWidth, M, D);
+      Column.MinWidth := MulDiv(Column.MinWidth, M, D);
+    end
+      else
+    begin
+      Properties.MaxWidth := MulDiv(Properties.MaxWidth, M, D);
+      Properties.MinWidth := MulDiv(Properties.MinWidth, M, D);
+    end;
   end;
-  ListView_SetColumnOrderArray(FListView.Handle, Count, PInteger(Temp));
 end;
 
-procedure TCustomListViewColProperties.UpdateListViewMaxMinWidth;
+procedure TCustomListViewColProperties.UpdateListViewOrder;
 var
   Index: Integer;
-  Column: TListColumn;
   Properties: TCustomListViewColProperty;
+  Temp: array of Integer;
 begin
-  Assert(ColumnsExists);
-
-  for Index := 0 to Count-1 do
+  SetLength(Temp, Count);
+  // Seemingly useless,
+  // but probably only because we swallow HDN_ENDDRAG in TCustomIEListView.WMNotify,
+  // what prevents VLC from actually reordering columns collection
+  ListView_GetColumnOrderArray(FListView.Handle, Count, PInteger(Temp));
+  for Index := 0 to Count - 1 do
   begin
-    Column := GetColumn(Index);
     Properties := GetProperties(Index);
-
-    if Properties.Visible then
-    begin
-      Column.MaxWidth := Properties.MaxWidth;
-      if Column.Width > Column.MaxWidth then Column.Width := Column.MaxWidth;
-      Column.MinWidth := Properties.MinWidth;
-      if Column.Width < Column.MinWidth then Column.Width := Column.MinWidth;
-    end;
+    Temp[Properties.Order] := Index;
   end;
+  ListView_SetColumnOrderArray(FListView.Handle, Count, PInteger(Temp));
 end;
 
 procedure TCustomListViewColProperties.UpdateListView;

+ 1 - 0
source/packages/my/NortonLikeListView.pas

@@ -1131,6 +1131,7 @@ begin
     HandleNeeded;
   end;
   inherited;
+  ColProperties.ChangeScale(M, D);
 end;
 
 end.

+ 1 - 1
source/resource/LicenseExpat.txt

@@ -1,6 +1,6 @@
 Expat
 Copyright (c) 1998-2000 Thai Open Source Software Center Ltd and Clark Cooper
-Copyright (c) 2001-2022 Expat maintainers
+Copyright (c) 2001-2025 Expat maintainers
 
 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 

+ 7 - 5
source/windows/VCLCommon.cpp

@@ -987,9 +987,9 @@ void __fastcall ApplySystemSettingsOnControl(TControl * Control)
   #endif
 
   TCustomListView * ListView = dynamic_cast<TCustomListView *>(Control);
-  TCustomIEListView * IEListView = dynamic_cast<TCustomIEListView *>(Control);
-  // For IEListView, this is (somewhat) handled in the TCustomListViewColProperties
-  if ((ListView != NULL) && (IEListView == NULL))
+  TCustomNortonLikeListView * NortonLikeListView = dynamic_cast<TCustomNortonLikeListView *>(Control);
+  // For NortonLikeListView, this is (somewhat) handled in the TCustomListViewColProperties
+  if ((ListView != NULL) && (NortonLikeListView == NULL))
   {
     TListView * PublicListView = reinterpret_cast<TListView *>(ListView);
 
@@ -1003,14 +1003,14 @@ void __fastcall ApplySystemSettingsOnControl(TControl * Control)
 
   // WORKAROUND for lack of public API for mimicking Explorer-style mouse selection
   // See https://stackoverflow.com/q/15750842/850848
-  if (IEListView != NULL)
+  if (NortonLikeListView != NULL)
   {
     // It should not be a problem to call the LVM_QUERYINTERFACE
     // on earlier versions of Windows. It should be noop.
     if (IsWin7())
     {
       IListView_Win7 * ListViewIntf = NULL;
-      SendMessage(IEListView->Handle, LVM_QUERYINTERFACE, reinterpret_cast<WPARAM>(&IID_IListView_Win7), reinterpret_cast<LPARAM>(&ListViewIntf));
+      SendMessage(NortonLikeListView->Handle, LVM_QUERYINTERFACE, reinterpret_cast<WPARAM>(&IID_IListView_Win7), reinterpret_cast<LPARAM>(&ListViewIntf));
       if (ListViewIntf != NULL)
       {
         ListViewIntf->SetSelectionFlags(1, 1);
@@ -2301,6 +2301,8 @@ void __fastcall LinkLabel(TStaticText * StaticText, UnicodeString Url,
 //---------------------------------------------------------------------------
 void __fastcall LinkActionLabel(TStaticText * StaticText)
 {
+  // Must be called only after setting Parent, as it modifies Font and hence clears ParentFont
+  DebugAssert(StaticText->Parent != NULL);
   DoLinkLabel(StaticText);
 
   StaticText->Font->Color = LinkColor;