1/*
2*******************************************************************************
3*
4*   Copyright (C) 1998-2014, International Business Machines
5*   Corporation and others.  All Rights Reserved.
6*
7*******************************************************************************
8*
9* File parse.cpp
10*
11* Modification History:
12*
13*   Date          Name          Description
14*   05/26/99     stephen       Creation.
15*   02/25/00     weiv          Overhaul to write udata
16*   5/10/01      Ram           removed ustdio dependency
17*   06/10/2001  Dominic Ludlam <dom@recoil.org> Rewritten
18*******************************************************************************
19*/
20
21// Safer use of UnicodeString.
22#ifndef UNISTR_FROM_CHAR_EXPLICIT
23#   define UNISTR_FROM_CHAR_EXPLICIT explicit
24#endif
25
26// Less important, but still a good idea.
27#ifndef UNISTR_FROM_STRING_EXPLICIT
28#   define UNISTR_FROM_STRING_EXPLICIT explicit
29#endif
30
31#include "parse.h"
32#include "errmsg.h"
33#include "uhash.h"
34#include "cmemory.h"
35#include "cstring.h"
36#include "uinvchar.h"
37#include "read.h"
38#include "ustr.h"
39#include "reslist.h"
40#include "rbt_pars.h"
41#include "genrb.h"
42#include "unicode/ustring.h"
43#include "unicode/uscript.h"
44#include "unicode/utf16.h"
45#include "unicode/putil.h"
46#include "collationbuilder.h"
47#include "collationdata.h"
48#include "collationdatareader.h"
49#include "collationdatawriter.h"
50#include "collationfastlatinbuilder.h"
51#include "collationinfo.h"
52#include "collationroot.h"
53#include "collationruleparser.h"
54#include "collationtailoring.h"
55#include <stdio.h>
56
57/* Number of tokens to read ahead of the current stream position */
58#define MAX_LOOKAHEAD   3
59
60#define CR               0x000D
61#define LF               0x000A
62#define SPACE            0x0020
63#define TAB              0x0009
64#define ESCAPE           0x005C
65#define HASH             0x0023
66#define QUOTE            0x0027
67#define ZERO             0x0030
68#define STARTCOMMAND     0x005B
69#define ENDCOMMAND       0x005D
70#define OPENSQBRACKET    0x005B
71#define CLOSESQBRACKET   0x005D
72
73using icu::LocalPointer;
74using icu::UnicodeString;
75
76struct Lookahead
77{
78     enum   ETokenType type;
79     struct UString    value;
80     struct UString    comment;
81     uint32_t          line;
82};
83
84/* keep in sync with token defines in read.h */
85const char *tokenNames[TOK_TOKEN_COUNT] =
86{
87     "string",             /* A string token, such as "MonthNames" */
88     "'{'",                 /* An opening brace character */
89     "'}'",                 /* A closing brace character */
90     "','",                 /* A comma */
91     "':'",                 /* A colon */
92
93     "<end of file>",     /* End of the file has been reached successfully */
94     "<end of line>"
95};
96
97/* Just to store "TRUE" */
98//static const UChar trueValue[] = {0x0054, 0x0052, 0x0055, 0x0045, 0x0000};
99
100typedef struct {
101    struct Lookahead  lookahead[MAX_LOOKAHEAD + 1];
102    uint32_t          lookaheadPosition;
103    UCHARBUF         *buffer;
104    struct SRBRoot *bundle;
105    const char     *inputdir;
106    uint32_t        inputdirLength;
107    const char     *outputdir;
108    uint32_t        outputdirLength;
109    const char     *filename;
110    UBool           makeBinaryCollation;
111    UBool           omitCollationRules;
112} ParseState;
113
114typedef struct SResource *
115ParseResourceFunction(ParseState* state, char *tag, uint32_t startline, const struct UString* comment, UErrorCode *status);
116
117static struct SResource *parseResource(ParseState* state, char *tag, const struct UString *comment, UErrorCode *status);
118
119/* The nature of the lookahead buffer:
120   There are MAX_LOOKAHEAD + 1 slots, used as a circular buffer.  This provides
121   MAX_LOOKAHEAD lookahead tokens and a slot for the current token and value.
122   When getToken is called, the current pointer is moved to the next slot and the
123   old slot is filled with the next token from the reader by calling getNextToken.
124   The token values are stored in the slot, which means that token values don't
125   survive a call to getToken, ie.
126
127   UString *value;
128
129   getToken(&value, NULL, status);
130   getToken(NULL,   NULL, status);       bad - value is now a different string
131*/
132static void
133initLookahead(ParseState* state, UCHARBUF *buf, UErrorCode *status)
134{
135    static uint32_t initTypeStrings = 0;
136    uint32_t i;
137
138    if (!initTypeStrings)
139    {
140        initTypeStrings = 1;
141    }
142
143    state->lookaheadPosition   = 0;
144    state->buffer              = buf;
145
146    resetLineNumber();
147
148    for (i = 0; i < MAX_LOOKAHEAD; i++)
149    {
150        state->lookahead[i].type = getNextToken(state->buffer, &state->lookahead[i].value, &state->lookahead[i].line, &state->lookahead[i].comment, status);
151        if (U_FAILURE(*status))
152        {
153            return;
154        }
155    }
156
157    *status = U_ZERO_ERROR;
158}
159
160static void
161cleanupLookahead(ParseState* state)
162{
163    uint32_t i;
164    for (i = 0; i <= MAX_LOOKAHEAD; i++)
165    {
166        ustr_deinit(&state->lookahead[i].value);
167        ustr_deinit(&state->lookahead[i].comment);
168    }
169
170}
171
172static enum ETokenType
173getToken(ParseState* state, struct UString **tokenValue, struct UString* comment, uint32_t *linenumber, UErrorCode *status)
174{
175    enum ETokenType result;
176    uint32_t          i;
177
178    result = state->lookahead[state->lookaheadPosition].type;
179
180    if (tokenValue != NULL)
181    {
182        *tokenValue = &state->lookahead[state->lookaheadPosition].value;
183    }
184
185    if (linenumber != NULL)
186    {
187        *linenumber = state->lookahead[state->lookaheadPosition].line;
188    }
189
190    if (comment != NULL)
191    {
192        ustr_cpy(comment, &(state->lookahead[state->lookaheadPosition].comment), status);
193    }
194
195    i = (state->lookaheadPosition + MAX_LOOKAHEAD) % (MAX_LOOKAHEAD + 1);
196    state->lookaheadPosition = (state->lookaheadPosition + 1) % (MAX_LOOKAHEAD + 1);
197    ustr_setlen(&state->lookahead[i].comment, 0, status);
198    ustr_setlen(&state->lookahead[i].value, 0, status);
199    state->lookahead[i].type = getNextToken(state->buffer, &state->lookahead[i].value, &state->lookahead[i].line, &state->lookahead[i].comment, status);
200
201    /* printf("getToken, returning %s\n", tokenNames[result]); */
202
203    return result;
204}
205
206static enum ETokenType
207peekToken(ParseState* state, uint32_t lookaheadCount, struct UString **tokenValue, uint32_t *linenumber, struct UString *comment, UErrorCode *status)
208{
209    uint32_t i = (state->lookaheadPosition + lookaheadCount) % (MAX_LOOKAHEAD + 1);
210
211    if (U_FAILURE(*status))
212    {
213        return TOK_ERROR;
214    }
215
216    if (lookaheadCount >= MAX_LOOKAHEAD)
217    {
218        *status = U_INTERNAL_PROGRAM_ERROR;
219        return TOK_ERROR;
220    }
221
222    if (tokenValue != NULL)
223    {
224        *tokenValue = &state->lookahead[i].value;
225    }
226
227    if (linenumber != NULL)
228    {
229        *linenumber = state->lookahead[i].line;
230    }
231
232    if(comment != NULL){
233        ustr_cpy(comment, &(state->lookahead[state->lookaheadPosition].comment), status);
234    }
235
236    return state->lookahead[i].type;
237}
238
239static void
240expect(ParseState* state, enum ETokenType expectedToken, struct UString **tokenValue, struct UString *comment, uint32_t *linenumber, UErrorCode *status)
241{
242    uint32_t        line;
243
244    enum ETokenType token = getToken(state, tokenValue, comment, &line, status);
245
246    if (linenumber != NULL)
247    {
248        *linenumber = line;
249    }
250
251    if (U_FAILURE(*status))
252    {
253        return;
254    }
255
256    if (token != expectedToken)
257    {
258        *status = U_INVALID_FORMAT_ERROR;
259        error(line, "expecting %s, got %s", tokenNames[expectedToken], tokenNames[token]);
260    }
261    else
262    {
263        *status = U_ZERO_ERROR;
264    }
265}
266
267static char *getInvariantString(ParseState* state, uint32_t *line, struct UString *comment, UErrorCode *status)
268{
269    struct UString *tokenValue;
270    char           *result;
271    uint32_t        count;
272
273    expect(state, TOK_STRING, &tokenValue, comment, line, status);
274
275    if (U_FAILURE(*status))
276    {
277        return NULL;
278    }
279
280    count = u_strlen(tokenValue->fChars);
281    if(!uprv_isInvariantUString(tokenValue->fChars, count)) {
282        *status = U_INVALID_FORMAT_ERROR;
283        error(*line, "invariant characters required for table keys, binary data, etc.");
284        return NULL;
285    }
286
287    result = static_cast<char *>(uprv_malloc(count+1));
288
289    if (result == NULL)
290    {
291        *status = U_MEMORY_ALLOCATION_ERROR;
292        return NULL;
293    }
294
295    u_UCharsToChars(tokenValue->fChars, result, count+1);
296    return result;
297}
298
299static struct SResource *
300parseUCARules(ParseState* state, char *tag, uint32_t startline, const struct UString* /*comment*/, UErrorCode *status)
301{
302    struct SResource *result = NULL;
303    struct UString   *tokenValue;
304    FileStream       *file          = NULL;
305    char              filename[256] = { '\0' };
306    char              cs[128]       = { '\0' };
307    uint32_t          line;
308    UBool quoted = FALSE;
309    UCHARBUF *ucbuf=NULL;
310    UChar32   c     = 0;
311    const char* cp  = NULL;
312    UChar *pTarget     = NULL;
313    UChar *target      = NULL;
314    UChar *targetLimit = NULL;
315    int32_t size = 0;
316
317    expect(state, TOK_STRING, &tokenValue, NULL, &line, status);
318
319    if(isVerbose()){
320        printf(" %s at line %i \n",  (tag == NULL) ? "(null)" : tag, (int)startline);
321    }
322
323    if (U_FAILURE(*status))
324    {
325        return NULL;
326    }
327    /* make the filename including the directory */
328    if (state->inputdir != NULL)
329    {
330        uprv_strcat(filename, state->inputdir);
331
332        if (state->inputdir[state->inputdirLength - 1] != U_FILE_SEP_CHAR)
333        {
334            uprv_strcat(filename, U_FILE_SEP_STRING);
335        }
336    }
337
338    u_UCharsToChars(tokenValue->fChars, cs, tokenValue->fLength);
339
340    expect(state, TOK_CLOSE_BRACE, NULL, NULL, NULL, status);
341
342    if (U_FAILURE(*status))
343    {
344        return NULL;
345    }
346    uprv_strcat(filename, cs);
347
348    if(state->omitCollationRules) {
349        return res_none();
350    }
351
352    ucbuf = ucbuf_open(filename, &cp, getShowWarning(),FALSE, status);
353
354    if (U_FAILURE(*status)) {
355        error(line, "An error occured while opening the input file %s\n", filename);
356        return NULL;
357    }
358
359    /* We allocate more space than actually required
360    * since the actual size needed for storing UChars
361    * is not known in UTF-8 byte stream
362    */
363    size        = ucbuf_size(ucbuf) + 1;
364    pTarget     = (UChar*) uprv_malloc(U_SIZEOF_UCHAR * size);
365    uprv_memset(pTarget, 0, size*U_SIZEOF_UCHAR);
366    target      = pTarget;
367    targetLimit = pTarget+size;
368
369    /* read the rules into the buffer */
370    while (target < targetLimit)
371    {
372        c = ucbuf_getc(ucbuf, status);
373        if(c == QUOTE) {
374            quoted = (UBool)!quoted;
375        }
376        /* weiv (06/26/2002): adding the following:
377         * - preserving spaces in commands [...]
378         * - # comments until the end of line
379         */
380        if (c == STARTCOMMAND && !quoted)
381        {
382            /* preserve commands
383             * closing bracket will be handled by the
384             * append at the end of the loop
385             */
386            while(c != ENDCOMMAND) {
387                U_APPEND_CHAR32_ONLY(c, target);
388                c = ucbuf_getc(ucbuf, status);
389            }
390        }
391        else if (c == HASH && !quoted) {
392            /* skip comments */
393            while(c != CR && c != LF) {
394                c = ucbuf_getc(ucbuf, status);
395            }
396            continue;
397        }
398        else if (c == ESCAPE)
399        {
400            c = unescape(ucbuf, status);
401
402            if (c == (UChar32)U_ERR)
403            {
404                uprv_free(pTarget);
405                T_FileStream_close(file);
406                return NULL;
407            }
408        }
409        else if (!quoted && (c == SPACE || c == TAB || c == CR || c == LF))
410        {
411            /* ignore spaces carriage returns
412            * and line feed unless in the form \uXXXX
413            */
414            continue;
415        }
416
417        /* Append UChar * after dissembling if c > 0xffff*/
418        if (c != (UChar32)U_EOF)
419        {
420            U_APPEND_CHAR32_ONLY(c, target);
421        }
422        else
423        {
424            break;
425        }
426    }
427
428    /* terminate the string */
429    if(target < targetLimit){
430        *target = 0x0000;
431    }
432
433    result = string_open(state->bundle, tag, pTarget, (int32_t)(target - pTarget), NULL, status);
434
435
436    ucbuf_close(ucbuf);
437    uprv_free(pTarget);
438    T_FileStream_close(file);
439
440    return result;
441}
442
443static struct SResource *
444parseTransliterator(ParseState* state, char *tag, uint32_t startline, const struct UString* /*comment*/, UErrorCode *status)
445{
446    struct SResource *result = NULL;
447    struct UString   *tokenValue;
448    FileStream       *file          = NULL;
449    char              filename[256] = { '\0' };
450    char              cs[128]       = { '\0' };
451    uint32_t          line;
452    UCHARBUF *ucbuf=NULL;
453    const char* cp  = NULL;
454    UChar *pTarget     = NULL;
455    const UChar *pSource     = NULL;
456    int32_t size = 0;
457
458    expect(state, TOK_STRING, &tokenValue, NULL, &line, status);
459
460    if(isVerbose()){
461        printf(" %s at line %i \n",  (tag == NULL) ? "(null)" : tag, (int)startline);
462    }
463
464    if (U_FAILURE(*status))
465    {
466        return NULL;
467    }
468    /* make the filename including the directory */
469    if (state->inputdir != NULL)
470    {
471        uprv_strcat(filename, state->inputdir);
472
473        if (state->inputdir[state->inputdirLength - 1] != U_FILE_SEP_CHAR)
474        {
475            uprv_strcat(filename, U_FILE_SEP_STRING);
476        }
477    }
478
479    u_UCharsToChars(tokenValue->fChars, cs, tokenValue->fLength);
480
481    expect(state, TOK_CLOSE_BRACE, NULL, NULL, NULL, status);
482
483    if (U_FAILURE(*status))
484    {
485        return NULL;
486    }
487    uprv_strcat(filename, cs);
488
489
490    ucbuf = ucbuf_open(filename, &cp, getShowWarning(),FALSE, status);
491
492    if (U_FAILURE(*status)) {
493        error(line, "An error occured while opening the input file %s\n", filename);
494        return NULL;
495    }
496
497    /* We allocate more space than actually required
498    * since the actual size needed for storing UChars
499    * is not known in UTF-8 byte stream
500    */
501    pSource = ucbuf_getBuffer(ucbuf, &size, status);
502    pTarget     = (UChar*) uprv_malloc(U_SIZEOF_UCHAR * (size + 1));
503    uprv_memset(pTarget, 0, size*U_SIZEOF_UCHAR);
504
505#if !UCONFIG_NO_TRANSLITERATION
506    size = utrans_stripRules(pSource, size, pTarget, status);
507#else
508    size = 0;
509    fprintf(stderr, " Warning: writing empty transliteration data ( UCONFIG_NO_TRANSLITERATION ) \n");
510#endif
511    result = string_open(state->bundle, tag, pTarget, size, NULL, status);
512
513    ucbuf_close(ucbuf);
514    uprv_free(pTarget);
515    T_FileStream_close(file);
516
517    return result;
518}
519static struct SResource* dependencyArray = NULL;
520
521static struct SResource *
522parseDependency(ParseState* state, char *tag, uint32_t startline, const struct UString* comment, UErrorCode *status)
523{
524    struct SResource *result = NULL;
525    struct SResource *elem = NULL;
526    struct UString   *tokenValue;
527    uint32_t          line;
528    char              filename[256] = { '\0' };
529    char              cs[128]       = { '\0' };
530
531    expect(state, TOK_STRING, &tokenValue, NULL, &line, status);
532
533    if(isVerbose()){
534        printf(" %s at line %i \n",  (tag == NULL) ? "(null)" : tag, (int)startline);
535    }
536
537    if (U_FAILURE(*status))
538    {
539        return NULL;
540    }
541    /* make the filename including the directory */
542    if (state->outputdir != NULL)
543    {
544        uprv_strcat(filename, state->outputdir);
545
546        if (state->outputdir[state->outputdirLength - 1] != U_FILE_SEP_CHAR)
547        {
548            uprv_strcat(filename, U_FILE_SEP_STRING);
549        }
550    }
551
552    u_UCharsToChars(tokenValue->fChars, cs, tokenValue->fLength);
553
554    if (U_FAILURE(*status))
555    {
556        return NULL;
557    }
558    uprv_strcat(filename, cs);
559    if(!T_FileStream_file_exists(filename)){
560        if(isStrict()){
561            error(line, "The dependency file %s does not exist. Please make sure it exists.\n",filename);
562        }else{
563            warning(line, "The dependency file %s does not exist. Please make sure it exists.\n",filename);
564        }
565    }
566    if(dependencyArray==NULL){
567        dependencyArray = array_open(state->bundle, "%%DEPENDENCY", NULL, status);
568    }
569    if(tag!=NULL){
570        result = string_open(state->bundle, tag, tokenValue->fChars, tokenValue->fLength, comment, status);
571    }
572    elem = string_open(state->bundle, NULL, tokenValue->fChars, tokenValue->fLength, comment, status);
573
574    array_add(dependencyArray, elem, status);
575
576    if (U_FAILURE(*status))
577    {
578        return NULL;
579    }
580    expect(state, TOK_CLOSE_BRACE, NULL, NULL, NULL, status);
581    return result;
582}
583static struct SResource *
584parseString(ParseState* state, char *tag, uint32_t startline, const struct UString* comment, UErrorCode *status)
585{
586    struct UString   *tokenValue;
587    struct SResource *result = NULL;
588
589/*    if (tag != NULL && uprv_strcmp(tag, "%%UCARULES") == 0)
590    {
591        return parseUCARules(tag, startline, status);
592    }*/
593    if(isVerbose()){
594        printf(" string %s at line %i \n",  (tag == NULL) ? "(null)" : tag, (int)startline);
595    }
596    expect(state, TOK_STRING, &tokenValue, NULL, NULL, status);
597
598    if (U_SUCCESS(*status))
599    {
600        /* create the string now - tokenValue doesn't survive a call to getToken (and therefore
601        doesn't survive expect either) */
602
603        result = string_open(state->bundle, tag, tokenValue->fChars, tokenValue->fLength, comment, status);
604        if(U_SUCCESS(*status) && result) {
605            expect(state, TOK_CLOSE_BRACE, NULL, NULL, NULL, status);
606
607            if (U_FAILURE(*status))
608            {
609                res_close(result);
610                return NULL;
611            }
612        }
613    }
614
615    return result;
616}
617
618static struct SResource *
619parseAlias(ParseState* state, char *tag, uint32_t startline, const struct UString *comment, UErrorCode *status)
620{
621    struct UString   *tokenValue;
622    struct SResource *result  = NULL;
623
624    expect(state, TOK_STRING, &tokenValue, NULL, NULL, status);
625
626    if(isVerbose()){
627        printf(" alias %s at line %i \n",  (tag == NULL) ? "(null)" : tag, (int)startline);
628    }
629
630    if (U_SUCCESS(*status))
631    {
632        /* create the string now - tokenValue doesn't survive a call to getToken (and therefore
633        doesn't survive expect either) */
634
635        result = alias_open(state->bundle, tag, tokenValue->fChars, tokenValue->fLength, comment, status);
636
637        expect(state, TOK_CLOSE_BRACE, NULL, NULL, NULL, status);
638
639        if (U_FAILURE(*status))
640        {
641            res_close(result);
642            return NULL;
643        }
644    }
645
646    return result;
647}
648
649#if !UCONFIG_NO_COLLATION
650
651namespace {
652
653static struct SResource* resLookup(struct SResource* res, const char* key){
654    struct SResource *current = NULL;
655    struct SResTable *list;
656    if (res == res_none()) {
657        return NULL;
658    }
659
660    list = &(res->u.fTable);
661
662    current = list->fFirst;
663    while (current != NULL) {
664        if (uprv_strcmp(((list->fRoot->fKeys) + (current->fKey)), key) == 0) {
665            return current;
666        }
667        current = current->fNext;
668    }
669    return NULL;
670}
671
672class GenrbImporter : public icu::CollationRuleParser::Importer {
673public:
674    GenrbImporter(const char *in, const char *out) : inputDir(in), outputDir(out) {}
675    virtual ~GenrbImporter();
676    virtual const UnicodeString *getRules(
677            const char *localeID, const char *collationType,
678            const char *&errorReason, UErrorCode &errorCode);
679
680private:
681    const char *inputDir;
682    const char *outputDir;
683    UnicodeString rules;
684};
685
686GenrbImporter::~GenrbImporter() {}
687
688const UnicodeString *
689GenrbImporter::getRules(
690        const char *localeID, const char *collationType,
691        const char *& /*errorReason*/, UErrorCode &errorCode) {
692    struct SRBRoot *data         = NULL;
693    UCHARBUF       *ucbuf        = NULL;
694    int localeLength = strlen(localeID);
695    char* filename = (char*)uprv_malloc(localeLength+5);
696    char           *inputDirBuf  = NULL;
697    char           *openFileName = NULL;
698    const char* cp = "";
699    int32_t i = 0;
700    int32_t dirlen  = 0;
701    int32_t filelen = 0;
702    struct SResource* root;
703    struct SResource* collations;
704    struct SResource* collation;
705    struct SResource* sequence;
706
707    memcpy(filename, localeID, localeLength);
708    for(i = 0; i < localeLength; i++){
709        if(filename[i] == '-'){
710            filename[i] = '_';
711        }
712    }
713    filename[localeLength]   = '.';
714    filename[localeLength+1] = 't';
715    filename[localeLength+2] = 'x';
716    filename[localeLength+3] = 't';
717    filename[localeLength+4] = 0;
718
719
720    if (U_FAILURE(errorCode)) {
721        return NULL;
722    }
723    if(filename==NULL){
724        errorCode=U_ILLEGAL_ARGUMENT_ERROR;
725        return NULL;
726    }else{
727        filelen = (int32_t)uprv_strlen(filename);
728    }
729    if(inputDir == NULL) {
730        const char *filenameBegin = uprv_strrchr(filename, U_FILE_SEP_CHAR);
731        openFileName = (char *) uprv_malloc(dirlen + filelen + 2);
732        openFileName[0] = '\0';
733        if (filenameBegin != NULL) {
734            /*
735             * When a filename ../../../data/root.txt is specified,
736             * we presume that the input directory is ../../../data
737             * This is very important when the resource file includes
738             * another file, like UCARules.txt or thaidict.brk.
739             */
740            int32_t filenameSize = (int32_t)(filenameBegin - filename + 1);
741            inputDirBuf = (char *)uprv_malloc(filenameSize);
742
743            /* test for NULL */
744            if(inputDirBuf == NULL) {
745                errorCode = U_MEMORY_ALLOCATION_ERROR;
746                goto finish;
747            }
748
749            uprv_strncpy(inputDirBuf, filename, filenameSize);
750            inputDirBuf[filenameSize - 1] = 0;
751            inputDir = inputDirBuf;
752            dirlen  = (int32_t)uprv_strlen(inputDir);
753        }
754    }else{
755        dirlen  = (int32_t)uprv_strlen(inputDir);
756
757        if(inputDir[dirlen-1] != U_FILE_SEP_CHAR) {
758            openFileName = (char *) uprv_malloc(dirlen + filelen + 2);
759
760            /* test for NULL */
761            if(openFileName == NULL) {
762                errorCode = U_MEMORY_ALLOCATION_ERROR;
763                goto finish;
764            }
765
766            openFileName[0] = '\0';
767            /*
768             * append the input dir to openFileName if the first char in
769             * filename is not file seperation char and the last char input directory is  not '.'.
770             * This is to support :
771             * genrb -s. /home/icu/data
772             * genrb -s. icu/data
773             * The user cannot mix notations like
774             * genrb -s. /icu/data --- the absolute path specified. -s redundant
775             * user should use
776             * genrb -s. icu/data  --- start from CWD and look in icu/data dir
777             */
778            if( (filename[0] != U_FILE_SEP_CHAR) && (inputDir[dirlen-1] !='.')){
779                uprv_strcpy(openFileName, inputDir);
780                openFileName[dirlen]     = U_FILE_SEP_CHAR;
781            }
782            openFileName[dirlen + 1] = '\0';
783        } else {
784            openFileName = (char *) uprv_malloc(dirlen + filelen + 1);
785
786            /* test for NULL */
787            if(openFileName == NULL) {
788                errorCode = U_MEMORY_ALLOCATION_ERROR;
789                goto finish;
790            }
791
792            uprv_strcpy(openFileName, inputDir);
793
794        }
795    }
796    uprv_strcat(openFileName, filename);
797    /* printf("%s\n", openFileName);  */
798    errorCode = U_ZERO_ERROR;
799    ucbuf = ucbuf_open(openFileName, &cp,getShowWarning(),TRUE, &errorCode);
800
801    if(errorCode == U_FILE_ACCESS_ERROR) {
802
803        fprintf(stderr, "couldn't open file %s\n", openFileName == NULL ? filename : openFileName);
804        goto finish;
805    }
806    if (ucbuf == NULL || U_FAILURE(errorCode)) {
807        fprintf(stderr, "An error occured processing file %s. Error: %s\n", openFileName == NULL ? filename : openFileName,u_errorName(errorCode));
808        goto finish;
809    }
810
811    /* Parse the data into an SRBRoot */
812    data = parse(ucbuf, inputDir, outputDir, filename, FALSE, FALSE, &errorCode);
813
814    root = data->fRoot;
815    collations = resLookup(root, "collations");
816    if (collations != NULL) {
817      collation = resLookup(collations, collationType);
818      if (collation != NULL) {
819        sequence = resLookup(collation, "Sequence");
820        if (sequence != NULL) {
821          rules.setTo(FALSE, sequence->u.fString.fChars, sequence->u.fString.fLength);
822        }
823      }
824    }
825
826finish:
827    if (inputDirBuf != NULL) {
828        uprv_free(inputDirBuf);
829    }
830
831    if (openFileName != NULL) {
832        uprv_free(openFileName);
833    }
834
835    if(ucbuf) {
836        ucbuf_close(ucbuf);
837    }
838
839    return &rules;
840}
841
842// Quick-and-dirty escaping function.
843// Assumes that we are on an ASCII-based platform.
844static void
845escape(const UChar *s, char *buffer) {
846    int32_t length = u_strlen(s);
847    int32_t i = 0;
848    for (;;) {
849        UChar32 c;
850        U16_NEXT(s, i, length, c);
851        if (c == 0) {
852            *buffer = 0;
853            return;
854        } else if (0x20 <= c && c <= 0x7e) {
855            // printable ASCII
856            *buffer++ = (char)c;  // assumes ASCII-based platform
857        } else {
858            buffer += sprintf(buffer, "\\u%04X", (int)c);
859        }
860    }
861}
862
863}  // namespace
864
865#endif  // !UCONFIG_NO_COLLATION
866
867static struct SResource *
868addCollation(ParseState* state, struct SResource  *result, const char *collationType,
869             uint32_t startline, UErrorCode *status)
870{
871    // TODO: Use LocalPointer for result, or make caller close it when there is a failure.
872    struct SResource  *member = NULL;
873    struct UString    *tokenValue;
874    struct UString     comment;
875    enum   ETokenType  token;
876    char               subtag[1024];
877    UnicodeString      rules;
878    UBool              haveRules = FALSE;
879    UVersionInfo       version;
880    uint32_t           line;
881
882    /* '{' . (name resource)* '}' */
883    version[0]=0; version[1]=0; version[2]=0; version[3]=0;
884
885    for (;;)
886    {
887        ustr_init(&comment);
888        token = getToken(state, &tokenValue, &comment, &line, status);
889
890        if (token == TOK_CLOSE_BRACE)
891        {
892            break;
893        }
894
895        if (token != TOK_STRING)
896        {
897            res_close(result);
898            *status = U_INVALID_FORMAT_ERROR;
899
900            if (token == TOK_EOF)
901            {
902                error(startline, "unterminated table");
903            }
904            else
905            {
906                error(line, "Unexpected token %s", tokenNames[token]);
907            }
908
909            return NULL;
910        }
911
912        u_UCharsToChars(tokenValue->fChars, subtag, u_strlen(tokenValue->fChars) + 1);
913
914        if (U_FAILURE(*status))
915        {
916            res_close(result);
917            return NULL;
918        }
919
920        member = parseResource(state, subtag, NULL, status);
921
922        if (U_FAILURE(*status))
923        {
924            res_close(result);
925            return NULL;
926        }
927        if (result == NULL)
928        {
929            // Ignore the parsed resources, continue parsing.
930        }
931        else if (uprv_strcmp(subtag, "Version") == 0)
932        {
933            char     ver[40];
934            int32_t length = member->u.fString.fLength;
935
936            if (length >= (int32_t) sizeof(ver))
937            {
938                length = (int32_t) sizeof(ver) - 1;
939            }
940
941            u_UCharsToChars(member->u.fString.fChars, ver, length + 1); /* +1 for copying NULL */
942            u_versionFromString(version, ver);
943
944            table_add(result, member, line, status);
945            member = NULL;
946        }
947        else if(uprv_strcmp(subtag, "%%CollationBin")==0)
948        {
949            /* discard duplicate %%CollationBin if any*/
950        }
951        else if (uprv_strcmp(subtag, "Sequence") == 0)
952        {
953            rules.setTo(member->u.fString.fChars, member->u.fString.fLength);
954            haveRules = TRUE;
955            // Defer building the collator until we have seen
956            // all sub-elements of the collation table, including the Version.
957            /* in order to achieve smaller data files, we can direct genrb */
958            /* to omit collation rules */
959            if(!state->omitCollationRules) {
960                table_add(result, member, line, status);
961                member = NULL;
962            }
963        }
964        else  // Just copy non-special items.
965        {
966            table_add(result, member, line, status);
967            member = NULL;
968        }
969        res_close(member);  // TODO: use LocalPointer
970        if (U_FAILURE(*status))
971        {
972            res_close(result);
973            return NULL;
974        }
975    }
976
977    if (!haveRules) { return result; }
978
979#if UCONFIG_NO_COLLATION || UCONFIG_NO_FILE_IO
980    warning(line, "Not building collation elements because of UCONFIG_NO_COLLATION and/or UCONFIG_NO_FILE_IO, see uconfig.h");
981    (void)collationType;
982#else
983    if(!state->makeBinaryCollation) {
984        if(isVerbose()) {
985            printf("Not building %s~%s collation binary\n", state->filename, collationType);
986        }
987        return result;
988    }
989    UErrorCode intStatus = U_ZERO_ERROR;
990    UParseError parseError;
991    uprv_memset(&parseError, 0, sizeof(parseError));
992    GenrbImporter importer(state->inputdir, state->outputdir);
993    const icu::CollationTailoring *base = icu::CollationRoot::getRoot(intStatus);
994    if(U_FAILURE(intStatus)) {
995        error(line, "failed to load root collator (ucadata.icu) - %s", u_errorName(intStatus));
996        res_close(result);
997        return NULL;  // TODO: use LocalUResourceBundlePointer for result
998    }
999    icu::CollationBuilder builder(base, intStatus);
1000    if(uprv_strncmp(collationType, "search", 6) == 0) {
1001        builder.disableFastLatin();  // build fast-Latin table unless search collator
1002    }
1003    LocalPointer<icu::CollationTailoring> t(
1004            builder.parseAndBuild(rules, version, &importer, &parseError, intStatus));
1005    if(U_FAILURE(intStatus)) {
1006        const char *reason = builder.getErrorReason();
1007        if(reason == NULL) { reason = ""; }
1008        error(line, "CollationBuilder failed at %s~%s/Sequence rule offset %ld: %s  %s",
1009                state->filename, collationType,
1010                (long)parseError.offset, u_errorName(intStatus), reason);
1011        if(parseError.preContext[0] != 0 || parseError.postContext[0] != 0) {
1012            // Print pre- and post-context.
1013            char preBuffer[100], postBuffer[100];
1014            escape(parseError.preContext, preBuffer);
1015            escape(parseError.postContext, postBuffer);
1016            error(line, "  error context: \"...%s\" ! \"%s...\"", preBuffer, postBuffer);
1017        }
1018        if(isStrict()) {
1019            *status = intStatus;
1020            res_close(result);
1021            return NULL;
1022        }
1023    }
1024    icu::LocalMemory<uint8_t> buffer;
1025    int32_t capacity = 100000;
1026    uint8_t *dest = buffer.allocateInsteadAndCopy(capacity);
1027    if(dest == NULL) {
1028        fprintf(stderr, "memory allocation (%ld bytes) for file contents failed\n",
1029                (long)capacity);
1030        *status = U_MEMORY_ALLOCATION_ERROR;
1031        res_close(result);
1032        return NULL;
1033    }
1034    int32_t indexes[icu::CollationDataReader::IX_TOTAL_SIZE + 1];
1035    int32_t totalSize = icu::CollationDataWriter::writeTailoring(
1036            *t, *t->settings, indexes, dest, capacity, intStatus);
1037    if(intStatus == U_BUFFER_OVERFLOW_ERROR) {
1038        intStatus = U_ZERO_ERROR;
1039        capacity = totalSize;
1040        dest = buffer.allocateInsteadAndCopy(capacity);
1041        if(dest == NULL) {
1042            fprintf(stderr, "memory allocation (%ld bytes) for file contents failed\n",
1043                    (long)capacity);
1044            *status = U_MEMORY_ALLOCATION_ERROR;
1045            res_close(result);
1046            return NULL;
1047        }
1048        totalSize = icu::CollationDataWriter::writeTailoring(
1049                *t, *t->settings, indexes, dest, capacity, intStatus);
1050    }
1051    if(U_FAILURE(intStatus)) {
1052        fprintf(stderr, "CollationDataWriter::writeTailoring() failed: %s\n",
1053                u_errorName(intStatus));
1054        res_close(result);
1055        return NULL;
1056    }
1057    if(isVerbose()) {
1058        printf("%s~%s collation tailoring part sizes:\n", state->filename, collationType);
1059        icu::CollationInfo::printSizes(totalSize, indexes);
1060    }
1061    struct SResource *collationBin = bin_open(state->bundle, "%%CollationBin", totalSize, dest, NULL, NULL, status);
1062    table_add(result, collationBin, line, status);
1063    if (U_FAILURE(*status)) {
1064        res_close(result);
1065        return NULL;
1066    }
1067#endif
1068    return result;
1069}
1070
1071static UBool
1072keepCollationType(const char *type) {
1073    // BEGIN android-added
1074    if (uprv_strcmp(type, "big5han") == 0 && !gIncludeBig5HanColl) { return FALSE; }
1075    if (uprv_strcmp(type, "gb2312han") == 0 && !gIncludeGb2312HanColl) { return FALSE; }
1076    if (uprv_strcmp(type, "zhuyin") == 0 && !gIncludeZhuyinHanColl) { return FALSE; }
1077    // END android-added
1078    return gIncludeUnihanColl || uprv_strcmp(type, "unihan") != 0;
1079}
1080
1081static struct SResource *
1082parseCollationElements(ParseState* state, char *tag, uint32_t startline, UBool newCollation, UErrorCode *status)
1083{
1084    struct SResource  *result = NULL;
1085    struct SResource  *member = NULL;
1086    struct SResource  *collationRes = NULL;
1087    struct UString    *tokenValue;
1088    struct UString     comment;
1089    enum   ETokenType  token;
1090    char               subtag[1024], typeKeyword[1024];
1091    uint32_t           line;
1092
1093    result = table_open(state->bundle, tag, NULL, status);
1094
1095    if (result == NULL || U_FAILURE(*status))
1096    {
1097        return NULL;
1098    }
1099    if(isVerbose()){
1100        printf(" collation elements %s at line %i \n",  (tag == NULL) ? "(null)" : tag, (int)startline);
1101    }
1102    if(!newCollation) {
1103        return addCollation(state, result, "(no type)", startline, status);
1104    }
1105    else {
1106        for(;;) {
1107            ustr_init(&comment);
1108            token = getToken(state, &tokenValue, &comment, &line, status);
1109
1110            if (token == TOK_CLOSE_BRACE)
1111            {
1112                return result;
1113            }
1114
1115            if (token != TOK_STRING)
1116            {
1117                res_close(result);
1118                *status = U_INVALID_FORMAT_ERROR;
1119
1120                if (token == TOK_EOF)
1121                {
1122                    error(startline, "unterminated table");
1123                }
1124                else
1125                {
1126                    error(line, "Unexpected token %s", tokenNames[token]);
1127                }
1128
1129                return NULL;
1130            }
1131
1132            u_UCharsToChars(tokenValue->fChars, subtag, u_strlen(tokenValue->fChars) + 1);
1133
1134            if (U_FAILURE(*status))
1135            {
1136                res_close(result);
1137                return NULL;
1138            }
1139
1140            if (uprv_strcmp(subtag, "default") == 0)
1141            {
1142                member = parseResource(state, subtag, NULL, status);
1143
1144                if (U_FAILURE(*status))
1145                {
1146                    res_close(result);
1147                    return NULL;
1148                }
1149
1150                table_add(result, member, line, status);
1151            }
1152            else
1153            {
1154                token = peekToken(state, 0, &tokenValue, &line, &comment, status);
1155                /* this probably needs to be refactored or recursively use the parser */
1156                /* first we assume that our collation table won't have the explicit type */
1157                /* then, we cannot handle aliases */
1158                if(token == TOK_OPEN_BRACE) {
1159                    token = getToken(state, &tokenValue, &comment, &line, status);
1160                    if (keepCollationType(subtag)) {
1161                        collationRes = table_open(state->bundle, subtag, NULL, status);
1162                    } else {
1163                        collationRes = NULL;
1164                    }
1165                    // need to parse the collation data regardless
1166                    collationRes = addCollation(state, collationRes, subtag, startline, status);
1167                    if (collationRes != NULL) {
1168                        table_add(result, collationRes, startline, status);
1169                    }
1170                } else if(token == TOK_COLON) { /* right now, we'll just try to see if we have aliases */
1171                    /* we could have a table too */
1172                    token = peekToken(state, 1, &tokenValue, &line, &comment, status);
1173                    u_UCharsToChars(tokenValue->fChars, typeKeyword, u_strlen(tokenValue->fChars) + 1);
1174                    if(uprv_strcmp(typeKeyword, "alias") == 0) {
1175                        member = parseResource(state, subtag, NULL, status);
1176                        if (U_FAILURE(*status))
1177                        {
1178                            res_close(result);
1179                            return NULL;
1180                        }
1181
1182                        table_add(result, member, line, status);
1183                    } else {
1184                        res_close(result);
1185                        *status = U_INVALID_FORMAT_ERROR;
1186                        return NULL;
1187                    }
1188                } else {
1189                    res_close(result);
1190                    *status = U_INVALID_FORMAT_ERROR;
1191                    return NULL;
1192                }
1193            }
1194
1195            /*member = string_open(bundle, subtag, tokenValue->fChars, tokenValue->fLength, status);*/
1196
1197            /*expect(TOK_CLOSE_BRACE, NULL, NULL, status);*/
1198
1199            if (U_FAILURE(*status))
1200            {
1201                res_close(result);
1202                return NULL;
1203            }
1204        }
1205    }
1206}
1207
1208/* Necessary, because CollationElements requires the bundle->fRoot member to be present which,
1209   if this weren't special-cased, wouldn't be set until the entire file had been processed. */
1210static struct SResource *
1211realParseTable(ParseState* state, struct SResource *table, char *tag, uint32_t startline, UErrorCode *status)
1212{
1213    struct SResource  *member = NULL;
1214    struct UString    *tokenValue=NULL;
1215    struct UString    comment;
1216    enum   ETokenType token;
1217    char              subtag[1024];
1218    uint32_t          line;
1219    UBool             readToken = FALSE;
1220
1221    /* '{' . (name resource)* '}' */
1222
1223    if(isVerbose()){
1224        printf(" parsing table %s at line %i \n", (tag == NULL) ? "(null)" : tag, (int)startline);
1225    }
1226    for (;;)
1227    {
1228        ustr_init(&comment);
1229        token = getToken(state, &tokenValue, &comment, &line, status);
1230
1231        if (token == TOK_CLOSE_BRACE)
1232        {
1233            if (!readToken) {
1234                warning(startline, "Encountered empty table");
1235            }
1236            return table;
1237        }
1238
1239        if (token != TOK_STRING)
1240        {
1241            *status = U_INVALID_FORMAT_ERROR;
1242
1243            if (token == TOK_EOF)
1244            {
1245                error(startline, "unterminated table");
1246            }
1247            else
1248            {
1249                error(line, "unexpected token %s", tokenNames[token]);
1250            }
1251
1252            return NULL;
1253        }
1254
1255        if(uprv_isInvariantUString(tokenValue->fChars, -1)) {
1256            u_UCharsToChars(tokenValue->fChars, subtag, u_strlen(tokenValue->fChars) + 1);
1257        } else {
1258            *status = U_INVALID_FORMAT_ERROR;
1259            error(line, "invariant characters required for table keys");
1260            return NULL;
1261        }
1262
1263        if (U_FAILURE(*status))
1264        {
1265            error(line, "parse error. Stopped parsing tokens with %s", u_errorName(*status));
1266            return NULL;
1267        }
1268
1269        member = parseResource(state, subtag, &comment, status);
1270
1271        if (member == NULL || U_FAILURE(*status))
1272        {
1273            error(line, "parse error. Stopped parsing resource with %s", u_errorName(*status));
1274            return NULL;
1275        }
1276
1277        table_add(table, member, line, status);
1278
1279        if (U_FAILURE(*status))
1280        {
1281            error(line, "parse error. Stopped parsing table with %s", u_errorName(*status));
1282            return NULL;
1283        }
1284        readToken = TRUE;
1285        ustr_deinit(&comment);
1286   }
1287
1288    /* not reached */
1289    /* A compiler warning will appear if all paths don't contain a return statement. */
1290/*     *status = U_INTERNAL_PROGRAM_ERROR;
1291     return NULL;*/
1292}
1293
1294static struct SResource *
1295parseTable(ParseState* state, char *tag, uint32_t startline, const struct UString *comment, UErrorCode *status)
1296{
1297    struct SResource *result;
1298
1299    if (tag != NULL && uprv_strcmp(tag, "CollationElements") == 0)
1300    {
1301        return parseCollationElements(state, tag, startline, FALSE, status);
1302    }
1303    if (tag != NULL && uprv_strcmp(tag, "collations") == 0)
1304    {
1305        return parseCollationElements(state, tag, startline, TRUE, status);
1306    }
1307    if(isVerbose()){
1308        printf(" table %s at line %i \n",  (tag == NULL) ? "(null)" : tag, (int)startline);
1309    }
1310
1311    result = table_open(state->bundle, tag, comment, status);
1312
1313    if (result == NULL || U_FAILURE(*status))
1314    {
1315        return NULL;
1316    }
1317    return realParseTable(state, result, tag, startline,  status);
1318}
1319
1320static struct SResource *
1321parseArray(ParseState* state, char *tag, uint32_t startline, const struct UString *comment, UErrorCode *status)
1322{
1323    struct SResource  *result = NULL;
1324    struct SResource  *member = NULL;
1325    struct UString    *tokenValue;
1326    struct UString    memberComments;
1327    enum   ETokenType token;
1328    UBool             readToken = FALSE;
1329
1330    result = array_open(state->bundle, tag, comment, status);
1331
1332    if (result == NULL || U_FAILURE(*status))
1333    {
1334        return NULL;
1335    }
1336    if(isVerbose()){
1337        printf(" array %s at line %i \n",  (tag == NULL) ? "(null)" : tag, (int)startline);
1338    }
1339
1340    ustr_init(&memberComments);
1341
1342    /* '{' . resource [','] '}' */
1343    for (;;)
1344    {
1345        /* reset length */
1346        ustr_setlen(&memberComments, 0, status);
1347
1348        /* check for end of array, but don't consume next token unless it really is the end */
1349        token = peekToken(state, 0, &tokenValue, NULL, &memberComments, status);
1350
1351
1352        if (token == TOK_CLOSE_BRACE)
1353        {
1354            getToken(state, NULL, NULL, NULL, status);
1355            if (!readToken) {
1356                warning(startline, "Encountered empty array");
1357            }
1358            break;
1359        }
1360
1361        if (token == TOK_EOF)
1362        {
1363            res_close(result);
1364            *status = U_INVALID_FORMAT_ERROR;
1365            error(startline, "unterminated array");
1366            return NULL;
1367        }
1368
1369        /* string arrays are a special case */
1370        if (token == TOK_STRING)
1371        {
1372            getToken(state, &tokenValue, &memberComments, NULL, status);
1373            member = string_open(state->bundle, NULL, tokenValue->fChars, tokenValue->fLength, &memberComments, status);
1374        }
1375        else
1376        {
1377            member = parseResource(state, NULL, &memberComments, status);
1378        }
1379
1380        if (member == NULL || U_FAILURE(*status))
1381        {
1382            res_close(result);
1383            return NULL;
1384        }
1385
1386        array_add(result, member, status);
1387
1388        if (U_FAILURE(*status))
1389        {
1390            res_close(result);
1391            return NULL;
1392        }
1393
1394        /* eat optional comma if present */
1395        token = peekToken(state, 0, NULL, NULL, NULL, status);
1396
1397        if (token == TOK_COMMA)
1398        {
1399            getToken(state, NULL, NULL, NULL, status);
1400        }
1401
1402        if (U_FAILURE(*status))
1403        {
1404            res_close(result);
1405            return NULL;
1406        }
1407        readToken = TRUE;
1408    }
1409
1410    ustr_deinit(&memberComments);
1411    return result;
1412}
1413
1414static struct SResource *
1415parseIntVector(ParseState* state, char *tag, uint32_t startline, const struct UString *comment, UErrorCode *status)
1416{
1417    struct SResource  *result = NULL;
1418    enum   ETokenType  token;
1419    char              *string;
1420    int32_t            value;
1421    UBool              readToken = FALSE;
1422    char              *stopstring;
1423    uint32_t           len;
1424    struct UString     memberComments;
1425
1426    result = intvector_open(state->bundle, tag, comment, status);
1427
1428    if (result == NULL || U_FAILURE(*status))
1429    {
1430        return NULL;
1431    }
1432
1433    if(isVerbose()){
1434        printf(" vector %s at line %i \n",  (tag == NULL) ? "(null)" : tag, (int)startline);
1435    }
1436    ustr_init(&memberComments);
1437    /* '{' . string [','] '}' */
1438    for (;;)
1439    {
1440        ustr_setlen(&memberComments, 0, status);
1441
1442        /* check for end of array, but don't consume next token unless it really is the end */
1443        token = peekToken(state, 0, NULL, NULL,&memberComments, status);
1444
1445        if (token == TOK_CLOSE_BRACE)
1446        {
1447            /* it's the end, consume the close brace */
1448            getToken(state, NULL, NULL, NULL, status);
1449            if (!readToken) {
1450                warning(startline, "Encountered empty int vector");
1451            }
1452            ustr_deinit(&memberComments);
1453            return result;
1454        }
1455
1456        string = getInvariantString(state, NULL, NULL, status);
1457
1458        if (U_FAILURE(*status))
1459        {
1460            res_close(result);
1461            return NULL;
1462        }
1463
1464        /* For handling illegal char in the Intvector */
1465        value = uprv_strtoul(string, &stopstring, 0);/* make intvector support decimal,hexdigit,octal digit ranging from -2^31-2^32-1*/
1466        len=(uint32_t)(stopstring-string);
1467
1468        if(len==uprv_strlen(string))
1469        {
1470            intvector_add(result, value, status);
1471            uprv_free(string);
1472            token = peekToken(state, 0, NULL, NULL, NULL, status);
1473        }
1474        else
1475        {
1476            uprv_free(string);
1477            *status=U_INVALID_CHAR_FOUND;
1478        }
1479
1480        if (U_FAILURE(*status))
1481        {
1482            res_close(result);
1483            return NULL;
1484        }
1485
1486        /* the comma is optional (even though it is required to prevent the reader from concatenating
1487        consecutive entries) so that a missing comma on the last entry isn't an error */
1488        if (token == TOK_COMMA)
1489        {
1490            getToken(state, NULL, NULL, NULL, status);
1491        }
1492        readToken = TRUE;
1493    }
1494
1495    /* not reached */
1496    /* A compiler warning will appear if all paths don't contain a return statement. */
1497/*    intvector_close(result, status);
1498    *status = U_INTERNAL_PROGRAM_ERROR;
1499    return NULL;*/
1500}
1501
1502static struct SResource *
1503parseBinary(ParseState* state, char *tag, uint32_t startline, const struct UString *comment, UErrorCode *status)
1504{
1505    struct SResource *result = NULL;
1506    uint8_t          *value;
1507    char             *string;
1508    char              toConv[3] = {'\0', '\0', '\0'};
1509    uint32_t          count;
1510    uint32_t          i;
1511    uint32_t          line;
1512    char             *stopstring;
1513    uint32_t          len;
1514
1515    string = getInvariantString(state, &line, NULL, status);
1516
1517    if (string == NULL || U_FAILURE(*status))
1518    {
1519        return NULL;
1520    }
1521
1522    expect(state, TOK_CLOSE_BRACE, NULL, NULL, NULL, status);
1523
1524    if (U_FAILURE(*status))
1525    {
1526        uprv_free(string);
1527        return NULL;
1528    }
1529
1530    if(isVerbose()){
1531        printf(" binary %s at line %i \n",  (tag == NULL) ? "(null)" : tag, (int)startline);
1532    }
1533
1534    count = (uint32_t)uprv_strlen(string);
1535    if (count > 0){
1536        if((count % 2)==0){
1537            value = static_cast<uint8_t *>(uprv_malloc(sizeof(uint8_t) * count));
1538
1539            if (value == NULL)
1540            {
1541                uprv_free(string);
1542                *status = U_MEMORY_ALLOCATION_ERROR;
1543                return NULL;
1544            }
1545
1546            for (i = 0; i < count; i += 2)
1547            {
1548                toConv[0] = string[i];
1549                toConv[1] = string[i + 1];
1550
1551                value[i >> 1] = (uint8_t) uprv_strtoul(toConv, &stopstring, 16);
1552                len=(uint32_t)(stopstring-toConv);
1553
1554                if(len!=uprv_strlen(toConv))
1555                {
1556                    uprv_free(string);
1557                    *status=U_INVALID_CHAR_FOUND;
1558                    return NULL;
1559                }
1560            }
1561
1562            result = bin_open(state->bundle, tag, (i >> 1), value,NULL, comment, status);
1563
1564            uprv_free(value);
1565        }
1566        else
1567        {
1568            *status = U_INVALID_CHAR_FOUND;
1569            uprv_free(string);
1570            error(line, "Encountered invalid binary string");
1571            return NULL;
1572        }
1573    }
1574    else
1575    {
1576        result = bin_open(state->bundle, tag, 0, NULL, "",comment,status);
1577        warning(startline, "Encountered empty binary tag");
1578    }
1579    uprv_free(string);
1580
1581    return result;
1582}
1583
1584static struct SResource *
1585parseInteger(ParseState* state, char *tag, uint32_t startline, const struct UString *comment, UErrorCode *status)
1586{
1587    struct SResource *result = NULL;
1588    int32_t           value;
1589    char             *string;
1590    char             *stopstring;
1591    uint32_t          len;
1592
1593    string = getInvariantString(state, NULL, NULL, status);
1594
1595    if (string == NULL || U_FAILURE(*status))
1596    {
1597        return NULL;
1598    }
1599
1600    expect(state, TOK_CLOSE_BRACE, NULL, NULL, NULL, status);
1601
1602    if (U_FAILURE(*status))
1603    {
1604        uprv_free(string);
1605        return NULL;
1606    }
1607
1608    if(isVerbose()){
1609        printf(" integer %s at line %i \n",  (tag == NULL) ? "(null)" : tag, (int)startline);
1610    }
1611
1612    if (uprv_strlen(string) <= 0)
1613    {
1614        warning(startline, "Encountered empty integer. Default value is 0.");
1615    }
1616
1617    /* Allow integer support for hexdecimal, octal digit and decimal*/
1618    /* and handle illegal char in the integer*/
1619    value = uprv_strtoul(string, &stopstring, 0);
1620    len=(uint32_t)(stopstring-string);
1621    if(len==uprv_strlen(string))
1622    {
1623        result = int_open(state->bundle, tag, value, comment, status);
1624    }
1625    else
1626    {
1627        *status=U_INVALID_CHAR_FOUND;
1628    }
1629    uprv_free(string);
1630
1631    return result;
1632}
1633
1634static struct SResource *
1635parseImport(ParseState* state, char *tag, uint32_t startline, const struct UString* comment, UErrorCode *status)
1636{
1637    struct SResource *result;
1638    FileStream       *file;
1639    int32_t           len;
1640    uint8_t          *data;
1641    char             *filename;
1642    uint32_t          line;
1643    char     *fullname = NULL;
1644    filename = getInvariantString(state, &line, NULL, status);
1645
1646    if (U_FAILURE(*status))
1647    {
1648        return NULL;
1649    }
1650
1651    expect(state, TOK_CLOSE_BRACE, NULL, NULL, NULL, status);
1652
1653    if (U_FAILURE(*status))
1654    {
1655        uprv_free(filename);
1656        return NULL;
1657    }
1658
1659    if(isVerbose()){
1660        printf(" import %s at line %i \n",  (tag == NULL) ? "(null)" : tag, (int)startline);
1661    }
1662
1663    /* Open the input file for reading */
1664    if (state->inputdir == NULL)
1665    {
1666#if 1
1667        /*
1668         * Always save file file name, even if there's
1669         * no input directory specified. MIGHT BREAK SOMETHING
1670         */
1671        int32_t filenameLength = uprv_strlen(filename);
1672
1673        fullname = (char *) uprv_malloc(filenameLength + 1);
1674        uprv_strcpy(fullname, filename);
1675#endif
1676
1677        file = T_FileStream_open(filename, "rb");
1678    }
1679    else
1680    {
1681
1682        int32_t  count     = (int32_t)uprv_strlen(filename);
1683
1684        if (state->inputdir[state->inputdirLength - 1] != U_FILE_SEP_CHAR)
1685        {
1686            fullname = (char *) uprv_malloc(state->inputdirLength + count + 2);
1687
1688            /* test for NULL */
1689            if(fullname == NULL)
1690            {
1691                *status = U_MEMORY_ALLOCATION_ERROR;
1692                return NULL;
1693            }
1694
1695            uprv_strcpy(fullname, state->inputdir);
1696
1697            fullname[state->inputdirLength]      = U_FILE_SEP_CHAR;
1698            fullname[state->inputdirLength + 1] = '\0';
1699
1700            uprv_strcat(fullname, filename);
1701        }
1702        else
1703        {
1704            fullname = (char *) uprv_malloc(state->inputdirLength + count + 1);
1705
1706            /* test for NULL */
1707            if(fullname == NULL)
1708            {
1709                *status = U_MEMORY_ALLOCATION_ERROR;
1710                return NULL;
1711            }
1712
1713            uprv_strcpy(fullname, state->inputdir);
1714            uprv_strcat(fullname, filename);
1715        }
1716
1717        file = T_FileStream_open(fullname, "rb");
1718
1719    }
1720
1721    if (file == NULL)
1722    {
1723        error(line, "couldn't open input file %s", filename);
1724        *status = U_FILE_ACCESS_ERROR;
1725        return NULL;
1726    }
1727
1728    len  = T_FileStream_size(file);
1729    data = (uint8_t*)uprv_malloc(len * sizeof(uint8_t));
1730    /* test for NULL */
1731    if(data == NULL)
1732    {
1733        *status = U_MEMORY_ALLOCATION_ERROR;
1734        T_FileStream_close (file);
1735        return NULL;
1736    }
1737
1738    /* int32_t numRead = */ T_FileStream_read  (file, data, len);
1739    T_FileStream_close (file);
1740
1741    result = bin_open(state->bundle, tag, len, data, fullname, comment, status);
1742
1743    uprv_free(data);
1744    uprv_free(filename);
1745    uprv_free(fullname);
1746
1747    return result;
1748}
1749
1750static struct SResource *
1751parseInclude(ParseState* state, char *tag, uint32_t startline, const struct UString* comment, UErrorCode *status)
1752{
1753    struct SResource *result;
1754    int32_t           len=0;
1755    char             *filename;
1756    uint32_t          line;
1757    UChar *pTarget     = NULL;
1758
1759    UCHARBUF *ucbuf;
1760    char     *fullname = NULL;
1761    int32_t  count     = 0;
1762    const char* cp = NULL;
1763    const UChar* uBuffer = NULL;
1764
1765    filename = getInvariantString(state, &line, NULL, status);
1766    count     = (int32_t)uprv_strlen(filename);
1767
1768    if (U_FAILURE(*status))
1769    {
1770        return NULL;
1771    }
1772
1773    expect(state, TOK_CLOSE_BRACE, NULL, NULL, NULL, status);
1774
1775    if (U_FAILURE(*status))
1776    {
1777        uprv_free(filename);
1778        return NULL;
1779    }
1780
1781    if(isVerbose()){
1782        printf(" include %s at line %i \n",  (tag == NULL) ? "(null)" : tag, (int)startline);
1783    }
1784
1785    fullname = (char *) uprv_malloc(state->inputdirLength + count + 2);
1786    /* test for NULL */
1787    if(fullname == NULL)
1788    {
1789        *status = U_MEMORY_ALLOCATION_ERROR;
1790        uprv_free(filename);
1791        return NULL;
1792    }
1793
1794    if(state->inputdir!=NULL){
1795        if (state->inputdir[state->inputdirLength - 1] != U_FILE_SEP_CHAR)
1796        {
1797
1798            uprv_strcpy(fullname, state->inputdir);
1799
1800            fullname[state->inputdirLength]      = U_FILE_SEP_CHAR;
1801            fullname[state->inputdirLength + 1] = '\0';
1802
1803            uprv_strcat(fullname, filename);
1804        }
1805        else
1806        {
1807            uprv_strcpy(fullname, state->inputdir);
1808            uprv_strcat(fullname, filename);
1809        }
1810    }else{
1811        uprv_strcpy(fullname,filename);
1812    }
1813
1814    ucbuf = ucbuf_open(fullname, &cp,getShowWarning(),FALSE,status);
1815
1816    if (U_FAILURE(*status)) {
1817        error(line, "couldn't open input file %s\n", filename);
1818        return NULL;
1819    }
1820
1821    uBuffer = ucbuf_getBuffer(ucbuf,&len,status);
1822    result = string_open(state->bundle, tag, uBuffer, len, comment, status);
1823
1824    ucbuf_close(ucbuf);
1825
1826    uprv_free(pTarget);
1827
1828    uprv_free(filename);
1829    uprv_free(fullname);
1830
1831    return result;
1832}
1833
1834
1835
1836
1837
1838U_STRING_DECL(k_type_string,    "string",    6);
1839U_STRING_DECL(k_type_binary,    "binary",    6);
1840U_STRING_DECL(k_type_bin,       "bin",       3);
1841U_STRING_DECL(k_type_table,     "table",     5);
1842U_STRING_DECL(k_type_table_no_fallback,     "table(nofallback)",         17);
1843U_STRING_DECL(k_type_int,       "int",       3);
1844U_STRING_DECL(k_type_integer,   "integer",   7);
1845U_STRING_DECL(k_type_array,     "array",     5);
1846U_STRING_DECL(k_type_alias,     "alias",     5);
1847U_STRING_DECL(k_type_intvector, "intvector", 9);
1848U_STRING_DECL(k_type_import,    "import",    6);
1849U_STRING_DECL(k_type_include,   "include",   7);
1850
1851/* Various non-standard processing plugins that create one or more special resources. */
1852U_STRING_DECL(k_type_plugin_uca_rules,      "process(uca_rules)",        18);
1853U_STRING_DECL(k_type_plugin_collation,      "process(collation)",        18);
1854U_STRING_DECL(k_type_plugin_transliterator, "process(transliterator)",   23);
1855U_STRING_DECL(k_type_plugin_dependency,     "process(dependency)",       19);
1856
1857typedef enum EResourceType
1858{
1859    RESTYPE_UNKNOWN,
1860    RESTYPE_STRING,
1861    RESTYPE_BINARY,
1862    RESTYPE_TABLE,
1863    RESTYPE_TABLE_NO_FALLBACK,
1864    RESTYPE_INTEGER,
1865    RESTYPE_ARRAY,
1866    RESTYPE_ALIAS,
1867    RESTYPE_INTVECTOR,
1868    RESTYPE_IMPORT,
1869    RESTYPE_INCLUDE,
1870    RESTYPE_PROCESS_UCA_RULES,
1871    RESTYPE_PROCESS_COLLATION,
1872    RESTYPE_PROCESS_TRANSLITERATOR,
1873    RESTYPE_PROCESS_DEPENDENCY,
1874    RESTYPE_RESERVED
1875} EResourceType;
1876
1877static struct {
1878    const char *nameChars;   /* only used for debugging */
1879    const UChar *nameUChars;
1880    ParseResourceFunction *parseFunction;
1881} gResourceTypes[] = {
1882    {"Unknown", NULL, NULL},
1883    {"string", k_type_string, parseString},
1884    {"binary", k_type_binary, parseBinary},
1885    {"table", k_type_table, parseTable},
1886    {"table(nofallback)", k_type_table_no_fallback, NULL}, /* parseFunction will never be called */
1887    {"integer", k_type_integer, parseInteger},
1888    {"array", k_type_array, parseArray},
1889    {"alias", k_type_alias, parseAlias},
1890    {"intvector", k_type_intvector, parseIntVector},
1891    {"import", k_type_import, parseImport},
1892    {"include", k_type_include, parseInclude},
1893    {"process(uca_rules)", k_type_plugin_uca_rules, parseUCARules},
1894    {"process(collation)", k_type_plugin_collation, NULL /* not implemented yet */},
1895    {"process(transliterator)", k_type_plugin_transliterator, parseTransliterator},
1896    {"process(dependency)", k_type_plugin_dependency, parseDependency},
1897    {"reserved", NULL, NULL}
1898};
1899
1900void initParser()
1901{
1902    U_STRING_INIT(k_type_string,    "string",    6);
1903    U_STRING_INIT(k_type_binary,    "binary",    6);
1904    U_STRING_INIT(k_type_bin,       "bin",       3);
1905    U_STRING_INIT(k_type_table,     "table",     5);
1906    U_STRING_INIT(k_type_table_no_fallback,     "table(nofallback)",         17);
1907    U_STRING_INIT(k_type_int,       "int",       3);
1908    U_STRING_INIT(k_type_integer,   "integer",   7);
1909    U_STRING_INIT(k_type_array,     "array",     5);
1910    U_STRING_INIT(k_type_alias,     "alias",     5);
1911    U_STRING_INIT(k_type_intvector, "intvector", 9);
1912    U_STRING_INIT(k_type_import,    "import",    6);
1913    U_STRING_INIT(k_type_include,   "include",   7);
1914
1915    U_STRING_INIT(k_type_plugin_uca_rules,      "process(uca_rules)",        18);
1916    U_STRING_INIT(k_type_plugin_collation,      "process(collation)",        18);
1917    U_STRING_INIT(k_type_plugin_transliterator, "process(transliterator)",   23);
1918    U_STRING_INIT(k_type_plugin_dependency,     "process(dependency)",       19);
1919}
1920
1921static inline UBool isTable(enum EResourceType type) {
1922    return (UBool)(type==RESTYPE_TABLE || type==RESTYPE_TABLE_NO_FALLBACK);
1923}
1924
1925static enum EResourceType
1926parseResourceType(ParseState* state, UErrorCode *status)
1927{
1928    struct UString        *tokenValue;
1929    struct UString        comment;
1930    enum   EResourceType  result = RESTYPE_UNKNOWN;
1931    uint32_t              line=0;
1932    ustr_init(&comment);
1933    expect(state, TOK_STRING, &tokenValue, &comment, &line, status);
1934
1935    if (U_FAILURE(*status))
1936    {
1937        return RESTYPE_UNKNOWN;
1938    }
1939
1940    *status = U_ZERO_ERROR;
1941
1942    /* Search for normal types */
1943    result=RESTYPE_UNKNOWN;
1944    while ((result=(EResourceType)(result+1)) < RESTYPE_RESERVED) {
1945        if (u_strcmp(tokenValue->fChars, gResourceTypes[result].nameUChars) == 0) {
1946            break;
1947        }
1948    }
1949    /* Now search for the aliases */
1950    if (u_strcmp(tokenValue->fChars, k_type_int) == 0) {
1951        result = RESTYPE_INTEGER;
1952    }
1953    else if (u_strcmp(tokenValue->fChars, k_type_bin) == 0) {
1954        result = RESTYPE_BINARY;
1955    }
1956    else if (result == RESTYPE_RESERVED) {
1957        char tokenBuffer[1024];
1958        u_austrncpy(tokenBuffer, tokenValue->fChars, sizeof(tokenBuffer));
1959        tokenBuffer[sizeof(tokenBuffer) - 1] = 0;
1960        *status = U_INVALID_FORMAT_ERROR;
1961        error(line, "unknown resource type '%s'", tokenBuffer);
1962    }
1963
1964    return result;
1965}
1966
1967/* parse a non-top-level resource */
1968static struct SResource *
1969parseResource(ParseState* state, char *tag, const struct UString *comment, UErrorCode *status)
1970{
1971    enum   ETokenType      token;
1972    enum   EResourceType  resType = RESTYPE_UNKNOWN;
1973    ParseResourceFunction *parseFunction = NULL;
1974    struct UString        *tokenValue;
1975    uint32_t                 startline;
1976    uint32_t                 line;
1977
1978
1979    token = getToken(state, &tokenValue, NULL, &startline, status);
1980
1981    if(isVerbose()){
1982        printf(" resource %s at line %i \n",  (tag == NULL) ? "(null)" : tag, (int)startline);
1983    }
1984
1985    /* name . [ ':' type ] '{' resource '}' */
1986    /* This function parses from the colon onwards.  If the colon is present, parse the
1987    type then try to parse a resource of that type.  If there is no explicit type,
1988    work it out using the lookahead tokens. */
1989    switch (token)
1990    {
1991    case TOK_EOF:
1992        *status = U_INVALID_FORMAT_ERROR;
1993        error(startline, "Unexpected EOF encountered");
1994        return NULL;
1995
1996    case TOK_ERROR:
1997        *status = U_INVALID_FORMAT_ERROR;
1998        return NULL;
1999
2000    case TOK_COLON:
2001        resType = parseResourceType(state, status);
2002        expect(state, TOK_OPEN_BRACE, &tokenValue, NULL, &startline, status);
2003
2004        if (U_FAILURE(*status))
2005        {
2006            return NULL;
2007        }
2008
2009        break;
2010
2011    case TOK_OPEN_BRACE:
2012        break;
2013
2014    default:
2015        *status = U_INVALID_FORMAT_ERROR;
2016        error(startline, "syntax error while reading a resource, expected '{' or ':'");
2017        return NULL;
2018    }
2019
2020
2021    if (resType == RESTYPE_UNKNOWN)
2022    {
2023        /* No explicit type, so try to work it out.  At this point, we've read the first '{'.
2024        We could have any of the following:
2025        { {         => array (nested)
2026        { :/}       => array
2027        { string ,  => string array
2028
2029        { string {  => table
2030
2031        { string :/{    => table
2032        { string }      => string
2033        */
2034
2035        token = peekToken(state, 0, NULL, &line, NULL,status);
2036
2037        if (U_FAILURE(*status))
2038        {
2039            return NULL;
2040        }
2041
2042        if (token == TOK_OPEN_BRACE || token == TOK_COLON ||token ==TOK_CLOSE_BRACE )
2043        {
2044            resType = RESTYPE_ARRAY;
2045        }
2046        else if (token == TOK_STRING)
2047        {
2048            token = peekToken(state, 1, NULL, &line, NULL, status);
2049
2050            if (U_FAILURE(*status))
2051            {
2052                return NULL;
2053            }
2054
2055            switch (token)
2056            {
2057            case TOK_COMMA:         resType = RESTYPE_ARRAY;  break;
2058            case TOK_OPEN_BRACE:    resType = RESTYPE_TABLE;  break;
2059            case TOK_CLOSE_BRACE:   resType = RESTYPE_STRING; break;
2060            case TOK_COLON:         resType = RESTYPE_TABLE;  break;
2061            default:
2062                *status = U_INVALID_FORMAT_ERROR;
2063                error(line, "Unexpected token after string, expected ',', '{' or '}'");
2064                return NULL;
2065            }
2066        }
2067        else
2068        {
2069            *status = U_INVALID_FORMAT_ERROR;
2070            error(line, "Unexpected token after '{'");
2071            return NULL;
2072        }
2073
2074        /* printf("Type guessed as %s\n", resourceNames[resType]); */
2075    } else if(resType == RESTYPE_TABLE_NO_FALLBACK) {
2076        *status = U_INVALID_FORMAT_ERROR;
2077        error(startline, "error: %s resource type not valid except on top bundle level", gResourceTypes[resType].nameChars);
2078        return NULL;
2079    }
2080
2081
2082    /* We should now know what we need to parse next, so call the appropriate parser
2083    function and return. */
2084    parseFunction = gResourceTypes[resType].parseFunction;
2085    if (parseFunction != NULL) {
2086        return parseFunction(state, tag, startline, comment, status);
2087    }
2088    else {
2089        *status = U_INTERNAL_PROGRAM_ERROR;
2090        error(startline, "internal error: %s resource type found and not handled", gResourceTypes[resType].nameChars);
2091    }
2092
2093    return NULL;
2094}
2095
2096/* parse the top-level resource */
2097struct SRBRoot *
2098parse(UCHARBUF *buf, const char *inputDir, const char *outputDir, const char *filename,
2099      UBool makeBinaryCollation, UBool omitCollationRules, UErrorCode *status)
2100{
2101    struct UString    *tokenValue;
2102    struct UString    comment;
2103    uint32_t           line;
2104    enum EResourceType bundleType;
2105    enum ETokenType    token;
2106    ParseState state;
2107    uint32_t i;
2108
2109
2110    for (i = 0; i < MAX_LOOKAHEAD + 1; i++)
2111    {
2112        ustr_init(&state.lookahead[i].value);
2113        ustr_init(&state.lookahead[i].comment);
2114    }
2115
2116    initLookahead(&state, buf, status);
2117
2118    state.inputdir       = inputDir;
2119    state.inputdirLength = (state.inputdir != NULL) ? (uint32_t)uprv_strlen(state.inputdir) : 0;
2120    state.outputdir       = outputDir;
2121    state.outputdirLength = (state.outputdir != NULL) ? (uint32_t)uprv_strlen(state.outputdir) : 0;
2122    state.filename = filename;
2123    state.makeBinaryCollation = makeBinaryCollation;
2124    state.omitCollationRules = omitCollationRules;
2125
2126    ustr_init(&comment);
2127    expect(&state, TOK_STRING, &tokenValue, &comment, NULL, status);
2128
2129    state.bundle = bundle_open(&comment, FALSE, status);
2130
2131    if (state.bundle == NULL || U_FAILURE(*status))
2132    {
2133        return NULL;
2134    }
2135
2136
2137    bundle_setlocale(state.bundle, tokenValue->fChars, status);
2138
2139    /* The following code is to make Empty bundle work no matter with :table specifer or not */
2140    token = getToken(&state, NULL, NULL, &line, status);
2141    if(token==TOK_COLON) {
2142        *status=U_ZERO_ERROR;
2143        bundleType=parseResourceType(&state, status);
2144
2145        if(isTable(bundleType))
2146        {
2147            expect(&state, TOK_OPEN_BRACE, NULL, NULL, &line, status);
2148        }
2149        else
2150        {
2151            *status=U_PARSE_ERROR;
2152             error(line, "parse error. Stopped parsing with %s", u_errorName(*status));
2153        }
2154    }
2155    else
2156    {
2157        /* not a colon */
2158        if(token==TOK_OPEN_BRACE)
2159        {
2160            *status=U_ZERO_ERROR;
2161            bundleType=RESTYPE_TABLE;
2162        }
2163        else
2164        {
2165            /* neither colon nor open brace */
2166            *status=U_PARSE_ERROR;
2167            bundleType=RESTYPE_UNKNOWN;
2168            error(line, "parse error, did not find open-brace '{' or colon ':', stopped with %s", u_errorName(*status));
2169        }
2170    }
2171
2172    if (U_FAILURE(*status))
2173    {
2174        bundle_close(state.bundle, status);
2175        return NULL;
2176    }
2177
2178    if(bundleType==RESTYPE_TABLE_NO_FALLBACK) {
2179        /*
2180         * Parse a top-level table with the table(nofallback) declaration.
2181         * This is the same as a regular table, but also sets the
2182         * URES_ATT_NO_FALLBACK flag in indexes[URES_INDEX_ATTRIBUTES] .
2183         */
2184        state.bundle->noFallback=TRUE;
2185    }
2186    /* top-level tables need not handle special table names like "collations" */
2187    realParseTable(&state, state.bundle->fRoot, NULL, line, status);
2188    if(dependencyArray!=NULL){
2189        table_add(state.bundle->fRoot, dependencyArray, 0, status);
2190        dependencyArray = NULL;
2191    }
2192   if (U_FAILURE(*status))
2193    {
2194        bundle_close(state.bundle, status);
2195        res_close(dependencyArray);
2196        return NULL;
2197    }
2198
2199    if (getToken(&state, NULL, NULL, &line, status) != TOK_EOF)
2200    {
2201        warning(line, "extraneous text after resource bundle (perhaps unmatched braces)");
2202        if(isStrict()){
2203            *status = U_INVALID_FORMAT_ERROR;
2204            return NULL;
2205        }
2206    }
2207
2208    cleanupLookahead(&state);
2209    ustr_deinit(&comment);
2210    return state.bundle;
2211}
2212