1/*
2******************************************************************************
3*
4*   Copyright (C) 1999-2010, International Business Machines
5*   Corporation and others.  All Rights Reserved.
6*
7******************************************************************************
8*   file name:  udata.cpp
9*   encoding:   US-ASCII
10*   tab size:   8 (not used)
11*   indentation:4
12*
13*   created on: 1999oct25
14*   created by: Markus W. Scherer
15*/
16
17#include "unicode/utypes.h"  /* U_LINUX */
18
19#ifdef U_LINUX
20/* if gcc
21#define ATTRIBUTE_WEAK __attribute__ ((weak))
22might have to #include some other header
23*/
24#endif
25
26#include "unicode/putil.h"
27#include "unicode/udata.h"
28#include "unicode/uversion.h"
29#include "charstr.h"
30#include "cmemory.h"
31#include "cstring.h"
32#include "putilimp.h"
33#include "ucln_cmn.h"
34#include "ucmndata.h"
35#include "udatamem.h"
36#include "uhash.h"
37#include "umapfile.h"
38#include "umutex.h"
39
40/***********************************************************************
41*
42*   Notes on the organization of the ICU data implementation
43*
44*      All of the public API is defined in udata.h
45*
46*      The implementation is split into several files...
47*
48*         - udata.c  (this file) contains higher level code that knows about
49*                     the search paths for locating data, caching opened data, etc.
50*
51*         - umapfile.c  contains the low level platform-specific code for actually loading
52*                     (memory mapping, file reading, whatever) data into memory.
53*
54*         - ucmndata.c  deals with the tables of contents of ICU data items within
55*                     an ICU common format data file.  The implementation includes
56*                     an abstract interface and support for multiple TOC formats.
57*                     All knowledge of any specific TOC format is encapsulated here.
58*
59*         - udatamem.c has code for managing UDataMemory structs.  These are little
60*                     descriptor objects for blocks of memory holding ICU data of
61*                     various types.
62*/
63
64/* configuration ---------------------------------------------------------- */
65
66/* If you are excruciatingly bored turn this on .. */
67/* #define UDATA_DEBUG 1 */
68
69#if defined(UDATA_DEBUG)
70#   include <stdio.h>
71#endif
72
73#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
74
75U_NAMESPACE_USE
76
77/***********************************************************************
78*
79*    static (Global) data
80*
81************************************************************************/
82
83/*
84 * Pointers to the common ICU data.
85 *
86 * We store multiple pointers to ICU data packages and iterate through them
87 * when looking for a data item.
88 *
89 * It is possible to combine this with dependency inversion:
90 * One or more data package libraries may export
91 * functions that each return a pointer to their piece of the ICU data,
92 * and this file would import them as weak functions, without a
93 * strong linker dependency from the common library on the data library.
94 *
95 * Then we can have applications depend on only that part of ICU's data
96 * that they really need, reducing the size of binaries that take advantage
97 * of this.
98 */
99static UDataMemory *gCommonICUDataArray[10] = { NULL };
100
101static UBool gHaveTriedToLoadCommonData = FALSE;  /* See extendICUData(). */
102
103static UHashtable  *gCommonDataCache = NULL;  /* Global hash table of opened ICU data files.  */
104
105static UDataFileAccess  gDataFileAccess = UDATA_DEFAULT_ACCESS;
106
107static UBool U_CALLCONV
108udata_cleanup(void)
109{
110    int32_t i;
111
112    if (gCommonDataCache) {             /* Delete the cache of user data mappings.  */
113        uhash_close(gCommonDataCache);  /*   Table owns the contents, and will delete them. */
114        gCommonDataCache = NULL;        /*   Cleanup is not thread safe.                */
115    }
116
117    for (i = 0; i < LENGTHOF(gCommonICUDataArray) && gCommonICUDataArray[i] != NULL; ++i) {
118        udata_close(gCommonICUDataArray[i]);
119        gCommonICUDataArray[i] = NULL;
120    }
121    gHaveTriedToLoadCommonData = FALSE;
122
123    return TRUE;                   /* Everything was cleaned up */
124}
125
126
127
128
129/*
130 * setCommonICUData.   Set a UDataMemory to be the global ICU Data
131 */
132static UBool
133setCommonICUData(UDataMemory *pData,     /*  The new common data.  Belongs to caller, we copy it. */
134                 UBool       warn,       /*  If true, set USING_DEFAULT warning if ICUData was    */
135                                         /*    changed by another thread before we got to it.     */
136                 UErrorCode *pErr)
137{
138    UDataMemory  *newCommonData = UDataMemory_createNewInstance(pErr);
139    int32_t i;
140    UBool didUpdate = FALSE;
141    if (U_FAILURE(*pErr)) {
142        return FALSE;
143    }
144
145    /*  For the assignment, other threads must cleanly see either the old            */
146    /*    or the new, not some partially initialized new.  The old can not be        */
147    /*    deleted - someone may still have a pointer to it lying around in           */
148    /*    their locals.                                                              */
149    UDatamemory_assign(newCommonData, pData);
150    umtx_lock(NULL);
151    for (i = 0; i < LENGTHOF(gCommonICUDataArray); ++i) {
152        if (gCommonICUDataArray[i] == NULL) {
153            gCommonICUDataArray[i] = newCommonData;
154            ucln_common_registerCleanup(UCLN_COMMON_UDATA, udata_cleanup);
155            didUpdate = TRUE;
156            break;
157        } else if (gCommonICUDataArray[i]->pHeader == pData->pHeader) {
158            /* The same data pointer is already in the array. */
159            break;
160        }
161    }
162    umtx_unlock(NULL);
163
164    if (i == LENGTHOF(gCommonICUDataArray) && warn) {
165        *pErr = U_USING_DEFAULT_WARNING;
166    }
167    if (!didUpdate) {
168        uprv_free(newCommonData);
169    }
170    return didUpdate;
171}
172
173static UBool
174setCommonICUDataPointer(const void *pData, UBool /*warn*/, UErrorCode *pErrorCode) {
175    UDataMemory tData;
176    UDataMemory_init(&tData);
177    tData.pHeader = (const DataHeader *)pData;
178    udata_checkCommonData(&tData, pErrorCode);
179    return setCommonICUData(&tData, FALSE, pErrorCode);
180}
181
182static const char *
183findBasename(const char *path) {
184    const char *basename=uprv_strrchr(path, U_FILE_SEP_CHAR);
185    if(basename==NULL) {
186        return path;
187    } else {
188        return basename+1;
189    }
190}
191
192#ifdef UDATA_DEBUG
193static const char *
194packageNameFromPath(const char *path)
195{
196    if((path == NULL) || (*path == 0)) {
197        return U_ICUDATA_NAME;
198    }
199
200    path = findBasename(path);
201
202    if((path == NULL) || (*path == 0)) {
203        return U_ICUDATA_NAME;
204    }
205
206    return path;
207}
208#endif
209
210/*----------------------------------------------------------------------*
211 *                                                                      *
212 *   Cache for common data                                              *
213 *      Functions for looking up or adding entries to a cache of        *
214 *      data that has been previously opened.  Avoids a potentially     *
215 *      expensive operation of re-opening the data for subsequent       *
216 *      uses.                                                           *
217 *                                                                      *
218 *      Data remains cached for the duration of the process.            *
219 *                                                                      *
220 *----------------------------------------------------------------------*/
221
222typedef struct DataCacheElement {
223    char          *name;
224    UDataMemory   *item;
225} DataCacheElement;
226
227
228
229/*
230 * Deleter function for DataCacheElements.
231 *         udata cleanup function closes the hash table; hash table in turn calls back to
232 *         here for each entry.
233 */
234static void U_CALLCONV DataCacheElement_deleter(void *pDCEl) {
235    DataCacheElement *p = (DataCacheElement *)pDCEl;
236    udata_close(p->item);              /* unmaps storage */
237    uprv_free(p->name);                /* delete the hash key string. */
238    uprv_free(pDCEl);                  /* delete 'this'          */
239}
240
241 /*   udata_getCacheHashTable()
242 *     Get the hash table used to store the data cache entries.
243 *     Lazy create it if it doesn't yet exist.
244 */
245static UHashtable *udata_getHashTable() {
246    UErrorCode   err = U_ZERO_ERROR;
247    UBool        cacheIsInitialized;
248    UHashtable  *tHT = NULL;
249
250    UMTX_CHECK(NULL, (gCommonDataCache != NULL), cacheIsInitialized);
251
252    if (cacheIsInitialized) {
253        return gCommonDataCache;
254    }
255
256    tHT = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &err);
257    /* Check for null pointer. */
258    if (tHT == NULL) {
259    	return NULL; /* TODO:  Handle this error better. */
260    }
261    uhash_setValueDeleter(tHT, DataCacheElement_deleter);
262
263    umtx_lock(NULL);
264    if (gCommonDataCache == NULL) {
265        gCommonDataCache = tHT;
266        tHT = NULL;
267        ucln_common_registerCleanup(UCLN_COMMON_UDATA, udata_cleanup);
268    }
269    umtx_unlock(NULL);
270    if (tHT != NULL) {
271        uhash_close(tHT);
272    }
273
274    if (U_FAILURE(err)) {
275        return NULL;      /* TODO:  handle this error better.  */
276    }
277    return gCommonDataCache;
278}
279
280
281
282static UDataMemory *udata_findCachedData(const char *path)
283{
284    UHashtable        *htable;
285    UDataMemory       *retVal = NULL;
286    DataCacheElement  *el;
287    const char        *baseName;
288
289    baseName = findBasename(path);   /* Cache remembers only the base name, not the full path. */
290    htable = udata_getHashTable();
291    umtx_lock(NULL);
292    el = (DataCacheElement *)uhash_get(htable, baseName);
293    umtx_unlock(NULL);
294    if (el != NULL) {
295        retVal = el->item;
296    }
297#ifdef UDATA_DEBUG
298    fprintf(stderr, "Cache: [%s] -> %p\n", baseName, retVal);
299#endif
300    return retVal;
301}
302
303
304static UDataMemory *udata_cacheDataItem(const char *path, UDataMemory *item, UErrorCode *pErr) {
305    DataCacheElement *newElement;
306    const char       *baseName;
307    int32_t           nameLen;
308    UHashtable       *htable;
309    DataCacheElement *oldValue = NULL;
310    UErrorCode        subErr = U_ZERO_ERROR;
311
312    if (U_FAILURE(*pErr)) {
313        return NULL;
314    }
315
316    /* Create a new DataCacheElement - the thingy we store in the hash table -
317     * and copy the supplied path and UDataMemoryItems into it.
318     */
319    newElement = (DataCacheElement *)uprv_malloc(sizeof(DataCacheElement));
320    if (newElement == NULL) {
321        *pErr = U_MEMORY_ALLOCATION_ERROR;
322        return NULL;
323    }
324    newElement->item = UDataMemory_createNewInstance(pErr);
325    if (U_FAILURE(*pErr)) {
326        uprv_free(newElement);
327        return NULL;
328    }
329    UDatamemory_assign(newElement->item, item);
330
331    baseName = findBasename(path);
332    nameLen = (int32_t)uprv_strlen(baseName);
333    newElement->name = (char *)uprv_malloc(nameLen+1);
334    if (newElement->name == NULL) {
335        *pErr = U_MEMORY_ALLOCATION_ERROR;
336        uprv_free(newElement->item);
337        uprv_free(newElement);
338        return NULL;
339    }
340    uprv_strcpy(newElement->name, baseName);
341
342    /* Stick the new DataCacheElement into the hash table.
343    */
344    htable = udata_getHashTable();
345    umtx_lock(NULL);
346    oldValue = (DataCacheElement *)uhash_get(htable, path);
347    if (oldValue != NULL) {
348        subErr = U_USING_DEFAULT_WARNING;
349    }
350    else {
351        uhash_put(
352            htable,
353            newElement->name,               /* Key   */
354            newElement,                     /* Value */
355            &subErr);
356    }
357    umtx_unlock(NULL);
358
359#ifdef UDATA_DEBUG
360    fprintf(stderr, "Cache: [%s] <<< %p : %s. vFunc=%p\n", newElement->name,
361    newElement->item, u_errorName(subErr), newElement->item->vFuncs);
362#endif
363
364    if (subErr == U_USING_DEFAULT_WARNING || U_FAILURE(subErr)) {
365        *pErr = subErr; /* copy sub err unto fillin ONLY if something happens. */
366        uprv_free(newElement->name);
367        uprv_free(newElement->item);
368        uprv_free(newElement);
369        return oldValue ? oldValue->item : NULL;
370    }
371
372    return newElement->item;
373}
374
375/*----------------------------------------------------------------------*==============
376 *                                                                      *
377 *  Path management.  Could be shared with other tools/etc if need be   *
378 * later on.                                                            *
379 *                                                                      *
380 *----------------------------------------------------------------------*/
381
382#define U_DATA_PATHITER_BUFSIZ  128        /* Size of local buffer for paths         */
383                                           /*   Overflow causes malloc of larger buf */
384
385U_NAMESPACE_BEGIN
386
387class UDataPathIterator
388{
389public:
390    UDataPathIterator(const char *path, const char *pkg,
391                      const char *item, const char *suffix, UBool doCheckLastFour,
392                      UErrorCode *pErrorCode);
393    const char *next(UErrorCode *pErrorCode);
394
395private:
396    const char *path;                              /* working path (u_icudata_Dir) */
397    const char *nextPath;                          /* path following this one */
398    const char *basename;                          /* item's basename (icudt22e_mt.res)*/
399    const char *suffix;                            /* item suffix (can be null) */
400
401    uint32_t    basenameLen;                       /* length of basename */
402
403    CharString  itemPath;                          /* path passed in with item name */
404    CharString  pathBuffer;                        /* output path for this it'ion */
405    CharString  packageStub;                       /* example:  "/icudt28b". Will ignore that leaf in set paths. */
406
407    UBool       checkLastFour;                     /* if TRUE then allow paths such as '/foo/myapp.dat'
408                                                    * to match, checks last 4 chars of suffix with
409                                                    * last 4 of path, then previous chars. */
410};
411
412/**
413 * @param iter  The iterator to be initialized. Its current state does not matter.
414 * @param path  The full pathname to be iterated over.  If NULL, defaults to U_ICUDATA_NAME
415 * @param pkg   Package which is being searched for, ex "icudt28l".  Will ignore leave directories such as /icudt28l
416 * @param item  Item to be searched for.  Can include full path, such as /a/b/foo.dat
417 * @param suffix  Optional item suffix, if not-null (ex. ".dat") then 'path' can contain 'item' explicitly.
418 *               Ex:   'stuff.dat' would be found in '/a/foo:/tmp/stuff.dat:/bar/baz' as item #2.
419 *                     '/blarg/stuff.dat' would also be found.
420 */
421UDataPathIterator::UDataPathIterator(const char *inPath, const char *pkg,
422                                     const char *item, const char *inSuffix, UBool doCheckLastFour,
423                                     UErrorCode *pErrorCode)
424{
425#ifdef UDATA_DEBUG
426        fprintf(stderr, "SUFFIX1=%s PATH=%s\n", inSuffix, inPath);
427#endif
428    /** Path **/
429    if(inPath == NULL) {
430        path = u_getDataDirectory();
431    } else {
432        path = inPath;
433    }
434
435    /** Package **/
436    if(pkg != NULL) {
437      packageStub.append(U_FILE_SEP_CHAR, *pErrorCode).append(pkg, *pErrorCode);
438#ifdef UDATA_DEBUG
439      fprintf(stderr, "STUB=%s [%d]\n", packageStub.data(), packageStub.length());
440#endif
441    }
442
443    /** Item **/
444    basename = findBasename(item);
445    basenameLen = (int32_t)uprv_strlen(basename);
446
447    /** Item path **/
448    if(basename == item) {
449        nextPath = path;
450    } else {
451        itemPath.append(item, (int32_t)(basename-item), *pErrorCode);
452        nextPath = itemPath.data();
453    }
454#ifdef UDATA_DEBUG
455    fprintf(stderr, "SUFFIX=%s [%p]\n", inSuffix, inSuffix);
456#endif
457
458    /** Suffix  **/
459    if(inSuffix != NULL) {
460        suffix = inSuffix;
461    } else {
462        suffix = "";
463    }
464
465    checkLastFour = doCheckLastFour;
466
467    /* pathBuffer will hold the output path strings returned by this iterator */
468
469#ifdef UDATA_DEBUG
470    fprintf(stderr, "%p: init %s -> [path=%s], [base=%s], [suff=%s], [itempath=%s], [nextpath=%s], [checklast4=%s]\n",
471            iter,
472            item,
473            path,
474            basename,
475            suffix,
476            itemPath.data(),
477            nextPath,
478            checkLastFour?"TRUE":"false");
479#endif
480}
481
482/**
483 * Get the next path on the list.
484 *
485 * @param iter The Iter to be used
486 * @param len  If set, pointer to the length of the returned path, for convenience.
487 * @return Pointer to the next path segment, or NULL if there are no more.
488 */
489const char *UDataPathIterator::next(UErrorCode *pErrorCode)
490{
491    if(U_FAILURE(*pErrorCode)) {
492        return NULL;
493    }
494
495    const char *currentPath = NULL;
496    int32_t     pathLen = 0;
497    const char *pathBasename;
498
499    do
500    {
501        if( nextPath == NULL ) {
502            break;
503        }
504        currentPath = nextPath;
505
506        if(nextPath == itemPath.data()) { /* we were processing item's path. */
507            nextPath = path; /* start with regular path next tm. */
508            pathLen = (int32_t)uprv_strlen(currentPath);
509        } else {
510            /* fix up next for next time */
511            nextPath = uprv_strchr(currentPath, U_PATH_SEP_CHAR);
512            if(nextPath == NULL) {
513                /* segment: entire path */
514                pathLen = (int32_t)uprv_strlen(currentPath);
515            } else {
516                /* segment: until next segment */
517                pathLen = (int32_t)(nextPath - currentPath);
518                /* skip divider */
519                nextPath ++;
520            }
521        }
522
523        if(pathLen == 0) {
524            continue;
525        }
526
527#ifdef UDATA_DEBUG
528        fprintf(stderr, "rest of path (IDD) = %s\n", currentPath);
529        fprintf(stderr, "                     ");
530        {
531            uint32_t qqq;
532            for(qqq=0;qqq<pathLen;qqq++)
533            {
534                fprintf(stderr, " ");
535            }
536
537            fprintf(stderr, "^\n");
538        }
539#endif
540        pathBuffer.clear().append(currentPath, pathLen, *pErrorCode);
541
542        /* check for .dat files */
543        pathBasename = findBasename(pathBuffer.data());
544
545        if(checkLastFour == TRUE &&
546           (pathLen>=4) &&
547           uprv_strncmp(pathBuffer.data() +(pathLen-4), suffix, 4)==0 && /* suffix matches */
548           uprv_strncmp(findBasename(pathBuffer.data()), basename, basenameLen)==0  && /* base matches */
549           uprv_strlen(pathBasename)==(basenameLen+4)) { /* base+suffix = full len */
550
551#ifdef UDATA_DEBUG
552            fprintf(stderr, "Have %s file on the path: %s\n", suffix, pathBuffer.data());
553#endif
554            /* do nothing */
555        }
556        else
557        {       /* regular dir path */
558            if(pathBuffer[pathLen-1] != U_FILE_SEP_CHAR) {
559                if((pathLen>=4) &&
560                   uprv_strncmp(pathBuffer.data()+(pathLen-4), ".dat", 4) == 0)
561                {
562#ifdef UDATA_DEBUG
563                    fprintf(stderr, "skipping non-directory .dat file %s\n", pathBuffer.data());
564#endif
565                    continue;
566                }
567
568                /* Check if it is a directory with the same name as our package */
569                if(!packageStub.isEmpty() &&
570                   (pathLen > packageStub.length()) &&
571                   !uprv_strcmp(pathBuffer.data() + pathLen - packageStub.length(), packageStub.data())) {
572#ifdef UDATA_DEBUG
573                  fprintf(stderr, "Found stub %s (will add package %s of len %d)\n", packageStub.data(), basename, basenameLen);
574#endif
575                  pathBuffer.truncate(pathLen - packageStub.length());
576                }
577                pathBuffer.append(U_FILE_SEP_CHAR, *pErrorCode);
578            }
579
580            /* + basename */
581            pathBuffer.append(packageStub.data()+1, packageStub.length()-1, *pErrorCode);
582
583            if(*suffix)  /* tack on suffix */
584            {
585                pathBuffer.append(suffix, *pErrorCode);
586            }
587        }
588
589#ifdef UDATA_DEBUG
590        fprintf(stderr, " -->  %s\n", pathBuffer.data());
591#endif
592
593        return pathBuffer.data();
594
595    } while(path);
596
597    /* fell way off the end */
598    return NULL;
599}
600
601U_NAMESPACE_END
602
603/* ==================================================================================*/
604
605
606/*----------------------------------------------------------------------*
607 *                                                                      *
608 *  Add a static reference to the common data  library                  *
609 *   Unless overridden by an explicit udata_setCommonData, this will be *
610 *      our common data.                                                *
611 *                                                                      *
612 *----------------------------------------------------------------------*/
613extern "C" const ICU_Data_Header U_DATA_API U_ICUDATA_ENTRY_POINT;
614
615/*
616 * This would be a good place for weak-linkage declarations of
617 * partial-data-library access functions where each returns a pointer
618 * to its data package, if it is linked in.
619 */
620/*
621extern const void *uprv_getICUData_collation(void) ATTRIBUTE_WEAK;
622extern const void *uprv_getICUData_conversion(void) ATTRIBUTE_WEAK;
623*/
624
625/*----------------------------------------------------------------------*
626 *                                                                      *
627 *   openCommonData   Attempt to open a common format (.dat) file       *
628 *                    Map it into memory (if it's not there already)    *
629 *                    and return a UDataMemory object for it.           *
630 *                                                                      *
631 *                    If the requested data is already open and cached  *
632 *                       just return the cached UDataMem object.        *
633 *                                                                      *
634 *----------------------------------------------------------------------*/
635static UDataMemory *
636openCommonData(const char *path,          /*  Path from OpenChoice?          */
637               int32_t commonDataIndex,   /*  ICU Data (index >= 0) if path == NULL */
638               UErrorCode *pErrorCode)
639{
640    UDataMemory tData;
641    const char *pathBuffer;
642    const char *inBasename;
643
644    if (U_FAILURE(*pErrorCode)) {
645        return NULL;
646    }
647
648    UDataMemory_init(&tData);
649
650    /* ??????? TODO revisit this */
651    if (commonDataIndex >= 0) {
652        /* "mini-cache" for common ICU data */
653        if(commonDataIndex >= LENGTHOF(gCommonICUDataArray)) {
654            return NULL;
655        }
656        if(gCommonICUDataArray[commonDataIndex] == NULL) {
657            int32_t i;
658            for(i = 0; i < commonDataIndex; ++i) {
659                if(gCommonICUDataArray[i]->pHeader == &U_ICUDATA_ENTRY_POINT.hdr) {
660                    /* The linked-in data is already in the list. */
661                    return NULL;
662                }
663            }
664
665            /* Add the linked-in data to the list. */
666            /*
667             * This is where we would check and call weakly linked partial-data-library
668             * access functions.
669             */
670            /*
671            if (uprv_getICUData_collation) {
672                setCommonICUDataPointer(uprv_getICUData_collation(), FALSE, pErrorCode);
673            }
674            if (uprv_getICUData_conversion) {
675                setCommonICUDataPointer(uprv_getICUData_conversion(), FALSE, pErrorCode);
676            }
677            */
678            setCommonICUDataPointer(&U_ICUDATA_ENTRY_POINT.hdr, FALSE, pErrorCode);
679        }
680        return gCommonICUDataArray[commonDataIndex];
681    }
682
683
684    /* request is NOT for ICU Data.  */
685
686    /* Find the base name portion of the supplied path.   */
687    /*   inBasename will be left pointing somewhere within the original path string.      */
688    inBasename = findBasename(path);
689#ifdef UDATA_DEBUG
690    fprintf(stderr, "inBasename = %s\n", inBasename);
691#endif
692
693    if(*inBasename==0) {
694        /* no basename.     This will happen if the original path was a directory name,   */
695        /*    like  "a/b/c/".   (Fallback to separate files will still work.)             */
696#ifdef UDATA_DEBUG
697        fprintf(stderr, "ocd: no basename in %s, bailing.\n", path);
698#endif
699        *pErrorCode=U_FILE_ACCESS_ERROR;
700        return NULL;
701    }
702
703   /* Is the requested common data file already open and cached?                     */
704   /*   Note that the cache is keyed by the base name only.  The rest of the path,   */
705   /*     if any, is not considered.                                                 */
706   {
707        UDataMemory  *dataToReturn = udata_findCachedData(inBasename);
708        if (dataToReturn != NULL) {
709            return dataToReturn;
710        }
711    }
712
713    /* Requested item is not in the cache.
714     * Hunt it down, trying all the path locations
715     */
716
717    UDataPathIterator iter(u_getDataDirectory(), inBasename, path, ".dat", TRUE, pErrorCode);
718
719    while((UDataMemory_isLoaded(&tData)==FALSE) && (pathBuffer = iter.next(pErrorCode)) != NULL)
720    {
721#ifdef UDATA_DEBUG
722        fprintf(stderr, "ocd: trying path %s - ", pathBuffer);
723#endif
724        uprv_mapFile(&tData, pathBuffer);
725#ifdef UDATA_DEBUG
726        fprintf(stderr, "%s\n", UDataMemory_isLoaded(&tData)?"LOADED":"not loaded");
727#endif
728    }
729
730#if defined(OS390_STUBDATA) && defined(OS390BATCH)
731    if (!UDataMemory_isLoaded(&tData)) {
732        char ourPathBuffer[1024];
733        /* One more chance, for extendCommonData() */
734        uprv_strncpy(ourPathBuffer, path, 1019);
735        ourPathBuffer[1019]=0;
736        uprv_strcat(ourPathBuffer, ".dat");
737        uprv_mapFile(&tData, ourPathBuffer);
738    }
739#endif
740
741    if (!UDataMemory_isLoaded(&tData)) {
742        /* no common data */
743        *pErrorCode=U_FILE_ACCESS_ERROR;
744        return NULL;
745    }
746
747    /* we have mapped a file, check its header */
748    udata_checkCommonData(&tData, pErrorCode);
749
750
751    /* Cache the UDataMemory struct for this .dat file,
752     *   so we won't need to hunt it down and map it again next time
753     *   something is needed from it.                */
754    return udata_cacheDataItem(inBasename, &tData, pErrorCode);
755}
756
757
758/*----------------------------------------------------------------------*
759 *                                                                      *
760 *   extendICUData   If the full set of ICU data was not loaded at      *
761 *                   program startup, load it now.  This function will  *
762 *                   be called when the lookup of an ICU data item in   *
763 *                   the common ICU data fails.                         *
764 *                                                                      *
765 *                   return true if new data is loaded, false otherwise.*
766 *                                                                      *
767 *----------------------------------------------------------------------*/
768static UBool extendICUData(UErrorCode *pErr)
769{
770    UDataMemory   *pData;
771    UDataMemory   copyPData;
772    UBool         didUpdate = FALSE;
773
774    /*
775     * There is a chance for a race condition here.
776     * Normally, ICU data is loaded from a DLL or via mmap() and
777     * setCommonICUData() will detect if the same address is set twice.
778     * If ICU is built with data loading via fread() then the address will
779     * be different each time the common data is loaded and we may add
780     * multiple copies of the data.
781     * In this case, use a mutex to prevent the race.
782     * Use a specific mutex to avoid nested locks of the global mutex.
783     */
784#if MAP_IMPLEMENTATION==MAP_STDIO
785    static UMTX extendICUDataMutex = NULL;
786    umtx_lock(&extendICUDataMutex);
787#endif
788    if(!gHaveTriedToLoadCommonData) {
789        gHaveTriedToLoadCommonData = TRUE;
790
791        /* See if we can explicitly open a .dat file for the ICUData. */
792        pData = openCommonData(
793                   U_ICUDATA_NAME,            /*  "icudt20l" , for example.          */
794                   -1,                        /*  Pretend we're not opening ICUData  */
795                   pErr);
796
797        /* How about if there is no pData, eh... */
798
799       UDataMemory_init(&copyPData);
800       if(pData != NULL) {
801          UDatamemory_assign(&copyPData, pData);
802          copyPData.map = 0;              /* The mapping for this data is owned by the hash table */
803          copyPData.mapAddr = 0;          /*   which will unmap it when ICU is shut down.         */
804                                          /* CommonICUData is also unmapped when ICU is shut down.*/
805                                          /* To avoid unmapping the data twice, zero out the map  */
806                                          /*   fields in the UDataMemory that we're assigning     */
807                                          /*   to CommonICUData.                                  */
808
809          didUpdate =
810              setCommonICUData(&copyPData,/*  The new common data.                                */
811                       FALSE,             /*  No warnings if write didn't happen                  */
812                       pErr);             /*  setCommonICUData honors errors; NOP if error set    */
813        }
814    }
815#if MAP_IMPLEMENTATION==MAP_STDIO
816    umtx_unlock(&extendICUDataMutex);
817#endif
818    return didUpdate;               /* Return true if ICUData pointer was updated.   */
819                                    /*   (Could potentialy have been done by another thread racing */
820                                    /*   us through here, but that's fine, we still return true    */
821                                    /*   so that current thread will also examine extended data.   */
822}
823
824/*----------------------------------------------------------------------*
825 *                                                                      *
826 *   udata_setCommonData                                                *
827 *                                                                      *
828 *----------------------------------------------------------------------*/
829U_CAPI void U_EXPORT2
830udata_setCommonData(const void *data, UErrorCode *pErrorCode) {
831    UDataMemory dataMemory;
832
833    if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
834        return;
835    }
836
837    if(data==NULL) {
838        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
839        return;
840    }
841
842    /* set the data pointer and test for validity */
843    UDataMemory_init(&dataMemory);
844    UDataMemory_setData(&dataMemory, data);
845    udata_checkCommonData(&dataMemory, pErrorCode);
846    if (U_FAILURE(*pErrorCode)) {return;}
847
848    /* we have good data */
849    /* Set it up as the ICU Common Data.  */
850    setCommonICUData(&dataMemory, TRUE, pErrorCode);
851}
852
853/*---------------------------------------------------------------------------
854 *
855 *  udata_setAppData
856 *
857 *---------------------------------------------------------------------------- */
858U_CAPI void U_EXPORT2
859udata_setAppData(const char *path, const void *data, UErrorCode *err)
860{
861    UDataMemory     udm;
862
863    if(err==NULL || U_FAILURE(*err)) {
864        return;
865    }
866    if(data==NULL) {
867        *err=U_ILLEGAL_ARGUMENT_ERROR;
868        return;
869    }
870
871    UDataMemory_init(&udm);
872    UDataMemory_setData(&udm, data);
873    udata_checkCommonData(&udm, err);
874    udata_cacheDataItem(path, &udm, err);
875}
876
877/*----------------------------------------------------------------------------*
878 *                                                                            *
879 *  checkDataItem     Given a freshly located/loaded data item, either        *
880 *                    an entry in a common file or a separately loaded file,  *
881 *                    sanity check its header, and see if the data is         *
882 *                    acceptable to the app.                                  *
883 *                    If the data is good, create and return a UDataMemory    *
884 *                    object that can be returned to the application.         *
885 *                    Return NULL on any sort of failure.                     *
886 *                                                                            *
887 *----------------------------------------------------------------------------*/
888static UDataMemory *
889checkDataItem
890(
891 const DataHeader         *pHeader,         /* The data item to be checked.                */
892 UDataMemoryIsAcceptable  *isAcceptable,    /* App's call-back function                    */
893 void                     *context,         /*   pass-thru param for above.                */
894 const char               *type,            /*   pass-thru param for above.                */
895 const char               *name,            /*   pass-thru param for above.                */
896 UErrorCode               *nonFatalErr,     /* Error code if this data was not acceptable  */
897                                            /*   but openChoice should continue with       */
898                                            /*   trying to get data from fallback path.    */
899 UErrorCode               *fatalErr         /* Bad error, caller should return immediately */
900 )
901{
902    UDataMemory  *rDataMem = NULL;          /* the new UDataMemory, to be returned.        */
903
904    if (U_FAILURE(*fatalErr)) {
905        return NULL;
906    }
907
908    if(pHeader->dataHeader.magic1==0xda &&
909        pHeader->dataHeader.magic2==0x27 &&
910        (isAcceptable==NULL || isAcceptable(context, type, name, &pHeader->info))
911    ) {
912        rDataMem=UDataMemory_createNewInstance(fatalErr);
913        if (U_FAILURE(*fatalErr)) {
914            return NULL;
915        }
916        rDataMem->pHeader = pHeader;
917    } else {
918        /* the data is not acceptable, look further */
919        /* If we eventually find something good, this errorcode will be */
920        /*    cleared out.                                              */
921        *nonFatalErr=U_INVALID_FORMAT_ERROR;
922    }
923    return rDataMem;
924}
925
926/**
927 * @return 0 if not loaded, 1 if loaded or err
928 */
929static UDataMemory *doLoadFromIndividualFiles(const char *pkgName,
930        const char *dataPath, const char *tocEntryPathSuffix,
931            /* following arguments are the same as doOpenChoice itself */
932            const char *path, const char *type, const char *name,
933             UDataMemoryIsAcceptable *isAcceptable, void *context,
934             UErrorCode *subErrorCode,
935             UErrorCode *pErrorCode)
936{
937    const char         *pathBuffer;
938    UDataMemory         dataMemory;
939    UDataMemory *pEntryData;
940
941    /* look in ind. files: package\nam.typ  ========================= */
942    /* init path iterator for individual files */
943    UDataPathIterator iter(dataPath, pkgName, path, tocEntryPathSuffix, FALSE, pErrorCode);
944
945    while((pathBuffer = iter.next(pErrorCode)))
946    {
947#ifdef UDATA_DEBUG
948        fprintf(stderr, "UDATA: trying individual file %s\n", pathBuffer);
949#endif
950        if(uprv_mapFile(&dataMemory, pathBuffer))
951        {
952            pEntryData = checkDataItem(dataMemory.pHeader, isAcceptable, context, type, name, subErrorCode, pErrorCode);
953            if (pEntryData != NULL) {
954                /* Data is good.
955                *  Hand off ownership of the backing memory to the user's UDataMemory.
956                *  and return it.   */
957                pEntryData->mapAddr = dataMemory.mapAddr;
958                pEntryData->map     = dataMemory.map;
959
960#ifdef UDATA_DEBUG
961                fprintf(stderr, "** Mapped file: %s\n", pathBuffer);
962#endif
963                return pEntryData;
964            }
965
966            /* the data is not acceptable, or some error occured.  Either way, unmap the memory */
967            udata_close(&dataMemory);
968
969            /* If we had a nasty error, bail out completely.  */
970            if (U_FAILURE(*pErrorCode)) {
971                return NULL;
972            }
973
974            /* Otherwise remember that we found data but didn't like it for some reason  */
975            *subErrorCode=U_INVALID_FORMAT_ERROR;
976        }
977#ifdef UDATA_DEBUG
978        fprintf(stderr, "%s\n", UDataMemory_isLoaded(&dataMemory)?"LOADED":"not loaded");
979#endif
980    }
981    return NULL;
982}
983
984/**
985 * @return 0 if not loaded, 1 if loaded or err
986 */
987static UDataMemory *doLoadFromCommonData(UBool isICUData, const char * /*pkgName*/,
988        const char * /*dataPath*/, const char * /*tocEntryPathSuffix*/, const char *tocEntryName,
989            /* following arguments are the same as doOpenChoice itself */
990            const char *path, const char *type, const char *name,
991             UDataMemoryIsAcceptable *isAcceptable, void *context,
992             UErrorCode *subErrorCode,
993             UErrorCode *pErrorCode)
994{
995    UDataMemory        *pEntryData;
996    const DataHeader   *pHeader;
997    UDataMemory        *pCommonData;
998    int32_t            commonDataIndex;
999    /* try to get common data.  The loop is for platforms such as the 390 that do
1000     *  not initially load the full set of ICU data.  If the lookup of an ICU data item
1001     *  fails, the full (but slower to load) set is loaded, the and the loop repeats,
1002     *  trying the lookup again.  Once the full set of ICU data is loaded, the loop wont
1003     *  repeat because the full set will be checked the first time through.
1004     *
1005     *  The loop also handles the fallback to a .dat file if the application linked
1006     *   to the stub data library rather than a real library.
1007     */
1008    for (commonDataIndex = isICUData ? 0 : -1;;) {
1009        pCommonData=openCommonData(path, commonDataIndex, subErrorCode); /** search for pkg **/
1010
1011        if(U_SUCCESS(*subErrorCode) && pCommonData!=NULL) {
1012            int32_t length;
1013
1014            /* look up the data piece in the common data */
1015            pHeader=pCommonData->vFuncs->Lookup(pCommonData, tocEntryName, &length, subErrorCode);
1016#ifdef UDATA_DEBUG
1017            fprintf(stderr, "%s: pHeader=%p - %s\n", tocEntryName, pHeader, u_errorName(*subErrorCode));
1018#endif
1019
1020            if(pHeader!=NULL) {
1021                pEntryData = checkDataItem(pHeader, isAcceptable, context, type, name, subErrorCode, pErrorCode);
1022#ifdef UDATA_DEBUG
1023                fprintf(stderr, "pEntryData=%p\n", pEntryData);
1024#endif
1025                if (U_FAILURE(*pErrorCode)) {
1026                    return NULL;
1027                }
1028                if (pEntryData != NULL) {
1029                    pEntryData->length = length;
1030                    return pEntryData;
1031                }
1032            }
1033        }
1034        /* Data wasn't found.  If we were looking for an ICUData item and there is
1035         * more data available, load it and try again,
1036         * otherwise break out of this loop. */
1037        if (!isICUData) {
1038            return NULL;
1039        } else if (pCommonData != NULL) {
1040            ++commonDataIndex;  /* try the next data package */
1041        } else if (extendICUData(subErrorCode)) {
1042            /* try this data package slot again: it changed from NULL to non-NULL */
1043        } else {
1044            return NULL;
1045        }
1046    }
1047}
1048
1049/*
1050 *  A note on the ownership of Mapped Memory
1051 *
1052 *  For common format files, ownership resides with the UDataMemory object
1053 *    that lives in the cache of opened common data.  These UDataMemorys are private
1054 *    to the udata implementation, and are never seen directly by users.
1055 *
1056 *    The UDataMemory objects returned to users will have the address of some desired
1057 *    data within the mapped region, but they wont have the mapping info itself, and thus
1058 *    won't cause anything to be removed from memory when they are closed.
1059 *
1060 *  For individual data files, the UDataMemory returned to the user holds the
1061 *  information necessary to unmap the data on close.  If the user independently
1062 *  opens the same data file twice, two completely independent mappings will be made.
1063 *  (There is no cache of opened data items from individual files, only a cache of
1064 *   opened Common Data files, that is, files containing a collection of data items.)
1065 *
1066 *  For common data passed in from the user via udata_setAppData() or
1067 *  udata_setCommonData(), ownership remains with the user.
1068 *
1069 *  UDataMemory objects themselves, as opposed to the memory they describe,
1070 *  can be anywhere - heap, stack/local or global.
1071 *  They have a flag to indicate when they're heap allocated and thus
1072 *  must be deleted when closed.
1073 */
1074
1075
1076/*----------------------------------------------------------------------------*
1077 *                                                                            *
1078 * main data loading functions                                                *
1079 *                                                                            *
1080 *----------------------------------------------------------------------------*/
1081static UDataMemory *
1082doOpenChoice(const char *path, const char *type, const char *name,
1083             UDataMemoryIsAcceptable *isAcceptable, void *context,
1084             UErrorCode *pErrorCode)
1085{
1086    UDataMemory         *retVal = NULL;
1087
1088    const char         *dataPath;
1089
1090    int32_t             tocEntrySuffixIndex;
1091    const char         *tocEntryPathSuffix;
1092    UErrorCode          subErrorCode=U_ZERO_ERROR;
1093    const char         *treeChar;
1094
1095    UBool               isICUData = FALSE;
1096
1097
1098    /* Is this path ICU data? */
1099    if(path == NULL ||
1100       !strcmp(path, U_ICUDATA_ALIAS) ||  /* "ICUDATA" */
1101       !uprv_strncmp(path, U_ICUDATA_NAME U_TREE_SEPARATOR_STRING, /* "icudt26e-" */
1102                     uprv_strlen(U_ICUDATA_NAME U_TREE_SEPARATOR_STRING)) ||
1103       !uprv_strncmp(path, U_ICUDATA_ALIAS U_TREE_SEPARATOR_STRING, /* "ICUDATA-" */
1104                     uprv_strlen(U_ICUDATA_ALIAS U_TREE_SEPARATOR_STRING))) {
1105      isICUData = TRUE;
1106    }
1107
1108#if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR)  /* Windows:  try "foo\bar" and "foo/bar" */
1109    /* remap from alternate path char to the main one */
1110    CharString altSepPath;
1111    if(path) {
1112        if(uprv_strchr(path,U_FILE_ALT_SEP_CHAR) != NULL) {
1113            altSepPath.append(path, *pErrorCode);
1114            char *p;
1115            while((p=uprv_strchr(altSepPath.data(), U_FILE_ALT_SEP_CHAR))) {
1116                *p = U_FILE_SEP_CHAR;
1117            }
1118#if defined (UDATA_DEBUG)
1119            fprintf(stderr, "Changed path from [%s] to [%s]\n", path, altSepPath.s);
1120#endif
1121            path = altSepPath.data();
1122        }
1123    }
1124#endif
1125
1126    CharString tocEntryName; /* entry name in tree format. ex:  'icudt28b/coll/ar.res' */
1127    CharString tocEntryPath; /* entry name in path format. ex:  'icudt28b\\coll\\ar.res' */
1128
1129    CharString pkgName;
1130    CharString treeName;
1131
1132    /* ======= Set up strings */
1133    if(path==NULL) {
1134        pkgName.append(U_ICUDATA_NAME, *pErrorCode);
1135    } else {
1136        const char *pkg;
1137        const char *first;
1138        pkg = uprv_strrchr(path, U_FILE_SEP_CHAR);
1139        first = uprv_strchr(path, U_FILE_SEP_CHAR);
1140        if(uprv_pathIsAbsolute(path) || (pkg != first)) { /* more than one slash in the path- not a tree name */
1141            /* see if this is an /absolute/path/to/package  path */
1142            if(pkg) {
1143                pkgName.append(pkg+1, *pErrorCode);
1144            } else {
1145                pkgName.append(path, *pErrorCode);
1146            }
1147        } else {
1148            treeChar = uprv_strchr(path, U_TREE_SEPARATOR);
1149            if(treeChar) {
1150                treeName.append(treeChar+1, *pErrorCode); /* following '-' */
1151                if(isICUData) {
1152                    pkgName.append(U_ICUDATA_NAME, *pErrorCode);
1153                } else {
1154                    pkgName.append(path, (int32_t)(treeChar-path), *pErrorCode);
1155                    if (first == NULL) {
1156                        /*
1157                        This user data has no path, but there is a tree name.
1158                        Look up the correct path from the data cache later.
1159                        */
1160                        path = pkgName.data();
1161                    }
1162                }
1163            } else {
1164                if(isICUData) {
1165                    pkgName.append(U_ICUDATA_NAME, *pErrorCode);
1166                } else {
1167                    pkgName.append(path, *pErrorCode);
1168                }
1169            }
1170        }
1171    }
1172
1173#ifdef UDATA_DEBUG
1174    fprintf(stderr, " P=%s T=%s\n", pkgName.data(), treeName.data());
1175#endif
1176
1177    /* setting up the entry name and file name
1178     * Make up a full name by appending the type to the supplied
1179     *  name, assuming that a type was supplied.
1180     */
1181
1182    /* prepend the package */
1183    tocEntryName.append(pkgName, *pErrorCode);
1184    tocEntryPath.append(pkgName, *pErrorCode);
1185    tocEntrySuffixIndex = tocEntryName.length();
1186
1187    if(!treeName.isEmpty()) {
1188        tocEntryName.append(U_TREE_ENTRY_SEP_CHAR, *pErrorCode).append(treeName, *pErrorCode);
1189        tocEntryPath.append(U_FILE_SEP_CHAR, *pErrorCode).append(treeName, *pErrorCode);
1190    }
1191
1192    tocEntryName.append(U_TREE_ENTRY_SEP_CHAR, *pErrorCode).append(name, *pErrorCode);
1193    tocEntryPath.append(U_FILE_SEP_CHAR, *pErrorCode).append(name, *pErrorCode);
1194    if(type!=NULL && *type!=0) {
1195        tocEntryName.append(".", *pErrorCode).append(type, *pErrorCode);
1196        tocEntryPath.append(".", *pErrorCode).append(type, *pErrorCode);
1197    }
1198    tocEntryPathSuffix = tocEntryPath.data()+tocEntrySuffixIndex; /* suffix starts here */
1199
1200#ifdef UDATA_DEBUG
1201    fprintf(stderr, " tocEntryName = %s\n", tocEntryName.data());
1202    fprintf(stderr, " tocEntryPath = %s\n", tocEntryName.data());
1203#endif
1204
1205    if(path == NULL) {
1206        path = COMMON_DATA_NAME; /* "icudt26e" */
1207    }
1208
1209    /************************ Begin loop looking for ind. files ***************/
1210#ifdef UDATA_DEBUG
1211    fprintf(stderr, "IND: inBasename = %s, pkg=%s\n", "(n/a)", packageNameFromPath(path));
1212#endif
1213
1214    /* End of dealing with a null basename */
1215    dataPath = u_getDataDirectory();
1216
1217    /****    COMMON PACKAGE  - only if packages are first. */
1218    if(gDataFileAccess == UDATA_PACKAGES_FIRST) {
1219#ifdef UDATA_DEBUG
1220        fprintf(stderr, "Trying packages (UDATA_PACKAGES_FIRST)\n");
1221#endif
1222        /* #2 */
1223        retVal = doLoadFromCommonData(isICUData,
1224                            pkgName.data(), dataPath, tocEntryPathSuffix, tocEntryName.data(),
1225                            path, type, name, isAcceptable, context, &subErrorCode, pErrorCode);
1226        if((retVal != NULL) || U_FAILURE(*pErrorCode)) {
1227            return retVal;
1228        }
1229    }
1230
1231    /****    INDIVIDUAL FILES  */
1232    if((gDataFileAccess==UDATA_PACKAGES_FIRST) ||
1233       (gDataFileAccess==UDATA_FILES_FIRST)) {
1234#ifdef UDATA_DEBUG
1235        fprintf(stderr, "Trying individual files\n");
1236#endif
1237        /* Check to make sure that there is a dataPath to iterate over */
1238        if ((dataPath && *dataPath) || !isICUData) {
1239            retVal = doLoadFromIndividualFiles(pkgName.data(), dataPath, tocEntryPathSuffix,
1240                            path, type, name, isAcceptable, context, &subErrorCode, pErrorCode);
1241            if((retVal != NULL) || U_FAILURE(*pErrorCode)) {
1242                return retVal;
1243            }
1244        }
1245    }
1246
1247    /****    COMMON PACKAGE  */
1248    if((gDataFileAccess==UDATA_ONLY_PACKAGES) ||
1249       (gDataFileAccess==UDATA_FILES_FIRST)) {
1250#ifdef UDATA_DEBUG
1251        fprintf(stderr, "Trying packages (UDATA_ONLY_PACKAGES || UDATA_FILES_FIRST)\n");
1252#endif
1253        retVal = doLoadFromCommonData(isICUData,
1254                            pkgName.data(), dataPath, tocEntryPathSuffix, tocEntryName.data(),
1255                            path, type, name, isAcceptable, context, &subErrorCode, pErrorCode);
1256        if((retVal != NULL) || U_FAILURE(*pErrorCode)) {
1257            return retVal;
1258        }
1259    }
1260
1261    /* Load from DLL.  If we haven't attempted package load, we also haven't had any chance to
1262        try a DLL (static or setCommonData/etc)  load.
1263         If we ever have a "UDATA_ONLY_FILES", add it to the or list here.  */
1264    if(gDataFileAccess==UDATA_NO_FILES) {
1265#ifdef UDATA_DEBUG
1266        fprintf(stderr, "Trying common data (UDATA_NO_FILES)\n");
1267#endif
1268        retVal = doLoadFromCommonData(isICUData,
1269                            pkgName.data(), "", tocEntryPathSuffix, tocEntryName.data(),
1270                            path, type, name, isAcceptable, context, &subErrorCode, pErrorCode);
1271        if((retVal != NULL) || U_FAILURE(*pErrorCode)) {
1272            return retVal;
1273        }
1274    }
1275
1276    /* data not found */
1277    if(U_SUCCESS(*pErrorCode)) {
1278        if(U_SUCCESS(subErrorCode)) {
1279            /* file not found */
1280            *pErrorCode=U_FILE_ACCESS_ERROR;
1281        } else {
1282            /* entry point not found or rejected */
1283            *pErrorCode=subErrorCode;
1284        }
1285    }
1286    return retVal;
1287}
1288
1289
1290
1291/* API ---------------------------------------------------------------------- */
1292
1293U_CAPI UDataMemory * U_EXPORT2
1294udata_open(const char *path, const char *type, const char *name,
1295           UErrorCode *pErrorCode) {
1296#ifdef UDATA_DEBUG
1297  fprintf(stderr, "udata_open(): Opening: %s : %s . %s\n", (path?path:"NULL"), name, type);
1298    fflush(stderr);
1299#endif
1300
1301    if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
1302        return NULL;
1303    } else if(name==NULL || *name==0) {
1304        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
1305        return NULL;
1306    } else {
1307        return doOpenChoice(path, type, name, NULL, NULL, pErrorCode);
1308    }
1309}
1310
1311
1312
1313U_CAPI UDataMemory * U_EXPORT2
1314udata_openChoice(const char *path, const char *type, const char *name,
1315                 UDataMemoryIsAcceptable *isAcceptable, void *context,
1316                 UErrorCode *pErrorCode) {
1317#ifdef UDATA_DEBUG
1318  fprintf(stderr, "udata_openChoice(): Opening: %s : %s . %s\n", (path?path:"NULL"), name, type);
1319#endif
1320
1321    if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
1322        return NULL;
1323    } else if(name==NULL || *name==0 || isAcceptable==NULL) {
1324        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
1325        return NULL;
1326    } else {
1327        return doOpenChoice(path, type, name, isAcceptable, context, pErrorCode);
1328    }
1329}
1330
1331
1332
1333U_CAPI void U_EXPORT2
1334udata_getInfo(UDataMemory *pData, UDataInfo *pInfo) {
1335    if(pInfo!=NULL) {
1336        if(pData!=NULL && pData->pHeader!=NULL) {
1337            const UDataInfo *info=&pData->pHeader->info;
1338            uint16_t dataInfoSize=udata_getInfoSize(info);
1339            if(pInfo->size>dataInfoSize) {
1340                pInfo->size=dataInfoSize;
1341            }
1342            uprv_memcpy((uint16_t *)pInfo+1, (const uint16_t *)info+1, pInfo->size-2);
1343            if(info->isBigEndian!=U_IS_BIG_ENDIAN) {
1344                /* opposite endianness */
1345                uint16_t x=info->reservedWord;
1346                pInfo->reservedWord=(uint16_t)((x<<8)|(x>>8));
1347            }
1348        } else {
1349            pInfo->size=0;
1350        }
1351    }
1352}
1353
1354
1355U_CAPI void U_EXPORT2 udata_setFileAccess(UDataFileAccess access, UErrorCode * /*status*/)
1356{
1357    gDataFileAccess = access;
1358}
1359