]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/common/locdispnames.cpp
ICU-64260.0.1.tar.gz
[apple/icu.git] / icuSources / common / locdispnames.cpp
index c3362d38f38e2fce89142da03f8116d59df3320c..7024bc464e991c7ae08bdaca287a0c8f08d074c5 100644 (file)
@@ -1,12 +1,14 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
 /*
 *******************************************************************************
 *
-*   Copyright (C) 1997-2010, International Business Machines
+*   Copyright (C) 1997-2016, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 *
 *******************************************************************************
 *   file name:  locdispnames.cpp
-*   encoding:   US-ASCII
+*   encoding:   UTF-8
 *   tab size:   8 (not used)
 *   indentation:4
 *
 #include "unicode/utypes.h"
 #include "unicode/brkiter.h"
 #include "unicode/locid.h"
+#include "unicode/uenum.h"
 #include "unicode/uloc.h"
 #include "unicode/ures.h"
 #include "unicode/ustring.h"
+#include "unicode/uldnames.h"
+#include "charstr.h"
 #include "cmemory.h"
 #include "cstring.h"
 #include "putilimp.h"
@@ -277,11 +282,12 @@ U_NAMESPACE_USE
  the locale ResourceBundle data file.*/
 static const char _kLanguages[]       = "Languages";
 static const char _kScripts[]         = "Scripts";
+static const char _kScriptsStandAlone[] = "Scripts%stand-alone";
 static const char _kCountries[]       = "Countries";
 static const char _kVariants[]        = "Variants";
 static const char _kKeys[]            = "Keys";
 static const char _kTypes[]           = "Types";
-static const char _kRootName[]        = "root";
+//static const char _kRootName[]        = "root";
 static const char _kCurrency[]        = "currency";
 static const char _kCurrencies[]      = "Currencies";
 static const char _kLocaleDisplayPattern[] = "localeDisplayPattern";
@@ -303,14 +309,11 @@ _getStringOrCopyKey(const char *path, const char *locale,
 
     if(itemKey==NULL) {
         /* top-level item: normal resource bundle access */
-        UResourceBundle *rb;
-
-        rb=ures_open(path, locale, pErrorCode);
+        icu::LocalUResourceBundlePointer rb(ures_open(path, locale, pErrorCode));
 
         if(U_SUCCESS(*pErrorCode)) {
-            s=ures_getStringByKey(rb, tableKey, &length, pErrorCode);
+            s=ures_getStringByKey(rb.getAlias(), tableKey, &length, pErrorCode);
             /* see comment about closing rb near "return item;" in _res_getTableStringWithFallback() */
-            ures_close(rb);
         }
     } else {
         /* Language code should not be a number. If it is, set the error code. */
@@ -399,9 +402,28 @@ uloc_getDisplayScript(const char* locale,
                       const char* displayLocale,
                       UChar *dest, int32_t destCapacity,
                       UErrorCode *pErrorCode)
+{
+       UErrorCode err = U_ZERO_ERROR;
+       int32_t res = _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity,
+                uloc_getScript, _kScriptsStandAlone, &err);
+       
+       if ( err == U_USING_DEFAULT_WARNING ) {
+        return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity,
+                    uloc_getScript, _kScripts, pErrorCode);
+       } else {
+               *pErrorCode = err;
+               return res;
+       }
+}
+
+U_INTERNAL int32_t U_EXPORT2
+uloc_getDisplayScriptInContext(const char* locale,
+                      const char* displayLocale,
+                      UChar *dest, int32_t destCapacity,
+                      UErrorCode *pErrorCode)
 {
     return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity,
-                uloc_getScript, _kScripts, pErrorCode);
+                    uloc_getScript, _kScripts, pErrorCode);
 }
 
 U_CAPI int32_t U_EXPORT2
@@ -427,38 +449,56 @@ uloc_getDisplayVariant(const char *locale,
                 uloc_getVariant, _kVariants, pErrorCode);
 }
 
