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