~ruther/guix-local

fee676c7285c00128d5cebfd64bab761314146bf — Ian Eure a month ago 1c1c9e1
gnu: Add icu4c-78.

* gnu/packages/icu4c.scm (icu4c-78): New variable.
* gnu/local.mk (dist_patch_DATA): Add icu4c patches.
* gnu/packages/patches/icu4c-78-double-conversion.patch: New file.
* gnu/packages/patches/icu4c-bug-1706949-wasi-workaround.patch: New file.
* gnu/packages/patches/icu4c-bug-1790071-ICU-22132-standardize-vtzone-output.patch: New file.
* gnu/packages/patches/icu4c-bug-1856290-ICU-20548-dateinterval-timezone.patch: New file.
* gnu/packages/patches/icu4c-bug-1954138-dtitvfmt-adopt-calendar.patch: New file.
* gnu/packages/patches/icu4c-bug-1972781-chinese-based-calendar.patch: New file.
* gnu/packages/patches/icu4c-bug-2000225-ICU-23264-increase-measure-unit-capacity.patch: New file.
* gnu/packages/patches/icu4c-bug-2002735-ICU-23277-coptic-single-era.patch: New file.
* gnu/packages/patches/icu4c-suppress-warnings.patch: New file.
M gnu/local.mk => gnu/local.mk +17 -8
@@ 1610,15 1610,24 @@ dist_patch_DATA =						\
  %D%/packages/patches/icedove-observer-fix.patch               \
  %D%/packages/patches/icedtea-7-hotspot-aarch64-use-c++98.patch	\
  %D%/packages/patches/icedtea-7-hotspot-pointer-comparison.patch	\
  %D%/packages/patches/icu4c-icu-22132-fix-vtimezone.patch	\
  %D%/packages/patches/icu4c-20548-dateinterval-timezone.patch  \
  %D%/packages/patches/icu4c-22132-standardize-vtzone-output.patch      \
  %D%/packages/patches/icu4c-23069-rosh-hashanah-postponement.patch     \
  %D%/packages/patches/icu4c-dayperiod-fractional-seconds.patch \
  %D%/packages/patches/icu4c-double-conversion.patch            \
  %D%/packages/patches/icu4c-dtitvfmt-adopt-calendar.patch      \
  %D%/packages/patches/icu4c-20548-dateinterval-timezone.patch	\
  %D%/packages/patches/icu4c-22132-standardize-vtzone-output.patch	\
  %D%/packages/patches/icu4c-23069-rosh-hashanah-postponement.patch	\
  %D%/packages/patches/icu4c-78-double-conversion.patch		\
  %D%/packages/patches/icu4c-bug-1706949-wasi-workaround.patch	\
  %D%/packages/patches/icu4c-bug-1790071-ICU-22132-standardize-vtzone-output.patch	\
  %D%/packages/patches/icu4c-bug-1856290-ICU-20548-dateinterval-timezone.patch	\
  %D%/packages/patches/icu4c-bug-1954138-dtitvfmt-adopt-calendar.patch	\
  %D%/packages/patches/icu4c-bug-1972781-chinese-based-calendar.patch	\
  %D%/packages/patches/icu4c-bug-2000225-ICU-23264-increase-measure-unit-capacity.patch	\
  %D%/packages/patches/icu4c-bug-2002735-ICU-23277-coptic-single-era.patch	\
  %D%/packages/patches/icu4c-dayperiod-fractional-seconds.patch	\
  %D%/packages/patches/icu4c-double-conversion.patch		\
  %D%/packages/patches/icu4c-dtitvfmt-adopt-calendar.patch	\
  %D%/packages/patches/icu4c-fix-TestHebrewCalendarInTemporalLeapYear.patch	\
  %D%/packages/patches/icu4c-wasi-workaround.patch              \
  %D%/packages/patches/icu4c-icu-22132-fix-vtimezone.patch	\
  %D%/packages/patches/icu4c-suppress-warnings.patch		\
  %D%/packages/patches/icu4c-wasi-workaround.patch		\
  %D%/packages/patches/id3lib-CVE-2007-4460.patch			\
  %D%/packages/patches/id3lib-UTF16-writing-bug.patch			\
  %D%/packages/patches/ilmbase-fix-tests.patch			\

M gnu/packages/icu4c.scm => gnu/packages/icu4c.scm +27 -0
@@ 52,6 52,12 @@
   (string-map (lambda (x) (if (char=? x #\.) #\_ x)) version)
   "-src.tgz"))

;; The URI format has changed starting with 78.1.
(define (icu4c-uri->=78 version)
  (string-append
   "https://github.com/unicode-org/icu/releases/download/release-"
   version "/icu4c-" version "-sources.tgz"))

(define-public icu4c-73
  (package
    (name "icu4c")


@@ 201,6 207,27 @@ C/C++ part.")
         "icu4c-dtitvfmt-adopt-calendar.patch"
         "icu4c-wasi-workaround.patch"))))))

(define-public icu4c-78
  (package
    (inherit icu4c-77)
    (name "icu4c")
    (version "78.2")
    (source
     (origin
       (inherit (package-source icu4c-77))
       (uri (icu4c-uri->=78 version))
       (sha256 (base32 "0dfzi4yf0wmng1866y2yd22cj1lrnzmx5qihjqh4npa3bixni69y"))
       (patches
        (search-patches
         "icu4c-bug-1706949-wasi-workaround.patch"
         "icu4c-bug-1790071-ICU-22132-standardize-vtzone-output.patch"
         "icu4c-bug-1856290-ICU-20548-dateinterval-timezone.patch"
         "icu4c-bug-1954138-dtitvfmt-adopt-calendar.patch"
         "icu4c-bug-1972781-chinese-based-calendar.patch"
         "icu4c-bug-2000225-ICU-23264-increase-measure-unit-capacity.patch"
         "icu4c-78-double-conversion.patch"
         "icu4c-suppress-warnings.patch"))))))

