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