|  | // © 2016 and later: Unicode, Inc. and others. | 
|  | // License & terms of use: http://www.unicode.org/copyright.html | 
|  | /****************************************************************************** | 
|  | *   Copyright (C) 2008-2012, International Business Machines | 
|  | *   Corporation and others.  All Rights Reserved. | 
|  | ******************************************************************************* | 
|  | */ | 
|  | #include "unicode/utypes.h" | 
|  |  | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include "unicode/utypes.h" | 
|  | #include "unicode/putil.h" | 
|  | #include "cmemory.h" | 
|  | #include "cstring.h" | 
|  | #include "filestrm.h" | 
|  | #include "toolutil.h" | 
|  | #include "unicode/uclean.h" | 
|  | #include "unewdata.h" | 
|  | #include "putilimp.h" | 
|  | #include "pkg_gencmn.h" | 
|  |  | 
|  | #define STRING_STORE_SIZE 200000 | 
|  |  | 
|  | #define COMMON_DATA_NAME U_ICUDATA_NAME | 
|  | #define DATA_TYPE "dat" | 
|  |  | 
|  | /* ICU package data file format (.dat files) ------------------------------- *** | 
|  |  | 
|  | Description of the data format after the usual ICU data file header | 
|  | (UDataInfo etc.). | 
|  |  | 
|  | Format version 1 | 
|  |  | 
|  | A .dat package file contains a simple Table of Contents of item names, | 
|  | followed by the items themselves: | 
|  |  | 
|  | 1. ToC table | 
|  |  | 
|  | uint32_t count; - number of items | 
|  | UDataOffsetTOCEntry entry[count]; - pair of uint32_t values per item: | 
|  | uint32_t nameOffset; - offset of the item name | 
|  | uint32_t dataOffset; - offset of the item data | 
|  | both are byte offsets from the beginning of the data | 
|  |  | 
|  | 2. item name strings | 
|  |  | 
|  | All item names are stored as char * strings in one block between the ToC table | 
|  | and the data items. | 
|  |  | 
|  | 3. data items | 
|  |  | 
|  | The data items are stored following the item names block. | 
|  | Each data item is 16-aligned. | 
|  | The data items are stored in the sorted order of their names. | 
|  |  | 
|  | Therefore, the top of the name strings block is the offset of the first item, | 
|  | the length of the last item is the difference between its offset and | 
|  | the .dat file length, and the length of all previous items is the difference | 
|  | between its offset and the next one. | 
|  |  | 
|  | ----------------------------------------------------------------------------- */ | 
|  |  | 
|  | /* UDataInfo cf. udata.h */ | 
|  | static const UDataInfo dataInfo={ | 
|  | sizeof(UDataInfo), | 
|  | 0, | 
|  |  | 
|  | U_IS_BIG_ENDIAN, | 
|  | U_CHARSET_FAMILY, | 
|  | sizeof(UChar), | 
|  | 0, | 
|  |  | 
|  | {0x43, 0x6d, 0x6e, 0x44},     /* dataFormat="CmnD" */ | 
|  | {1, 0, 0, 0},                 /* formatVersion */ | 
|  | {3, 0, 0, 0}                  /* dataVersion */ | 
|  | }; | 
|  |  | 
|  | static uint32_t maxSize; | 
|  |  | 
|  | static char stringStore[STRING_STORE_SIZE]; | 
|  | static uint32_t stringTop=0, basenameTotal=0; | 
|  |  | 
|  | typedef struct { | 
|  | char *pathname, *basename; | 
|  | uint32_t basenameLength, basenameOffset, fileSize, fileOffset; | 
|  | } File; | 
|  |  | 
|  | #define CHUNK_FILE_COUNT 256 | 
|  | static File *files = NULL; | 
|  | static uint32_t fileCount=0; | 
|  | static uint32_t fileMax = 0; | 
|  |  | 
|  |  | 
|  | static char *symPrefix = NULL; | 
|  |  | 
|  | #define LINE_BUFFER_SIZE 512 | 
|  | /* prototypes --------------------------------------------------------------- */ | 
|  |  | 
|  | static void | 
|  | addFile(const char *filename, const char *name, const char *source, UBool sourceTOC, UBool verbose); | 
|  |  | 
|  | static char * | 
|  | allocString(uint32_t length); | 
|  |  | 
|  | U_CDECL_BEGIN | 
|  | static int | 
|  | compareFiles(const void *file1, const void *file2); | 
|  | U_CDECL_END | 
|  |  | 
|  | static char * | 
|  | pathToFullPath(const char *path, const char *source); | 
|  |  | 
|  | /* map non-tree separator (such as '\') to tree separator ('/') inplace. */ | 
|  | static void | 
|  | fixDirToTreePath(char *s); | 
|  | /* -------------------------------------------------------------------------- */ | 
|  |  | 
|  | U_CAPI void U_EXPORT2 | 
|  | createCommonDataFile(const char *destDir, const char *name, const char *entrypointName, const char *type, const char *source, const char *copyRight, | 
|  | const char *dataFile, uint32_t max_size, UBool sourceTOC, UBool verbose, char *gencmnFileName) { | 
|  | static char buffer[4096]; | 
|  | char *line; | 
|  | char *linePtr; | 
|  | char *s = NULL; | 
|  | UErrorCode errorCode=U_ZERO_ERROR; | 
|  | uint32_t i, fileOffset, basenameOffset, length, nread; | 
|  | FileStream *in, *file; | 
|  |  | 
|  | line = (char *)uprv_malloc(sizeof(char) * LINE_BUFFER_SIZE); | 
|  | if (line == NULL) { | 
|  | fprintf(stderr, "gencmn: unable to allocate memory for line buffer of size %d\n", LINE_BUFFER_SIZE); | 
|  | exit(U_MEMORY_ALLOCATION_ERROR); | 
|  | } | 
|  |  | 
|  | linePtr = line; | 
|  |  | 
|  | maxSize = max_size; | 
|  |  | 
|  | if (destDir == NULL) { | 
|  | destDir = u_getDataDirectory(); | 
|  | } | 
|  | if (name == NULL) { | 
|  | name = COMMON_DATA_NAME; | 
|  | } | 
|  | if (type == NULL) { | 
|  | type = DATA_TYPE; | 
|  | } | 
|  | if (source == NULL) { | 
|  | source = "."; | 
|  | } | 
|  |  | 
|  | if (dataFile == NULL) { | 
|  | in = T_FileStream_stdin(); | 
|  | } else { | 
|  | in = T_FileStream_open(dataFile, "r"); | 
|  | if(in == NULL) { | 
|  | fprintf(stderr, "gencmn: unable to open input file %s\n", dataFile); | 
|  | exit(U_FILE_ACCESS_ERROR); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (verbose) { | 
|  | if(sourceTOC) { | 
|  | printf("generating %s_%s.c (table of contents source file)\n", name, type); | 
|  | } else { | 
|  | printf("generating %s.%s (common data file with table of contents)\n", name, type); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* read the list of files and get their lengths */ | 
|  | while((s != NULL && *s != 0) || (s=T_FileStream_readLine(in, (line=linePtr), | 
|  | LINE_BUFFER_SIZE))!=NULL) { | 
|  | /* remove trailing newline characters and parse space separated items */ | 
|  | if (s != NULL && *s != 0) { | 
|  | line=s; | 
|  | } else { | 
|  | s=line; | 
|  | } | 
|  | while(*s!=0) { | 
|  | if(*s==' ') { | 
|  | *s=0; | 
|  | ++s; | 
|  | break; | 
|  | } else if(*s=='\r' || *s=='\n') { | 
|  | *s=0; | 
|  | break; | 
|  | } | 
|  | ++s; | 
|  | } | 
|  |  | 
|  | /* check for comment */ | 
|  |  | 
|  | if (*line == '#') { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* add the file */ | 
|  | #if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR) | 
|  | { | 
|  | char *t; | 
|  | while((t = uprv_strchr(line,U_FILE_ALT_SEP_CHAR))) { | 
|  | *t = U_FILE_SEP_CHAR; | 
|  | } | 
|  | } | 
|  | #endif | 
|  | addFile(getLongPathname(line), name, source, sourceTOC, verbose); | 
|  | } | 
|  |  | 
|  | uprv_free(linePtr); | 
|  |  | 
|  | if(in!=T_FileStream_stdin()) { | 
|  | T_FileStream_close(in); | 
|  | } | 
|  |  | 
|  | if(fileCount==0) { | 
|  | fprintf(stderr, "gencmn: no files listed in %s\n", dataFile == NULL ? "<stdin>" : dataFile); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* sort the files by basename */ | 
|  | qsort(files, fileCount, sizeof(File), compareFiles); | 
|  |  | 
|  | if(!sourceTOC) { | 
|  | UNewDataMemory *out; | 
|  |  | 
|  | /* determine the offsets of all basenames and files in this common one */ | 
|  | basenameOffset=4+8*fileCount; | 
|  | fileOffset=(basenameOffset+(basenameTotal+15))&~0xf; | 
|  | for(i=0; i<fileCount; ++i) { | 
|  | files[i].fileOffset=fileOffset; | 
|  | fileOffset+=(files[i].fileSize+15)&~0xf; | 
|  | files[i].basenameOffset=basenameOffset; | 
|  | basenameOffset+=files[i].basenameLength; | 
|  | } | 
|  |  | 
|  | /* create the output file */ | 
|  | out=udata_create(destDir, type, name, | 
|  | &dataInfo, | 
|  | copyRight == NULL ? U_COPYRIGHT_STRING : copyRight, | 
|  | &errorCode); | 
|  | if(U_FAILURE(errorCode)) { | 
|  | fprintf(stderr, "gencmn: udata_create(-d %s -n %s -t %s) failed - %s\n", | 
|  | destDir, name, type, | 
|  | u_errorName(errorCode)); | 
|  | exit(errorCode); | 
|  | } | 
|  |  | 
|  | /* write the table of contents */ | 
|  | udata_write32(out, fileCount); | 
|  | for(i=0; i<fileCount; ++i) { | 
|  | udata_write32(out, files[i].basenameOffset); | 
|  | udata_write32(out, files[i].fileOffset); | 
|  | } | 
|  |  | 
|  | /* write the basenames */ | 
|  | for(i=0; i<fileCount; ++i) { | 
|  | udata_writeString(out, files[i].basename, files[i].basenameLength); | 
|  | } | 
|  | length=4+8*fileCount+basenameTotal; | 
|  |  | 
|  | /* copy the files */ | 
|  | for(i=0; i<fileCount; ++i) { | 
|  | /* pad to 16-align the next file */ | 
|  | length&=0xf; | 
|  | if(length!=0) { | 
|  | udata_writePadding(out, 16-length); | 
|  | } | 
|  |  | 
|  | if (verbose) { | 
|  | printf("adding %s (%ld byte%s)\n", files[i].pathname, (long)files[i].fileSize, files[i].fileSize == 1 ? "" : "s"); | 
|  | } | 
|  |  | 
|  | /* copy the next file */ | 
|  | file=T_FileStream_open(files[i].pathname, "rb"); | 
|  | if(file==NULL) { | 
|  | fprintf(stderr, "gencmn: unable to open listed file %s\n", files[i].pathname); | 
|  | exit(U_FILE_ACCESS_ERROR); | 
|  | } | 
|  | for(nread = 0;;) { | 
|  | length=T_FileStream_read(file, buffer, sizeof(buffer)); | 
|  | if(length <= 0) { | 
|  | break; | 
|  | } | 
|  | nread += length; | 
|  | udata_writeBlock(out, buffer, length); | 
|  | } | 
|  | T_FileStream_close(file); | 
|  | length=files[i].fileSize; | 
|  |  | 
|  | if (nread != files[i].fileSize) { | 
|  | fprintf(stderr, "gencmn: unable to read %s properly (got %ld/%ld byte%s)\n", files[i].pathname,  (long)nread, (long)files[i].fileSize, files[i].fileSize == 1 ? "" : "s"); | 
|  | exit(U_FILE_ACCESS_ERROR); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* pad to 16-align the last file (cleaner, avoids growing .dat files in icuswap) */ | 
|  | length&=0xf; | 
|  | if(length!=0) { | 
|  | udata_writePadding(out, 16-length); | 
|  | } | 
|  |  | 
|  | /* finish */ | 
|  | udata_finish(out, &errorCode); | 
|  | if(U_FAILURE(errorCode)) { | 
|  | fprintf(stderr, "gencmn: udata_finish() failed - %s\n", u_errorName(errorCode)); | 
|  | exit(errorCode); | 
|  | } | 
|  | } else { | 
|  | /* write a .c source file with the table of contents */ | 
|  | char *filename; | 
|  | FileStream *out; | 
|  |  | 
|  | /* create the output filename */ | 
|  | filename=s=buffer; | 
|  | uprv_strcpy(filename, destDir); | 
|  | s=filename+uprv_strlen(filename); | 
|  | if(s>filename && *(s-1)!=U_FILE_SEP_CHAR) { | 
|  | *s++=U_FILE_SEP_CHAR; | 
|  | } | 
|  | uprv_strcpy(s, name); | 
|  | if(*(type)!=0) { | 
|  | s+=uprv_strlen(s); | 
|  | *s++='_'; | 
|  | uprv_strcpy(s, type); | 
|  | } | 
|  | s+=uprv_strlen(s); | 
|  | uprv_strcpy(s, ".c"); | 
|  |  | 
|  | /* open the output file */ | 
|  | out=T_FileStream_open(filename, "w"); | 
|  | if (gencmnFileName != NULL) { | 
|  | uprv_strcpy(gencmnFileName, filename); | 
|  | } | 
|  | if(out==NULL) { | 
|  | fprintf(stderr, "gencmn: unable to open .c output file %s\n", filename); | 
|  | exit(U_FILE_ACCESS_ERROR); | 
|  | } | 
|  |  | 
|  | /* write the source file */ | 
|  | sprintf(buffer, | 
|  | "/*\n" | 
|  | " * ICU common data table of contents for %s.%s\n" | 
|  | " * Automatically generated by icu/source/tools/gencmn/gencmn .\n" | 
|  | " */\n\n" | 
|  | "#include \"unicode/utypes.h\"\n" | 
|  | "#include \"unicode/udata.h\"\n" | 
|  | "\n" | 
|  | "/* external symbol declarations for data (%d files) */\n", | 
|  | name, type, fileCount); | 
|  | T_FileStream_writeLine(out, buffer); | 
|  |  | 
|  | sprintf(buffer, "extern const char\n    %s%s[]", symPrefix?symPrefix:"", files[0].pathname); | 
|  | T_FileStream_writeLine(out, buffer); | 
|  | for(i=1; i<fileCount; ++i) { | 
|  | sprintf(buffer, ",\n    %s%s[]", symPrefix?symPrefix:"", files[i].pathname); | 
|  | T_FileStream_writeLine(out, buffer); | 
|  | } | 
|  | T_FileStream_writeLine(out, ";\n\n"); | 
|  |  | 
|  | sprintf( | 
|  | buffer, | 
|  | "U_EXPORT struct {\n" | 
|  | "    uint16_t headerSize;\n" | 
|  | "    uint8_t magic1, magic2;\n" | 
|  | "    UDataInfo info;\n" | 
|  | "    char padding[%lu];\n" | 
|  | "    uint32_t count, reserved;\n" | 
|  | "    struct {\n" | 
|  | "        const char *name;\n" | 
|  | "        const void *data;\n" | 
|  | "    } toc[%lu];\n" | 
|  | "} U_EXPORT2 %s_dat = {\n" | 
|  | "    32, 0xda, 0x27, {\n" | 
|  | "        %lu, 0,\n" | 
|  | "        %u, %u, %u, 0,\n" | 
|  | "        {0x54, 0x6f, 0x43, 0x50},\n" | 
|  | "        {1, 0, 0, 0},\n" | 
|  | "        {0, 0, 0, 0}\n" | 
|  | "    },\n" | 
|  | "    \"\", %lu, 0, {\n", | 
|  | static_cast<unsigned long>(32-4-sizeof(UDataInfo)), | 
|  | static_cast<unsigned long>(fileCount), | 
|  | entrypointName, | 
|  | static_cast<unsigned long>(sizeof(UDataInfo)), | 
|  | U_IS_BIG_ENDIAN, | 
|  | U_CHARSET_FAMILY, | 
|  | U_SIZEOF_UCHAR, | 
|  | static_cast<unsigned long>(fileCount) | 
|  | ); | 
|  | T_FileStream_writeLine(out, buffer); | 
|  |  | 
|  | sprintf(buffer, "        { \"%s\", %s%s }", files[0].basename, symPrefix?symPrefix:"", files[0].pathname); | 
|  | T_FileStream_writeLine(out, buffer); | 
|  | for(i=1; i<fileCount; ++i) { | 
|  | sprintf(buffer, ",\n        { \"%s\", %s%s }", files[i].basename, symPrefix?symPrefix:"", files[i].pathname); | 
|  | T_FileStream_writeLine(out, buffer); | 
|  | } | 
|  |  | 
|  | T_FileStream_writeLine(out, "\n    }\n};\n"); | 
|  | T_FileStream_close(out); | 
|  |  | 
|  | uprv_free(symPrefix); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | addFile(const char *filename, const char *name, const char *source, UBool sourceTOC, UBool verbose) { | 
|  | char *s; | 
|  | uint32_t length; | 
|  | char *fullPath = NULL; | 
|  |  | 
|  | if(fileCount==fileMax) { | 
|  | fileMax += CHUNK_FILE_COUNT; | 
|  | files = (File *)uprv_realloc(files, fileMax*sizeof(files[0])); /* note: never freed. */ | 
|  | if(files==NULL) { | 
|  | fprintf(stderr, "pkgdata/gencmn: Could not allocate %u bytes for %d files\n", (unsigned int)(fileMax*sizeof(files[0])), fileCount); | 
|  | exit(U_MEMORY_ALLOCATION_ERROR); | 
|  | } | 
|  | } | 
|  |  | 
|  | if(!sourceTOC) { | 
|  | FileStream *file; | 
|  |  | 
|  | if(uprv_pathIsAbsolute(filename)) { | 
|  | fprintf(stderr, "gencmn: Error: absolute path encountered. Old style paths are not supported. Use relative paths such as 'fur.res' or 'translit%cfur.res'.\n\tBad path: '%s'\n", U_FILE_SEP_CHAR, filename); | 
|  | exit(U_ILLEGAL_ARGUMENT_ERROR); | 
|  | } | 
|  | fullPath = pathToFullPath(filename, source); | 
|  | /* store the pathname */ | 
|  | length = (uint32_t)(uprv_strlen(filename) + 1 + uprv_strlen(name) + 1); | 
|  | s=allocString(length); | 
|  | uprv_strcpy(s, name); | 
|  | uprv_strcat(s, U_TREE_ENTRY_SEP_STRING); | 
|  | uprv_strcat(s, filename); | 
|  |  | 
|  | /* get the basename */ | 
|  | fixDirToTreePath(s); | 
|  | files[fileCount].basename=s; | 
|  | files[fileCount].basenameLength=length; | 
|  |  | 
|  | files[fileCount].pathname=fullPath; | 
|  |  | 
|  | basenameTotal+=length; | 
|  |  | 
|  | /* try to open the file */ | 
|  | file=T_FileStream_open(fullPath, "rb"); | 
|  | if(file==NULL) { | 
|  | fprintf(stderr, "gencmn: unable to open listed file %s\n", fullPath); | 
|  | exit(U_FILE_ACCESS_ERROR); | 
|  | } | 
|  |  | 
|  | /* get the file length */ | 
|  | length=T_FileStream_size(file); | 
|  | if(T_FileStream_error(file) || length<=20) { | 
|  | fprintf(stderr, "gencmn: unable to get length of listed file %s\n", fullPath); | 
|  | exit(U_FILE_ACCESS_ERROR); | 
|  | } | 
|  |  | 
|  | T_FileStream_close(file); | 
|  |  | 
|  | /* do not add files that are longer than maxSize */ | 
|  | if(maxSize && length>maxSize) { | 
|  | if (verbose) { | 
|  | printf("%s ignored (size %ld > %ld)\n", fullPath, (long)length, (long)maxSize); | 
|  | } | 
|  | return; | 
|  | } | 
|  | files[fileCount].fileSize=length; | 
|  | } else { | 
|  | char *t; | 
|  | /* get and store the basename */ | 
|  | /* need to include the package name */ | 
|  | length = (uint32_t)(uprv_strlen(filename) + 1 + uprv_strlen(name) + 1); | 
|  | s=allocString(length); | 
|  | uprv_strcpy(s, name); | 
|  | uprv_strcat(s, U_TREE_ENTRY_SEP_STRING); | 
|  | uprv_strcat(s, filename); | 
|  | fixDirToTreePath(s); | 
|  | files[fileCount].basename=s; | 
|  | /* turn the basename into an entry point name and store in the pathname field */ | 
|  | t=files[fileCount].pathname=allocString(length); | 
|  | while(--length>0) { | 
|  | if(*s=='.' || *s=='-' || *s=='/') { | 
|  | *t='_'; | 
|  | } else { | 
|  | *t=*s; | 
|  | } | 
|  | ++s; | 
|  | ++t; | 
|  | } | 
|  | *t=0; | 
|  | } | 
|  | ++fileCount; | 
|  | } | 
|  |  | 
|  | static char * | 
|  | allocString(uint32_t length) { | 
|  | uint32_t top=stringTop+length; | 
|  | char *p; | 
|  |  | 
|  | if(top>STRING_STORE_SIZE) { | 
|  | fprintf(stderr, "gencmn: out of memory\n"); | 
|  | exit(U_MEMORY_ALLOCATION_ERROR); | 
|  | } | 
|  | p=stringStore+stringTop; | 
|  | stringTop=top; | 
|  | return p; | 
|  | } | 
|  |  | 
|  | static char * | 
|  | pathToFullPath(const char *path, const char *source) { | 
|  | int32_t length; | 
|  | int32_t newLength; | 
|  | char *fullPath; | 
|  | int32_t n; | 
|  |  | 
|  | length = (uint32_t)(uprv_strlen(path) + 1); | 
|  | newLength = (length + 1 + (int32_t)uprv_strlen(source)); | 
|  | fullPath = (char *)uprv_malloc(newLength); | 
|  | if(source != NULL) { | 
|  | uprv_strcpy(fullPath, source); | 
|  | uprv_strcat(fullPath, U_FILE_SEP_STRING); | 
|  | } else { | 
|  | fullPath[0] = 0; | 
|  | } | 
|  | n = (int32_t)uprv_strlen(fullPath); | 
|  | fullPath[n] = 0;       /* Suppress compiler warning for unused variable n    */ | 
|  | /*  when conditional code below is not compiled.      */ | 
|  | uprv_strcat(fullPath, path); | 
|  |  | 
|  | #if (U_FILE_ALT_SEP_CHAR != U_TREE_ENTRY_SEP_CHAR) | 
|  | #if (U_FILE_ALT_SEP_CHAR != U_FILE_SEP_CHAR) | 
|  | /* replace tree separator (such as '/') with file sep char (such as ':' or '\\') */ | 
|  | for(;fullPath[n];n++) { | 
|  | if(fullPath[n] == U_FILE_ALT_SEP_CHAR) { | 
|  | fullPath[n] = U_FILE_SEP_CHAR; | 
|  | } | 
|  | } | 
|  | #endif | 
|  | #endif | 
|  | #if (U_FILE_SEP_CHAR != U_TREE_ENTRY_SEP_CHAR) | 
|  | /* replace tree separator (such as '/') with file sep char (such as ':' or '\\') */ | 
|  | for(;fullPath[n];n++) { | 
|  | if(fullPath[n] == U_TREE_ENTRY_SEP_CHAR) { | 
|  | fullPath[n] = U_FILE_SEP_CHAR; | 
|  | } | 
|  | } | 
|  | #endif | 
|  | return fullPath; | 
|  | } | 
|  |  | 
|  | U_CDECL_BEGIN | 
|  | static int | 
|  | compareFiles(const void *file1, const void *file2) { | 
|  | /* sort by basename */ | 
|  | return uprv_strcmp(((File *)file1)->basename, ((File *)file2)->basename); | 
|  | } | 
|  | U_CDECL_END | 
|  |  | 
|  | static void | 
|  | fixDirToTreePath(char *s) | 
|  | { | 
|  | (void)s; | 
|  | #if (U_FILE_SEP_CHAR != U_TREE_ENTRY_SEP_CHAR) || ((U_FILE_ALT_SEP_CHAR != U_FILE_SEP_CHAR) && (U_FILE_ALT_SEP_CHAR != U_TREE_ENTRY_SEP_CHAR)) | 
|  | char *t; | 
|  | #endif | 
|  | #if (U_FILE_SEP_CHAR != U_TREE_ENTRY_SEP_CHAR) | 
|  | for(t=s;t=uprv_strchr(t,U_FILE_SEP_CHAR);) { | 
|  | *t = U_TREE_ENTRY_SEP_CHAR; | 
|  | } | 
|  | #endif | 
|  | #if (U_FILE_ALT_SEP_CHAR != U_FILE_SEP_CHAR) && (U_FILE_ALT_SEP_CHAR != U_TREE_ENTRY_SEP_CHAR) | 
|  | for(t=s;t=uprv_strchr(t,U_FILE_ALT_SEP_CHAR);) { | 
|  | *t = U_TREE_ENTRY_SEP_CHAR; | 
|  | } | 
|  | #endif | 
|  | } |