+/* Instead of having a separate pass for 'special' patterns, reintegrate the two
+ * so we don't get bitten by preflight bugs again.  We can be reasonably efficient
+ * without two separate code paths, this code isn't that performance-critical.
+ *
+ * This code is general enough to deal with patterns that have a prefix or swap the
+ * language and remainder components, since we gave developers enough rope to do such
+ * things if they futz with the pattern data.  But since we don't give them a way to
+ * specify a pattern for arbitrary combinations of components, there's not much use in
+ * that.  I don't think our data includes such patterns, the only variable I know if is
+ * whether there is a space before the open paren, or not.  Oh, and zh uses different
+ * chars than the standard open/close paren (which ja and ko use, btw).
+ */
 U_CAPI int32_t U_EXPORT2
 uloc_getDisplayName(const char *locale,
                     const char *displayLocale,
                     UChar *dest, int32_t destCapacity,
                     UErrorCode *pErrorCode)
 {
-    int32_t length, length2, length3 = 0;
-    UBool hasLanguage, hasScript, hasCountry, hasVariant, hasKeywords;
-    UEnumeration* keywordEnum = NULL;
-    int32_t keywordCount = 0;
-    const char *keyword = NULL;
-    int32_t keywordLen = 0;
-    char keywordValue[256];
-    int32_t keywordValueLen = 0;
-
-    int32_t locSepLen = 0;
-    int32_t locPatLen = 0;
-    int32_t p0Len = 0;
-    int32_t defaultPatternLen = 9;
-    const UChar *dispLocSeparator;
-    const UChar *dispLocPattern;
-    static const UChar defaultSeparator[3] = { 0x002c, 0x0020 , 0x0000 }; /* comma + space */
-    static const UChar defaultPattern[10] = { 0x007b, 0x0030, 0x007d, 0x0020, 0x0028, 0x007b, 0x0031, 0x007d, 0x0029, 0x0000 }; /* {0} ({1}) */
-    static const UChar pat0[4] = { 0x007b, 0x0030, 0x007d , 0x0000 } ; /* {0} */
-    static const UChar pat1[4] = { 0x007b, 0x0031, 0x007d , 0x0000 } ; /* {1} */
-    
-    UResourceBundle *bundle = NULL;
-    UResourceBundle *locdsppat = NULL;
+    static const UChar defaultSeparator[9] = { 0x007b, 0x0030, 0x007d, 0x002c, 0x0020, 0x007b, 0x0031, 0x007d, 0x0000 }; /* "{0}, {1}" */
+    static const UChar sub0[4] = { 0x007b, 0x0030, 0x007d , 0x0000 } ; /* {0} */
+    static const UChar sub1[4] = { 0x007b, 0x0031, 0x007d , 0x0000 } ; /* {1} */
+    static const int32_t subLen = 3;
+    static const UChar defaultPattern[10] = {
+        0x007b, 0x0030, 0x007d, 0x0020, 0x0028, 0x007b, 0x0031, 0x007d, 0x0029, 0x0000
+    }; /* {0} ({1}) */
+    static const int32_t defaultPatLen = 9;
+    static const int32_t defaultSub0Pos = 0;
+    static const int32_t defaultSub1Pos = 5;
+
+    int32_t length; /* of formatted result */
+
+    const UChar *separator;
+    int32_t sepLen = 0;
+    const UChar *pattern;
+    int32_t patLen = 0;
+    int32_t sub0Pos, sub1Pos;
     
-    UErrorCode status = U_ZERO_ERROR;
+    UChar formatOpenParen         = 0x0028; // (
+    UChar formatReplaceOpenParen  = 0x005B; // [
+    UChar formatCloseParen        = 0x0029; // )
+    UChar formatReplaceCloseParen = 0x005D; // ]
+
+    UBool haveLang = TRUE; /* assume true, set false if we find we don't have
+                              a lang component in the locale */
+    UBool haveRest = TRUE; /* assume true, set false if we find we don't have
+                              any other component in the locale */
+    UBool retry = FALSE; /* set true if we need to retry, see below */
+
+    int32_t langi = 0; /* index of the language substitution (0 or 1), virtually always 0 */
 
-    /* argument checking */
     if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
         return 0;
     }
@@ -468,238 +508,273 @@ uloc_getDisplayName(const char *locale,
         return 0;
     }
 
