From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:06:44 +0200 Subject: Adding upstream version 4:7.4.7. Signed-off-by: Daniel Baumann --- external/icu/ExternalPackage_icu.mk | 42 ++ external/icu/ExternalPackage_icu_ure.mk | 48 ++ external/icu/ExternalProject_icu.mk | 100 +++ external/icu/Makefile | 7 + external/icu/Module_icu.mk | 19 + external/icu/README | 1 + external/icu/UnpackedTarball_icu.mk | 51 ++ external/icu/Wdeprecated-copy-dtor.patch | 25 + external/icu/c++20-comparison.patch.1 | 82 ++ external/icu/cross-bin/icu-config | 12 + ...t-useful-cache-to-empty-in-populateNear.patch.2 | 37 + external/icu/gcc9.patch | 26 + external/icu/icu4c-aix.patch.1 | 143 ++++ external/icu/icu4c-android.patch.1 | 75 ++ external/icu/icu4c-build.patch.1 | 91 +++ external/icu/icu4c-clang-cl.patch.1 | 28 + external/icu/icu4c-emscripten-cross.patch.1 | 99 +++ external/icu/icu4c-icudata-stdlibs.patch.1 | 14 + external/icu/icu4c-khmerbreakengine.patch.1 | 836 +++++++++++++++++++++ external/icu/icu4c-macosx.patch.1 | 20 + external/icu/icu4c-mkdir.patch.1 | 17 + external/icu/icu4c-rpath.patch.1 | 36 + external/icu/icu4c-rtti.patch.1 | 12 + external/icu/icu4c-scriptrun.patch.1 | 60 ++ external/icu/icu4c-solarisgcc.patch.1 | 12 + external/icu/icu4c-ubsan.patch.1 | 14 + ...cu4c-use-pkgdata-single-ccode-file-mode.patch.1 | 12 + external/icu/icu4c-warnings.patch.1 | 11 + external/icu/icu4c-windows-cygwin-cross.patch.1 | 131 ++++ external/icu/khmerdict.dict | Bin 0 -> 263537 bytes external/icu/strict_ansi.patch | 15 + external/icu/ubsan.patch.1 | 31 + 32 files changed, 2107 insertions(+) create mode 100644 external/icu/ExternalPackage_icu.mk create mode 100644 external/icu/ExternalPackage_icu_ure.mk create mode 100644 external/icu/ExternalProject_icu.mk create mode 100644 external/icu/Makefile create mode 100644 external/icu/Module_icu.mk create mode 100644 external/icu/README create mode 100644 external/icu/UnpackedTarball_icu.mk create mode 100644 external/icu/Wdeprecated-copy-dtor.patch create mode 100644 external/icu/c++20-comparison.patch.1 create mode 100755 external/icu/cross-bin/icu-config create mode 100644 external/icu/do-not-reset-useful-cache-to-empty-in-populateNear.patch.2 create mode 100644 external/icu/gcc9.patch create mode 100644 external/icu/icu4c-aix.patch.1 create mode 100644 external/icu/icu4c-android.patch.1 create mode 100644 external/icu/icu4c-build.patch.1 create mode 100644 external/icu/icu4c-clang-cl.patch.1 create mode 100644 external/icu/icu4c-emscripten-cross.patch.1 create mode 100644 external/icu/icu4c-icudata-stdlibs.patch.1 create mode 100644 external/icu/icu4c-khmerbreakengine.patch.1 create mode 100644 external/icu/icu4c-macosx.patch.1 create mode 100644 external/icu/icu4c-mkdir.patch.1 create mode 100644 external/icu/icu4c-rpath.patch.1 create mode 100644 external/icu/icu4c-rtti.patch.1 create mode 100644 external/icu/icu4c-scriptrun.patch.1 create mode 100644 external/icu/icu4c-solarisgcc.patch.1 create mode 100644 external/icu/icu4c-ubsan.patch.1 create mode 100644 external/icu/icu4c-use-pkgdata-single-ccode-file-mode.patch.1 create mode 100644 external/icu/icu4c-warnings.patch.1 create mode 100644 external/icu/icu4c-windows-cygwin-cross.patch.1 create mode 100644 external/icu/khmerdict.dict create mode 100644 external/icu/strict_ansi.patch create mode 100644 external/icu/ubsan.patch.1 (limited to 'external/icu') diff --git a/external/icu/ExternalPackage_icu.mk b/external/icu/ExternalPackage_icu.mk new file mode 100644 index 000000000..dcd4da216 --- /dev/null +++ b/external/icu/ExternalPackage_icu.mk @@ -0,0 +1,42 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +icu_VERSION := $(ICU_MAJOR).$(ICU_MINOR)$(if $(ICU_MICRO),.$(ICU_MICRO)) + +$(eval $(call gb_ExternalPackage_ExternalPackage,icu,icu)) + +$(eval $(call gb_ExternalPackage_use_external_project,icu,icu)) + +ifneq ($(DISABLE_DYNLOADING),TRUE) +ifeq ($(OS),WNT) + +ifeq ($(COM),GCC) +$(eval $(call gb_ExternalPackage_add_files,icu,$(LIBO_LIB_FOLDER),\ + source/lib/icuin$(ICU_MAJOR).dll \ +)) +else +$(eval $(call gb_ExternalPackage_add_files,icu,$(LIBO_LIB_FOLDER),\ + source/lib/icuin$(if $(MSVC_USE_DEBUG_RUNTIME),d)$(ICU_MAJOR).dll \ +)) +endif # $(COM) + +else ifeq ($(OS),ANDROID) + +$(eval $(call gb_ExternalPackage_add_files,icu,$(LIBO_LIB_FOLDER),\ + source/lib/libicui18nlo.so \ +)) + +else # $(OS) != WNT/ANDROID + +$(eval $(call gb_ExternalPackage_add_file,icu,$(LIBO_LIB_FOLDER)/libicui18n$(gb_Library_DLLEXT).$(ICU_MAJOR),source/lib/libicui18n$(gb_Library_DLLEXT).$(icu_VERSION))) + +endif # $(OS) +endif # DISABLE_DYNLOADING + +# vim: set noet sw=4 ts=4: diff --git a/external/icu/ExternalPackage_icu_ure.mk b/external/icu/ExternalPackage_icu_ure.mk new file mode 100644 index 000000000..fefe71afd --- /dev/null +++ b/external/icu/ExternalPackage_icu_ure.mk @@ -0,0 +1,48 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +# libxml2 is in URE and depends on icuuc*.dll on Windows; the i18nlangtag lib is +# in URE and depends on the icuuc lib (which in turn depends on the icudata lib) +# on all platforms: + +$(eval $(call gb_ExternalPackage_ExternalPackage,icu_ure,icu)) + +$(eval $(call gb_ExternalPackage_use_external_project,icu_ure,icu)) + +ifneq ($(DISABLE_DYNLOADING),TRUE) +ifeq ($(OS),WNT) + +ifeq ($(COM),GCC) +$(eval $(call gb_ExternalPackage_add_files,icu_ure,$(LIBO_URE_LIB_FOLDER),\ + source/lib/icudt$(ICU_MAJOR).dll \ + source/lib/icuuc$(ICU_MAJOR).dll \ +)) +else +$(eval $(call gb_ExternalPackage_add_files,icu_ure,$(LIBO_URE_LIB_FOLDER),\ + source/lib/icudt$(if $(MSVC_USE_DEBUG_RUNTIME),d)$(ICU_MAJOR).dll \ + source/lib/icuuc$(if $(MSVC_USE_DEBUG_RUNTIME),d)$(ICU_MAJOR).dll \ +)) +endif # $(COM) + +else ifeq ($(OS),ANDROID) + +$(eval $(call gb_ExternalPackage_add_files,icu_ure,$(LIBO_URE_LIB_FOLDER),\ + source/lib/libicudatalo.so \ + source/lib/libicuuclo.so \ +)) + +else # $(OS) != WNT/ANDROID + +$(eval $(call gb_ExternalPackage_add_file,icu_ure,$(LIBO_URE_LIB_FOLDER)/libicudata$(gb_Library_DLLEXT).$(ICU_MAJOR),source/lib/libicudata$(gb_Library_DLLEXT).$(icu_VERSION))) +$(eval $(call gb_ExternalPackage_add_file,icu_ure,$(LIBO_URE_LIB_FOLDER)/libicuuc$(gb_Library_DLLEXT).$(ICU_MAJOR),source/lib/libicuuc$(gb_Library_DLLEXT).$(icu_VERSION))) + +endif # $(OS) +endif # DISABLE_DYNLOADING + +# vim: set noet sw=4 ts=4: diff --git a/external/icu/ExternalProject_icu.mk b/external/icu/ExternalProject_icu.mk new file mode 100644 index 000000000..d677016f9 --- /dev/null +++ b/external/icu/ExternalProject_icu.mk @@ -0,0 +1,100 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_ExternalProject_ExternalProject,icu)) + +$(eval $(call gb_ExternalProject_register_targets,icu,\ + build \ +)) + +icu_CPPFLAGS:="-DHAVE_GCC_ATOMICS=$(if $(filter TRUE,$(GCC_HAVE_BUILTIN_ATOMIC)),1,0)" + +ifeq ($(OS),WNT) + +$(call gb_ExternalProject_get_state_target,icu,build) : + $(call gb_Trace_StartRange,icu,EXTERNAL) + $(call gb_ExternalProject_run,build,\ + autoconf \ + && export LIB="$(ILIB)" PYTHONWARNINGS="default" \ + gb_ICU_XFLAGS="-FS $(SOLARINC) $(gb_DEBUGINFO_FLAGS) $(if $(MSVC_USE_DEBUG_RUNTIME),-MDd,-MD -Gy)" \ + && CFLAGS="$${gb_ICU_XFLAGS}" CPPFLAGS="$(SOLARINC)" CXXFLAGS="$${gb_ICU_XFLAGS}" \ + INSTALL=`cygpath -m /usr/bin/install` $(if $(MSVC_USE_DEBUG_RUNTIME),LDFLAGS="-DEBUG") \ + $(gb_RUN_CONFIGURE) ./configure \ + $(if $(MSVC_USE_DEBUG_RUNTIME),--enable-debug --disable-release) \ + $(gb_CONFIGURE_PLATFORMS) \ + $(if $(CROSS_COMPILING), \ + --with-cross-build=$(WORKDIR_FOR_BUILD)/UnpackedTarball/icu/source \ + --disable-tools --disable-extras) \ + && $(MAKE) $(if $(CROSS_COMPILING),DATASUBDIR=data) $(if $(verbose),VERBOSE=1) \ + ,source) + $(call gb_Trace_EndRange,icu,EXTERNAL) + +else # $(OS) + +icu_CFLAGS:="$(CFLAGS) \ + $(if $(filter iOS,$(OS)),-DUCONFIG_NO_FILE_IO) \ + $(if $(SYSBASE),-I$(SYSBASE)/usr/include) \ + $(call gb_ExternalProject_get_build_flags,icu) \ + $(if $(ENABLE_LTO),$(gb_LTOFLAGS)) \ + $(if $(filter GCC,$(COM)),-fno-strict-aliasing) \ + $(if $(filter FUZZERS,$(BUILD_TYPE)),-DU_USE_STRTOD_L=0) \ + $(if $(filter ANDROID,$(OS)),-fvisibility=hidden -fno-omit-frame-pointer)" +icu_CXXFLAGS:="$(CXXFLAGS) $(CXXFLAGS_CXX11) \ + $(if $(filter iOS,$(OS)),-DUCONFIG_NO_FILE_IO) \ + $(call gb_ExternalProject_get_build_flags,icu) \ + $(if $(ENABLE_LTO),$(gb_LTOFLAGS)) \ + $(if $(filter GCC,$(COM)),-fno-strict-aliasing) \ + $(if $(filter FUZZERS,$(BUILD_TYPE)),-DU_USE_STRTOD_L=0) \ + $(if $(filter ANDROID,$(OS)),-fvisibility=hidden -fno-omit-frame-pointer -I$(SRCDIR)/include)" +icu_LDFLAGS:=" \ + $(if $(ENABLE_LTO),$(gb_LTOFLAGS)) \ + $(call gb_ExternalProject_get_link_flags,icu) \ + $(if $(filter TRUE,$(HAVE_LD_HASH_STYLE)),-Wl$(COMMA)--hash-style=$(WITH_LINKER_HASH_STYLE)) \ + $(if $(SYSBASE),-L../lib -L../../lib -L../stubdata -L../../stubdata -L$(SYSBASE)/usr/lib) \ + $(if $(filter TRUE,$(HAVE_LD_BSYMBOLIC_FUNCTIONS)), -Wl$(COMMA)-Bsymbolic-functions) \ + $(if $(filter ANDROID,$(OS)),$(gb_STDLIBS))" + +# DATASUBDIR=data in cross-compiling case, because --disable-tools completely skips the +# data directory/doesn't build the requested library in that case (icu/source/Makefile.in) +# so we need to add it back to the list of subdirectories to build +$(call gb_ExternalProject_get_state_target,icu,build) : + $(call gb_Trace_StartRange,icu,EXTERNAL) + $(call gb_ExternalProject_run,build,\ + autoconf && \ + CPPFLAGS=$(icu_CPPFLAGS) CFLAGS=$(icu_CFLAGS) \ + CXXFLAGS=$(icu_CXXFLAGS) LDFLAGS=$(icu_LDFLAGS) \ + PYTHONWARNINGS="default" \ + $(gb_RUN_CONFIGURE) ./configure \ + --disable-layout --disable-samples \ + $(if $(filter FUZZERS,$(BUILD_TYPE)),--disable-release) \ + $(if $(filter EMSCRIPTEN ANDROID,$(OS)),--disable-strict ac_cv_c_bigendian=no) \ + $(if $(filter SOLARIS AIX,$(OS)),--disable-64bit-libs) \ + $(if $(filter TRUE,$(DISABLE_DYNLOADING)),\ + --with-data-packaging=static --enable-static --disable-shared --disable-dyload,\ + --disable-static --enable-shared $(if $(filter ANDROID,$(OS)),--with-library-suffix=lo)) \ + $(gb_CONFIGURE_PLATFORMS) \ + $(if $(CROSS_COMPILING), \ + --with-cross-build=$(WORKDIR_FOR_BUILD)/UnpackedTarball/icu/source \ + --disable-tools --disable-extras) \ + AR="$(AR)" RANLIB="$(RANLIB)" \ + && $(MAKE) $(if $(CROSS_COMPILING),DATASUBDIR=data) $(if $(verbose),VERBOSE=1) \ + $(if $(filter MACOSX,$(OS)), \ + && $(PERL) $(SRCDIR)/solenv/bin/macosx-change-install-names.pl shl \ + URELIB \ + $(EXTERNAL_WORKDIR)/source/lib/libicuuc$(gb_Library_DLLEXT).$(icu_VERSION) \ + $(EXTERNAL_WORKDIR)/source/lib/libicui18n$(gb_Library_DLLEXT).$(icu_VERSION) \ + && $(PERL) $(SRCDIR)/solenv/bin/macosx-change-install-names.pl shl \ + OOO \ + $(EXTERNAL_WORKDIR)/source/lib/libicudata$(gb_Library_DLLEXT).$(icu_VERSION)) \ + ,source) + $(call gb_Trace_EndRange,icu,EXTERNAL) + +endif + +# vim: set noet sw=4 ts=4: diff --git a/external/icu/Makefile b/external/icu/Makefile new file mode 100644 index 000000000..e4968cf85 --- /dev/null +++ b/external/icu/Makefile @@ -0,0 +1,7 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- + +module_directory:=$(dir $(realpath $(firstword $(MAKEFILE_LIST)))) + +include $(module_directory)/../../solenv/gbuild/partial_build.mk + +# vim: set noet sw=4 ts=4: diff --git a/external/icu/Module_icu.mk b/external/icu/Module_icu.mk new file mode 100644 index 000000000..5c99b930f --- /dev/null +++ b/external/icu/Module_icu.mk @@ -0,0 +1,19 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_Module_Module,icu)) + +$(eval $(call gb_Module_add_targets,icu,\ + UnpackedTarball_icu \ + ExternalPackage_icu \ + ExternalPackage_icu_ure \ + ExternalProject_icu \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/external/icu/README b/external/icu/README new file mode 100644 index 000000000..484de1f41 --- /dev/null +++ b/external/icu/README @@ -0,0 +1 @@ +Library providing Unicode support, from [http://site.icu-project.org/]. diff --git a/external/icu/UnpackedTarball_icu.mk b/external/icu/UnpackedTarball_icu.mk new file mode 100644 index 000000000..ccb1a1a8b --- /dev/null +++ b/external/icu/UnpackedTarball_icu.mk @@ -0,0 +1,51 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_UnpackedTarball_UnpackedTarball,icu)) + +$(eval $(call gb_UnpackedTarball_set_tarball,icu,$(ICU_TARBALL))) + +$(eval $(call gb_UnpackedTarball_update_autoconf_configs,icu,source)) + +# Data zip contains data/... and needs to end up in icu/source/data/... +# Only data/misc/icudata.rc is needed for a Cygwin/MSVC build. +$(eval $(call gb_UnpackedTarball_set_pre_action,icu,\ + unzip -q -d source -o $(gb_UnpackedTarget_TARFILE_LOCATION)/$(ICU_DATA_TARBALL) data/misc/icudata.rc \ +)) + +$(eval $(call gb_UnpackedTarball_set_patchlevel,icu,0)) + +$(eval $(call gb_UnpackedTarball_add_patches,icu,\ + external/icu/icu4c-build.patch.1 \ + external/icu/icu4c-aix.patch.1 \ + external/icu/icu4c-warnings.patch.1 \ + external/icu/icu4c-macosx.patch.1 \ + external/icu/icu4c-solarisgcc.patch.1 \ + external/icu/icu4c-mkdir.patch.1 \ + external/icu/icu4c-ubsan.patch.1 \ + external/icu/icu4c-scriptrun.patch.1 \ + external/icu/icu4c-rtti.patch.1 \ + external/icu/icu4c-clang-cl.patch.1 \ + external/icu/gcc9.patch \ + external/icu/c++20-comparison.patch.1 \ + external/icu/ubsan.patch.1 \ + external/icu/Wdeprecated-copy-dtor.patch \ + external/icu/strict_ansi.patch \ + external/icu/icu4c-windows-cygwin-cross.patch.1 \ + external/icu/icu4c-emscripten-cross.patch.1 \ + external/icu/icu4c-use-pkgdata-single-ccode-file-mode.patch.1 \ + external/icu/do-not-reset-useful-cache-to-empty-in-populateNear.patch.2 \ + external/icu/icu4c-khmerbreakengine.patch.1 \ + external/icu/icu4c-$(if $(filter ANDROID,$(OS)),android,rpath).patch.1 \ + $(if $(filter-out ANDROID,$(OS)),external/icu/icu4c-icudata-stdlibs.patch.1) \ +)) + +$(eval $(call gb_UnpackedTarball_add_file,icu,source/data/brkitr/khmerdict.dict,external/icu/khmerdict.dict)) + +# vim: set noet sw=4 ts=4: diff --git a/external/icu/Wdeprecated-copy-dtor.patch b/external/icu/Wdeprecated-copy-dtor.patch new file mode 100644 index 000000000..67078ef1b --- /dev/null +++ b/external/icu/Wdeprecated-copy-dtor.patch @@ -0,0 +1,25 @@ +--- source/common/unicode/uobject.h ++++ source/common/unicode/uobject.h +@@ -245,10 +245,10 @@ + // direct use of UObject itself + + // default constructor +- // inline UObject() {} ++ UObject() = default; + + // copy constructor +- // inline UObject(const UObject &other) {} ++ UObject(const UObject &other) = default; + + #if 0 + // TODO Sometime in the future. Implement operator==(). +@@ -280,8 +280,8 @@ + * Subclasses need this assignment operator if they use compiler-provided + * assignment operators of their own. An alternative to not declaring one + * here would be to declare and empty-implement a protected or public one. +- UObject &UObject::operator=(const UObject &); + */ ++ UObject &operator=(const UObject &) = default; + }; + + #ifndef U_HIDE_INTERNAL_API diff --git a/external/icu/c++20-comparison.patch.1 b/external/icu/c++20-comparison.patch.1 new file mode 100644 index 000000000..3d2d7c042 --- /dev/null +++ b/external/icu/c++20-comparison.patch.1 @@ -0,0 +1,82 @@ +diff -ur icu.org/source/i18n/unicode/rbtz.h icu/source/i18n/unicode/rbtz.h +--- icu.org/source/i18n/unicode/rbtz.h 2021-10-28 18:04:57.000000000 +0200 ++++ icu/source/i18n/unicode/rbtz.h 2021-11-15 18:56:24.364137609 +0100 +@@ -87,6 +87,7 @@ + * @stable ICU 3.8 + */ + virtual bool operator!=(const TimeZone& that) const; ++ bool operator!=(const RuleBasedTimeZone& that) const {return !operator==(that);} + + /** + * Adds the `TimeZoneRule` which represents time transitions. +diff -ur icu.org/source/i18n/unicode/simpletz.h icu/source/i18n/unicode/simpletz.h +--- icu.org/source/i18n/unicode/simpletz.h 2021-10-28 18:04:57.000000000 +0200 ++++ icu/source/i18n/unicode/simpletz.h 2021-11-15 19:01:41.774487719 +0100 +@@ -112,6 +112,7 @@ + * @stable ICU 2.0 + */ + virtual bool operator==(const TimeZone& that) const override; ++ bool operator!=(const SimpleTimeZone& that) const {return !operator==(that);} + + /** + * Constructs a SimpleTimeZone with the given raw GMT offset and time zone ID, +diff -ur icu.org/source/i18n/unicode/smpdtfmt.h icu/source/i18n/unicode/smpdtfmt.h +--- icu.org/source/i18n/unicode/smpdtfmt.h 2021-10-28 18:04:57.000000000 +0200 ++++ icu/source/i18n/unicode/smpdtfmt.h 2021-11-15 19:02:47.382353381 +0100 +@@ -877,6 +877,7 @@ + * @stable ICU 2.0 + */ + virtual bool operator==(const Format& other) const override; ++ bool operator!=(const SimpleDateFormat& that) const {return !operator==(that);} + + + using DateFormat::format; +diff -ur icu.org/source/i18n/unicode/stsearch.h icu/source/i18n/unicode/stsearch.h +--- icu.org/source/i18n/unicode/stsearch.h 2021-10-28 18:04:57.000000000 +0200 ++++ icu/source/i18n/unicode/stsearch.h 2021-11-15 19:03:27.014272230 +0100 +@@ -298,6 +298,7 @@ + * @stable ICU 2.0 + */ + virtual bool operator==(const SearchIterator &that) const override; ++ bool operator!=(const StringSearch &that) const {return !operator==(that);} + + // public get and set methods ---------------------------------------- + +diff -ur icu.org/source/i18n/unicode/tzrule.h icu/source/i18n/unicode/tzrule.h +--- icu.org/source/i18n/unicode/tzrule.h 2021-10-28 18:04:57.000000000 +0200 ++++ icu/source/i18n/unicode/tzrule.h 2021-11-15 19:14:52.191331967 +0100 +@@ -257,6 +257,7 @@ + * @stable ICU 3.8 + */ + virtual bool operator!=(const TimeZoneRule& that) const override; ++ bool operator!=(const InitialTimeZoneRule& that) const {return !operator==(that);} + + /** + * Gets the time when this rule takes effect in the given year. +@@ -468,6 +469,7 @@ + * @stable ICU 3.8 + */ + virtual bool operator!=(const TimeZoneRule& that) const override; ++ bool operator!=(const AnnualTimeZoneRule& that) const {return !operator==(that);} + + /** + * Gets the start date/time rule used by this rule. +@@ -684,6 +686,7 @@ + * @stable ICU 3.8 + */ + virtual bool operator!=(const TimeZoneRule& that) const override; ++ bool operator!=(const TimeArrayTimeZoneRule& that) const {return !operator==(that);} + + /** + * Gets the time type of the start times used by this rule. The return value +diff -ur icu.org/source/i18n/unicode/vtzone.h icu/source/i18n/unicode/vtzone.h +--- icu.org/source/i18n/unicode/vtzone.h 2021-10-28 18:04:57.000000000 +0200 ++++ icu/source/i18n/unicode/vtzone.h 2021-11-15 19:16:07.461130004 +0100 +@@ -83,6 +83,7 @@ + * @stable ICU 3.8 + */ + virtual bool operator!=(const TimeZone& that) const; ++ bool operator!=(const VTimeZone& that) const {return !operator==(that);} + + /** + * Create a VTimeZone instance by the time zone ID. diff --git a/external/icu/cross-bin/icu-config b/external/icu/cross-bin/icu-config new file mode 100755 index 000000000..8ccf94f9b --- /dev/null +++ b/external/icu/cross-bin/icu-config @@ -0,0 +1,12 @@ +#!/bin/sh + +case $1 in +--version) + echo whatever + ;; +--cppflags) + echo ${ICU_CFLAGS} + ;; +--ldflags-searchpath) + echo ${ICU_LIBS} +esac diff --git a/external/icu/do-not-reset-useful-cache-to-empty-in-populateNear.patch.2 b/external/icu/do-not-reset-useful-cache-to-empty-in-populateNear.patch.2 new file mode 100644 index 000000000..bd7fe74fb --- /dev/null +++ b/external/icu/do-not-reset-useful-cache-to-empty-in-populateNear.patch.2 @@ -0,0 +1,37 @@ +From 34b2f7174ba187d99dfb8704b9cf19d369accc13 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Lubo=C5=A1=20Lu=C5=88=C3=A1k?= +Date: Wed, 16 Mar 2022 10:54:03 +0100 +Subject: [PATCH] do not reset useful cache to empty in populateNear() + +--- + icu4c/source/common/rbbi_cache.cpp | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/icu4c/source/common/rbbi_cache.cpp b/icu4c/source/common/rbbi_cache.cpp +index 26d82df781..54a77c97c6 100644 +--- a/icu/source/common/rbbi_cache.cpp ++++ b/icu/source/common/rbbi_cache.cpp +@@ -352,7 +352,7 @@ UBool RuleBasedBreakIterator::BreakCache + if ((position < fBoundaries[fStartBufIdx] - 15) || position > (fBoundaries[fEndBufIdx] + 15)) { + int32_t aBoundary = 0; + int32_t ruleStatusIndex = 0; +- if (position > 20) { ++ if (position > 20 && false) { // handleSafePrevious() is broken and always returns 0 + int32_t backupPos = fBI->handleSafePrevious(position); + + if (backupPos > 0) { +@@ -376,7 +376,10 @@ UBool RuleBasedBreakIterator::BreakCache::populateNear(int32_t position, UErrorC + ruleStatusIndex = fBI->fRuleStatusIndex; + } + } +- reset(aBoundary, ruleStatusIndex); // Reset cache to hold aBoundary as a single starting point. ++ // Reset cache to hold aBoundary as a single starting point. ++ // Do not do so if what's in the cache is still more useful than an empty cache. ++ if (!(aBoundary == 0 && position > fBoundaries[fEndBufIdx])) ++ reset(aBoundary, ruleStatusIndex); + } + + // Fill in boundaries between existing cache content and the new requested position. +-- +2.34.1 + diff --git a/external/icu/gcc9.patch b/external/icu/gcc9.patch new file mode 100644 index 000000000..5c9808f8c --- /dev/null +++ b/external/icu/gcc9.patch @@ -0,0 +1,26 @@ +--- source/i18n/unicode/format.h ++++ source/i18n/unicode/format.h +@@ -22,6 +22,13 @@ + + #ifndef FORMAT_H + #define FORMAT_H ++ ++#ifdef __GNUC__ ++#pragma GCC diagnostic push ++#pragma GCC diagnostic ignored "-Wpragmas" // for old GCC ++#pragma GCC diagnostic ignored "-Wunknown-warning-option" // for Clang ++#pragma GCC diagnostic ignored "-Wdeprecated-copy" ++#endif + + + #include "unicode/utypes.h" +@@ -314,5 +314,9 @@ + + #endif /* U_SHOW_CPLUSPLUS_API */ + ++#ifdef __GNUC__ ++#pragma GCC diagnostic pop ++#endif ++ + #endif // _FORMAT + //eof diff --git a/external/icu/icu4c-aix.patch.1 b/external/icu/icu4c-aix.patch.1 new file mode 100644 index 000000000..bcbbe3ab9 --- /dev/null +++ b/external/icu/icu4c-aix.patch.1 @@ -0,0 +1,143 @@ +diff -ur icu.org/source/config/mh-aix-gcc icu/source/config/mh-aix-gcc +--- icu.org/source/config/mh-aix-gcc 2021-10-28 18:04:57.000000000 +0200 ++++ icu/source/config/mh-aix-gcc 2021-11-15 18:35:48.737774348 +0100 +@@ -18,84 +18,29 @@ + GEN_DEPS.c= $(CC) -E -MM $(DEFS) $(CPPFLAGS) + GEN_DEPS.cc= $(CXX) -E -MM $(DEFS) $(CPPFLAGS) + +-## Commands to link +-## We need to use the C++ linker, even when linking C programs, since +-## our libraries contain C++ code (C++ static init not called) +-LINK.c= $(AIX_PREDELETE) $(CXX) $(CXXFLAGS) $(LDFLAGS) +-LINK.cc= $(AIX_PREDELETE) $(CXX) $(CXXFLAGS) $(LDFLAGS) +- +-## Shared library options +-LD_SOOPTIONS= -Wl,-bsymbolic +- +-## Commands to make a shared library +-SHLIB.c= $(AIX_PREDELETE) $(CC) $(CFLAGS) $(LDFLAGS) -shared $(LD_SOOPTIONS) +-SHLIB.cc= $(AIX_PREDELETE) $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(LD_SOOPTIONS) +- +-## Compiler switch to embed a runtime search path +-LD_RPATH= -I +-LD_RPATH_PRE= ++## Flags for position independent code ++SHAREDLIBCFLAGS = -fPIC ++SHAREDLIBCXXFLAGS = -fPIC ++SHAREDLIBCPPFLAGS = -DPIC ++ ++## Additional flags when building libraries and with threads ++THREADSCPPFLAGS = -D_REENTRANT -D_THREAD_SAFE ++LIBCPPFLAGS = + +-## enable the shared lib loader +-LDFLAGS += -Wl,-bbigtoc ++LD_RPATH= ++LD_RPATH_PRE= + + ## These are the library specific LDFLAGS + LDFLAGSICUDT=-nodefaultlibs -nostdlib + +-## We need to delete things prior to linking, or else we'll get +-## SEVERE ERROR: output file in use .. on AIX. +-## But, shell script version should NOT delete target as we don't +-## have $@ in that context. (SH = only shell script, icu-config) +-AIX_PREDELETE=rm -f $@ ; +-#SH# AIX_PREDELETE= +- + ## Environment variable to set a runtime search path + LDLIBRARYPATH_ENVVAR = LIBPATH + +-## Override Versioned target for a shared library. +-FINAL_SO_TARGET= $(basename $(SO_TARGET))$(SO_TARGET_VERSION).$(SO) +-MIDDLE_SO_TARGET= $(basename $(SO_TARGET))$(SO_TARGET_VERSION_MAJOR).$(SO) +-SHARED_OBJECT = $(notdir $(FINAL_SO_TARGET:.$(SO)=.$(SOBJ))) +-SHARED_OBJECT_NO_VERSION = $(basename $(SO_TARGET)).$(SOBJ) +- +-# The following is for Makefile.inc's use. +-ICULIBSUFFIX_VERSION = $(LIB_VERSION_MAJOR) +- +-# this one is for icudefs.mk's use +-ifeq ($(ENABLE_SHARED),YES) +-SO_TARGET_VERSION_SUFFIX = $(SO_TARGET_VERSION_MAJOR) +-endif +- +-## Compiler switch to embed a library name. Not present on AIX. +-LD_SONAME = +- +-## The type of assembly needed when pkgdata is used for generating shared libraries. +-GENCCODE_ASSEMBLY=-a xlc +- + ## Shared object suffix +-SOBJ= so +-# without the -brtl option, the library names use .a. AIX is funny that way. +-SO= a +-A= a ++SO= so + + ## Non-shared intermediate object suffix +-STATIC_O = o +- +-## Special AIX rules +- +-## Build archive from shared object +-%.a : %.so +- ln -f $< $(SHARED_OBJECT_NO_VERSION) +- $(AR) $(ARFLAGS) $@ $(SHARED_OBJECT_NO_VERSION) +- rm -f $(SHARED_OBJECT_NO_VERSION) +-$(LIBDIR)/%.a : %.so +- ln -f $< $(SHARED_OBJECT_NO_VERSION) +- $(AR) $(ARFLAGS) $@ $(SHARED_OBJECT_NO_VERSION) +- rm -f $(SHARED_OBJECT_NO_VERSION) +- +-## Build import list from export list +-%.e : %.exp +- @echo "Building an import list for $<" +- @$(SHELL) -ec "echo '#! $*.a($*.so)' | cat - $< > $@" ++STATIC_O = ao + + ## Compilation rules + %.$(STATIC_O): $(srcdir)/%.c +@@ -123,10 +68,10 @@ + [ -s $@ ] || rm -f $@' + + ## Versioned libraries rules +-%$(SO_TARGET_VERSION_MAJOR).$(SO): %$(SO_TARGET_VERSION).$(SO) +- $(RM) $@ && ln -s ${*F}$(SO_TARGET_VERSION).$(SO) $@ +-%.$(SO): %$(SO_TARGET_VERSION).$(SO) +- $(RM) $@ && ln -s ${*F}$(SO_TARGET_VERSION).$(SO) $@ ++%.$(SO).$(SO_TARGET_VERSION_MAJOR): %.$(SO).$(SO_TARGET_VERSION) ++ $(RM) $@ && ln -s ${= 0x550) \ + || (defined(__SUNPRO_C) && __SUNPRO_C >= 0x550) + # define U_EXPORT __global +diff -ur icu.org/source/config/mh-linux icu/source/config/mh-linux +--- icu.org/source/config/mh-linux 2021-11-15 20:56:39.460705065 +0100 ++++ icu/source/config/mh-linux 2021-11-15 21:03:11.474638494 +0100 +@@ -27,7 +27,7 @@ + + ## Compiler switch to embed a library name + # The initial tab in the next line is to prevent icu-config from reading it. +- LD_SONAME = -Wl,-soname -Wl,$(notdir $(MIDDLE_SO_TARGET)) ++ #LD_SONAME = -Wl,-soname -Wl,$(notdir $(MIDDLE_SO_TARGET)) + #SH# # We can't depend on MIDDLE_SO_TARGET being set. + #SH# LD_SONAME= + +diff -ur icu.org/source/configure icu/source/configure +--- icu.org/source/configure 2021-11-15 20:56:39.875703936 +0100 ++++ icu/source/configure 2021-11-15 21:03:11.475638491 +0100 +@@ -5272,7 +5273,7 @@ + else + icu_cv_host_frag=mh-linux-va + fi ;; +-*-*-linux*|*-*-gnu|*-*-k*bsd*-gnu|*-*-kopensolaris*-gnu) icu_cv_host_frag=mh-linux ;; ++*-*-linux*|*-*-gnu|*-*-k*bsd*-gnu|*-*-kopensolaris*-gnu|*-*-*-androideabi*) icu_cv_host_frag=mh-linux ;; + i[34567]86-*-cygwin) + if test "$GCC" = yes; then + icu_cv_host_frag=mh-cygwin +@@ -6472,6 +6466,10 @@ + # Check to see if genccode can generate simple assembly. + GENCCODE_ASSEMBLY= + case "${host}" in ++arm-*-linux-androideabi) ++ if test "$GCC" = yes; then ++ GENCCODE_ASSEMBLY="-a gcc-android-arm" ++ fi ;; + *-linux*|*-kfreebsd*-gnu*|i*86-*-*bsd*|i*86-pc-gnu) + if test "$GCC" = yes; then + # We're using gcc, and the simple -a gcc command line works for genccode +@@ -7594,6 +7592,10 @@ + # wchar_t can be used + CHECK_UTF16_STRING_RESULT="available" + ;; ++*-*-*-androideabi|mips-unknown-linux-android) ++ # no UTF-16 strings thanks, I think, this is to avoid the -std=c++0x which causes trouble with uint64_t ++ CHECK_UTF16_STRING_RESULT="nope" ++ ;; + *) + ;; + esac +diff -ur icu.org/source/i18n/decimfmt.cpp icu/source/i18n/decimfmt.cpp +--- icu.org/source/i18n/decimfmt.cpp 2021-10-28 18:04:57.000000000 +0200 ++++ icu/source/i18n/decimfmt.cpp 2021-11-15 21:03:11.476638489 +0100 +@@ -9,6 +9,13 @@ + // Helpful in toString methods and elsewhere. + #define UNISTR_FROM_STRING_EXPLICIT + ++#ifdef __ANDROID__ ++#ifndef ARM ++#define ARM ++#endif ++#include ++#endif ++ + #include + #include + #include diff --git a/external/icu/icu4c-build.patch.1 b/external/icu/icu4c-build.patch.1 new file mode 100644 index 000000000..a878de732 --- /dev/null +++ b/external/icu/icu4c-build.patch.1 @@ -0,0 +1,91 @@ +diff -ur icu.org/source/config/mh-darwin icu/source/config/mh-darwin +--- icu.org/source/config/mh-darwin 2016-06-15 20:58:17.000000000 +0200 ++++ icu/source/config/mh-darwin 2017-04-21 21:30:23.584568210 +0200 +@@ -30,11 +30,7 @@ + SHLIB.cc= $(CXX) -dynamiclib -dynamic $(CXXFLAGS) $(LDFLAGS) $(LD_SOOPTIONS) + + ## Compiler switches to embed a library name and version information +-ifeq ($(ENABLE_RPATH),YES) +-LD_SONAME = -Wl,-compatibility_version -Wl,$(SO_TARGET_VERSION_MAJOR) -Wl,-current_version -Wl,$(SO_TARGET_VERSION) -install_name $(libdir)/$(notdir $(MIDDLE_SO_TARGET)) +-else +-LD_SONAME = -Wl,-compatibility_version -Wl,$(SO_TARGET_VERSION_MAJOR) -Wl,-current_version -Wl,$(SO_TARGET_VERSION) -install_name $(notdir $(MIDDLE_SO_TARGET)) $(PKGDATA_TRAILING_SPACE) +-endif ++LD_SONAME = -Wl,-compatibility_version -Wl,$(SO_TARGET_VERSION_MAJOR) -Wl,-current_version -Wl,$(SO_TARGET_VERSION) -install_name @__________________________________________________URELIB/$(notdir $(MIDDLE_SO_TARGET)) + + ## Compiler switch to embed a runtime search path + LD_RPATH= +@@ -50,10 +46,6 @@ + ## Non-shared intermediate object suffix + STATIC_O = ao + +-## Override Versioned target for a shared library. +-FINAL_SO_TARGET= $(basename $(SO_TARGET)).$(SO_TARGET_VERSION).$(SO) +-MIDDLE_SO_TARGET= $(basename $(SO_TARGET)).$(SO_TARGET_VERSION_MAJOR).$(SO) +- + ## Compilation and dependency rules + %.$(STATIC_O): $(srcdir)/%.c + $(call SILENT_COMPILE,$(strip $(COMPILE.c) $(STATICCPPFLAGS) $(STATICCFLAGS)) -MMD -MT "$*.d $*.o $*.$(STATIC_O)" -o $@ $<) +@@ -67,16 +59,10 @@ + + ## Versioned libraries rules + +-%.$(SO_TARGET_VERSION_MAJOR).$(SO): %.$(SO_TARGET_VERSION).$(SO) ++%.$(SO).$(SO_TARGET_VERSION_MAJOR): %.$(SO).$(SO_TARGET_VERSION) + $(RM) $@ && ln -s ${ $@; \ ++ [ -s $@ ] || rm -f $@' ++ ++%.d: $(srcdir)/%.cpp ++ $(call ICU_MSG,(deps)) $< ++ @$(SHELL) -ec '$(GEN_DEPS.cc) $< \ ++ | sed '\''s%\($*\)\.o[ :]*%\1.o $@ : %g'\'' > $@; \ ++ [ -s $@ ] || rm -f $@' ++ ++## Versioned libraries rules ++ ++%.$(SO).$(SO_TARGET_VERSION_MAJOR): %.$(SO).$(SO_TARGET_VERSION) ++ $(RM) $@ && ln -s ${= 0) { ++ if (!fSkipStartSet.contains(c)) { ++ res = (c == ZWSP); ++ break; ++ } ++ --start; ++ c = utext_previous32(ut); ++ doBreak = false; ++ } ++ utext_close(ut); ++ return res; ++} ++ ++bool ++DictionaryBreakEngine::scanAfterEnd(UText *text, int32_t textEnd, int32_t& end, bool &doBreak) const { ++ UErrorCode status = U_ZERO_ERROR; ++ UText* ut = utext_clone(NULL, text, false, true, &status); ++ utext_setNativeIndex(ut, end); ++ UChar32 c = utext_current32(ut); ++ bool res = false; ++ doBreak = !fNBeforeSet.contains(c); ++ while (end < textEnd) { ++ if (!fSkipEndSet.contains(c)) { ++ res = (c == ZWSP); ++ break; ++ } ++ ++end; ++ c = utext_next32(ut); ++ doBreak = false; ++ } ++ utext_close(ut); ++ return res; ++} ++ ++void ++DictionaryBreakEngine::scanBackClusters(UText *text, int32_t textStart, int32_t& start) const { ++ UChar32 c = 0; ++ start = utext_getNativeIndex(text); ++ while (start > textStart) { ++ c = utext_previous32(text); ++ --start; ++ if (!fSkipEndSet.contains(c)) ++ break; ++ } ++ for (int i = 0; i < clusterLimit; ++i) { // scan backwards clusterLimit clusters ++ while (start > textStart) { ++ while (fIgnoreSet.contains(c)) ++ c = utext_previous32(text); ++ if (!fMarkSet.contains(c)) { ++ if (fBaseSet.contains(c)) { ++ c = utext_previous32(text); ++ if (!fViramaSet.contains(c)) { // Virama (e.g. coeng) preceding base. Treat sequence as a mark ++ utext_next32(text); ++ c = utext_current32(text); ++ break; ++ } else { ++ --start; ++ } ++ } else { ++ break; ++ } ++ } ++ c = utext_previous32(text); ++ --start; ++ } ++ if (!fBaseSet.contains(c) || start < textStart) { // not a cluster start so finish ++ break; ++ } ++ c = utext_previous32(text); ++ --start; // go round again ++ } // ignore hitting previous inhibitor since scanning for it should have found us! ++ ++start; // counteract --before ++} ++ ++void ++DictionaryBreakEngine::scanFwdClusters(UText *text, int32_t textEnd, int32_t& end) const { ++ UChar32 c = utext_current32(text); ++ end = utext_getNativeIndex(text); ++ while (end < textEnd) { ++ if (!fSkipStartSet.contains(c)) ++ break; ++ utext_next32(text); ++ c = utext_current32(text); ++ ++end; ++ } ++ for (int i = 0; i < clusterLimit; ++i) { // scan forwards clusterLimit clusters ++ while (fIgnoreSet.contains(c)) { ++ utext_next32(text); ++ c = utext_current32(text); ++ } ++ if (fBaseSet.contains(c)) { ++ while (end < textEnd) { ++ utext_next32(text); ++ c = utext_current32(text); ++ ++end; ++ if (!fMarkSet.contains(c)) ++ break; ++ else if (fViramaSet.contains(c)) { // handle coeng + base as mark ++ utext_next32(text); ++ c = utext_current32(text); ++ ++end; ++ if (!fBaseSet.contains(c)) ++ break; ++ } ++ } ++ } else { ++ --end; // bad char so break after char before it ++ break; ++ } ++ } ++} ++ ++bool ++DictionaryBreakEngine::scanWJ(UText *text, int32_t &start, int32_t end, int32_t &before, int32_t &after) const { ++ UErrorCode status = U_ZERO_ERROR; ++ UText* ut = utext_clone(NULL, text, false, true, &status); ++ int32_t nat = start; ++ utext_setNativeIndex(ut, nat); ++ bool foundFirst = true; ++ int32_t curr = start; ++ while (nat < end) { ++ UChar32 c = utext_current32(ut); ++ if (c == ZWSP || c == WJ) { ++ curr = nat + 1; ++ if (foundFirst) // only scan backwards for first inhibitor ++ scanBackClusters(ut, start, before); ++ foundFirst = false; // don't scan backwards if we go around again. Also marks found something ++ ++ utext_next32(ut); ++ scanFwdClusters(ut, end, after); ++ nat = after + 1; ++ ++ if (c == ZWSP || c == WJ) { // did we hit another one? ++ continue; ++ } else { ++ break; ++ } ++ } ++ ++ ++nat; // keep hunting ++ utext_next32(ut); ++ } ++ ++ utext_close(ut); ++ ++ if (nat >= end && foundFirst) { ++ start = before = after = nat; ++ return false; // failed to find anything ++ } ++ else { ++ start = curr; ++ } ++ return true; // yup hit one ++} ++ + /* + ****************************************************************** + * PossibleWord +@@ -114,7 +289,7 @@ + ~PossibleWord() {} + + // Fill the list of candidates if needed, select the longest, and return the number found +- int32_t candidates( UText *text, DictionaryMatcher *dict, int32_t rangeEnd ); ++ int32_t candidates( UText *text, DictionaryMatcher *dict, int32_t rangeEnd, UnicodeSet const *ignoreSet = NULL, int32_t minLength = 0 ); + + // Select the currently marked candidate, point after it in the text, and invalidate self + int32_t acceptMarked( UText *text ); +@@ -135,12 +310,12 @@ + }; + + +-int32_t PossibleWord::candidates( UText *text, DictionaryMatcher *dict, int32_t rangeEnd ) { ++int32_t PossibleWord::candidates( UText *text, DictionaryMatcher *dict, int32_t rangeEnd, UnicodeSet const *ignoreSet, int32_t minLength) { + // TODO: If getIndex is too slow, use offset < 0 and add discardAll() + int32_t start = (int32_t)utext_getNativeIndex(text); + if (start != offset) { + offset = start; +- count = dict->matches(text, rangeEnd-start, UPRV_LENGTHOF(cuLengths), cuLengths, cpLengths, NULL, &prefix); ++ count = dict->matches(text, rangeEnd-start, UPRV_LENGTHOF(cuLengths), cuLengths, cpLengths, NULL, &prefix, ignoreSet, minLength); + // Dictionary leaves text after longest prefix, not longest word. Back up. + if (count <= 0) { + utext_setNativeIndex(text, start); +@@ -814,53 +989,30 @@ + * KhmerBreakEngine + */ + +-// How many words in a row are "good enough"? +-static const int32_t KHMER_LOOKAHEAD = 3; +- +-// Will not combine a non-word with a preceding dictionary word longer than this +-static const int32_t KHMER_ROOT_COMBINE_THRESHOLD = 3; +- +-// Will not combine a non-word that shares at least this much prefix with a +-// dictionary word, with a preceding word +-static const int32_t KHMER_PREFIX_COMBINE_THRESHOLD = 3; +- +-// Minimum word size +-static const int32_t KHMER_MIN_WORD = 2; +- +-// Minimum number of characters for two words +-static const int32_t KHMER_MIN_WORD_SPAN = KHMER_MIN_WORD * 2; +- + KhmerBreakEngine::KhmerBreakEngine(DictionaryMatcher *adoptDictionary, UErrorCode &status) +- : DictionaryBreakEngine(), ++ : DictionaryBreakEngine((1 << UBRK_WORD) | (1 << UBRK_LINE)), + fDictionary(adoptDictionary) + { + UTRACE_ENTRY(UTRACE_UBRK_CREATE_BREAK_ENGINE); + UTRACE_DATA1(UTRACE_INFO, "dictbe=%s", "Khmr"); +- UnicodeSet khmerWordSet(UnicodeString(u"[[:Khmr:]&[:LineBreak=SA:]]"), status); ++ ++ clusterLimit = 3; ++ ++ UnicodeSet khmerWordSet(UnicodeString(u"[[:Khmr:]\\u2060\\u200C\\u200D]"), status); + if (U_SUCCESS(status)) { + setCharacters(khmerWordSet); + } + fMarkSet.applyPattern(UnicodeString(u"[[:Khmr:]&[:LineBreak=SA:]&[:M:]]"), status); +- fMarkSet.add(0x0020); +- fEndWordSet = khmerWordSet; +- fBeginWordSet.add(0x1780, 0x17B3); +- //fBeginWordSet.add(0x17A3, 0x17A4); // deprecated vowels +- //fEndWordSet.remove(0x17A5, 0x17A9); // Khmer independent vowels that can't end a word +- //fEndWordSet.remove(0x17B2); // Khmer independent vowel that can't end a word +- fEndWordSet.remove(0x17D2); // KHMER SIGN COENG that combines some following characters +- //fEndWordSet.remove(0x17B6, 0x17C5); // Remove dependent vowels +-// fEndWordSet.remove(0x0E31); // MAI HAN-AKAT +-// fEndWordSet.remove(0x0E40, 0x0E44); // SARA E through SARA AI MAIMALAI +-// fBeginWordSet.add(0x0E01, 0x0E2E); // KO KAI through HO NOKHUK +-// fBeginWordSet.add(0x0E40, 0x0E44); // SARA E through SARA AI MAIMALAI +-// fSuffixSet.add(THAI_PAIYANNOI); +-// fSuffixSet.add(THAI_MAIYAMOK); ++ fIgnoreSet.add(0x2060); // WJ ++ fIgnoreSet.add(0x200C, 0x200D); // ZWJ, ZWNJ ++ fBaseSet.applyPattern(UnicodeString(u"[[:Khmr:]&[:lb=SA:]&[:^M:]]"), status); ++ fPuncSet.applyPattern(UnicodeString(u"[\\u17D4\\u17D5\\u17D6\\u17D7\\u17D9:]"), status); + + // Compact for caching. + fMarkSet.compact(); +- fEndWordSet.compact(); +- fBeginWordSet.compact(); +-// fSuffixSet.compact(); ++ fIgnoreSet.compact(); ++ fBaseSet.compact(); ++ fPuncSet.compact(); + UTRACE_EXIT_STATUS(status); + } + +@@ -876,175 +1028,205 @@ + UBool /* isPhraseBreaking */, + UErrorCode& status ) const { + if (U_FAILURE(status)) return 0; +- if ((rangeEnd - rangeStart) < KHMER_MIN_WORD_SPAN) { +- return 0; // Not enough characters for two words ++ uint32_t wordsFound = foundBreaks.size(); ++ int32_t before = 0; ++ int32_t after = 0; ++ int32_t finalBefore = 0; ++ int32_t initAfter = 0; ++ int32_t scanStart = rangeStart; ++ int32_t scanEnd = rangeEnd; ++ ++ bool startZwsp = false; ++ bool breakStart = false; ++ bool breakEnd = false; ++ ++ if (rangeStart > 0) { ++ --scanStart; ++ startZwsp = scanBeforeStart(text, scanStart, breakStart); + } + +- uint32_t wordsFound = 0; +- int32_t cpWordLength = 0; +- int32_t cuWordLength = 0; +- int32_t current; +- PossibleWord words[KHMER_LOOKAHEAD]; +- + utext_setNativeIndex(text, rangeStart); ++ scanFwdClusters(text, rangeEnd, initAfter); ++ bool endZwsp = scanAfterEnd(text, utext_nativeLength(text), scanEnd, breakEnd); ++ utext_setNativeIndex(text, rangeEnd - 1); ++ scanBackClusters(text, rangeStart, finalBefore); ++ if (finalBefore < initAfter) { // the whole run is tented so no breaks ++ if (breakStart || fTypes < UBRK_LINE) ++ foundBreaks.push(rangeStart, status); ++ if (breakEnd || fTypes < UBRK_LINE) ++ foundBreaks.push(rangeEnd, status); ++ return foundBreaks.size() - wordsFound; ++ } + +- while (U_SUCCESS(status) && (current = (int32_t)utext_getNativeIndex(text)) < rangeEnd) { +- cuWordLength = 0; +- cpWordLength = 0; +- +- // Look for candidate words at the current position +- int32_t candidates = words[wordsFound%KHMER_LOOKAHEAD].candidates(text, fDictionary, rangeEnd); +- +- // If we found exactly one, use that +- if (candidates == 1) { +- cuWordLength = words[wordsFound % KHMER_LOOKAHEAD].acceptMarked(text); +- cpWordLength = words[wordsFound % KHMER_LOOKAHEAD].markedCPLength(); +- wordsFound += 1; +- } ++ scanStart = rangeStart; ++ scanWJ(text, scanStart, rangeEnd, before, after); ++ if (startZwsp || initAfter >= before) { ++ after = initAfter; ++ before = 0; ++ } ++ if (!endZwsp && after > finalBefore && after < rangeEnd) ++ endZwsp = true; ++ if (endZwsp && before > finalBefore) ++ before = finalBefore; + +- // If there was more than one, see which one can take us forward the most words +- else if (candidates > 1) { +- // If we're already at the end of the range, we're done +- if ((int32_t)utext_getNativeIndex(text) >= rangeEnd) { +- goto foundBest; +- } +- do { +- if (words[(wordsFound + 1) % KHMER_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) > 0) { +- // Followed by another dictionary word; mark first word as a good candidate +- words[wordsFound % KHMER_LOOKAHEAD].markCurrent(); ++ utext_setNativeIndex(text, rangeStart); ++ int32_t numCodePts = rangeEnd - rangeStart; ++ // bestSnlp[i] is the snlp of the best segmentation of the first i ++ // code points in the range to be matched. ++ UVector32 bestSnlp(numCodePts + 1, status); ++ bestSnlp.addElement(0, status); ++ for(int32_t i = 1; i <= numCodePts; i++) { ++ bestSnlp.addElement(kuint32max, status); ++ } + +- // If we're already at the end of the range, we're done +- if ((int32_t)utext_getNativeIndex(text) >= rangeEnd) { +- goto foundBest; +- } ++ // prev[i] is the index of the last code point in the previous word in ++ // the best segmentation of the first i characters. Note negative implies ++ // that the code point is part of an unknown word. ++ UVector32 prev(numCodePts + 1, status); ++ for(int32_t i = 0; i <= numCodePts; i++) { ++ prev.addElement(kuint32max, status); ++ } + +- // See if any of the possible second words is followed by a third word +- do { +- // If we find a third word, stop right away +- if (words[(wordsFound + 2) % KHMER_LOOKAHEAD].candidates(text, fDictionary, rangeEnd)) { +- words[wordsFound % KHMER_LOOKAHEAD].markCurrent(); +- goto foundBest; +- } +- } +- while (words[(wordsFound + 1) % KHMER_LOOKAHEAD].backUp(text)); +- } ++ const int32_t maxWordSize = 20; ++ UVector32 values(maxWordSize, status); ++ values.setSize(maxWordSize); ++ UVector32 lengths(maxWordSize, status); ++ lengths.setSize(maxWordSize); ++ ++ // Dynamic programming to find the best segmentation. ++ ++ // In outer loop, i is the code point index, ++ // ix is the corresponding string (code unit) index. ++ // They differ when the string contains supplementary characters. ++ int32_t ix = rangeStart; ++ for (int32_t i = 0; i < numCodePts; ++i, utext_setNativeIndex(text, ++ix)) { ++ if ((uint32_t)bestSnlp.elementAti(i) == kuint32max) { ++ continue; ++ } ++ ++ int32_t count; ++ count = fDictionary->matches(text, numCodePts - i, maxWordSize, ++ NULL, lengths.getBuffer(), values.getBuffer(), NULL, &fIgnoreSet, 2); ++ // Note: lengths is filled with code point lengths ++ // The NULL parameter is the ignored code unit lengths. ++ ++ for (int32_t j = 0; j < count; j++) { ++ int32_t ln = lengths.elementAti(j); ++ if (ln + i >= numCodePts) ++ continue; ++ utext_setNativeIndex(text, ln+ix); ++ int32_t c = utext_current32(text); ++ if (fMarkSet.contains(c) || c == 0x17D2) { // Coeng ++ lengths.removeElementAt(j); ++ values.removeElementAt(j); ++ --j; ++ --count; + } +- while (words[wordsFound % KHMER_LOOKAHEAD].backUp(text)); +-foundBest: +- cuWordLength = words[wordsFound % KHMER_LOOKAHEAD].acceptMarked(text); +- cpWordLength = words[wordsFound % KHMER_LOOKAHEAD].markedCPLength(); +- wordsFound += 1; + } +- +- // We come here after having either found a word or not. We look ahead to the +- // next word. If it's not a dictionary word, we will combine it with the word we +- // just found (if there is one), but only if the preceding word does not exceed +- // the threshold. +- // The text iterator should now be positioned at the end of the word we found. +- if ((int32_t)utext_getNativeIndex(text) < rangeEnd && cpWordLength < KHMER_ROOT_COMBINE_THRESHOLD) { +- // if it is a dictionary word, do nothing. If it isn't, then if there is +- // no preceding word, or the non-word shares less than the minimum threshold +- // of characters with a dictionary word, then scan to resynchronize +- if (words[wordsFound % KHMER_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) <= 0 +- && (cuWordLength == 0 +- || words[wordsFound % KHMER_LOOKAHEAD].longestPrefix() < KHMER_PREFIX_COMBINE_THRESHOLD)) { +- // Look for a plausible word boundary +- int32_t remaining = rangeEnd - (current+cuWordLength); +- UChar32 pc; +- UChar32 uc; +- int32_t chars = 0; +- for (;;) { +- int32_t pcIndex = (int32_t)utext_getNativeIndex(text); +- pc = utext_next32(text); +- int32_t pcSize = (int32_t)utext_getNativeIndex(text) - pcIndex; +- chars += pcSize; +- remaining -= pcSize; +- if (remaining <= 0) { ++ if (count == 0) { ++ utext_setNativeIndex(text, ix); ++ int32_t c = utext_current32(text); ++ if (fPuncSet.contains(c) || fIgnoreSet.contains(c) || c == ZWSP) { ++ values.setElementAt(0, count); ++ lengths.setElementAt(1, count++); ++ } else if (fBaseSet.contains(c)) { ++ int32_t currix = utext_getNativeIndex(text); ++ do { ++ utext_next32(text); ++ c = utext_current32(text); ++ if (utext_getNativeIndex(text) >= rangeEnd) + break; +- } +- uc = utext_current32(text); +- if (fEndWordSet.contains(pc) && fBeginWordSet.contains(uc)) { +- // Maybe. See if it's in the dictionary. +- int32_t num_candidates = words[(wordsFound + 1) % KHMER_LOOKAHEAD].candidates(text, fDictionary, rangeEnd); +- utext_setNativeIndex(text, current+cuWordLength+chars); +- if (num_candidates > 0) { ++ if (c == 0x17D2) { // Coeng ++ utext_next32(text); ++ c = utext_current32(text); ++ if (!fBaseSet.contains(c) || utext_getNativeIndex(text) >= rangeEnd) { + break; ++ } else { ++ utext_next32(text); ++ c = utext_current32(text); ++ if (utext_getNativeIndex(text) >= rangeEnd) ++ break; + } + } +- } +- +- // Bump the word count if there wasn't already one +- if (cuWordLength <= 0) { +- wordsFound += 1; +- } ++ } while (fMarkSet.contains(c) || fIgnoreSet.contains(c)); ++ values.setElementAt(BADSNLP, count); ++ lengths.setElementAt(utext_getNativeIndex(text) - currix, count++); ++ } else { ++ values.setElementAt(BADSNLP, count); ++ lengths.setElementAt(1, count++); ++ } ++ } + +- // Update the length with the passed-over characters +- cuWordLength += chars; ++ for (int32_t j = 0; j < count; j++) { ++ uint32_t v = values.elementAti(j); ++ int32_t newSnlp = bestSnlp.elementAti(i) + v; ++ int32_t ln = lengths.elementAti(j); ++ utext_setNativeIndex(text, ln+ix); ++ int32_t c = utext_current32(text); ++ while ((fPuncSet.contains(c) || fIgnoreSet.contains(c)) && ln + i < numCodePts) { ++ ++ln; ++ utext_next32(text); ++ c = utext_current32(text); + } +- else { +- // Back up to where we were for next iteration +- utext_setNativeIndex(text, current+cuWordLength); ++ int32_t ln_j_i = ln + i; // yes really i! ++ if (newSnlp < bestSnlp.elementAti(ln_j_i)) { ++ if (v == BADSNLP) { ++ int32_t p = prev.elementAti(i); ++ if (p < 0) ++ prev.setElementAt(p, ln_j_i); ++ else ++ prev.setElementAt(-i, ln_j_i); ++ } ++ else ++ prev.setElementAt(i, ln_j_i); ++ bestSnlp.setElementAt(newSnlp, ln_j_i); + } + } +- +- // Never stop before a combining mark. +- int32_t currPos; +- while ((currPos = (int32_t)utext_getNativeIndex(text)) < rangeEnd && fMarkSet.contains(utext_current32(text))) { +- utext_next32(text); +- cuWordLength += (int32_t)utext_getNativeIndex(text) - currPos; ++ } ++ // Start pushing the optimal offset index into t_boundary (t for tentative). ++ // prev[numCodePts] is guaranteed to be meaningful. ++ // We'll first push in the reverse order, i.e., ++ // t_boundary[0] = numCodePts, and afterwards do a swap. ++ UVector32 t_boundary(numCodePts+1, status); ++ ++ int32_t numBreaks = 0; ++ // No segmentation found, set boundary to end of range ++ while (numCodePts >= 0 && (uint32_t)bestSnlp.elementAti(numCodePts) == kuint32max) { ++ --numCodePts; ++ } ++ if (numCodePts < 0) { ++ t_boundary.addElement(numCodePts, status); ++ numBreaks++; ++ } else { ++ for (int32_t i = numCodePts; (uint32_t)i != kuint32max; i = prev.elementAti(i)) { ++ if (i < 0) i = -i; ++ t_boundary.addElement(i, status); ++ numBreaks++; + } ++ // U_ASSERT(prev.elementAti(t_boundary.elementAti(numBreaks - 1)) == 0); ++ } + +- // Look ahead for possible suffixes if a dictionary word does not follow. +- // We do this in code rather than using a rule so that the heuristic +- // resynch continues to function. For example, one of the suffix characters +- // could be a typo in the middle of a word. +-// if ((int32_t)utext_getNativeIndex(text) < rangeEnd && wordLength > 0) { +-// if (words[wordsFound%KHMER_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) <= 0 +-// && fSuffixSet.contains(uc = utext_current32(text))) { +-// if (uc == KHMER_PAIYANNOI) { +-// if (!fSuffixSet.contains(utext_previous32(text))) { +-// // Skip over previous end and PAIYANNOI +-// utext_next32(text); +-// utext_next32(text); +-// wordLength += 1; // Add PAIYANNOI to word +-// uc = utext_current32(text); // Fetch next character +-// } +-// else { +-// // Restore prior position +-// utext_next32(text); +-// } +-// } +-// if (uc == KHMER_MAIYAMOK) { +-// if (utext_previous32(text) != KHMER_MAIYAMOK) { +-// // Skip over previous end and MAIYAMOK +-// utext_next32(text); +-// utext_next32(text); +-// wordLength += 1; // Add MAIYAMOK to word +-// } +-// else { +-// // Restore prior position +-// utext_next32(text); +-// } +-// } +-// } +-// else { +-// utext_setNativeIndex(text, current+wordLength); +-// } +-// } +- +- // Did we find a word on this iteration? If so, push it on the break stack +- if (cuWordLength > 0) { +- foundBreaks.push((current+cuWordLength), status); ++ // Now that we're done, convert positions in t_boundary[] (indices in ++ // the normalized input string) back to indices in the original input UText ++ // while reversing t_boundary and pushing values to foundBreaks. ++ for (int32_t i = numBreaks-1; i >= 0; i--) { ++ int32_t cpPos = t_boundary.elementAti(i); ++ if (cpPos == 0 && !breakStart && fTypes >= UBRK_LINE) continue; ++ int32_t utextPos = cpPos + rangeStart; ++ while (utextPos > after && scanWJ(text, utextPos, scanEnd, before, after)); ++ if (utextPos < before) { ++ // Boundaries are added to foundBreaks output in ascending order. ++ U_ASSERT(foundBreaks.size() == 0 ||foundBreaks.peeki() < utextPos); ++ foundBreaks.push(utextPos, status); + } + } +- ++ + // Don't return a break for the end of the dictionary range if there is one there. +- if (foundBreaks.peeki() >= rangeEnd) { ++ if (!breakEnd && fTypes >= UBRK_LINE && foundBreaks.peeki() >= rangeEnd) { + (void) foundBreaks.popi(); +- wordsFound -= 1; + } + +- return wordsFound; ++ return foundBreaks.size() - wordsFound; + } + + #if !UCONFIG_NO_NORMALIZATION +diff -ur icu.org/source/common/dictbe.h icu/source/common/dictbe.h +--- icu.org/source/common/dictbe.h 2022-04-08 00:41:55.000000000 +0200 ++++ icu/source/common/dictbe.h 2022-05-16 13:49:33.820459894 +0200 +@@ -35,7 +35,8 @@ + * threads without synchronization.