(define-public icu4c-build-root
  (package
    (inherit icu4c)

A gnu/packages/patches/icu4c-78-double-conversion.patch => gnu/packages/patches/icu4c-78-double-conversion.patch +198 -0
@@ 0,0 1,198 @@
diff --git a/source/i18n/measunit_extra.cpp b/source/i18n/measunit_extra.cpp
--- a/source/i18n/measunit_extra.cpp
+++ b/source/i18n/measunit_extra.cpp
@@ -10,17 +10,21 @@
 
 // Allow implicit conversion from char16_t* to UnicodeString for this file:
 // Helpful in toString methods and elsewhere.
 #define UNISTR_FROM_STRING_EXPLICIT
 
 #include "charstr.h"
 #include "cmemory.h"
 #include "cstring.h"
+#ifdef JS_HAS_INTL_API
+#include "double-conversion/string-to-double.h"
+#else
 #include "double-conversion-string-to-double.h"
+#endif
 #include "measunit_impl.h"
 #include "resource.h"
 #include "uarrsort.h"
 #include "uassert.h"
 #include "ucln_in.h"
 #include "umutex.h"
 #include "unicode/bytestrie.h"
 #include "unicode/bytestriebuilder.h"
@@ -33,17 +37,21 @@
 #include "util.h"
 #include <limits.h>
 #include <cstdlib>
 U_NAMESPACE_BEGIN
 
 
 namespace {
 
+#ifdef JS_HAS_INTL_API
+using double_conversion::StringToDoubleConverter;
+#else
 using icu::double_conversion::StringToDoubleConverter;
+#endif
 
 // TODO: Propose a new error code for this?
 constexpr UErrorCode kUnitIdentifierSyntaxError = U_ILLEGAL_ARGUMENT_ERROR;
 
 // Trie value offset for SI or binary prefixes. This is big enough to ensure we only
 // insert positive integers into the trie.
 constexpr int32_t kPrefixOffset = 64;
 static_assert(kPrefixOffset + UMEASURE_PREFIX_INTERNAL_MIN_BIN > 0,
diff --git a/source/i18n/number_decimalquantity.cpp b/source/i18n/number_decimalquantity.cpp
--- a/source/i18n/number_decimalquantity.cpp
+++ b/source/i18n/number_decimalquantity.cpp
@@ -11,28 +11,37 @@
 #include <stdlib.h>
 
 #include "unicode/plurrule.h"
 #include "cmemory.h"
 #include "number_decnum.h"
 #include "putilimp.h"
 #include "number_decimalquantity.h"
 #include "number_roundingutils.h"
+#ifdef JS_HAS_INTL_API
+#include "double-conversion/double-conversion.h"
+#else
 #include "double-conversion.h"
+#endif
 #include "charstr.h"
 #include "number_utils.h"
 #include "uassert.h"
 #include "util.h"
 
 using namespace icu;
 using namespace icu::number;
 using namespace icu::number::impl;
 
+#ifdef JS_HAS_INTL_API
+using double_conversion::DoubleToStringConverter;
+using double_conversion::StringToDoubleConverter;
+#else
 using icu::double_conversion::DoubleToStringConverter;
 using icu::double_conversion::StringToDoubleConverter;
+#endif
 
 namespace {
 
 int8_t NEGATIVE_FLAG = 1;
 int8_t INFINITY_FLAG = 2;
 int8_t NAN_FLAG = 4;
 
 /** Helper function for safe subtraction (no overflow). */
diff --git a/source/i18n/number_rounding.cpp b/source/i18n/number_rounding.cpp
--- a/source/i18n/number_rounding.cpp
+++ b/source/i18n/number_rounding.cpp
@@ -5,17 +5,21 @@
 
 #if !UCONFIG_NO_FORMATTING
 
 #include "charstr.h"
 #include "uassert.h"
 #include "unicode/numberformatter.h"
 #include "number_types.h"
 #include "number_decimalquantity.h"
+#ifdef JS_HAS_INTL_API
+#include "double-conversion/double-conversion.h"
+#else
 #include "double-conversion.h"
+#endif
 #include "number_roundingutils.h"
 #include "number_skeletons.h"
 #include "number_decnum.h"
 #include "putilimp.h"
 #include "string_segment.h"
 
 using namespace icu;
 using namespace icu::number;
diff --git a/source/i18n/number_utils.cpp b/source/i18n/number_utils.cpp
--- a/source/i18n/number_utils.cpp
+++ b/source/i18n/number_utils.cpp
@@ -12,26 +12,34 @@
 #include <stdlib.h>
 #include <cmath>
 #include "number_decnum.h"
 #include "number_types.h"
 #include "number_utils.h"
 #include "charstr.h"
 #include "decContext.h"
 #include "decNumber.h"
+#ifdef JS_HAS_INTL_API
+#include "double-conversion/double-conversion.h"
+#else
 #include "double-conversion.h"
+#endif
 #include "fphdlimp.h"
 #include "uresimp.h"
 #include "ureslocs.h"
 
 using namespace icu;
 using namespace icu::number;
 using namespace icu::number::impl;
 
+#ifdef JS_HAS_INTL_API
+using double_conversion::DoubleToStringConverter;
+#else
 using icu::double_conversion::DoubleToStringConverter;
+#endif
 
 
 namespace {
 
 const char16_t*
 doGetPattern(UResourceBundle* res, const char* nsName, const char* patternKey, UErrorCode& publicStatus,
              UErrorCode& localStatus) {
     // Construct the path into the resource bundle
diff --git a/source/i18n/units_converter.cpp b/source/i18n/units_converter.cpp
--- a/source/i18n/units_converter.cpp
+++ b/source/i18n/units_converter.cpp
@@ -3,17 +3,21 @@
 
 #include "unicode/utypes.h"
 
 #if !UCONFIG_NO_FORMATTING
 
 #include "charstr.h"
 #include "cmemory.h"
 #include "cstring.h"
+#ifdef JS_HAS_INTL_API
+#include "double-conversion/string-to-double.h"
+#else
 #include "double-conversion-string-to-double.h"
+#endif
 #include "measunit_impl.h"
 #include "putilimp.h"
 #include "uassert.h"
 #include "unicode/errorcode.h"
 #include "unicode/localpointer.h"
 #include "unicode/stringpiece.h"
 #include "units_converter.h"
 #include <algorithm>
@@ -105,17 +109,21 @@ void Factor::substituteConstants() {
         this->constantExponents[i] = 0;
     }
 }
 
 namespace {
 
 /* Helpers */
 
+#ifdef JS_HAS_INTL_API
+using double_conversion::StringToDoubleConverter;
+#else
 using icu::double_conversion::StringToDoubleConverter;
+#endif
 
 // TODO: Make this a shared-utility function.
 // Returns `double` from a scientific number(i.e. "1", "2.01" or "3.09E+4")
 double strToDouble(StringPiece strNum, UErrorCode &status) {
     // We are processing well-formed input, so we don't need any special options to
     // StringToDoubleConverter.
     StringToDoubleConverter converter(0, 0, 0, "", "");
     int32_t count;

A gnu/packages/patches/icu4c-bug-1706949-wasi-workaround.patch => gnu/packages/patches/icu4c-bug-1706949-wasi-workaround.patch +832 -0
@@ 0,0 1,832 @@
# Handle WASI lack of support for <thread> and <atomic>.
#
# WASI issue: https://github.com/WebAssembly/wasi-sdk/issues/180

diff --git a/source/common/putilimp.h b/source/common/putilimp.h
--- a/source/common/putilimp.h
+++ b/source/common/putilimp.h
@@ -105,10 +105,12 @@ typedef size_t uintptr_t;
 #endif
 #elif U_PLATFORM == U_PF_OS400
    /* not defined */
 #elif U_PLATFORM == U_PF_HAIKU
    /* not defined */
+#elif defined(__wasi__)
+   /* not defined */
 #else
 #   define U_TZSET tzset
 #endif
 
 #if defined(U_TIMEZONE) || defined(U_HAVE_TIMEZONE)
@@ -130,10 +132,12 @@ typedef size_t uintptr_t;
    /* not defined */
 #elif U_PLATFORM == U_PF_OS400
    /* not defined */
 #elif U_PLATFORM == U_PF_IPHONE
    /* not defined */
+#elif defined(__wasi__)
+   /* not defined */
 #else
 #   define U_TIMEZONE timezone
 #endif
 
 #if defined(U_TZNAME) || defined(U_HAVE_TZNAME)
@@ -145,10 +149,12 @@ typedef size_t uintptr_t;
 #endif
 #elif U_PLATFORM == U_PF_OS400
    /* not defined */
 #elif U_PLATFORM == U_PF_HAIKU
     /* not defined, (well it is but a loop back to icu) */
+#elif defined(__wasi__)
+   /* not defined */
 #else
 #   define U_TZNAME tzname
 #endif
 
 #ifdef U_HAVE_MMAP
diff --git a/source/common/umapfile.h b/source/common/umapfile.h
--- a/source/common/umapfile.h
+++ b/source/common/umapfile.h
@@ -38,10 +38,12 @@ U_CFUNC void  uprv_unmapFile(UDataMemory
 #define MAP_POSIX       2
 #define MAP_STDIO       3
 
 #if UCONFIG_NO_FILE_IO
 #   define MAP_IMPLEMENTATION MAP_NONE
+#elif defined(__wasi__)
+#   define MAP_IMPLEMENTATION MAP_STDIO
 #elif U_PLATFORM_USES_ONLY_WIN32_API
 #   define MAP_IMPLEMENTATION MAP_WIN32
 #elif U_HAVE_MMAP || U_PLATFORM == U_PF_OS390
 #   define MAP_IMPLEMENTATION MAP_POSIX
 #else /* unknown platform, no memory map implementation: use stdio.h and uprv_malloc() instead */
diff --git a/source/common/umutex.cpp b/source/common/umutex.cpp
--- a/source/common/umutex.cpp
+++ b/source/common/umutex.cpp
@@ -41,10 +41,11 @@ U_NAMESPACE_BEGIN
  *
  *  ICU Mutex wrappers.
  *
  *************************************************************************************************/
 
+#ifndef __wasi__
 namespace {
 std::mutex *initMutex;
 std::condition_variable *initCondition;
 
 // The ICU global mutex.
@@ -53,32 +54,38 @@ UMutex globalMutex;
 
 std::once_flag initFlag;
 std::once_flag *pInitFlag = &initFlag;
 
 }  // Anonymous namespace
+#endif
 
 U_CDECL_BEGIN
 static UBool U_CALLCONV umtx_cleanup() {
+#ifndef __wasi__
     initMutex->~mutex();
     initCondition->~condition_variable();
     UMutex::cleanup();
 
     // Reset the once_flag, by destructing it and creating a fresh one in its place.
     // Do not use this trick anywhere else in ICU; use umtx_initOnce, not std::call_once().
     pInitFlag->~once_flag();
     pInitFlag = new(&initFlag) std::once_flag();
+#endif
     return true;
 }
 
 static void U_CALLCONV umtx_init() {
+#ifndef __wasi__
     initMutex = STATIC_NEW(std::mutex);
     initCondition = STATIC_NEW(std::condition_variable);
     ucln_common_registerCleanup(UCLN_COMMON_MUTEX, umtx_cleanup);
+#endif
 }
 U_CDECL_END
 
 
+#ifndef __wasi__
 std::mutex *UMutex::getMutex() {
     std::mutex *retPtr = fMutex.load(std::memory_order_acquire);
     if (retPtr == nullptr) {
         std::call_once(*pInitFlag, umtx_init);
         std::lock_guard<std::mutex> guard(*initMutex);
@@ -91,41 +98,48 @@ std::mutex *UMutex::getMutex() {
         }
     }
     U_ASSERT(retPtr != nullptr);
     return retPtr;
 }
+#endif
 
 UMutex *UMutex::gListHead = nullptr;
 
 void UMutex::cleanup() {
     UMutex *next = nullptr;
     for (UMutex *m = gListHead; m != nullptr; m = next) {
+#ifndef __wasi__
         (*m->fMutex).~mutex();
         m->fMutex = nullptr;
+#endif
         next = m->fListLink;
         m->fListLink = nullptr;
     }
     gListHead = nullptr;
 }
 
 
 U_CAPI void  U_EXPORT2
 umtx_lock(UMutex *mutex) {
+#ifndef __wasi__
     if (mutex == nullptr) {
         mutex = &globalMutex;
     }
     mutex->lock();
+#endif
 }
 
 
 U_CAPI void  U_EXPORT2
 umtx_unlock(UMutex* mutex)
 {
+#ifndef __wasi__
     if (mutex == nullptr) {
         mutex = &globalMutex;
     }
     mutex->unlock();
+#endif
 }
 
 
 /*************************************************************************************************
  *
@@ -141,22 +155,26 @@ umtx_unlock(UMutex* mutex)
 //   that knows the C++ types involved. This function returns true if
 //   the caller needs to call the Init function.
 //
 U_COMMON_API UBool U_EXPORT2
 umtx_initImplPreInit(UInitOnce &uio) {
+#ifndef __wasi__
     std::call_once(*pInitFlag, umtx_init);
     std::unique_lock<std::mutex> lock(*initMutex);
+#endif
     if (umtx_loadAcquire(uio.fState) == 0) {
         umtx_storeRelease(uio.fState, 1);
         return true;      // Caller will next call the init function.
     } else {
+#ifndef __wasi__
         while (umtx_loadAcquire(uio.fState) == 1) {
             // Another thread is currently running the initialization.
             // Wait until it completes.
             initCondition->wait(lock);
         }
         U_ASSERT(uio.fState == 2);
+#endif
         return false;
     }
 }
 
 
@@ -166,15 +184,17 @@ umtx_initImplPreInit(UInitOnce &uio) {
 //   Some threads may be racing to test the fState variable outside of the mutex,
 //   requiring the use of store/release when changing its value.
 
 U_COMMON_API void U_EXPORT2
 umtx_initImplPostInit(UInitOnce &uio) {
+#ifndef __wasi__
     {
         std::unique_lock<std::mutex> lock(*initMutex);
         umtx_storeRelease(uio.fState, 2);
     }
     initCondition->notify_all();
+#endif
 }
 
 U_NAMESPACE_END
 
 /*************************************************************************************************
diff --git a/source/common/umutex.h b/source/common/umutex.h
--- a/source/common/umutex.h
+++ b/source/common/umutex.h
@@ -18,13 +18,16 @@
 */
 
 #ifndef UMUTEX_H
 #define UMUTEX_H
 
+#ifndef __wasi__
 #include <atomic>
 #include <condition_variable>
 #include <mutex>
+#endif
+
 #include <type_traits>
 
 #include "unicode/utypes.h"
 #include "unicode/uclean.h"
 #include "unicode/uobject.h"
@@ -43,10 +46,12 @@ U_NAMESPACE_BEGIN
  *
  *   Low Level Atomic Operations, ICU wrappers for.
  *
  ****************************************************************************/
 
+#ifndef __wasi__
+
 typedef std::atomic<int32_t> u_atomic_int32_t;
 
 inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
     return var.load(std::memory_order_acquire);
 }
@@ -61,10 +66,31 @@ inline int32_t umtx_atomic_inc(u_atomic_
 
 inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
     return var->fetch_sub(1) - 1;
 }
 
+#else
+
+typedef int32_t u_atomic_int32_t;
+
+inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
+    return var;
+}
+
+inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
+    var = val;
+}
+
+inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) {
+    return ++(*var);
+}
+
+inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
+    return --(*var);
+}
+
+#endif
 
 /*************************************************************************************************
  *
  *  UInitOnce Definitions.
  *
@@ -211,21 +237,29 @@ public:
     U_COMMON_API UMutex& operator=(const UMutex& other) = delete;
     U_COMMON_API void* operator new(size_t) = delete;
 
     // requirements for C++ BasicLockable, allows UMutex to work with std::lock_guard
     U_COMMON_API void lock() {
+#ifndef __wasi__
         std::mutex *m = fMutex.load(std::memory_order_acquire);
         if (m == nullptr) { m = getMutex(); }
         m->lock();
+#endif
     }
-    U_COMMON_API void unlock() { fMutex.load(std::memory_order_relaxed)->unlock(); }
+    U_COMMON_API void unlock() {
+#ifndef __wasi__
+        fMutex.load(std::memory_order_relaxed)->unlock();
+#endif
+    }
 
     U_COMMON_API static void cleanup();
 
 private:
+#ifndef __wasi__
     alignas(std::mutex) char fStorage[sizeof(std::mutex)] {};
     std::atomic<std::mutex *> fMutex { nullptr };
+#endif
 
     /** All initialized UMutexes are kept in a linked list, so that they can be found,
      * and the underlying std::mutex destructed, by u_cleanup().
      */
     UMutex *fListLink { nullptr };
@@ -233,11 +267,13 @@ private:
 
     /** Out-of-line function to lazily initialize a UMutex on first use.
      * Initial fast check is inline, in lock().  The returned value may never
      * be nullptr.
      */
+#ifndef __wasi__
     std::mutex *getMutex();
+#endif
 };
 
 
 /* Lock a mutex.
  * @param mutex The given mutex to be locked.  Pass NULL to specify
diff --git a/source/common/unifiedcache.cpp b/source/common/unifiedcache.cpp
--- a/source/common/unifiedcache.cpp
+++ b/source/common/unifiedcache.cpp
@@ -11,19 +11,23 @@
 */
 
 #include "unifiedcache.h"
 
 #include <algorithm>      // For std::max()
+#ifndef __wasi__
 #include <mutex>
+#endif
 
 #include "uassert.h"
 #include "uhash.h"
 #include "ucln_cmn.h"
 
 static icu::UnifiedCache *gCache = nullptr;
+#ifndef __wasi__
 static std::mutex *gCacheMutex = nullptr;
 static std::condition_variable *gInProgressValueAddedCond;
+#endif
 static icu::UInitOnce gCacheInitOnce {};
 
 static const int32_t MAX_EVICT_ITERATIONS = 10;
 static const int32_t DEFAULT_MAX_UNUSED = 1000;
 static const int32_t DEFAULT_PERCENTAGE_OF_IN_USE = 100;
@@ -32,14 +36,16 @@ static const int32_t DEFAULT_PERCENTAGE_
 U_CDECL_BEGIN
 static UBool U_CALLCONV unifiedcache_cleanup() {
     gCacheInitOnce.reset();
     delete gCache;
     gCache = nullptr;
+#ifndef __wasi__
     gCacheMutex->~mutex();
     gCacheMutex = nullptr;
     gInProgressValueAddedCond->~condition_variable();
     gInProgressValueAddedCond = nullptr;
+#endif
     return true;
 }
 U_CDECL_END
 
 
@@ -70,12 +76,14 @@ CacheKeyBase::~CacheKeyBase() {
 static void U_CALLCONV cacheInit(UErrorCode &status) {
     U_ASSERT(gCache == nullptr);
     ucln_common_registerCleanup(
             UCLN_COMMON_UNIFIED_CACHE, unifiedcache_cleanup);
 
+#ifndef __wasi__
     gCacheMutex = STATIC_NEW(std::mutex);
     gInProgressValueAddedCond = STATIC_NEW(std::condition_variable);
+#endif
     gCache = new UnifiedCache(status);
     if (gCache == nullptr) {
         status = U_MEMORY_ALLOCATION_ERROR;
     }
     if (U_FAILURE(status)) {
@@ -133,41 +141,53 @@ void UnifiedCache::setEvictionPolicy(
     }
     if (count < 0 || percentageOfInUseItems < 0) {
         status = U_ILLEGAL_ARGUMENT_ERROR;
         return;
     }
+#ifndef __wasi__
     std::lock_guard<std::mutex> lock(*gCacheMutex);
+#endif
     fMaxUnused = count;
     fMaxPercentageOfInUse = percentageOfInUseItems;
 }
 
 int32_t UnifiedCache::unusedCount() const {
+#ifndef __wasi__
     std::lock_guard<std::mutex> lock(*gCacheMutex);
+#endif
     return uhash_count(fHashtable) - fNumValuesInUse;
 }
 
 int64_t UnifiedCache::autoEvictedCount() const {
+#ifndef __wasi__
     std::lock_guard<std::mutex> lock(*gCacheMutex);
+#endif
     return fAutoEvictedCount;
 }
 
 int32_t UnifiedCache::keyCount() const {
+#ifndef __wasi__
     std::lock_guard<std::mutex> lock(*gCacheMutex);
+#endif
     return uhash_count(fHashtable);
 }
 
 void UnifiedCache::flush() const {
+#ifndef __wasi__
     std::lock_guard<std::mutex> lock(*gCacheMutex);
+#endif
 
     // Use a loop in case cache items that are flushed held hard references to
     // other cache items making those additional cache items eligible for
     // flushing.
     while (_flush(false));
 }
 
 void UnifiedCache::handleUnreferencedObject() const {
+#ifndef __wasi__
     std::lock_guard<std::mutex> lock(*gCacheMutex);
+#endif
     --fNumValuesInUse;
     _runEvictionSlice();
 }
 
 #ifdef UNIFIED_CACHE_DEBUG
@@ -182,11 +202,13 @@ void UnifiedCache::dump() {
     }
     cache->dumpContents();
 }
 
 void UnifiedCache::dumpContents() const {
+#ifndef __wasi__
     std::lock_guard<std::mutex> lock(*gCacheMutex);
+#endif
     _dumpContents();
 }
 
 // Dumps content of cache.
 // On entry, gCacheMutex must be held.
@@ -222,11 +244,13 @@ UnifiedCache::~UnifiedCache() {
     flush();
     {
         // Now all that should be left in the cache are entries that refer to
         // each other and entries with hard references from outside the cache.
         // Nothing we can do about these so proceed to wipe out the cache.
+#ifndef __wasi__
         std::lock_guard<std::mutex> lock(*gCacheMutex);
+#endif
         _flush(true);
     }
     uhash_close(fHashtable);
     fHashtable = nullptr;
     delete fNoValue;
@@ -323,11 +347,13 @@ void UnifiedCache::_putNew(
 
 void UnifiedCache::_putIfAbsentAndGet(
         const CacheKeyBase &key,
         const SharedObject *&value,
         UErrorCode &status) const {
+#ifndef __wasi__
     std::lock_guard<std::mutex> lock(*gCacheMutex);
+#endif
     const UHashElement *element = uhash_find(fHashtable, &key);
     if (element != nullptr && !_inProgress(element)) {
         _fetch(element, value, status);
         return;
     }
@@ -348,18 +374,22 @@ UBool UnifiedCache::_poll(
         const CacheKeyBase &key,
         const SharedObject *&value,
         UErrorCode &status) const {
     U_ASSERT(value == nullptr);
     U_ASSERT(status == U_ZERO_ERROR);
+#ifndef __wasi__
     std::unique_lock<std::mutex> lock(*gCacheMutex);
+#endif
     const UHashElement *element = uhash_find(fHashtable, &key);
 
     // If the hash table contains an inProgress placeholder entry for this key,
     // this means that another thread is currently constructing the value object.
     // Loop, waiting for that construction to complete.
      while (element != nullptr && _inProgress(element)) {
+#ifndef __wasi__
          gInProgressValueAddedCond->wait(lock);
+#endif
          element = uhash_find(fHashtable, &key);
     }
 
     // If the hash table contains an entry for the key,
     // fetch out the contents and return them.
@@ -426,13 +456,15 @@ void UnifiedCache::_put(
     UHashElement *ptr = const_cast<UHashElement *>(element);
     ptr->value.pointer = (void *) value;
     U_ASSERT(oldValue == fNoValue);
     removeSoftRef(oldValue);
 
+#ifndef __wasi__
     // Tell waiting threads that we replace in-progress status with
     // an error.
     gInProgressValueAddedCond->notify_all();
+#endif
 }
 
 void UnifiedCache::_fetch(
         const UHashElement *element,
         const SharedObject *&value,
diff --git a/source/i18n/decContext.h b/source/i18n/decContext.h
--- a/source/i18n/decContext.h
+++ b/source/i18n/decContext.h
@@ -59,11 +59,13 @@
 
   #if !defined(int32_t)
 /* #include <stdint.h>   */         /* C99 standard integers           */
   #endif
   #include <stdio.h>               /* for printf, etc.                */
+#ifndef __wasi__
   #include <signal.h>              /* for traps                       */
+#endif
 
   /* Extended flags setting -- set this to 0 to use only IEEE flags   */
   #if !defined(DECEXTFLAG)
   #define DECEXTFLAG 1             /* 1=enable extended flags         */
   #endif
diff --git a/source/i18n/decimfmt.cpp b/source/i18n/decimfmt.cpp
--- a/source/i18n/decimfmt.cpp
+++ b/source/i18n/decimfmt.cpp
@@ -478,12 +478,17 @@ DecimalFormat& DecimalFormat::operator=(
 }
 
 DecimalFormat::~DecimalFormat() {
     if (fields == nullptr) { return; }
 
+#ifndef __wasi__
     delete fields->atomicParser.exchange(nullptr);
     delete fields->atomicCurrencyParser.exchange(nullptr);
+#else
+    delete fields->atomicParser;
+    delete fields->atomicCurrencyParser;
+#endif
     delete fields;
 }
 
 DecimalFormat* DecimalFormat::clone() const {
     // can only clone valid objects.
@@ -1635,12 +1640,17 @@ void DecimalFormat::touch(UErrorCode& st
     
     // Do this after fields->exportedProperties are set up
     setupFastFormat();
 
     // Delete the parsers if they were made previously
+#ifndef __wasi__
     delete fields->atomicParser.exchange(nullptr);
     delete fields->atomicCurrencyParser.exchange(nullptr);
+#else
+    delete fields->atomicParser;
+    delete fields->atomicCurrencyParser;
+#endif
 
     // In order for the getters to work, we need to populate some fields in NumberFormat.
     NumberFormat::setCurrency(fields->exportedProperties.currency.get(status).getISOCurrency(), status);
     NumberFormat::setMaximumIntegerDigits(fields->exportedProperties.maximumIntegerDigits);
     NumberFormat::setMinimumIntegerDigits(fields->exportedProperties.minimumIntegerDigits);
@@ -1671,11 +1681,15 @@ const numparse::impl::NumberParserImpl* 
     if (U_FAILURE(status)) {
         return nullptr;
     }
 
     // First try to get the pre-computed parser
+#ifndef __wasi__
     auto* ptr = fields->atomicParser.load();
+#else
+    auto* ptr = fields->atomicParser;
+#endif
     if (ptr != nullptr) {
         return ptr;
     }
 
     // Try computing the parser on our own
@@ -1690,25 +1704,34 @@ const numparse::impl::NumberParserImpl* 
 
     // Note: ptr starts as nullptr; during compare_exchange,
     // it is set to what is actually stored in the atomic
     // if another thread beat us to computing the parser object.
     auto* nonConstThis = const_cast<DecimalFormat*>(this);
+#ifndef __wasi__
     if (!nonConstThis->fields->atomicParser.compare_exchange_strong(ptr, temp)) {
         // Another thread beat us to computing the parser
         delete temp;
         return ptr;
     } else {
         // Our copy of the parser got stored in the atomic
         return temp;
     }
+#else
+    nonConstThis->fields->atomicParser = temp;
+    return temp;
+#endif
 }
 
 const numparse::impl::NumberParserImpl* DecimalFormat::getCurrencyParser(UErrorCode& status) const {
     if (U_FAILURE(status)) { return nullptr; }
 
     // First try to get the pre-computed parser
+#ifndef __wasi__
     auto* ptr = fields->atomicCurrencyParser.load();
+#else
+    auto* ptr = fields->atomicCurrencyParser;
+#endif
     if (ptr != nullptr) {
         return ptr;
     }
 
     // Try computing the parser on our own
@@ -1719,18 +1742,23 @@ const numparse::impl::NumberParserImpl* 
     }
 
     // Note: ptr starts as nullptr; during compare_exchange, it is set to what is actually stored in the
     // atomic if another thread beat us to computing the parser object.
     auto* nonConstThis = const_cast<DecimalFormat*>(this);
+#ifndef __wasi__
     if (!nonConstThis->fields->atomicCurrencyParser.compare_exchange_strong(ptr, temp)) {
         // Another thread beat us to computing the parser
         delete temp;
         return ptr;
     } else {
         // Our copy of the parser got stored in the atomic
         return temp;
     }
+#else
+    nonConstThis->fields->atomicCurrencyParser = temp;
+    return temp;
+#endif
 }
 
 void
 DecimalFormat::fieldPositionHelper(
         const UFormattedNumberData& formatted,
diff --git a/source/i18n/number_mapper.h b/source/i18n/number_mapper.h
--- a/source/i18n/number_mapper.h
+++ b/source/i18n/number_mapper.h
@@ -5,18 +5,21 @@
 
 #if !UCONFIG_NO_FORMATTING
 #ifndef __NUMBER_MAPPER_H__
 #define __NUMBER_MAPPER_H__
 
-#include <atomic>
 #include "number_types.h"
 #include "unicode/currpinf.h"
 #include "standardplural.h"
 #include "number_patternstring.h"
 #include "number_currencysymbols.h"
 #include "numparse_impl.h"
 
+#ifndef __wasi__
+#include <atomic>
+#endif
+
 U_NAMESPACE_BEGIN
 namespace number::impl {
 
 class AutoAffixPatternProvider;
 class CurrencyPluralInfoAffixProvider;
@@ -194,14 +197,22 @@ struct DecimalFormatFields : public UMem
     * #format} method uses the formatter directly without needing to synchronize.
     */
     LocalizedNumberFormatter formatter;
 
     /** The lazy-computed parser for .parse() */
+#ifndef __wasi__
     std::atomic<::icu::numparse::impl::NumberParserImpl*> atomicParser = {};
+#else
+    ::icu::numparse::impl::NumberParserImpl* atomicParser = nullptr;
+#endif
 
     /** The lazy-computed parser for .parseCurrency() */
+#ifndef __wasi__
     std::atomic<::icu::numparse::impl::NumberParserImpl*> atomicCurrencyParser = {};
+#else
+    ::icu::numparse::impl::NumberParserImpl* atomicCurrencyParser = {};
+#endif
 
     /** Small object ownership warehouse for the formatter and parser */
     DecimalFormatWarehouse warehouse;
 
     /** The effective properties as exported from the formatter object. Used by some getters. */
diff --git a/source/i18n/numrange_fluent.cpp b/source/i18n/numrange_fluent.cpp
--- a/source/i18n/numrange_fluent.cpp
+++ b/source/i18n/numrange_fluent.cpp
@@ -246,33 +246,53 @@ LocalizedNumberRangeFormatter::Localized
 
 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(NFS<LNF>&& src) noexcept
         : NFS<LNF>(std::move(src)) {
     // Steal the compiled formatter
     LNF&& _src = static_cast<LNF&&>(src);
+#ifndef __wasi__
     auto* stolen = _src.fAtomicFormatter.exchange(nullptr);
     delete fAtomicFormatter.exchange(stolen);
+#else
+    delete fAtomicFormatter;
+    fAtomicFormatter = _src.fAtomicFormatter;
+    _src.fAtomicFormatter = nullptr;
+#endif
 }
 
 LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(const LNF& other) {
     if (this == &other) { return *this; }  // self-assignment: no-op
     NFS<LNF>::operator=(static_cast<const NFS<LNF>&>(other));
     // Do not steal; just clear
+#ifndef __wasi__
     delete fAtomicFormatter.exchange(nullptr);
+#else
+    delete fAtomicFormatter;
+#endif
     return *this;
 }
 
 LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(LNF&& src) noexcept {
     NFS<LNF>::operator=(static_cast<NFS<LNF>&&>(src));
     // Steal the compiled formatter
+#ifndef __wasi__
     auto* stolen = src.fAtomicFormatter.exchange(nullptr);
     delete fAtomicFormatter.exchange(stolen);
+#else
+    delete fAtomicFormatter;
+    fAtomicFormatter = src.fAtomicFormatter;
+    src.fAtomicFormatter = nullptr;
+#endif
     return *this;
 }
 
 
 LocalizedNumberRangeFormatter::~LocalizedNumberRangeFormatter() {
+#ifndef __wasi__
     delete fAtomicFormatter.exchange(nullptr);
+#else
+    delete fAtomicFormatter;
+#endif
 }
 
 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const RangeMacroProps& macros, const Locale& locale) {
     fMacros = macros;
     fMacros.locale = locale;
@@ -363,11 +383,15 @@ LocalizedNumberRangeFormatter::getFormat
     if (U_FAILURE(status)) {
         return nullptr;
     }
 
     // First try to get the pre-computed formatter
+#ifndef __wasi__
     auto* ptr = fAtomicFormatter.load();
+#else
+    auto* ptr = fAtomicFormatter;
+#endif
     if (ptr != nullptr) {
         return ptr;
     }
 
     // Try computing the formatter on our own
@@ -378,17 +402,22 @@ LocalizedNumberRangeFormatter::getFormat
 
     // Note: ptr starts as nullptr; during compare_exchange,
     // it is set to what is actually stored in the atomic
     // if another thread beat us to computing the formatter object.
     auto* nonConstThis = const_cast<LocalizedNumberRangeFormatter*>(this);
+#ifndef __wasi__
     if (!nonConstThis->fAtomicFormatter.compare_exchange_strong(ptr, temp.getAlias())) {
         // Another thread beat us to computing the formatter
         return ptr;
     } else {
         // Our copy of the formatter got stored in the atomic
         return temp.orphan();
     }
+#else
+    nonConstThis->fAtomicFormatter = temp.getAlias();
+    return temp.orphan();
+#endif
 
 }
 
 
 #endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/source/i18n/unicode/numberrangeformatter.h b/source/i18n/unicode/numberrangeformatter.h
--- a/source/i18n/unicode/numberrangeformatter.h
+++ b/source/i18n/unicode/numberrangeformatter.h
@@ -8,18 +8,21 @@
 
 #if U_SHOW_CPLUSPLUS_API
 
 #if !UCONFIG_NO_FORMATTING
 
-#include <atomic>
 #include "unicode/appendable.h"
 #include "unicode/fieldpos.h"
 #include "unicode/formattedvalue.h"
 #include "unicode/fpositer.h"
 #include "unicode/numberformatter.h"
 #include "unicode/unumberrangeformatter.h"
 
+#ifndef __wasi__
+#include <atomic>
+#endif
+
 /**
  * \file
  * \brief C++ API: Library for localized formatting of number, currency, and unit ranges.
  *
  * The main entrypoint to the formatting of ranges of numbers, including currencies and other units of measurement.
@@ -559,11 +562,15 @@ class U_I18N_API_CLASS LocalizedNumberRa
      * @stable ICU 63
      */
     U_I18N_API ~LocalizedNumberRangeFormatter();
 
   private:
+#ifndef __wasi__
     std::atomic<impl::NumberRangeFormatterImpl*> fAtomicFormatter = {};
+#else
+    impl::NumberRangeFormatterImpl* fAtomicFormatter = nullptr;
+#endif
 
     const impl::NumberRangeFormatterImpl* getFormatter(UErrorCode& stauts) const;
 
     explicit LocalizedNumberRangeFormatter(
         const NumberRangeFormatterSettings<LocalizedNumberRangeFormatter>& other);

A gnu/packages/patches/icu4c-bug-1790071-ICU-22132-standardize-vtzone-output.patch => gnu/packages/patches/icu4c-bug-1790071-ICU-22132-standardize-vtzone-output.patch +28 -0
@@ 0,0 1,28 @@
diff --git a/source/i18n/vtzone.cpp b/source/i18n/vtzone.cpp
--- a/source/i18n/vtzone.cpp
+++ b/source/i18n/vtzone.cpp
@@ -1735,14 +1735,17 @@ VTimeZone::write(VTZWriter& writer, UErr
             }
         }
     } else {
-        UnicodeString icutzprop;
-        UVector customProps(nullptr, uhash_compareUnicodeString, status);
+        UVector customProps(uprv_deleteUObject, uhash_compareUnicodeString, status);
         if (olsonzid.length() > 0 && icutzver.length() > 0) {
-            icutzprop.append(olsonzid);
-            icutzprop.append(u'[');
-            icutzprop.append(icutzver);
-            icutzprop.append(u']');
-            customProps.addElement(&icutzprop, status);
+            LocalPointer<UnicodeString> icutzprop(new UnicodeString(ICU_TZINFO_PROP), status);
+            if (U_FAILURE(status)) {
+                return;
+            }
+            icutzprop->append(olsonzid);
+            icutzprop->append(u'[');
+            icutzprop->append(icutzver);
+            icutzprop->append(u']');
+            customProps.adoptElement(icutzprop.orphan(), status);
         }
         writeZone(writer, *tz, &customProps, status);
     }

A gnu/packages/patches/icu4c-bug-1856290-ICU-20548-dateinterval-timezone.patch => gnu/packages/patches/icu4c-bug-1856290-ICU-20548-dateinterval-timezone.patch +163 -0
@@ 0,0 1,163 @@
# Handle 'O' time zone skeleton in DateIntervalFormat.
# Keep time zone skeleton field widths in DateIntervalFormat.
#
# ICU bug: https://unicode-org.atlassian.net/browse/ICU-20548

diff --git a/source/i18n/dtitv_impl.h b/source/i18n/dtitv_impl.h
--- a/source/i18n/dtitv_impl.h
+++ b/source/i18n/dtitv_impl.h
@@ -84,16 +84,19 @@
 #define CAP_W             ((char16_t)0x0057)
 #define CAP_Y             ((char16_t)0x0059)
 #define CAP_Z             ((char16_t)0x005A)
 
 //#define MINIMUM_SUPPORTED_CALENDAR_FIELD    UCAL_MINUTE
 
 #define MAX_E_COUNT      5
 #define MAX_M_COUNT      5
+#define MAX_z_COUNT      4
+#define MAX_v_COUNT      4
+#define MAX_O_COUNT      4
 //#define MAX_INTERVAL_INDEX 4
 #define MAX_POSITIVE_INT  56632
 
 
 #endif /* #if !UCONFIG_NO_FORMATTING */
 
 #endif 
 //eof
diff --git a/source/i18n/dtitvfmt.cpp b/source/i18n/dtitvfmt.cpp
--- a/source/i18n/dtitvfmt.cpp
+++ b/source/i18n/dtitvfmt.cpp
@@ -1061,16 +1061,17 @@ DateIntervalFormat::getDateTimeSkeleton(
     // timeSkeleton follows the sequence of hm*[v|z]?
     int32_t ECount = 0;
     int32_t dCount = 0;
     int32_t MCount = 0;
     int32_t yCount = 0;
     int32_t mCount = 0;
     int32_t vCount = 0;
     int32_t zCount = 0;
+    int32_t OCount = 0;
     char16_t hourChar = u'\0';
     int32_t i;
 
     for (i = 0; i < skeleton.length(); ++i) {
         char16_t ch = skeleton[i];
         switch ( ch ) {
           case CAP_E:
             dateSkeleton.append(ch);
@@ -1123,16 +1124,20 @@ DateIntervalFormat::getDateTimeSkeleton(
           case LOW_Z:
             ++zCount;
             timeSkeleton.append(ch);
             break;
           case LOW_V:
             ++vCount;
             timeSkeleton.append(ch);
             break;
+          case CAP_O:
+            ++OCount;
+            timeSkeleton.append(ch);
+            break;
           case LOW_A:
           case CAP_V:
           case CAP_Z:
           case LOW_J:
           case LOW_S:
           case CAP_S:
           case CAP_A:
           case LOW_B:
@@ -1174,20 +1179,41 @@ DateIntervalFormat::getDateTimeSkeleton(
     /* generate normalized form for time */
     if ( hourChar != u'\0' ) {
         normalizedTimeSkeleton.append(hourChar);
     }
     if ( mCount != 0 ) {
         normalizedTimeSkeleton.append(LOW_M);
     }
     if ( zCount != 0 ) {
-        normalizedTimeSkeleton.append(LOW_Z);
+        if ( zCount <= 3 ) {
+            normalizedTimeSkeleton.append(LOW_Z);
+        } else {
+            for ( int32_t j = 0; j < zCount && j < MAX_z_COUNT; ++j ) {
+                 normalizedTimeSkeleton.append(LOW_Z);
+            }
+        }
     }
     if ( vCount != 0 ) {
-        normalizedTimeSkeleton.append(LOW_V);
+        if ( vCount <= 3 ) {
+            normalizedTimeSkeleton.append(LOW_V);
+        } else {
+            for ( int32_t j = 0; j < vCount && j < MAX_v_COUNT; ++j ) {
+                 normalizedTimeSkeleton.append(LOW_V);
+            }
+        }
+    }
+    if ( OCount != 0 ) {
+        if ( OCount <= 3 ) {
+            normalizedTimeSkeleton.append(CAP_O);
+        } else {
+            for ( int32_t j = 0; j < OCount && j < MAX_O_COUNT; ++j ) {
+                 normalizedTimeSkeleton.append(CAP_O);
+            }
+        }
     }
 }
 
 
 /**
  * Generate date or time interval pattern from resource,
  * and set them into the interval pattern locale to this formatter.
  *
@@ -1732,18 +1758,23 @@ DateIntervalFormat::adjustFieldWidth(con
         findReplaceInPattern(adjustedPtn, UnicodeString(u"a\u202F",-1), UnicodeString());
         findReplaceInPattern(adjustedPtn, UnicodeString(LOW_A), UnicodeString());
         // adjust interior double spaces, remove exterior whitespace
         findReplaceInPattern(adjustedPtn, UnicodeString("  "), UnicodeString(" "));
         adjustedPtn.trim();
     }
     if ( differenceInfo == 2 ) {
         if (inputSkeleton.indexOf(LOW_Z) != -1) {
+             bestMatchSkeletonFieldWidth[(int)(LOW_Z - PATTERN_CHAR_BASE)] = bestMatchSkeletonFieldWidth[(int)(LOW_V - PATTERN_CHAR_BASE)];
              findReplaceInPattern(adjustedPtn, UnicodeString(LOW_V), UnicodeString(LOW_Z));
          }
+         if (inputSkeleton.indexOf(CAP_O) != -1) {
+             bestMatchSkeletonFieldWidth[(int)(CAP_O - PATTERN_CHAR_BASE)] = bestMatchSkeletonFieldWidth[(int)(LOW_V - PATTERN_CHAR_BASE)];
+             findReplaceInPattern(adjustedPtn, UnicodeString(LOW_V), UnicodeString(CAP_O));
+         }
          if (inputSkeleton.indexOf(CAP_K) != -1) {
              findReplaceInPattern(adjustedPtn, UnicodeString(LOW_H), UnicodeString(CAP_K));
          }
          if (inputSkeleton.indexOf(LOW_K) != -1) {
              findReplaceInPattern(adjustedPtn, UnicodeString(CAP_H), UnicodeString(LOW_K));
          }
          if (inputSkeleton.indexOf(LOW_B) != -1) {
              findReplaceInPattern(adjustedPtn, UnicodeString(LOW_A), UnicodeString(LOW_B));
diff --git a/source/i18n/dtitvinf.cpp b/source/i18n/dtitvinf.cpp
--- a/source/i18n/dtitvinf.cpp
+++ b/source/i18n/dtitvinf.cpp
@@ -582,19 +582,20 @@ DateIntervalInfo::getBestSkeleton(const 
 
     // hack for certain alternate characters
     // resource bundles only have time skeletons containing 'v', 'h', and 'H'
     // but not time skeletons containing 'z', 'K', or 'k'
     // the skeleton may also include 'a' or 'b', which never occur in the resource bundles, so strip them out too
     UBool replacedAlternateChars = false;
     const UnicodeString* inputSkeleton = &skeleton;
     UnicodeString copySkeleton;
-    if ( skeleton.indexOf(LOW_Z) != -1 || skeleton.indexOf(LOW_K) != -1 || skeleton.indexOf(CAP_K) != -1 || skeleton.indexOf(LOW_A) != -1 || skeleton.indexOf(LOW_B) != -1 ) {
+    if ( skeleton.indexOf(LOW_Z) != -1 || skeleton.indexOf(CAP_O) != -1 || skeleton.indexOf(LOW_K) != -1 || skeleton.indexOf(CAP_K) != -1 || skeleton.indexOf(LOW_A) != -1 || skeleton.indexOf(LOW_B) != -1 ) {
         copySkeleton = skeleton;
         copySkeleton.findAndReplace(UnicodeString(LOW_Z), UnicodeString(LOW_V));
+        copySkeleton.findAndReplace(UnicodeString(CAP_O), UnicodeString(LOW_V));
         copySkeleton.findAndReplace(UnicodeString(LOW_K), UnicodeString(CAP_H));
         copySkeleton.findAndReplace(UnicodeString(CAP_K), UnicodeString(LOW_H));
         copySkeleton.findAndReplace(UnicodeString(LOW_A), UnicodeString());
         copySkeleton.findAndReplace(UnicodeString(LOW_B), UnicodeString());
         inputSkeleton = &copySkeleton;
         replacedAlternateChars = true;
     }
 

A gnu/packages/patches/icu4c-bug-1954138-dtitvfmt-adopt-calendar.patch => gnu/packages/patches/icu4c-bug-1954138-dtitvfmt-adopt-calendar.patch +69 -0
@@ 0,0 1,69 @@
diff --git a/source/i18n/dtitvfmt.cpp b/source/i18n/dtitvfmt.cpp
--- a/source/i18n/dtitvfmt.cpp
+++ b/source/i18n/dtitvfmt.cpp
@@ -630,16 +630,38 @@ DateIntervalFormat::getTimeZone() const
 {
     if (fDateFormat != nullptr) {
         Mutex lock(&gFormatterMutex);
         return fDateFormat->getTimeZone();
     }
     // If fDateFormat is nullptr (unexpected), create default timezone.
     return *(TimeZone::createDefault());
 }
+ 
+void DateIntervalFormat::adoptCalendar(Calendar *calendarToAdopt) {
+    if (fDateFormat != nullptr) {
+        fDateFormat->adoptCalendar(calendarToAdopt);
+    }
+
+    // The fDateFormat has the primary calendar for the DateIntervalFormat and has
+    // ownership of any adopted Calendar; fFromCalendar and fToCalendar are internal
+    // work clones of that calendar.
+
+    delete fFromCalendar;
+    fFromCalendar = nullptr;
+
+    delete fToCalendar;
+    fToCalendar = nullptr;
+
+    const Calendar *calendar = fDateFormat->getCalendar();
+    if (calendar != nullptr) {
+        fFromCalendar = calendar->clone();
+        fToCalendar = calendar->clone();
+    }
+}
 
 void
 DateIntervalFormat::setContext(UDisplayContext value, UErrorCode& status)
 {
     if (U_FAILURE(status))
         return;
     if (static_cast<UDisplayContextType>(static_cast<uint32_t>(value) >> 8) == UDISPCTX_TYPE_CAPITALIZATION) {
         fCapitalizationContext = value;
diff --git a/source/i18n/unicode/dtitvfmt.h b/source/i18n/unicode/dtitvfmt.h
--- a/source/i18n/unicode/dtitvfmt.h
+++ b/source/i18n/unicode/dtitvfmt.h
@@ -625,16 +625,23 @@ public:
     /**
      * Sets the time zone for the calendar used by this DateIntervalFormat object.
      * @param zone the new time zone.
      * @stable ICU 4.8
      */
     U_I18N_API virtual void setTimeZone(const TimeZone& zone);
 
     /**
+     * Sets the calendar used by this DateIntervalFormat object. The caller no longer owns
+     * the Calendar object and should not delete it after this call.
+     * @param calendarToAdopt the Calendar to be adopted.
+     */
+    U_I18N_API virtual void adoptCalendar(Calendar *calendarToAdopt);
+
+    /**
      * Set a particular UDisplayContext value in the formatter, such as
      * UDISPCTX_CAPITALIZATION_FOR_STANDALONE. This causes the formatted
      * result to be capitalized appropriately for the context in which
      * it is intended to be used, considering both the locale and the
      * type of field at the beginning of the formatted result.
      * @param value The UDisplayContext value to set.
      * @param status Input/output status. If at entry this indicates a failure
      *               status, the function will do nothing; otherwise this will be

A gnu/packages/patches/icu4c-bug-1972781-chinese-based-calendar.patch => gnu/packages/patches/icu4c-bug-1972781-chinese-based-calendar.patch +25 -0
@@ 0,0 1,25 @@
diff --git a/source/i18n/smpdtfmt.cpp b/source/i18n/smpdtfmt.cpp
--- a/source/i18n/smpdtfmt.cpp
+++ b/source/i18n/smpdtfmt.cpp
@@ -1523,18 +1523,19 @@ SimpleDateFormat::subFormat(UnicodeStrin
     }
 
     switch (patternCharIndex) {
 
     // for any "G" symbol, write out the appropriate era string
     // "GGGG" is wide era name, "GGGGG" is narrow era name, anything else is abbreviated name
     case UDAT_ERA_FIELD:
         {
-            if (typeid(cal) == typeid(ChineseCalendar) ||
-                typeid(cal) == typeid(DangiCalendar)) {
+            const char* type = cal.getType();
+            if (strcmp(type, "chinese") == 0 ||
+                strcmp(type, "dangi") == 0) {
                 zeroPaddingNumber(currentNumberFormat,appendTo, value, 1, 9); // as in ICU4J
             } else {
                 if (count == 5) {
                     _appendSymbol(appendTo, value, fSymbols->fNarrowEras, fSymbols->fNarrowErasCount);
                     capContextUsageType = DateFormatSymbols::kCapContextUsageEraNarrow;
                 } else if (count == 4) {
                     _appendSymbol(appendTo, value, fSymbols->fEraNames, fSymbols->fEraNamesCount);
                     capContextUsageType = DateFormatSymbols::kCapContextUsageEraWide;

A gnu/packages/patches/icu4c-bug-2000225-ICU-23264-increase-measure-unit-capacity.patch => gnu/packages/patches/icu4c-bug-2000225-ICU-23264-increase-measure-unit-capacity.patch +27 -0
@@ 0,0 1,27 @@
# Increase measure unit capacity for ICU 78.
#
# ICU bug: https://unicode-org.atlassian.net/browse/ICU-23264

diff --git a/source/i18n/number_skeletons.cpp b/source/i18n/number_skeletons.cpp
--- a/source/i18n/number_skeletons.cpp
+++ b/source/i18n/number_skeletons.cpp
@@ -1067,18 +1067,17 @@ void blueprint_helpers::parseMeasureUnit
     }
 
     // Need to do char <-> char16_t conversion...
     CharString type;
     SKELETON_UCHAR_TO_CHAR(type, stemString, 0, firstHyphen, status);
     CharString subType;
     SKELETON_UCHAR_TO_CHAR(subType, stemString, firstHyphen + 1, stemString.length(), status);
 
-    // Note: the largest type as of this writing (Aug 2020) is "volume", which has 33 units.
-    static constexpr int32_t CAPACITY = 40;
+    static constexpr int32_t CAPACITY = 50;
     MeasureUnit units[CAPACITY];
     UErrorCode localStatus = U_ZERO_ERROR;
     int32_t numUnits = MeasureUnit::getAvailable(type.data(), units, CAPACITY, localStatus);
     if (U_FAILURE(localStatus)) {
         // More than 30 units in this type?
         status = U_INTERNAL_PROGRAM_ERROR;
         return;
     }

A gnu/packages/patches/icu4c-bug-2002735-ICU-23277-coptic-single-era.patch => gnu/packages/patches/icu4c-bug-2002735-ICU-23277-coptic-single-era.patch +109 -0
@@ 0,0 1,109 @@
diff --git a/source/i18n/coptccal.cpp b/source/i18n/coptccal.cpp
--- a/source/i18n/coptccal.cpp
+++ b/source/i18n/coptccal.cpp
@@ -61,46 +61,43 @@ int32_t
 CopticCalendar::handleGetExtendedYear(UErrorCode& status)
 {
     if (U_FAILURE(status)) {
         return 0;
     }
     if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) {
         return internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1
     }
-    // The year defaults to the epoch start, the era to CE
-    int32_t era = internalGet(UCAL_ERA, CE);
-    if (era == BCE) {
-        return 1 - internalGet(UCAL_YEAR, 1); // Convert to extended year
-    }
-    if (era == CE){
-        return internalGet(UCAL_YEAR, 1); // Default to year 1
-    }
-    status = U_ILLEGAL_ARGUMENT_ERROR;
-    return 0;
+    // The year defaults to the epoch start
+    return internalGet(UCAL_YEAR, 1); // Default to year 1
 }
 
 IMPL_SYSTEM_DEFAULT_CENTURY(CopticCalendar, "@calendar=coptic")
 
 int32_t
 CopticCalendar::getJDEpochOffset() const
 {
     return COPTIC_JD_EPOCH_OFFSET;
 }
 
 int32_t CopticCalendar::extendedYearToEra(int32_t extendedYear) const {
-    return extendedYear <= 0 ? BCE : CE;
+    return CE;
 }
 
 int32_t CopticCalendar::extendedYearToYear(int32_t extendedYear) const {
-    return extendedYear <= 0 ? 1 - extendedYear : extendedYear;
+    return extendedYear;
 }
 
-bool CopticCalendar::isEra0CountingBackward() const {
-    return true;
+int32_t
+CopticCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const
+{
+    if (field == UCAL_ERA) {
+        return 1; // Only one era, era is always 1
+    }
+    return CECalendar::handleGetLimit(field, limitType);
 }
 
 int32_t
 CopticCalendar::getRelatedYearDifference() const {
     constexpr int32_t kCopticCalendarRelatedYearDifference = 284;
     return kCopticCalendarRelatedYearDifference;
 }
 
diff --git a/source/i18n/coptccal.h b/source/i18n/coptccal.h
--- a/source/i18n/coptccal.h
+++ b/source/i18n/coptccal.h
@@ -165,16 +165,22 @@ protected:
     int32_t getRelatedYearDifference() const override;
 
     /**
      * Return the extended year defined by the current fields.
      * @internal
      */
     virtual int32_t handleGetExtendedYear(UErrorCode& status) override;
 
+    /**
+     * Calculate the limit for a specified type of limit and field
+     * @internal
+     */
+    virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType limitType) const override;
+
     DECLARE_OVERRIDE_SYSTEM_DEFAULT_CENTURY
 
     /**
      * Return the date offset from Julian
      * @internal
      */
     int32_t getJDEpochOffset() const override;
 
@@ -184,21 +190,16 @@ protected:
      */
     int32_t extendedYearToEra(int32_t extendedYear) const override;
 
     /**
      * Compute the year from extended year.
      * @internal
      */
     int32_t extendedYearToYear(int32_t extendedYear) const override;
-
-    /**
-     * @internal
-     */
-    bool isEra0CountingBackward() const override;
 public:
     /**
      * Override Calendar Returns a unique class ID POLYMORPHICALLY. Pure virtual
      * override. This method is to implement a simple version of RTTI, since not all C++
      * compilers support genuine RTTI. Polymorphic operator==() and clone() methods call
      * this method.
      *
      * @return   The class ID for this object. All objects of a given class have the

A gnu/packages/patches/icu4c-suppress-warnings.patch => gnu/packages/patches/icu4c-suppress-warnings.patch +80 -0
@@ 0,0 1,80 @@
diff --git a/source/acinclude.m4 b/source/acinclude.m4
--- a/source/acinclude.m4
+++ b/source/acinclude.m4
@@ -459,30 +459,36 @@ AC_DEFUN([AC_CHECK_STRICT_COMPILE],
       ], [ac_use_strict_options=yes])
     AC_MSG_RESULT($ac_use_strict_options)
 
     if test "$ac_use_strict_options" = yes
     then
         if test "$GCC" = yes
         then
             CFLAGS="$CFLAGS -Wall -pedantic -Wshadow -Wpointer-arith -Wmissing-prototypes -Wwrite-strings"
+
+            # Suppress clang C warnings:
+            CFLAGS="$CFLAGS -Wno-sign-compare -Wno-unused"
         else
             case "${host}" in
             *-*-cygwin)
                 if test "`$CC /help 2>&1 | head -c9`" = "Microsoft"
                 then
                     CFLAGS="$CFLAGS /W4"
                 fi ;;
             *-*-mingw*)
                 CFLAGS="$CFLAGS -W4" ;;
             esac
         fi
         if test "$GXX" = yes
         then
             CXXFLAGS="$CXXFLAGS -W -Wall -pedantic -Wpointer-arith -Wwrite-strings -Wno-long-long"
+
+            # Suppress clang C++ warnings:
+            CXXFLAGS="$CXXFLAGS -Wno-unused -Wno-unused-parameter"
         else
             case "${host}" in
             *-*-cygwin)
                 if test "`$CXX /help 2>&1 | head -c9`" = "Microsoft"
                 then
                     CXXFLAGS="$CXXFLAGS /W4"
                 fi ;;
             *-*-mingw*)
diff --git a/source/configure b/source/configure
--- a/source/configure
+++ b/source/configure
@@ -5227,30 +5227,36 @@ fi
     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_use_strict_options" >&5
 printf "%s\n" "$ac_use_strict_options" >&6; }
 
     if test "$ac_use_strict_options" = yes
     then
         if test "$GCC" = yes
         then
             CFLAGS="$CFLAGS -Wall -pedantic -Wshadow -Wpointer-arith -Wmissing-prototypes -Wwrite-strings"
+
+            # Suppress clang C warnings:
+            CFLAGS="$CFLAGS -Wno-sign-compare -Wno-unused"
         else
             case "${host}" in
             *-*-cygwin)
                 if test "`$CC /help 2>&1 | head -c9`" = "Microsoft"
                 then
                     CFLAGS="$CFLAGS /W4"
                 fi ;;
             *-*-mingw*)
                 CFLAGS="$CFLAGS -W4" ;;
             esac
         fi
         if test "$GXX" = yes
         then
             CXXFLAGS="$CXXFLAGS -W -Wall -pedantic -Wpointer-arith -Wwrite-strings -Wno-long-long"
+
+            # Suppress clang C++ warnings:
+            CXXFLAGS="$CXXFLAGS -Wno-unused -Wno-unused-parameter"
         else
             case "${host}" in
             *-*-cygwin)
                 if test "`$CXX /help 2>&1 | head -c9`" = "Microsoft"
                 then
                     CXXFLAGS="$CXXFLAGS /W4"
                 fi ;;
             *-*-mingw*)