-    bundle    = ures_open(U_ICUDATA_LANG, displayLocale, &status);
+    { // For select languages + script, route to uldn_localeDisplayName <rdar://problem/51418203>
+        char lang[ULOC_LANG_CAPACITY];
+        char script[ULOC_SCRIPT_CAPACITY ];
+        UErrorCode status = U_ZERO_ERROR;
+        int32_t langlen = uloc_getLanguage(locale, lang, ULOC_LANG_CAPACITY, &status);
+        int32_t scriptlen = uloc_getScript(locale, script, ULOC_SCRIPT_CAPACITY, &status);
+        if ( U_SUCCESS(status) && scriptlen>0 && (uprv_strcmp(lang, "zh") == 0 || uprv_strcmp(lang, "yue") == 0 ||
+                uprv_strcmp(lang, "ks") == 0 || uprv_strcmp(lang, "pa") == 0 || uprv_strcmp(lang, "ur") == 0) ) {
+            ULocaleDisplayNames* uldn = uldn_open(displayLocale, ULDN_STANDARD_NAMES, &status);
+            if ( U_SUCCESS(status) ) {
+                int32_t namelen = uldn_localeDisplayName(uldn, locale, dest, destCapacity, pErrorCode);
+                uldn_close(uldn);
+                return namelen;
+            }
+        }
+    }
 