+ */ + class DictionaryBreakEngine : public LanguageBreakEngine { +- private: ++ protected: ++ + /** + * The set of characters handled by this engine + * @internal +@@ -43,14 +44,84 @@ + + UnicodeSet fSet; + ++ const int32_t WJ = 0x2060; ++ const int32_t ZWSP = 0x200B; ++ ++ /** ++ * The break types it was constructed with ++ * @internal ++ */ ++ uint32_t fTypes; ++ ++ /** ++ * A Unicode set of all viramas ++ * @internal ++ */ ++ UnicodeSet fViramaSet; ++ ++ /** ++ * A Unicode set of all base characters ++ * @internal ++ */ ++ UnicodeSet fBaseSet; ++ ++ /** ++ * A Unicode set of all marks ++ * @internal ++ */ ++ UnicodeSet fMarkSet; ++ ++ /** ++ * A Unicode set of all characters ignored ignored in dictionary matching ++ * @internal ++ */ ++ UnicodeSet fIgnoreSet; ++ ++ /** ++ * A Unicode set of all characters ignored ignored in dictionary matching ++ * @internal ++ */ ++ UnicodeSet fSkipStartSet; ++ ++ /** ++ * A Unicode set of all characters ignored ignored in dictionary matching ++ * @internal ++ */ ++ UnicodeSet fSkipEndSet; ++ ++ /** ++ * A Unicode set of all characters that should not be broken before ++ * @internal ++ */ ++ UnicodeSet fNBeforeSet; ++ ++ /** ++ * The number of clusters within which breaks are inhibited ++ * @internal ++ */ ++ int32_t clusterLimit; ++ ++ bool scanWJ(UText *text, int32_t &start, int32_t end, int32_t &before, int32_t &after) const; ++ ++ bool scanBeforeStart(UText *text, int32_t& start, bool &doBreak) const; ++ bool scanAfterEnd(UText *text, int32_t rangeEnd, int32_t& end, bool &doBreak) const; ++ void scanBackClusters(UText *text, int32_t textStart, int32_t& start) const; ++ void scanFwdClusters(UText *text, int32_t textEnd, int32_t& end) const; ++ + public: + + /** +- *

Constructor

++ *

Default constructor.

++ * + */ + DictionaryBreakEngine(); + + /** ++ *

Constructor with break types.

++ */ ++ explicit DictionaryBreakEngine(uint32_t breakTypes); ++ ++ /** + *

Virtual destructor.

+ */ + virtual ~DictionaryBreakEngine(); +@@ -305,10 +376,12 @@ + * @internal + */ + +- UnicodeSet fEndWordSet; + UnicodeSet fBeginWordSet; +- UnicodeSet fMarkSet; +- DictionaryMatcher *fDictionary; ++ UnicodeSet fPuncSet; ++ DictionaryMatcher *fDictionary; ++ ++ const uint32_t BADSNLP = 256 * 20; ++ const uint32_t kuint32max = 0x7FFFFFFF; + + public: + +diff -ur icu.org/source/common/dictionarydata.cpp icu/source/common/dictionarydata.cpp +--- icu.org/source/common/dictionarydata.cpp 2022-04-08 00:41:55.000000000 +0200 ++++ icu/source/common/dictionarydata.cpp 2022-05-16 13:49:33.821459892 +0200 +@@ -44,7 +44,7 @@ + + int32_t UCharsDictionaryMatcher::matches(UText *text, int32_t maxLength, int32_t limit, + int32_t *lengths, int32_t *cpLengths, int32_t *values, +- int32_t *prefix) const { ++ int32_t *prefix, UnicodeSet const* ignoreSet, int32_t minLength) const { + + UCharsTrie uct(characters); + int32_t startingTextIndex = (int32_t)utext_getNativeIndex(text); +@@ -55,7 +55,13 @@ + UStringTrieResult result = (codePointsMatched == 0) ? uct.first(c) : uct.next(c); + int32_t lengthMatched = (int32_t)utext_getNativeIndex(text) - startingTextIndex; + codePointsMatched += 1; ++ if (ignoreSet != NULL && ignoreSet->contains(c)) { ++ continue; ++ } + if (USTRINGTRIE_HAS_VALUE(result)) { ++ if (codePointsMatched < minLength) { ++ continue; ++ } + if (wordCount < limit) { + if (values != NULL) { + values[wordCount] = uct.getValue(); +@@ -112,7 +118,7 @@ + + int32_t BytesDictionaryMatcher::matches(UText *text, int32_t maxLength, int32_t limit, + int32_t *lengths, int32_t *cpLengths, int32_t *values, +- int32_t *prefix) const { ++ int32_t *prefix, UnicodeSet const* ignoreSet, int32_t minLength) const { + BytesTrie bt(characters); + int32_t startingTextIndex = (int32_t)utext_getNativeIndex(text); + int32_t wordCount = 0; +@@ -122,7 +128,13 @@ + UStringTrieResult result = (codePointsMatched == 0) ? bt.first(transform(c)) : bt.next(transform(c)); + int32_t lengthMatched = (int32_t)utext_getNativeIndex(text) - startingTextIndex; + codePointsMatched += 1; ++ if (ignoreSet != NULL && ignoreSet->contains(c)) { ++ continue; ++ } + if (USTRINGTRIE_HAS_VALUE(result)) { ++ if (codePointsMatched < minLength) { ++ continue; ++ } + if (wordCount < limit) { + if (values != NULL) { + values[wordCount] = bt.getValue(); +diff -ur icu.org/source/common/dictionarydata.h icu/source/common/dictionarydata.h +--- icu.org/source/common/dictionarydata.h 2022-04-08 00:41:55.000000000 +0200 ++++ icu/source/common/dictionarydata.h 2022-05-16 13:49:33.822459891 +0200 +@@ -21,6 +21,7 @@ + #include "unicode/utext.h" + #include "unicode/udata.h" + #include "udataswp.h" ++#include "unicode/uniset.h" + #include "unicode/uobject.h" + #include "unicode/ustringtrie.h" + +@@ -92,7 +93,7 @@ + */ + virtual int32_t matches(UText *text, int32_t maxLength, int32_t limit, + int32_t *lengths, int32_t *cpLengths, int32_t *values, +- int32_t *prefix) const = 0; ++ int32_t *prefix, UnicodeSet const* ignoreSet = NULL, int32_t minLength = 0) const = 0; + + /** @return DictionaryData::TRIE_TYPE_XYZ */ + virtual int32_t getType() const = 0; +@@ -107,7 +108,7 @@ + virtual ~UCharsDictionaryMatcher(); + virtual int32_t matches(UText *text, int32_t maxLength, int32_t limit, + int32_t *lengths, int32_t *cpLengths, int32_t *values, +- int32_t *prefix) const override; ++ int32_t *prefix, UnicodeSet const* ignoreSet = NULL, int32_t minLength = 0) const override; + virtual int32_t getType() const override; + private: + const UChar *characters; +@@ -125,7 +126,7 @@ + virtual ~BytesDictionaryMatcher(); + virtual int32_t matches(UText *text, int32_t maxLength, int32_t limit, + int32_t *lengths, int32_t *cpLengths, int32_t *values, +- int32_t *prefix) const override; ++ int32_t *prefix, UnicodeSet const* ignoreSet = NULL, int32_t minLength = 0) const override; + virtual int32_t getType() const override; + private: + UChar32 transform(UChar32 c) const; diff --git a/external/icu/icu4c-macosx.patch.1 b/external/icu/icu4c-macosx.patch.1 new file mode 100644 index 000000000..fee08eb05 --- /dev/null +++ b/external/icu/icu4c-macosx.patch.1 @@ -0,0 +1,20 @@ +diff -ur icu.org/source/common/putil.cpp icu/source/common/putil.cpp +--- icu.org/source/common/putil.cpp 2017-04-10 16:22:16.000000000 +0200 ++++ icu/source/common/putil.cpp 2017-04-21 22:14:09.940217733 +0200 +@@ -1198,8 +1198,16 @@ + static const time_t decemberSolstice=1198332540; /*2007-12-22 06:09 UT*/ + + /* This probing will tell us when daylight savings occurs. */ ++#if U_PLATFORM_IS_DARWIN_BASED ++ struct tm *tmp; ++ tmp = localtime(&juneSolstice); ++ juneSol = *tmp; ++ tmp = localtime(&decemberSolstice); ++ decemberSol = *tmp; ++#else + localtime_r(&juneSolstice, &juneSol); + localtime_r(&decemberSolstice, &decemberSol); ++#endif + if(decemberSol.tm_isdst > 0) { + daylightType = U_DAYLIGHT_DECEMBER; + } else if(juneSol.tm_isdst > 0) { diff --git a/external/icu/icu4c-mkdir.patch.1 b/external/icu/icu4c-mkdir.patch.1 new file mode 100644 index 000000000..0cdcf2b07 --- /dev/null +++ b/external/icu/icu4c-mkdir.patch.1 @@ -0,0 +1,17 @@ +diff -ur icu.org/source/data/Makefile.in icu/source/data/Makefile.in +--- icu.org/source/data/Makefile.in 2020-10-28 22:21:12.000000000 +0100 ++++ icu/source/data/Makefile.in 2020-11-17 10:18:37.960032668 +0100 +@@ -239,6 +239,13 @@ + + ifeq ($(ENABLE_SO_VERSION_DATA),1) + ifeq ($(PKGDATA_MODE),dll) ++ ++# This should be in the included rules.mk but that is generated empty by ++# configure because we have no data/locales/root.txt with prebuilt data/in/ ++$(TMP_DIR)/dirs.timestamp: ++ $(MKINSTALLDIRS) $(OUTTMPDIR) $(TMP_DIR) ++ echo timestamp > $@ ++ + SO_VERSION_DATA = $(OUTTMPDIR)/icudata.res + $(SO_VERSION_DATA) : $(MISCSRCDIR)/icudata.rc | $(TMP_DIR)/dirs.timestamp + ifeq ($(MSYS_RC_MODE),1) diff --git a/external/icu/icu4c-rpath.patch.1 b/external/icu/icu4c-rpath.patch.1 new file mode 100644 index 000000000..35a545778 --- /dev/null +++ b/external/icu/icu4c-rpath.patch.1 @@ -0,0 +1,36 @@ +diff -ur icu.org/source/config/mh-linux icu/source/config/mh-linux +--- icu.org/source/config/mh-linux 2016-06-15 20:58:17.000000000 +0200 ++++ icu/source/config/mh-linux 2017-04-21 22:38:18.893927819 +0200 +@@ -22,6 +22,10 @@ + LD_RPATH= -Wl,-zorigin,-rpath,'$$'ORIGIN + LD_RPATH_PRE = -Wl,-rpath, + ++## Force RPATH=$ORIGIN to locate own dependencies w/o need for LD_LIBRARY_PATH: ++ENABLE_RPATH=YES ++RPATHLDFLAGS=${LD_RPATH_PRE}'$$ORIGIN' ++ + ## These are the library specific LDFLAGS + LDFLAGSICUDT=-nodefaultlibs -nostdlib + +diff -ur icu.org/source/data/pkgdataMakefile.in icu/source/data/pkgdataMakefile.in +--- icu.org/source/data/pkgdataMakefile.in 2016-06-15 20:58:17.000000000 +0200 ++++ icu/source/data/pkgdataMakefile.in 2017-04-21 22:38:18.892927822 +0200 +@@ -18,6 +18,9 @@ + MIDDLE_SO_TARGET= + PKGDATA_TRAILING_SPACE=" " + ++# escape $ with \ when passing to echo; needed to preserve $ORIGIN ++SHLIB.c.shell := $(subst $$,\$$,$(SHLIB.c)) ++ + all : clean + @echo GENCCODE_ASSEMBLY_TYPE=$(GENCCODE_ASSEMBLY) >> $(OUTPUTFILE) + @echo SO=$(SO) >> $(OUTPUTFILE) +@@ -26,7 +29,7 @@ + @echo LIB_EXT_ORDER=$(FINAL_SO_TARGET) >> $(OUTPUTFILE) + @echo COMPILE="$(COMPILE.c)" >> $(OUTPUTFILE) + @echo LIBFLAGS="-I$(top_srcdir)/common -I$(top_builddir)/common $(SHAREDLIBCPPFLAGS) $(SHAREDLIBCFLAGS)" >> $(OUTPUTFILE) +- @echo GENLIB="$(SHLIB.c)" >> $(OUTPUTFILE) ++ @echo GENLIB="$(SHLIB.c.shell)" >> $(OUTPUTFILE) + @echo LDICUDTFLAGS=$(LDFLAGSICUDT) >> $(OUTPUTFILE) + @echo LD_SONAME=$(LD_SONAME) >> $(OUTPUTFILE) + @echo RPATH_FLAGS=$(RPATH_FLAGS) >> $(OUTPUTFILE) diff --git a/external/icu/icu4c-rtti.patch.1 b/external/icu/icu4c-rtti.patch.1 new file mode 100644 index 000000000..c058c7f3c --- /dev/null +++ b/external/icu/icu4c-rtti.patch.1 @@ -0,0 +1,12 @@ +diff -ur icu.org/source/config/mh-linux icu/source/config/mh-linux +--- icu.org/source/config/mh-linux 2017-04-21 23:01:23.257769703 +0200 ++++ icu/source/config/mh-linux 2017-04-21 23:03:23.166481552 +0200 +@@ -36,7 +36,7 @@ + #SH# LD_SONAME= + + ## Shared library options +-LD_SOOPTIONS= -Wl,-Bsymbolic ++LD_SOOPTIONS= -Wl,-Bsymbolic-functions + + ## Shared object suffix + SO = so diff --git a/external/icu/icu4c-scriptrun.patch.1 b/external/icu/icu4c-scriptrun.patch.1 new file mode 100644 index 000000000..f2f2cf9f3 --- /dev/null +++ b/external/icu/icu4c-scriptrun.patch.1 @@ -0,0 +1,60 @@ +diff -ur icu.org/source/extra/scrptrun/scrptrun.cpp icu/source/extra/scrptrun/scrptrun.cpp +--- icu.org/source/extra/scrptrun/scrptrun.cpp 2017-01-20 01:20:31.000000000 +0100 ++++ icu/source/extra/scrptrun/scrptrun.cpp 2017-04-21 22:59:31.708037770 +0200 +@@ -151,7 +151,11 @@ + // characters above it on the stack will be poped. + if (pairIndex >= 0) { + if ((pairIndex & 1) == 0) { +- parenStack[++parenSP].pairIndex = pairIndex; ++ ++parenSP; ++ int32_t nVecSize = parenStack.size(); ++ if (parenSP == nVecSize) ++ parenStack.resize(nVecSize + 128); ++ parenStack[parenSP].pairIndex = pairIndex; + parenStack[parenSP].scriptCode = scriptCode; + } else if (parenSP >= 0) { + int32_t pi = pairIndex & ~1; +@@ -185,7 +189,14 @@ + // pop it from the stack + if (pairIndex >= 0 && (pairIndex & 1) != 0 && parenSP >= 0) { + parenSP -= 1; +- startSP -= 1; ++ /* decrement startSP only if it is >= 0, ++ decrementing it unnecessarily will lead to memory corruption ++ while processing the above while block. ++ e.g. startSP = -4 , parenSP = -1 ++ */ ++ if (startSP >= 0) { ++ startSP -= 1; ++ } + } + } else { + // if the run broke on a surrogate pair, +diff -ur icu.org/source/extra/scrptrun/scrptrun.h icu/source/extra/scrptrun/scrptrun.h +--- icu.org/source/extra/scrptrun/scrptrun.h 2017-01-20 01:20:31.000000000 +0100 ++++ icu/source/extra/scrptrun/scrptrun.h 2017-04-21 22:59:31.708037770 +0200 +@@ -19,6 +19,7 @@ + #include "unicode/utypes.h" + #include "unicode/uobject.h" + #include "unicode/uscript.h" ++#include + + U_NAMESPACE_BEGIN + +@@ -81,7 +82,7 @@ + int32_t scriptEnd; + UScriptCode scriptCode; + +- ParenStackEntry parenStack[128]; ++ std::vector parenStack; + int32_t parenSP; + + static int8_t highBit(int32_t value); +@@ -135,6 +136,7 @@ + scriptEnd = charStart; + scriptCode = USCRIPT_INVALID_CODE; + parenSP = -1; ++ parenStack.resize(128); + } + + inline void ScriptRun::reset(int32_t start, int32_t length) diff --git a/external/icu/icu4c-solarisgcc.patch.1 b/external/icu/icu4c-solarisgcc.patch.1 new file mode 100644 index 000000000..6000ed0cb --- /dev/null +++ b/external/icu/icu4c-solarisgcc.patch.1 @@ -0,0 +1,12 @@ +diff -ur icu.org/source/common/uposixdefs.h icu/source/common/uposixdefs.h +--- icu.org/source/common/uposixdefs.h 2017-03-09 03:12:45.000000000 +0100 ++++ icu/source/common/uposixdefs.h 2017-04-21 22:23:11.857926971 +0200 +@@ -54,7 +54,7 @@ + * + * z/OS needs this definition for timeval and to get usleep. + */ +-#if !defined(_XOPEN_SOURCE_EXTENDED) && defined(__TOS_MVS__) ++#if !defined(_XOPEN_SOURCE_EXTENDED) && (defined(__TOS_MVS__) || defined(__IBMC__) || defined(__IBMCPP__)) + # define _XOPEN_SOURCE_EXTENDED 1 + #endif + diff --git a/external/icu/icu4c-ubsan.patch.1 b/external/icu/icu4c-ubsan.patch.1 new file mode 100644 index 000000000..7b0c2efc9 --- /dev/null +++ b/external/icu/icu4c-ubsan.patch.1 @@ -0,0 +1,14 @@ +diff -ur icu.org/source/common/ubidiimp.h icu/source/common/ubidiimp.h +--- icu.org/source/common/ubidiimp.h 2019-10-03 13:16:41.000000000 +0200 ++++ icu/source/common/ubidiimp.h 2019-10-28 19:08:13.533284618 +0100 +@@ -198,8 +198,8 @@ + /* in a Run, logicalStart will get this bit set if the run level is odd */ + #define INDEX_ODD_BIT (1UL<<31) + +-#define MAKE_INDEX_ODD_PAIR(index, level) ((index)|((int32_t)((level)&1)<<31)) +-#define ADD_ODD_BIT_FROM_LEVEL(x, level) ((x)|=((int32_t)((level)&1)<<31)) ++#define MAKE_INDEX_ODD_PAIR(index, level) ((index)|((uint32_t)((level)&1)<<31)) ++#define ADD_ODD_BIT_FROM_LEVEL(x, level) ((x)|=((uint32_t)((level)&1)<<31)) + #define REMOVE_ODD_BIT(x) ((x)&=~INDEX_ODD_BIT) + + #define GET_INDEX(x) ((x)&~INDEX_ODD_BIT) diff --git a/external/icu/icu4c-use-pkgdata-single-ccode-file-mode.patch.1 b/external/icu/icu4c-use-pkgdata-single-ccode-file-mode.patch.1 new file mode 100644 index 000000000..237e554b8 --- /dev/null +++ b/external/icu/icu4c-use-pkgdata-single-ccode-file-mode.patch.1 @@ -0,0 +1,12 @@ +--- icu/source/tools/toolutil/pkg_genc.h.orig 2022-01-11 06:02:29.694678787 +0100 ++++ icu/source/tools/toolutil/pkg_genc.h 2022-01-11 06:02:41.602640965 +0100 +@@ -48,9 +48,7 @@ + * the data to generate the final data library. This can + * increase the performance of the pkdata tool. + */ +-#if U_PLATFORM == U_PF_OS400 + #define USE_SINGLE_CCODE_FILE +-#endif + + /* Need to fix the file seperator character when using MinGW. */ + #if defined(WINDOWS_WITH_GNUC) || defined(USING_CYGWIN) diff --git a/external/icu/icu4c-warnings.patch.1 b/external/icu/icu4c-warnings.patch.1 new file mode 100644 index 000000000..d8df0e14e --- /dev/null +++ b/external/icu/icu4c-warnings.patch.1 @@ -0,0 +1,11 @@ +diff -ur icu.org/source/common/unicode/utf16.h icu/source/common/unicode/utf16.h +--- icu.org/source/common/unicode/utf16.h 2020-10-28 22:21:12.000000000 +0100 ++++ icu/source/common/unicode/utf16.h 2020-11-16 19:31:03.356478154 +0100 +@@ -398,6 +398,7 @@ + (s)[(i)++]=(uint16_t)(((c)&0x3ff)|0xdc00); \ + } else /* c>0x10ffff or not enough space */ { \ + (isError)=true; \ ++ (void)(isError); \ + } \ + } UPRV_BLOCK_MACRO_END + diff --git a/external/icu/icu4c-windows-cygwin-cross.patch.1 b/external/icu/icu4c-windows-cygwin-cross.patch.1 new file mode 100644 index 000000000..dd6b47c17 --- /dev/null +++ b/external/icu/icu4c-windows-cygwin-cross.patch.1 @@ -0,0 +1,131 @@ +diff -ur icu.org/source/acinclude.m4 icu/source/acinclude.m4 +--- icu.org/source/acinclude.m4 2020-04-10 16:22:16.000000000 +0200 ++++ icu/source/acinclude.m4 2020-04-21 22:14:09.940217733 +0200 +@@ -52,6 +52,12 @@ + else + icu_cv_host_frag=mh-cygwin-msvc + fi ;; ++aarch64-*-cygwin) ++ if test "$GCC" = yes; then ++ icu_cv_host_frag=mh-cygwin64 ++ else ++ icu_cv_host_frag=mh-cygwin-msvc ++ fi ;; + *-*-mingw*) + if test "$GCC" = yes; then + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +--- icu/source/configure.ac.orig 2020-04-22 22:04:20.000000000 +0200 ++++ icu/source/configure.ac 2020-10-01 09:39:05.570900400 +0200 +@@ -213,23 +213,33 @@ + [cross_buildroot="${withval}"], + [cross_buildroot=""]) + ++cross_mixed_buildroot="$cross_buildroot" ++cross_unix_buildroot="$cross_buildroot" + if test "X$cross_buildroot" = "X"; then + if test "$cross_compiling" = "yes"; then + AC_MSG_ERROR([Error! Cross compiling but no --with-cross-build option specified - please supply the path to an executable ICU's build root]) + dnl ' + fi + else +- if test -f "${cross_buildroot}/config/icucross.mk"; then ++ case "${host}" in ++ *-*-cygwin*) ++ #M# -m isn't used because it doesn't work on Win98 ++ cross_mixed_buildroot=$(cygpath -ad "$cross_buildroot" | tr '\\' '/') ++ cross_unix_buildroot=$(cygpath -au "$cross_buildroot") ++ ;; ++ esac ++ if test -f "${cross_mixed_buildroot}/config/icucross.mk"; then + AC_MSG_RESULT([Using cross buildroot: $cross_buildroot]) + else +- if test -d "${cross_buildroot}"; then +- AC_MSG_ERROR([${cross_buildroot}/config/icucross.mk not found. Please build ICU in ${cross_buildroot} first.]) ++ if test -d "${cross_mixed_buildroot}"; then ++ AC_MSG_ERROR([${cross_mixed_buildroot}/config/icucross.mk not found. Please build ICU in ${cross_mixed_buildroot} first.]) + else +- AC_MSG_ERROR([No such directory ${cross_buildroot} supplied as the argument to --with-cross-build. Use an absolute path.]) ++ AC_MSG_ERROR([No such directory ${cross_mixed_buildroot} supplied as the argument to --with-cross-build. Use an absolute path.]) + fi + fi + fi +-AC_SUBST(cross_buildroot) ++AC_SUBST(cross_mixed_buildroot) ++AC_SUBST(cross_unix_buildroot) + + # Check for doxygen to generate documentation + AC_PATH_PROG(DOXYGEN,doxygen,,$PATH:/usr/local/bin:/usr/bin) +--- icu/source/test/testdata/Makefile.in.orig 2020-10-01 09:37:25.847888900 +0200 ++++ icu/source/test/testdata/Makefile.in 2020-10-01 09:36:41.859996500 +0200 +@@ -82,7 +82,7 @@ + # relative lib links from pkgdata are the same as for tmp + GENRBOPTS=-k + # use the cross root, in case we are cross compiling. Otherwise it is equal to top_builddir +-TOOLDIR=$(cross_buildroot)/tools ++TOOLDIR=$(cross_mixed_buildroot)/tools + SRCDATADIR=$(top_srcdir)/data + UNICODEDATADIR=$(SRCDATADIR)/unidata + OUTDIR=$(top_builddir)/data/out +--- icu/source/Makefile.in.orig 2020-04-22 22:04:20.000000000 +0200 ++++ icu/source/Makefile.in 2020-10-01 09:29:36.642364000 +0200 +@@ -255,16 +255,16 @@ + @(echo "CROSS_ICU_VERSION=$(VERSION)" ;\ + echo "TOOLEXEEXT=$(EXEEXT)" \ + ) > $@ +- @(echo 'TOOLBINDIR=$$(cross_buildroot)/bin' ;\ +- echo 'TOOLLIBDIR=$$(cross_buildroot)/lib' ;\ +- echo "INVOKE=$(LDLIBRARYPATH_ENVVAR)=$(LIBRARY_PATH_PREFIX)"'$$(TOOLLIBDIR):$$(cross_buildroot)/stubdata:$$(cross_buildroot)/tools/ctestfw:$$$$'"$(LDLIBRARYPATH_ENVVAR)" ;\ +- echo "PKGDATA_INVOKE=$(LDLIBRARYPATH_ENVVAR)=$(LIBRARY_PATH_PREFIX)"'$$(cross_buildroot)/stubdata:$$(cross_buildroot)/tools/ctestfw:$$(TOOLLIBDIR):$$$$'"$(LDLIBRARYPATH_ENVVAR) " ;\ ++ @(echo 'TOOLBINDIR=$$(cross_mixed_buildroot)/bin' ;\ ++ echo 'TOOLLIBDIR=$$(cross_mixed_buildroot)/lib' ;\ ++ echo "INVOKE=$(LDLIBRARYPATH_ENVVAR)=$(LIBRARY_PATH_PREFIX)"'$$(cross_unix_buildroot)/lib:$$(cross_unix_buildroot)/stubdata:$$(cross_unix_buildroot)/tools/ctestfw:$$$$'"$(LDLIBRARYPATH_ENVVAR)" ;\ ++ echo "PKGDATA_INVOKE=$(LDLIBRARYPATH_ENVVAR)=$(LIBRARY_PATH_PREFIX)"'$$(cross_unix_buildroot)/stubdata:$$(cross_unix_buildroot)/tools/ctestfw:$$(cross_unix_buildroot)/lib:$$$$'"$(LDLIBRARYPATH_ENVVAR) " ;\ + echo ) >> $@ + + config/icucross.inc: $(top_builddir)/icudefs.mk $(top_builddir)/Makefile @platform_make_fragment@ + @echo rebuilding $@ +- @(grep '^CURR_FULL_DIR' $(top_builddir)/icudefs.mk ; \ +- grep '^CURR_FULL_DIR' @platform_make_fragment@ || echo ""; \ ++ @(grep '^CURR_FULL_DIR' @platform_make_fragment@ || echo ""; \ ++ grep '^CURR_FULL_DIR' $(top_builddir)/icudefs.mk ; \ + ) > $@ + + config/icu.pc: $(srcdir)/config/icu.pc.in +--- icu/source/icudefs.mk.in.orig 2020-04-22 22:04:20.000000000 +0200 ++++ icu/source/icudefs.mk.in 2020-10-01 09:35:54.418128800 +0200 +@@ -35,7 +35,8 @@ + sysconfdir = @sysconfdir@ + # controls the include of $(top_builddir)/icucross.mk at bottom of file + cross_compiling = @cross_compiling@ +-cross_buildroot = @cross_buildroot@ ++cross_mixed_buildroot = @cross_mixed_buildroot@ ++cross_unix_buildroot = @cross_unix_buildroot@ + + # Package information + +@@ -303,8 +304,8 @@ + INSTALLED_INVOKE = $(LDLIBRARYPATH_ENVVAR)=$(libdir):$$$(LDLIBRARYPATH_ENVVAR) + + # Current full path directory for cross compilation +-ifneq ($(strip $(cross_buildroot)),) +-include $(cross_buildroot)/config/icucross.inc ++ifneq ($(strip $(cross_mixed_buildroot)),) ++include $(cross_mixed_buildroot)/config/icucross.inc + endif + + # Platform-specific setup +@@ -323,10 +324,11 @@ + + # some imported things from the cross env + TOOLEXEEXT = $(EXEEXT) +-ifneq ($(strip $(cross_buildroot)),) +-include $(cross_buildroot)/config/icucross.mk ++ifneq ($(strip $(cross_mixed_buildroot)),) ++include $(cross_mixed_buildroot)/config/icucross.mk + else +-cross_buildroot = $(top_builddir) ++cross_mixed_buildroot = $(top_builddir) ++cross_unix_buildroot = $(top_builddir) + endif + + # for tests diff --git a/external/icu/khmerdict.dict b/external/icu/khmerdict.dict new file mode 100644 index 000000000..52605b654 Binary files /dev/null and b/external/icu/khmerdict.dict differ diff --git a/external/icu/strict_ansi.patch b/external/icu/strict_ansi.patch new file mode 100644 index 000000000..972f0e65b --- /dev/null +++ b/external/icu/strict_ansi.patch @@ -0,0 +1,15 @@ +--- source/io/ufile.cpp ++++ source/io/ufile.cpp +@@ -21,12 +21,6 @@ + */ + + #include "unicode/platform.h" +-#if defined(__GNUC__) && !defined(__clang__) && defined(__STRICT_ANSI__) +-// g++, fileno isn't defined if __STRICT_ANSI__ is defined. +-// clang fails to compile the header unless __STRICT_ANSI__ is defined. +-// __GNUC__ is set by both gcc and clang. +-#undef __STRICT_ANSI__ +-#endif + + #include "locmap.h" + #include "unicode/ustdio.h" diff --git a/external/icu/ubsan.patch.1 b/external/icu/ubsan.patch.1 new file mode 100644 index 000000000..1c10f8cef --- /dev/null +++ b/external/icu/ubsan.patch.1 @@ -0,0 +1,31 @@ +diff -ur icu.org/source/tools/genrb/rbutil.c icu/source/tools/genrb/rbutil.c +--- icu.org/source/tools/genrb/rbutil.c 2020-10-28 22:21:12.000000000 +0100 ++++ icu/source/tools/genrb/rbutil.c 2020-11-16 19:50:44.005119253 +0100 +@@ -30,7 +30,12 @@ + get_dirname(char *dirname, + const char *filename) + { +- const char *lastSlash = uprv_strrchr(filename, U_FILE_SEP_CHAR) + 1; ++ const char *lastSlash = uprv_strrchr(filename, U_FILE_SEP_CHAR); ++ if(lastSlash == NULL) { ++ lastSlash = filename; ++ } else { ++ ++lastSlash; ++ } + + if(lastSlash>filename) { + uprv_strncpy(dirname, filename, (lastSlash - filename)); +@@ -46,7 +51,12 @@ + const char *filename) + { + /* strip off any leading directory portions */ +- const char *lastSlash = uprv_strrchr(filename, U_FILE_SEP_CHAR) + 1; ++ const char *lastSlash = uprv_strrchr(filename, U_FILE_SEP_CHAR); ++ if(lastSlash == NULL) { ++ lastSlash = filename; ++ } else { ++ ++lastSlash; ++ } + char *lastDot; + + if(lastSlash>filename) { -- cgit v1.2.3