1/*
2*******************************************************************************
3*   Copyright (C) 1996-2014, International Business Machines
4*   Corporation and others.  All Rights Reserved.
5*******************************************************************************
6*   file name:  ucol_res.cpp
7*   encoding:   US-ASCII
8*   tab size:   8 (not used)
9*   indentation:4
10*
11* Description:
12* This file contains dependencies that the collation run-time doesn't normally
13* need. This mainly contains resource bundle usage and collation meta information
14*
15* Modification history
16* Date        Name      Comments
17* 1996-1999   various members of ICU team maintained C API for collation framework
18* 02/16/2001  synwee    Added internal method getPrevSpecialCE
19* 03/01/2001  synwee    Added maxexpansion functionality.
20* 03/16/2001  weiv      Collation framework is rewritten in C and made UCA compliant
21* 12/08/2004  grhoten   Split part of ucol.cpp into ucol_res.cpp
22* 2012-2014   markus    Rewritten in C++ again.
23*/
24
25#include "unicode/utypes.h"
26
27#if !UCONFIG_NO_COLLATION
28
29#include "unicode/coll.h"
30#include "unicode/localpointer.h"
31#include "unicode/locid.h"
32#include "unicode/tblcoll.h"
33#include "unicode/ucol.h"
34#include "unicode/uloc.h"
35#include "unicode/unistr.h"
36#include "unicode/ures.h"
37#include "cmemory.h"
38#include "cstring.h"
39#include "collationdatareader.h"
40#include "collationroot.h"
41#include "collationtailoring.h"
42#include "putilimp.h"
43#include "uassert.h"
44#include "ucln_in.h"
45#include "ucol_imp.h"
46#include "uenumimp.h"
47#include "ulist.h"
48#include "umutex.h"
49#include "unifiedcache.h"
50#include "uresimp.h"
51#include "ustrenum.h"
52#include "utracimp.h"
53
54U_NAMESPACE_BEGIN
55
56namespace {
57
58static const UChar *rootRules = NULL;
59static int32_t rootRulesLength = 0;
60static UResourceBundle *rootBundle = NULL;
61static UInitOnce gInitOnce = U_INITONCE_INITIALIZER;
62
63}  // namespace
64
65U_CDECL_BEGIN
66
67static UBool U_CALLCONV
68ucol_res_cleanup() {
69    rootRules = NULL;
70    rootRulesLength = 0;
71    ures_close(rootBundle);
72    rootBundle = NULL;
73    gInitOnce.reset();
74    return TRUE;
75}
76
77U_CDECL_END
78
79void
80CollationLoader::loadRootRules(UErrorCode &errorCode) {
81    if(U_FAILURE(errorCode)) { return; }
82    rootBundle = ures_open(U_ICUDATA_COLL, kRootLocaleName, &errorCode);
83    if(U_FAILURE(errorCode)) { return; }
84    rootRules = ures_getStringByKey(rootBundle, "UCARules", &rootRulesLength, &errorCode);
85    if(U_FAILURE(errorCode)) {
86        ures_close(rootBundle);
87        rootBundle = NULL;
88        return;
89    }
90    ucln_i18n_registerCleanup(UCLN_I18N_UCOL_RES, ucol_res_cleanup);
91}
92
93void
94CollationLoader::appendRootRules(UnicodeString &s) {
95    UErrorCode errorCode = U_ZERO_ERROR;
96    umtx_initOnce(gInitOnce, CollationLoader::loadRootRules, errorCode);
97    if(U_SUCCESS(errorCode)) {
98        s.append(rootRules, rootRulesLength);
99    }
100}
101
102void
103CollationLoader::loadRules(const char *localeID, const char *collationType,
104                           UnicodeString &rules, UErrorCode &errorCode) {
105    if(U_FAILURE(errorCode)) { return; }
106    U_ASSERT(collationType != NULL && *collationType != 0);
107    // Copy the type for lowercasing.
108    char type[16];
109    int32_t typeLength = uprv_strlen(collationType);
110    if(typeLength >= UPRV_LENGTHOF(type)) {
111        errorCode = U_ILLEGAL_ARGUMENT_ERROR;
112        return;
113    }
114    uprv_memcpy(type, collationType, typeLength + 1);
115    T_CString_toLowerCase(type);
116
117    LocalUResourceBundlePointer bundle(ures_open(U_ICUDATA_COLL, localeID, &errorCode));
118    LocalUResourceBundlePointer collations(
119            ures_getByKey(bundle.getAlias(), "collations", NULL, &errorCode));
120    LocalUResourceBundlePointer data(
121            ures_getByKeyWithFallback(collations.getAlias(), type, NULL, &errorCode));
122    int32_t length;
123    const UChar *s =  ures_getStringByKey(data.getAlias(), "Sequence", &length, &errorCode);
124    if(U_FAILURE(errorCode)) { return; }
125
126    // No string pointer aliasing so that we need not hold onto the resource bundle.
127    rules.setTo(s, length);
128    if(rules.isBogus()) {
129        errorCode = U_MEMORY_ALLOCATION_ERROR;
130    }
131}
132
133template<> U_I18N_API
134const CollationCacheEntry *
135LocaleCacheKey<CollationCacheEntry>::createObject(const void *creationContext,
136                                                  UErrorCode &errorCode) const {
137    CollationLoader *loader =
138            reinterpret_cast<CollationLoader *>(
139                    const_cast<void *>(creationContext));
140    return loader->createCacheEntry(errorCode);
141}
142
143const CollationCacheEntry *
144CollationLoader::loadTailoring(const Locale &locale, UErrorCode &errorCode) {
145    const CollationCacheEntry *rootEntry = CollationRoot::getRootCacheEntry(errorCode);
146    if(U_FAILURE(errorCode)) { return NULL; }
147    const char *name = locale.getName();
148    if(*name == 0 || uprv_strcmp(name, "root") == 0) {
149
150        // Have to add a ref.
151        rootEntry->addRef();
152        return rootEntry;
153    }
154
155    // Clear warning codes before loading where they get cached.
156    errorCode = U_ZERO_ERROR;
157    CollationLoader loader(rootEntry, locale, errorCode);
158
159    // getCacheEntry adds a ref for us.
160    return loader.getCacheEntry(errorCode);
161}
162
163CollationLoader::CollationLoader(const CollationCacheEntry *re, const Locale &requested,
164                                 UErrorCode &errorCode)
165        : cache(UnifiedCache::getInstance(errorCode)), rootEntry(re),
166          validLocale(re->validLocale), locale(requested),
167          typesTried(0), typeFallback(FALSE),
168          bundle(NULL), collations(NULL), data(NULL) {
169    type[0] = 0;
170    defaultType[0] = 0;
171    if(U_FAILURE(errorCode)) { return; }
172
173    // Canonicalize the locale ID: Ignore all irrelevant keywords.
174    const char *baseName = locale.getBaseName();
175    if(uprv_strcmp(locale.getName(), baseName) != 0) {
176        locale = Locale(baseName);
177
178        // Fetch the collation type from the locale ID.
179        int32_t typeLength = requested.getKeywordValue("collation",
180                type, UPRV_LENGTHOF(type) - 1, errorCode);
181        if(U_FAILURE(errorCode)) {
182            errorCode = U_ILLEGAL_ARGUMENT_ERROR;
183            return;
184        }
185        type[typeLength] = 0;  // in case of U_NOT_TERMINATED_WARNING
186        if(typeLength == 0) {
187            // No collation type.
188        } else if(uprv_stricmp(type, "default") == 0) {
189            // Ignore "default" (case-insensitive).
190            type[0] = 0;
191        } else {
192            // Copy the collation type.
193            T_CString_toLowerCase(type);
194            locale.setKeywordValue("collation", type, errorCode);
195        }
196    }
197}
198
199CollationLoader::~CollationLoader() {
200    ures_close(data);
201    ures_close(collations);
202    ures_close(bundle);
203}
204
205const CollationCacheEntry *
206CollationLoader::createCacheEntry(UErrorCode &errorCode) {
207    // This is a linear lookup and fallback flow turned into a state machine.
208    // Most local variables have been turned into instance fields.
209    // In a cache miss, cache.get() calls CacheKey::createObject(),
210    // which means that we progress via recursion.
211    // loadFromCollations() will recurse to itself as well for collation type fallback.
212    if(bundle == NULL) {
213        return loadFromLocale(errorCode);
214    } else if(collations == NULL) {
215        return loadFromBundle(errorCode);
216    } else if(data == NULL) {
217        return loadFromCollations(errorCode);
218    } else {
219        return loadFromData(errorCode);
220    }
221}
222
223const CollationCacheEntry *
224CollationLoader::loadFromLocale(UErrorCode &errorCode) {
225    if(U_FAILURE(errorCode)) { return NULL; }
226    U_ASSERT(bundle == NULL);
227    bundle = ures_openNoDefault(U_ICUDATA_COLL, locale.getBaseName(), &errorCode);
228    if(errorCode == U_MISSING_RESOURCE_ERROR) {
229        errorCode = U_USING_DEFAULT_WARNING;
230
231        // Have to add that ref that we promise.
232        rootEntry->addRef();
233        return rootEntry;
234    }
235    Locale requestedLocale(locale);
236    const char *vLocale = ures_getLocaleByType(bundle, ULOC_ACTUAL_LOCALE, &errorCode);
237    if(U_FAILURE(errorCode)) { return NULL; }
238    locale = validLocale = Locale(vLocale);  // no type until loadFromCollations()
239    if(type[0] != 0) {
240        locale.setKeywordValue("collation", type, errorCode);
241    }
242    if(locale != requestedLocale) {
243        return getCacheEntry(errorCode);
244    } else {
245        return loadFromBundle(errorCode);
246    }
247}
248
249const CollationCacheEntry *
250CollationLoader::loadFromBundle(UErrorCode &errorCode) {
251    if(U_FAILURE(errorCode)) { return NULL; }
252    U_ASSERT(collations == NULL);
253    // There are zero or more tailorings in the collations table.
254    collations = ures_getByKey(bundle, "collations", NULL, &errorCode);
255    if(errorCode == U_MISSING_RESOURCE_ERROR) {
256        errorCode = U_USING_DEFAULT_WARNING;
257        // Return the root tailoring with the validLocale, without collation type.
258        return makeCacheEntryFromRoot(validLocale, errorCode);
259    }
260    if(U_FAILURE(errorCode)) { return NULL; }
261
262    // Fetch the default type from the data.
263    {
264        UErrorCode internalErrorCode = U_ZERO_ERROR;
265        LocalUResourceBundlePointer def(
266                ures_getByKeyWithFallback(collations, "default", NULL, &internalErrorCode));
267        int32_t length;
268        const UChar *s = ures_getString(def.getAlias(), &length, &internalErrorCode);
269        if(U_SUCCESS(internalErrorCode) && 0 < length && length < UPRV_LENGTHOF(defaultType)) {
270            u_UCharsToChars(s, defaultType, length + 1);
271        } else {
272            uprv_strcpy(defaultType, "standard");
273        }
274    }
275
276    // Record which collation types we have looked for already,
277    // so that we do not deadlock in the cache.
278    //
279    // If there is no explicit type, then we look in the cache
280    // for the entry with the default type.
281    // If the explicit type is the default type, then we do not look in the cache
282    // for the entry with an empty type.
283    // Otherwise, two concurrent requests with opposite fallbacks would deadlock each other.
284    // Also, it is easier to always enter the next method with a non-empty type.
285    if(type[0] == 0) {
286        uprv_strcpy(type, defaultType);
287        typesTried |= TRIED_DEFAULT;
288        if(uprv_strcmp(type, "search") == 0) {
289            typesTried |= TRIED_SEARCH;
290        }
291        if(uprv_strcmp(type, "standard") == 0) {
292            typesTried |= TRIED_STANDARD;
293        }
294        locale.setKeywordValue("collation", type, errorCode);
295        return getCacheEntry(errorCode);
296    } else {
297        if(uprv_strcmp(type, defaultType) == 0) {
298            typesTried |= TRIED_DEFAULT;
299        }
300        if(uprv_strcmp(type, "search") == 0) {
301            typesTried |= TRIED_SEARCH;
302        }
303        if(uprv_strcmp(type, "standard") == 0) {
304            typesTried |= TRIED_STANDARD;
305        }
306        return loadFromCollations(errorCode);
307    }
308}
309
310const CollationCacheEntry *
311CollationLoader::loadFromCollations(UErrorCode &errorCode) {
312    if(U_FAILURE(errorCode)) { return NULL; }
313    U_ASSERT(data == NULL);
314    // Load the collations/type tailoring, with type fallback.
315    LocalUResourceBundlePointer localData(
316            ures_getByKeyWithFallback(collations, type, NULL, &errorCode));
317    int32_t typeLength = uprv_strlen(type);
318    if(errorCode == U_MISSING_RESOURCE_ERROR) {
319        errorCode = U_USING_DEFAULT_WARNING;
320        typeFallback = TRUE;
321        if((typesTried & TRIED_SEARCH) == 0 &&
322                typeLength > 6 && uprv_strncmp(type, "search", 6) == 0) {
323            // fall back from something like "searchjl" to "search"
324            typesTried |= TRIED_SEARCH;
325            type[6] = 0;
326        } else if((typesTried & TRIED_DEFAULT) == 0) {
327            // fall back to the default type
328            typesTried |= TRIED_DEFAULT;
329            uprv_strcpy(type, defaultType);
330        } else if((typesTried & TRIED_STANDARD) == 0) {
331            // fall back to the "standard" type
332            typesTried |= TRIED_STANDARD;
333            uprv_strcpy(type, "standard");
334        } else {
335            // Return the root tailoring with the validLocale, without collation type.
336            return makeCacheEntryFromRoot(validLocale, errorCode);
337        }
338        locale.setKeywordValue("collation", type, errorCode);
339        return getCacheEntry(errorCode);
340    }
341    if(U_FAILURE(errorCode)) { return NULL; }
342
343    data = localData.orphan();
344    const char *actualLocale = ures_getLocaleByType(data, ULOC_ACTUAL_LOCALE, &errorCode);
345    if(U_FAILURE(errorCode)) { return NULL; }
346    const char *vLocale = validLocale.getBaseName();
347    UBool actualAndValidLocalesAreDifferent = uprv_strcmp(actualLocale, vLocale) != 0;
348
349    // Set the collation types on the informational locales,
350    // except when they match the default types (for brevity and backwards compatibility).
351    // For the valid locale, suppress the default type.
352    if(uprv_strcmp(type, defaultType) != 0) {
353        validLocale.setKeywordValue("collation", type, errorCode);
354        if(U_FAILURE(errorCode)) { return NULL; }
355    }
356
357    // Is this the same as the root collator? If so, then use that instead.
358    if((*actualLocale == 0 || uprv_strcmp(actualLocale, "root") == 0) &&
359            uprv_strcmp(type, "standard") == 0) {
360        if(typeFallback) {
361            errorCode = U_USING_DEFAULT_WARNING;
362        }
363        return makeCacheEntryFromRoot(validLocale, errorCode);
364    }
365
366    locale = Locale(actualLocale);
367    if(actualAndValidLocalesAreDifferent) {
368        locale.setKeywordValue("collation", type, errorCode);
369        const CollationCacheEntry *entry = getCacheEntry(errorCode);
370        return makeCacheEntry(validLocale, entry, errorCode);
371    } else {
372        return loadFromData(errorCode);
373    }
374}
375
376const CollationCacheEntry *
377CollationLoader::loadFromData(UErrorCode &errorCode) {
378    if(U_FAILURE(errorCode)) { return NULL; }
379    LocalPointer<CollationTailoring> t(new CollationTailoring(rootEntry->tailoring->settings));
380    if(t.isNull() || t->isBogus()) {
381        errorCode = U_MEMORY_ALLOCATION_ERROR;
382        return NULL;
383    }
384
385    // deserialize
386    LocalUResourceBundlePointer binary(ures_getByKey(data, "%%CollationBin", NULL, &errorCode));
387    // Note: U_MISSING_RESOURCE_ERROR --> The old code built from rules if available
388    // but that created undesirable dependencies.
389    int32_t length;
390    const uint8_t *inBytes = ures_getBinary(binary.getAlias(), &length, &errorCode);
391    CollationDataReader::read(rootEntry->tailoring, inBytes, length, *t, errorCode);
392    // Note: U_COLLATOR_VERSION_MISMATCH --> The old code built from rules if available
393    // but that created undesirable dependencies.
394    if(U_FAILURE(errorCode)) { return NULL; }
395
396    // Try to fetch the optional rules string.
397    {
398        UErrorCode internalErrorCode = U_ZERO_ERROR;
399        int32_t length;
400        const UChar *s = ures_getStringByKey(data, "Sequence", &length,
401                                             &internalErrorCode);
402        if(U_SUCCESS(internalErrorCode)) {
403            t->rules.setTo(TRUE, s, length);
404        }
405    }
406
407    const char *actualLocale = locale.getBaseName();  // without type
408    const char *vLocale = validLocale.getBaseName();
409    UBool actualAndValidLocalesAreDifferent = uprv_strcmp(actualLocale, vLocale) != 0;
410
411    // For the actual locale, suppress the default type *according to the actual locale*.
412    // For example, zh has default=pinyin and contains all of the Chinese tailorings.
413    // zh_Hant has default=stroke but has no other data.
414    // For the valid locale "zh_Hant" we need to suppress stroke.
415    // For the actual locale "zh" we need to suppress pinyin instead.
416    if(actualAndValidLocalesAreDifferent) {
417        // Opening a bundle for the actual locale should always succeed.
418        LocalUResourceBundlePointer actualBundle(
419                ures_open(U_ICUDATA_COLL, actualLocale, &errorCode));
420        if(U_FAILURE(errorCode)) { return NULL; }
421        UErrorCode internalErrorCode = U_ZERO_ERROR;
422        LocalUResourceBundlePointer def(
423                ures_getByKeyWithFallback(actualBundle.getAlias(), "collations/default", NULL,
424                                          &internalErrorCode));
425        int32_t length;
426        const UChar *s = ures_getString(def.getAlias(), &length, &internalErrorCode);
427        if(U_SUCCESS(internalErrorCode) && length < UPRV_LENGTHOF(defaultType)) {
428            u_UCharsToChars(s, defaultType, length + 1);
429        } else {
430            uprv_strcpy(defaultType, "standard");
431        }
432    }
433    t->actualLocale = locale;
434    if(uprv_strcmp(type, defaultType) != 0) {
435        t->actualLocale.setKeywordValue("collation", type, errorCode);
436    } else if(uprv_strcmp(locale.getName(), locale.getBaseName()) != 0) {
437        // Remove the collation keyword if it was set.
438        t->actualLocale.setKeywordValue("collation", NULL, errorCode);
439    }
440    if(U_FAILURE(errorCode)) { return NULL; }
441
442    if(typeFallback) {
443        errorCode = U_USING_DEFAULT_WARNING;
444    }
445    t->bundle = bundle;
446    bundle = NULL;
447    const CollationCacheEntry *entry = new CollationCacheEntry(validLocale, t.getAlias());
448    if(entry == NULL) {
449        errorCode = U_MEMORY_ALLOCATION_ERROR;
450    } else {
451        t.orphan();
452    }
453    // Have to add that reference that we promise.
454    entry->addRef();
455    return entry;
456}
457
458const CollationCacheEntry *
459CollationLoader::getCacheEntry(UErrorCode &errorCode) {
460    LocaleCacheKey<CollationCacheEntry> key(locale);
461    const CollationCacheEntry *entry = NULL;
462    cache->get(key, this, entry, errorCode);
463    return entry;
464}
465
466const CollationCacheEntry *
467CollationLoader::makeCacheEntryFromRoot(
468        const Locale &/*loc*/,
469        UErrorCode &errorCode) const {
470    if (U_FAILURE(errorCode)) {
471        return NULL;
472    }
473    rootEntry->addRef();
474    return makeCacheEntry(validLocale, rootEntry, errorCode);
475}
476
477const CollationCacheEntry *
478CollationLoader::makeCacheEntry(
479        const Locale &loc,
480        const CollationCacheEntry *entryFromCache,
481        UErrorCode &errorCode) {
482    if(U_FAILURE(errorCode) || loc == entryFromCache->validLocale) {
483        return entryFromCache;
484    }
485    CollationCacheEntry *entry = new CollationCacheEntry(loc, entryFromCache->tailoring);
486    if(entry == NULL) {
487        errorCode = U_MEMORY_ALLOCATION_ERROR;
488        entryFromCache->removeRef();
489        return NULL;
490    }
491    entry->addRef();
492    entryFromCache->removeRef();
493    return entry;
494}
495
496U_NAMESPACE_END
497
498U_NAMESPACE_USE
499
500U_CAPI UCollator*
501ucol_open(const char *loc,
502          UErrorCode *status)
503{
504    U_NAMESPACE_USE
505
506    UTRACE_ENTRY_OC(UTRACE_UCOL_OPEN);
507    UTRACE_DATA1(UTRACE_INFO, "locale = \"%s\"", loc);
508    UCollator *result = NULL;
509
510    Collator *coll = Collator::createInstance(loc, *status);
511    if(U_SUCCESS(*status)) {
512        result = coll->toUCollator();
513    }
514    UTRACE_EXIT_PTR_STATUS(result, *status);
515    return result;
516}
517
518
519U_CAPI int32_t U_EXPORT2
520ucol_getDisplayName(    const    char        *objLoc,
521                    const    char        *dispLoc,
522                    UChar             *result,
523                    int32_t         resultLength,
524                    UErrorCode        *status)
525{
526    U_NAMESPACE_USE
527
528    if(U_FAILURE(*status)) return -1;
529    UnicodeString dst;
530    if(!(result==NULL && resultLength==0)) {
531        // NULL destination for pure preflighting: empty dummy string
532        // otherwise, alias the destination buffer
533        dst.setTo(result, 0, resultLength);
534    }
535    Collator::getDisplayName(Locale(objLoc), Locale(dispLoc), dst);
536    return dst.extract(result, resultLength, *status);
537}
538
539U_CAPI const char* U_EXPORT2
540ucol_getAvailable(int32_t index)
541{
542    int32_t count = 0;
543    const Locale *loc = Collator::getAvailableLocales(count);
544    if (loc != NULL && index < count) {
545        return loc[index].getName();
546    }
547    return NULL;
548}
549
550U_CAPI int32_t U_EXPORT2
551ucol_countAvailable()
552{
553    int32_t count = 0;
554    Collator::getAvailableLocales(count);
555    return count;
556}
557
558#if !UCONFIG_NO_SERVICE
559U_CAPI UEnumeration* U_EXPORT2
560ucol_openAvailableLocales(UErrorCode *status) {
561    U_NAMESPACE_USE
562
563    // This is a wrapper over Collator::getAvailableLocales()
564    if (U_FAILURE(*status)) {
565        return NULL;
566    }
567    StringEnumeration *s = icu::Collator::getAvailableLocales();
568    if (s == NULL) {
569        *status = U_MEMORY_ALLOCATION_ERROR;
570        return NULL;
571    }
572    return uenum_openFromStringEnumeration(s, status);
573}
574#endif
575
576// Note: KEYWORDS[0] != RESOURCE_NAME - alan
577
578static const char RESOURCE_NAME[] = "collations";
579
580static const char* const KEYWORDS[] = { "collation" };
581
582#define KEYWORD_COUNT UPRV_LENGTHOF(KEYWORDS)
583
584U_CAPI UEnumeration* U_EXPORT2
585ucol_getKeywords(UErrorCode *status) {
586    UEnumeration *result = NULL;
587    if (U_SUCCESS(*status)) {
588        return uenum_openCharStringsEnumeration(KEYWORDS, KEYWORD_COUNT, status);
589    }
590    return result;
591}
592
593U_CAPI UEnumeration* U_EXPORT2
594ucol_getKeywordValues(const char *keyword, UErrorCode *status) {
595    if (U_FAILURE(*status)) {
596        return NULL;
597    }
598    // hard-coded to accept exactly one collation keyword
599    // modify if additional collation keyword is added later
600    if (keyword==NULL || uprv_strcmp(keyword, KEYWORDS[0])!=0)
601    {
602        *status = U_ILLEGAL_ARGUMENT_ERROR;
603        return NULL;
604    }
605    return ures_getKeywordValues(U_ICUDATA_COLL, RESOURCE_NAME, status);
606}
607
608static const UEnumeration defaultKeywordValues = {
609    NULL,
610    NULL,
611    ulist_close_keyword_values_iterator,
612    ulist_count_keyword_values,
613    uenum_unextDefault,
614    ulist_next_keyword_value,
615    ulist_reset_keyword_values_iterator
616};
617
618#include <stdio.h>
619
620U_CAPI UEnumeration* U_EXPORT2
621ucol_getKeywordValuesForLocale(const char* /*key*/, const char* locale,
622                               UBool /*commonlyUsed*/, UErrorCode* status) {
623    /* Get the locale base name. */
624    char localeBuffer[ULOC_FULLNAME_CAPACITY] = "";
625    uloc_getBaseName(locale, localeBuffer, sizeof(localeBuffer), status);
626
627    /* Create the 2 lists
628     * -values is the temp location for the keyword values
629     * -results hold the actual list used by the UEnumeration object
630     */
631    UList *values = ulist_createEmptyList(status);
632    UList *results = ulist_createEmptyList(status);
633    UEnumeration *en = (UEnumeration *)uprv_malloc(sizeof(UEnumeration));
634    if (U_FAILURE(*status) || en == NULL) {
635        if (en == NULL) {
636            *status = U_MEMORY_ALLOCATION_ERROR;
637        } else {
638            uprv_free(en);
639        }
640        ulist_deleteList(values);
641        ulist_deleteList(results);
642        return NULL;
643    }
644
645    memcpy(en, &defaultKeywordValues, sizeof(UEnumeration));
646    en->context = results;
647
648    /* Open the resource bundle for collation with the given locale. */
649    UResourceBundle bundle, collations, collres, defres;
650    ures_initStackObject(&bundle);
651    ures_initStackObject(&collations);
652    ures_initStackObject(&collres);
653    ures_initStackObject(&defres);
654
655    ures_openFillIn(&bundle, U_ICUDATA_COLL, localeBuffer, status);
656
657    while (U_SUCCESS(*status)) {
658        ures_getByKey(&bundle, RESOURCE_NAME, &collations, status);
659        ures_resetIterator(&collations);
660        while (U_SUCCESS(*status) && ures_hasNext(&collations)) {
661            ures_getNextResource(&collations, &collres, status);
662            const char *key = ures_getKey(&collres);
663            /* If the key is default, get the string and store it in results list only
664             * if results list is empty.
665             */
666            if (uprv_strcmp(key, "default") == 0) {
667                if (ulist_getListSize(results) == 0) {
668                    char *defcoll = (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY);
669                    int32_t defcollLength = ULOC_KEYWORDS_CAPACITY;
670
671                    ures_getNextResource(&collres, &defres, status);
672#if U_CHARSET_FAMILY==U_ASCII_FAMILY
673			/* optimize - use the utf-8 string */
674                    ures_getUTF8String(&defres, defcoll, &defcollLength, TRUE, status);
675#else
676                    {
677                       const UChar* defString = ures_getString(&defres, &defcollLength, status);
678                       if(U_SUCCESS(*status)) {
679			   if(defcollLength+1 > ULOC_KEYWORDS_CAPACITY) {
680				*status = U_BUFFER_OVERFLOW_ERROR;
681			   } else {
682                           	u_UCharsToChars(defString, defcoll, defcollLength+1);
683			   }
684                       }
685                    }
686#endif
687
688                    ulist_addItemBeginList(results, defcoll, TRUE, status);
689                }
690            } else if (uprv_strncmp(key, "private-", 8) != 0) {
691                ulist_addItemEndList(values, key, FALSE, status);
692            }
693        }
694
695        /* If the locale is "" this is root so exit. */
696        if (uprv_strlen(localeBuffer) == 0) {
697            break;
698        }
699        /* Get the parent locale and open a new resource bundle. */
700        uloc_getParent(localeBuffer, localeBuffer, sizeof(localeBuffer), status);
701        ures_openFillIn(&bundle, U_ICUDATA_COLL, localeBuffer, status);
702    }
703
704    ures_close(&defres);
705    ures_close(&collres);
706    ures_close(&collations);
707    ures_close(&bundle);
708
709    if (U_SUCCESS(*status)) {
710        char *value = NULL;
711        ulist_resetList(values);
712        while ((value = (char *)ulist_getNext(values)) != NULL) {
713            if (!ulist_containsString(results, value, (int32_t)uprv_strlen(value))) {
714                ulist_addItemEndList(results, value, FALSE, status);
715                if (U_FAILURE(*status)) {
716                    break;
717                }
718            }
719        }
720    }
721
722    ulist_deleteList(values);
723
724    if (U_FAILURE(*status)){
725        uenum_close(en);
726        en = NULL;
727    } else {
728        ulist_resetList(results);
729    }
730
731    return en;
732}
733
734U_CAPI int32_t U_EXPORT2
735ucol_getFunctionalEquivalent(char* result, int32_t resultCapacity,
736                             const char* keyword, const char* locale,
737                             UBool* isAvailable, UErrorCode* status)
738{
739    // N.B.: Resource name is "collations" but keyword is "collation"
740    return ures_getFunctionalEquivalent(result, resultCapacity, U_ICUDATA_COLL,
741        "collations", keyword, locale,
742        isAvailable, TRUE, status);
743}
744
745#endif /* #if !UCONFIG_NO_COLLATION */
746