-    locdsppat = ures_getByKeyWithFallback(bundle, _kLocaleDisplayPattern, NULL, &status);
-    dispLocSeparator = ures_getStringByKeyWithFallback(locdsppat, _kSeparator, &locSepLen, &status);
-    dispLocPattern = ures_getStringByKeyWithFallback(locdsppat, _kPattern, &locPatLen, &status);
-        
-    /*close the bundles */
-    ures_close(locdsppat);
-    ures_close(bundle);
+    {
+        UErrorCode status = U_ZERO_ERROR;
 
-    /* If we couldn't find any data, then use the defaults */
-    if ( locSepLen == 0) {
-       dispLocSeparator = defaultSeparator;
-       locSepLen = 2;
-    }
+        icu::LocalUResourceBundlePointer locbundle(
+                ures_open(U_ICUDATA_LANG, displayLocale, &status));
+        icu::LocalUResourceBundlePointer dspbundle(
+                ures_getByKeyWithFallback(locbundle.getAlias(), _kLocaleDisplayPattern, NULL, &status));
 
-    if ( locPatLen == 0) {
-       dispLocPattern = defaultPattern;
-       locPatLen = 9;
+        separator=ures_getStringByKeyWithFallback(dspbundle.getAlias(), _kSeparator, &sepLen, &status);
+        pattern=ures_getStringByKeyWithFallback(dspbundle.getAlias(), _kPattern, &patLen, &status);
     }
 
-    /*
-     * if there is a language, then write "language (country, variant)"
-     * otherwise write "country, variant"
+    /* If we couldn't find any data, then use the defaults */
+    if(sepLen == 0) {
+       separator = defaultSeparator;
+    }
+    /* #10244: Even though separator is now a pattern, it is awkward to handle it as such
+     * here since we are trying to build the display string in place in the dest buffer,
+     * and to handle it as a pattern would entail having separate storage for the
+     * substrings that need to be combined (the first of which may be the result of
+     * previous such combinations). So for now we continue to treat the portion between
+     * {0} and {1} as a string to be appended when joining substrings, ignoring anything
+     * that is before {0} or after {1} (no existing separator pattern has any such thing).
+     * This is similar to how pattern is handled below.
      */
-
-    /* write the language */
-    length=uloc_getDisplayLanguage(locale, displayLocale,
-                                   dest, destCapacity,
-                                   pErrorCode);
-    hasLanguage= length>0;
-
-    if(hasLanguage) {
-        p0Len = length;
-
-        /* append " (" */
-        if(length<destCapacity) {
-            dest[length]=0x20;
+    {
+        UChar *p0=u_strstr(separator, sub0);
+        UChar *p1=u_strstr(separator, sub1);
+        if (p0==NULL || p1==NULL || p1<p0) {
+            *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
+            return 0;
         }
-        ++length;
-        if(length<destCapacity) {
-            dest[length]=0x28;
+        separator = (const UChar *)p0 + subLen;
+        sepLen = static_cast<int32_t>(p1 - separator);
+    }
+
+    if(patLen==0 || (patLen==defaultPatLen && !u_strncmp(pattern, defaultPattern, patLen))) {
+        pattern=defaultPattern;
+        patLen=defaultPatLen;
+        sub0Pos=defaultSub0Pos;
+        sub1Pos=defaultSub1Pos;
+        // use default formatOpenParen etc. set above
+    } else { /* non-default pattern */
+        UChar *p0=u_strstr(pattern, sub0);
+        UChar *p1=u_strstr(pattern, sub1);
+        if (p0==NULL || p1==NULL) {
+            *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
+            return 0;
         }
-        ++length;
-    }
-
-    if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR) {
-        /* keep preflighting */
-        *pErrorCode=U_ZERO_ERROR;
-    }
-
-    /* append the script */
-    if(length<destCapacity) {
-        length2=uloc_getDisplayScript(locale, displayLocale,
-                                       dest+length, destCapacity-length,
-                                       pErrorCode);
-    } else {
-        length2=uloc_getDisplayScript(locale, displayLocale,
-                                       NULL, 0,
-                                       pErrorCode);
-    }
-    hasScript= length2>0;
-    length+=length2;
-
-    if(hasScript) {
-        /* append separator */
-        if(length+locSepLen<=destCapacity) {
-            u_memcpy(dest+length,dispLocSeparator,locSepLen);
+        sub0Pos = static_cast<int32_t>(p0-pattern);
+        sub1Pos = static_cast<int32_t>(p1-pattern);
+        if (sub1Pos < sub0Pos) { /* a very odd pattern */
+            int32_t t=sub0Pos; sub0Pos=sub1Pos; sub1Pos=t;
+            langi=1;
         }
-        length+=locSepLen;
-    }
-
-    if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR) {
-        /* keep preflighting */
-        *pErrorCode=U_ZERO_ERROR;
-    }
-
-    /* append the country */
-    if(length<destCapacity) {
-        length2=uloc_getDisplayCountry(locale, displayLocale,
-                                       dest+length, destCapacity-length,
-                                       pErrorCode);
-    } else {
-        length2=uloc_getDisplayCountry(locale, displayLocale,
-                                       NULL, 0,
-                                       pErrorCode);
-    }
-    hasCountry= length2>0;
-    length+=length2;
-
-    if(hasCountry) {
-        /* append separator */
-        if(length+locSepLen<=destCapacity) {
-            u_memcpy(dest+length,dispLocSeparator,locSepLen);
+        if (u_strchr(pattern, 0xFF08) != NULL) {
+            formatOpenParen         = 0xFF08; // fullwidth (
+            formatReplaceOpenParen  = 0xFF3B; // fullwidth [
+            formatCloseParen        = 0xFF09; // fullwidth )
+            formatReplaceCloseParen = 0xFF3D; // fullwidth ]
         }
-        length+=locSepLen;
     }
 
-    if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR) {
-        /* keep preflighting */
-        *pErrorCode=U_ZERO_ERROR;
-    }
-
-    /* append the variant */
-    if(length<destCapacity) {
-        length2=uloc_getDisplayVariant(locale, displayLocale,
-                                       dest+length, destCapacity-length,
-                                       pErrorCode);
-    } else {
-        length2=uloc_getDisplayVariant(locale, displayLocale,
-                                       NULL, 0,
-                                       pErrorCode);
-    }
-    hasVariant= length2>0;
-    length+=length2;
-
-    if(hasVariant) {
-        /* append separator */
-        if(length+locSepLen<=destCapacity) {
-            u_memcpy(dest+length,dispLocSeparator,locSepLen);
+    /* We loop here because there is one case in which after the first pass we could need to
+     * reextract the data.  If there's initial padding before the first element, we put in
+     * the padding and then write that element.  If it turns out there's no second element,
+     * we didn't need the padding.  If we do need the data (no preflight), and the first element
+     * would have fit but for the padding, we need to reextract.  In this case (only) we
+     * adjust the parameters so padding is not added, and repeat.
+     */
+    do {
+        UChar* p=dest;
+        int32_t patPos=0; /* position in the pattern, used for non-substitution portions */
+        int32_t langLen=0; /* length of language substitution */
+        int32_t langPos=0; /* position in output of language substitution */
+        int32_t restLen=0; /* length of 'everything else' substitution */
+        int32_t restPos=0; /* position in output of 'everything else' substitution */
+        icu::LocalUEnumerationPointer kenum; /* keyword enumeration */
+
+        /* prefix of pattern, extremely likely to be empty */
+        if(sub0Pos) {
+            if(destCapacity >= sub0Pos) {
+                while (patPos < sub0Pos) {
+                    *p++ = pattern[patPos++];
+                }
+            } else {
+                patPos=sub0Pos;
+            }
+            length=sub0Pos;
+        } else {
+            length=0;
         }
-        length+=locSepLen;
-    }
 
-    keywordEnum = uloc_openKeywords(locale, pErrorCode);
-    
-    for(keywordCount = uenum_count(keywordEnum, pErrorCode); keywordCount > 0 ; keywordCount--){
-          if(U_FAILURE(*pErrorCode)){
-              break;
-          }
-          /* the uenum_next returns NUL terminated string */
-          keyword = uenum_next(keywordEnum, &keywordLen, pErrorCode);
-          if(length + length3 < destCapacity) {
-            length3 += uloc_getDisplayKeyword(keyword, displayLocale, dest+length+length3, destCapacity-length-length3, pErrorCode);
-          } else {
-            length3 += uloc_getDisplayKeyword(keyword, displayLocale, NULL, 0, pErrorCode);
-          }
-          if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR) {
-              /* keep preflighting */
-              *pErrorCode=U_ZERO_ERROR;
-          }
-          keywordValueLen = uloc_getKeywordValue(locale, keyword, keywordValue, 256, pErrorCode);
-          if(keywordValueLen) {
-            if(length + length3 < destCapacity) {
-              dest[length + length3] = 0x3D;
-            }
-            length3++;
-            if(length + length3 < destCapacity) {
-              length3 += uloc_getDisplayKeywordValue(locale, keyword, displayLocale, dest+length+length3, destCapacity-length-length3, pErrorCode);
+        for(int32_t subi=0,resti=0;subi<2;) { /* iterate through patterns 0 and 1*/
+            UBool subdone = FALSE; /* set true when ready to move to next substitution */
+
+            /* prep p and cap for calls to get display components, pin cap to 0 since
+               they complain if cap is negative */
+            int32_t cap=destCapacity-length;
+            if (cap <= 0) {
+                cap=0;
             } else {
-              length3 += uloc_getDisplayKeywordValue(locale, keyword, displayLocale, NULL, 0, pErrorCode);
-            }
-            if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR) {
-                /* keep preflighting */
-                *pErrorCode=U_ZERO_ERROR;
+                p=dest+length;
             }
-          }
-          if(keywordCount > 1) {
-              if(length + length3 + locSepLen <= destCapacity && keywordCount) {
-                  u_memcpy(dest+length+length3,dispLocSeparator,locSepLen);
-                  length3+=locSepLen;
-              }
-          }
-    }
-    uenum_close(keywordEnum);
-
-    hasKeywords = length3 > 0;
-    length += length3;
 
+            if (subi == langi) { /* {0}*/
+                if(haveLang) {
+                    langPos=length;
+                    langLen=uloc_getDisplayLanguage(locale, displayLocale, p, cap, pErrorCode);
+                    length+=langLen;
+                    haveLang=langLen>0;
+                }
+                subdone=TRUE;
+            } else { /* {1} */
+                if(!haveRest) {
+                    subdone=TRUE;
+                } else {
+                    int32_t len; /* length of component (plus other stuff) we just fetched */
+                    switch(resti++) {
+                        case 0:
+                            restPos=length;
+                            len=uloc_getDisplayScriptInContext(locale, displayLocale, p, cap, pErrorCode);
+                            break;
+                        case 1:
+                            len=uloc_getDisplayCountry(locale, displayLocale, p, cap, pErrorCode);
+                            break;
+                        case 2:
+                            len=uloc_getDisplayVariant(locale, displayLocale, p, cap, pErrorCode);
+                            break;
+                        case 3:
+                            kenum.adoptInstead(uloc_openKeywords(locale, pErrorCode));
+                            U_FALLTHROUGH;
+                        default: {
+                            const char* kw=uenum_next(kenum.getAlias(), &len, pErrorCode);
+                            if (kw == NULL) {
+                                len=0; /* mark that we didn't add a component */
+                                subdone=TRUE;
+                            } else {
+                                /* incorporating this behavior into the loop made it even more complex,
+                                   so just special case it here */
+                                len = uloc_getDisplayKeyword(kw, displayLocale, p, cap, pErrorCode);
+                                if(len) {
+                                    if(len < cap) {
+                                        p[len]=0x3d; /* '=', assume we'll need it */
+                                    }
+                                    len+=1;
+
+                                    /* adjust for call to get keyword */
+                                    cap-=len;
+                                    if(cap <= 0) {
+                                        cap=0;
+                                    } else {
+                                        p+=len;
+                                    }
+                                }
+                                /* reset for call below */
+                                if(*pErrorCode == U_BUFFER_OVERFLOW_ERROR) {
+                                    *pErrorCode=U_ZERO_ERROR;
+                                }
+                                int32_t vlen = uloc_getDisplayKeywordValue(locale, kw, displayLocale,
+                                                                           p, cap, pErrorCode);
+                                if(len) {
+                                    if(vlen==0) {
+                                        --len; /* remove unneeded '=' */
+                                    }
+                                    /* restore cap and p to what they were at start */
+                                    cap=destCapacity-length;
+                                    if(cap <= 0) {
+                                        cap=0;
+                                    } else {
+                                        p=dest+length;
+                                    }
+                                }
+                                len+=vlen; /* total we added for key + '=' + value */
+                            }
+                        } break;
+                    } /* end switch */
+
+                    if (len>0) {
+                        /* we addeed a component, so add separator and write it if there's room. */
+                        if(len+sepLen<=cap) {
+                            const UChar * plimit = p + len;
+                            for (; p < plimit; p++) {
+                                if (*p == formatOpenParen) {
+                                    *p = formatReplaceOpenParen;
+                                } else if (*p == formatCloseParen) {
+                                    *p = formatReplaceCloseParen;
+                                }
+                            }
+                            for(int32_t i=0;i<sepLen;++i) {
+                                *p++=separator[i];
+                            }
+                        }
+                        length+=len+sepLen;
+                    } else if(subdone) {
+                        /* remove separator if we added it */
+                        if (length!=restPos) {
+                            length-=sepLen;
+                        }
+                        restLen=length-restPos;
+                        haveRest=restLen>0;
+                    }
+                }
+            }
 
-    if ((hasScript && !hasCountry)
-        || ((hasScript || hasCountry) && !hasVariant && !hasKeywords)
-        || ((hasScript || hasCountry || hasVariant) && !hasKeywords)) {
-        /* Remove separator  */
-        length -= locSepLen;
-    } else if (hasLanguage && !hasScript && !hasCountry && !hasVariant && !hasKeywords) {
-        /* Remove " (" */
-        length-=2;
-    }
+            if(*pErrorCode == U_BUFFER_OVERFLOW_ERROR) {
+                *pErrorCode=U_ZERO_ERROR;
+            }
 
-    if (hasLanguage && (hasScript || hasCountry || hasVariant || hasKeywords)) {
-        /* append ")" */
-        if(length<destCapacity) {
-            dest[length]=0x29;
-        }
-        ++length;
-
-        /* If the localized display pattern is something other than the default pattern of "{0} ({1})", then
-         * then we need to do the formatting here.  It would be easier to use a messageFormat to do this, but we
-         * can't since we don't have the APIs in the i18n library available to us at this point.
-         */
-        if (locPatLen != defaultPatternLen || u_strcmp(dispLocPattern,defaultPattern)) { /* Something other than the default pattern */
-           UChar *p0 = u_strstr(dispLocPattern,pat0);
-           UChar *p1 = u_strstr(dispLocPattern,pat1);
-           u_terminateUChars(dest, destCapacity, length, pErrorCode);
-
-           if ( p0 != NULL && p1 != NULL ) { /* The pattern is well formed */
-              if ( dest ) {
-                  int32_t destLen = 0;
-                  UChar *result = (UChar *)uprv_malloc((length+1)*sizeof(UChar));
-                  UChar *upos = (UChar *)dispLocPattern;
-                  u_strcpy(result,dest);
-                  dest[0] = 0;
-                  while ( *upos ) {
-                     if ( upos == p0 ) { /* Handle {0} substitution */
-                         u_strncat(dest,result,p0Len);
-                         destLen += p0Len;
-                         dest[destLen] = 0; /* Null terminate */
-                         upos += 3;
-                     } else if ( upos == p1 ) { /* Handle {1} substitution */
-                         UChar *p1Start = &result[p0Len+2];
-                         u_strncat(dest,p1Start,length-p0Len-3);
-                         destLen += (length-p0Len-3);
-                         dest[destLen] = 0; /* Null terminate */
-                         upos += 3;
-                     } else { /* Something from the pattern not {0} or {1} */
-                         u_strncat(dest,upos,1);
-                         upos++;
-                         destLen++;
-                         dest[destLen] = 0; /* Null terminate */
-                     }
-                  }
-                  length = destLen;
-                  uprv_free(result);
-              }
-           }
+            if(subdone) {
+                if(haveLang && haveRest) {
+                    /* append internal portion of pattern, the first time,
+                       or last portion of pattern the second time */
+                    int32_t padLen;
+                    patPos+=subLen;
+                    padLen=(subi==0 ? sub1Pos : patLen)-patPos;
+                    if(length+padLen < destCapacity) {
+                        p=dest+length;
+                        for(int32_t i=0;i<padLen;++i) {
+                            *p++=pattern[patPos++];
+                        }
+                    } else {
+                        patPos+=padLen;
+                    }
+                    length+=padLen;
+                } else if(subi==0) {
+                    /* don't have first component, reset for second component */
+                    sub0Pos=0;
+                    length=0;
+                } else if(length>0) {
+                    /* true length is the length of just the component we got. */
+                    length=haveLang?langLen:restLen;
+                    if(dest && sub0Pos!=0) {
+                        if (sub0Pos+length<=destCapacity) {
+                            /* first component not at start of result,
+                               but we have full component in buffer. */
+                            u_memmove(dest, dest+(haveLang?langPos:restPos), length);
+                        } else {
+                            /* would have fit, but didn't because of pattern prefix. */
+                            sub0Pos=0; /* stops initial padding (and a second retry,
+                                          so we won't end up here again) */
+                            retry=TRUE;
+                        }
+                    }
+                }
+
+                ++subi; /* move on to next substitution */
+            }
         }
-    }
-    if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR) {
-        /* keep preflighting */
-        *pErrorCode=U_ZERO_ERROR;
-    }
+    } while(retry);
 
     return u_terminateUChars(dest, destCapacity, length, pErrorCode);
 }
@@ -761,6 +836,8 @@ uloc_getDisplayKeywordValue(   const char* locale,
     /* get the keyword value */
     keywordValue[0]=0;
     keywordValueLen = uloc_getKeywordValue(locale, keyword, keywordValue, capacity, status);
+    if (*status == U_STRING_NOT_TERMINATED_WARNING)
+      *status = U_BUFFER_OVERFLOW_ERROR;
 
     /* 
      * if the keyword is equal to currency .. then to get the display name 
@@ -770,18 +847,16 @@ uloc_getDisplayKeywordValue(   const char* locale,
 
         int32_t dispNameLen = 0;
         const UChar *dispName = NULL;
-        
-        UResourceBundle *bundle     = ures_open(U_ICUDATA_CURR, displayLocale, status);
-        UResourceBundle *currencies = ures_getByKey(bundle, _kCurrencies, NULL, status);
-        UResourceBundle *currency   = ures_getByKeyWithFallback(currencies, keywordValue, NULL, status);
-        
-        dispName = ures_getStringByIndex(currency, UCURRENCY_DISPLAY_NAME_INDEX, &dispNameLen, status);
-        
-        /*close the bundles */
-        ures_close(currency);
-        ures_close(currencies);
-        ures_close(bundle);
-        
+
+        icu::LocalUResourceBundlePointer bundle(
+                ures_open(U_ICUDATA_CURR, displayLocale, status));
+        icu::LocalUResourceBundlePointer currencies(
+                ures_getByKey(bundle.getAlias(), _kCurrencies, NULL, status));
+        icu::LocalUResourceBundlePointer currency(
+                ures_getByKeyWithFallback(currencies.getAlias(), keywordValue, NULL, status));
+
+        dispName = ures_getStringByIndex(currency.getAlias(), UCURRENCY_DISPLAY_NAME_INDEX, &dispNameLen, status);
+
         if(U_FAILURE(*status)){
             if(*status == U_MISSING_RESOURCE_ERROR){
                 /* we just want to write the value over if nothing is available */
@@ -794,7 +869,7 @@ uloc_getDisplayKeywordValue(   const char* locale,
         /* now copy the dispName over if not NULL */
         if(dispName != NULL){
             if(dispNameLen <= destCapacity){
-                uprv_memcpy(dest, dispName, dispNameLen * U_SIZEOF_UCHAR);
+                u_memcpy(dest, dispName, dispNameLen);
                 return u_terminateUChars(dest, destCapacity, dispNameLen, status);
             }else{
                 *status = U_BUFFER_OVERFLOW_ERROR;