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 "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 void getRules( 677 const char *localeID, const char *collationType, 678 UnicodeString &rules, 679 const char *&errorReason, UErrorCode &errorCode); 680 681private: 682 const char *inputDir; 683 const char *outputDir; 684}; 685 686GenrbImporter::~GenrbImporter() {} 687 688void 689GenrbImporter::getRules( 690 const char *localeID, const char *collationType, 691 UnicodeString &rules, 692 const char *& /*errorReason*/, UErrorCode &errorCode) { 693 struct SRBRoot *data = NULL; 694 UCHARBUF *ucbuf = NULL; 695 int localeLength = strlen(localeID); 696 char* filename = (char*)uprv_malloc(localeLength+5); 697 char *inputDirBuf = NULL; 698 char *openFileName = NULL; 699 const char* cp = ""; 700 int32_t i = 0; 701 int32_t dirlen = 0; 702 int32_t filelen = 0; 703 struct SResource* root; 704 struct SResource* collations; 705 struct SResource* collation; 706 struct SResource* sequence; 707 708 memcpy(filename, localeID, localeLength); 709 for(i = 0; i < localeLength; i++){ 710 if(filename[i] == '-'){ 711 filename[i] = '_'; 712 } 713 } 714 filename[localeLength] = '.'; 715 filename[localeLength+1] = 't'; 716 filename[localeLength+2] = 'x'; 717 filename[localeLength+3] = 't'; 718 filename[localeLength+4] = 0; 719 720 721 if (U_FAILURE(errorCode)) { 722 return; 723 } 724 if(filename==NULL){ 725 errorCode=U_ILLEGAL_ARGUMENT_ERROR; 726 return; 727 }else{ 728 filelen = (int32_t)uprv_strlen(filename); 729 } 730 if(inputDir == NULL) { 731 const char *filenameBegin = uprv_strrchr(filename, U_FILE_SEP_CHAR); 732 openFileName = (char *) uprv_malloc(dirlen + filelen + 2); 733 openFileName[0] = '\0'; 734 if (filenameBegin != NULL) { 735 /* 736 * When a filename ../../../data/root.txt is specified, 737 * we presume that the input directory is ../../../data 738 * This is very important when the resource file includes 739 * another file, like UCARules.txt or thaidict.brk. 740 */ 741 int32_t filenameSize = (int32_t)(filenameBegin - filename + 1); 742 inputDirBuf = (char *)uprv_malloc(filenameSize); 743 744 /* test for NULL */ 745 if(inputDirBuf == NULL) { 746 errorCode = U_MEMORY_ALLOCATION_ERROR; 747 goto finish; 748 } 749 750 uprv_strncpy(inputDirBuf, filename, filenameSize); 751 inputDirBuf[filenameSize - 1] = 0; 752 inputDir = inputDirBuf; 753 dirlen = (int32_t)uprv_strlen(inputDir); 754 } 755 }else{ 756 dirlen = (int32_t)uprv_strlen(inputDir); 757 758 if(inputDir[dirlen-1] != U_FILE_SEP_CHAR) { 759 openFileName = (char *) uprv_malloc(dirlen + filelen + 2); 760 761 /* test for NULL */ 762 if(openFileName == NULL) { 763 errorCode = U_MEMORY_ALLOCATION_ERROR; 764 goto finish; 765 } 766 767 openFileName[0] = '\0'; 768 /* 769 * append the input dir to openFileName if the first char in 770 * filename is not file seperation char and the last char input directory is not '.'. 771 * This is to support : 772 * genrb -s. /home/icu/data 773 * genrb -s. icu/data 774 * The user cannot mix notations like 775 * genrb -s. /icu/data --- the absolute path specified. -s redundant 776 * user should use 777 * genrb -s. icu/data --- start from CWD and look in icu/data dir 778 */ 779 if( (filename[0] != U_FILE_SEP_CHAR) && (inputDir[dirlen-1] !='.')){ 780 uprv_strcpy(openFileName, inputDir); 781 openFileName[dirlen] = U_FILE_SEP_CHAR; 782 } 783 openFileName[dirlen + 1] = '\0'; 784 } else { 785 openFileName = (char *) uprv_malloc(dirlen + filelen + 1); 786 787 /* test for NULL */ 788 if(openFileName == NULL) { 789 errorCode = U_MEMORY_ALLOCATION_ERROR; 790 goto finish; 791 } 792 793 uprv_strcpy(openFileName, inputDir); 794 795 } 796 } 797 uprv_strcat(openFileName, filename); 798 /* printf("%s\n", openFileName); */ 799 errorCode = U_ZERO_ERROR; 800 ucbuf = ucbuf_open(openFileName, &cp,getShowWarning(),TRUE, &errorCode); 801 802 if(errorCode == U_FILE_ACCESS_ERROR) { 803 804 fprintf(stderr, "couldn't open file %s\n", openFileName == NULL ? filename : openFileName); 805 goto finish; 806 } 807 if (ucbuf == NULL || U_FAILURE(errorCode)) { 808 fprintf(stderr, "An error occured processing file %s. Error: %s\n", openFileName == NULL ? filename : openFileName,u_errorName(errorCode)); 809 goto finish; 810 } 811 812 /* Parse the data into an SRBRoot */ 813 data = parse(ucbuf, inputDir, outputDir, filename, FALSE, FALSE, &errorCode); 814 if (U_FAILURE(errorCode)) { 815 goto finish; 816 } 817 818 root = data->fRoot; 819 collations = resLookup(root, "collations"); 820 if (collations != NULL) { 821 collation = resLookup(collations, collationType); 822 if (collation != NULL) { 823 sequence = resLookup(collation, "Sequence"); 824 if (sequence != NULL) { 825 // No string pointer aliasing so that we need not hold onto the resource bundle. 826 rules.setTo(sequence->u.fString.fChars, sequence->u.fString.fLength); 827 } 828 } 829 } 830 831finish: 832 if (inputDirBuf != NULL) { 833 uprv_free(inputDirBuf); 834 } 835 836 if (openFileName != NULL) { 837 uprv_free(openFileName); 838 } 839 840 if(ucbuf) { 841 ucbuf_close(ucbuf); 842 } 843} 844 845// Quick-and-dirty escaping function. 846// Assumes that we are on an ASCII-based platform. 847static void 848escape(const UChar *s, char *buffer) { 849 int32_t length = u_strlen(s); 850 int32_t i = 0; 851 for (;;) { 852 UChar32 c; 853 U16_NEXT(s, i, length, c); 854 if (c == 0) { 855 *buffer = 0; 856 return; 857 } else if (0x20 <= c && c <= 0x7e) { 858 // printable ASCII 859 *buffer++ = (char)c; // assumes ASCII-based platform 860 } else { 861 buffer += sprintf(buffer, "\\u%04X", (int)c); 862 } 863 } 864} 865 866} // namespace 867 868#endif // !UCONFIG_NO_COLLATION 869 870static struct SResource * 871addCollation(ParseState* state, struct SResource *result, const char *collationType, 872 uint32_t startline, UErrorCode *status) 873{ 874 // TODO: Use LocalPointer for result, or make caller close it when there is a failure. 875 struct SResource *member = NULL; 876 struct UString *tokenValue; 877 struct UString comment; 878 enum ETokenType token; 879 char subtag[1024]; 880 UnicodeString rules; 881 UBool haveRules = FALSE; 882 UVersionInfo version; 883 uint32_t line; 884 885 /* '{' . (name resource)* '}' */ 886 version[0]=0; version[1]=0; version[2]=0; version[3]=0; 887 888 for (;;) 889 { 890 ustr_init(&comment); 891 token = getToken(state, &tokenValue, &comment, &line, status); 892 893 if (token == TOK_CLOSE_BRACE) 894 { 895 break; 896 } 897 898 if (token != TOK_STRING) 899 { 900 res_close(result); 901 *status = U_INVALID_FORMAT_ERROR; 902 903 if (token == TOK_EOF) 904 { 905 error(startline, "unterminated table"); 906 } 907 else 908 { 909 error(line, "Unexpected token %s", tokenNames[token]); 910 } 911 912 return NULL; 913 } 914 915 u_UCharsToChars(tokenValue->fChars, subtag, u_strlen(tokenValue->fChars) + 1); 916 917 if (U_FAILURE(*status)) 918 { 919 res_close(result); 920 return NULL; 921 } 922 923 member = parseResource(state, subtag, NULL, status); 924 925 if (U_FAILURE(*status)) 926 { 927 res_close(result); 928 return NULL; 929 } 930 if (result == NULL) 931 { 932 // Ignore the parsed resources, continue parsing. 933 } 934 else if (uprv_strcmp(subtag, "Version") == 0) 935 { 936 char ver[40]; 937 int32_t length = member->u.fString.fLength; 938 939 if (length >= (int32_t) sizeof(ver)) 940 { 941 length = (int32_t) sizeof(ver) - 1; 942 } 943 944 u_UCharsToChars(member->u.fString.fChars, ver, length + 1); /* +1 for copying NULL */ 945 u_versionFromString(version, ver); 946 947 table_add(result, member, line, status); 948 member = NULL; 949 } 950 else if(uprv_strcmp(subtag, "%%CollationBin")==0) 951 { 952 /* discard duplicate %%CollationBin if any*/ 953 } 954 else if (uprv_strcmp(subtag, "Sequence") == 0) 955 { 956 rules.setTo(member->u.fString.fChars, member->u.fString.fLength); 957 haveRules = TRUE; 958 // Defer building the collator until we have seen 959 // all sub-elements of the collation table, including the Version. 960 /* in order to achieve smaller data files, we can direct genrb */ 961 /* to omit collation rules */ 962 if(!state->omitCollationRules) { 963 table_add(result, member, line, status); 964 member = NULL; 965 } 966 } 967 else // Just copy non-special items. 968 { 969 table_add(result, member, line, status); 970 member = NULL; 971 } 972 res_close(member); // TODO: use LocalPointer 973 if (U_FAILURE(*status)) 974 { 975 res_close(result); 976 return NULL; 977 } 978 } 979 980 if (!haveRules) { return result; } 981 982#if UCONFIG_NO_COLLATION || UCONFIG_NO_FILE_IO 983 warning(line, "Not building collation elements because of UCONFIG_NO_COLLATION and/or UCONFIG_NO_FILE_IO, see uconfig.h"); 984 (void)collationType; 985#else 986 // CLDR ticket #3949, ICU ticket #8082: 987 // Do not build collation binary data for for-import-only "private" collation rule strings. 988 if (uprv_strncmp(collationType, "private-", 8) == 0) { 989 if(isVerbose()) { 990 printf("Not building %s~%s collation binary\n", state->filename, collationType); 991 } 992 return result; 993 } 994 995 if(!state->makeBinaryCollation) { 996 if(isVerbose()) { 997 printf("Not building %s~%s collation binary\n", state->filename, collationType); 998 } 999 return result; 1000 } 1001 UErrorCode intStatus = U_ZERO_ERROR; 1002 UParseError parseError; 1003 uprv_memset(&parseError, 0, sizeof(parseError)); 1004 GenrbImporter importer(state->inputdir, state->outputdir); 1005 const icu::CollationTailoring *base = icu::CollationRoot::getRoot(intStatus); 1006 if(U_FAILURE(intStatus)) { 1007 error(line, "failed to load root collator (ucadata.icu) - %s", u_errorName(intStatus)); 1008 res_close(result); 1009 return NULL; // TODO: use LocalUResourceBundlePointer for result 1010 } 1011 icu::CollationBuilder builder(base, intStatus); 1012 if(uprv_strncmp(collationType, "search", 6) == 0) { 1013 builder.disableFastLatin(); // build fast-Latin table unless search collator 1014 } 1015 LocalPointer<icu::CollationTailoring> t( 1016 builder.parseAndBuild(rules, version, &importer, &parseError, intStatus)); 1017 if(U_FAILURE(intStatus)) { 1018 const char *reason = builder.getErrorReason(); 1019 if(reason == NULL) { reason = ""; } 1020 error(line, "CollationBuilder failed at %s~%s/Sequence rule offset %ld: %s %s", 1021 state->filename, collationType, 1022 (long)parseError.offset, u_errorName(intStatus), reason); 1023 if(parseError.preContext[0] != 0 || parseError.postContext[0] != 0) { 1024 // Print pre- and post-context. 1025 char preBuffer[100], postBuffer[100]; 1026 escape(parseError.preContext, preBuffer); 1027 escape(parseError.postContext, postBuffer); 1028 error(line, " error context: \"...%s\" ! \"%s...\"", preBuffer, postBuffer); 1029 } 1030 if(isStrict()) { 1031 *status = intStatus; 1032 res_close(result); 1033 return NULL; 1034 } 1035 } 1036 icu::LocalMemory<uint8_t> buffer; 1037 int32_t capacity = 100000; 1038 uint8_t *dest = buffer.allocateInsteadAndCopy(capacity); 1039 if(dest == NULL) { 1040 fprintf(stderr, "memory allocation (%ld bytes) for file contents failed\n", 1041 (long)capacity); 1042 *status = U_MEMORY_ALLOCATION_ERROR; 1043 res_close(result); 1044 return NULL; 1045 } 1046 int32_t indexes[icu::CollationDataReader::IX_TOTAL_SIZE + 1]; 1047 int32_t totalSize = icu::CollationDataWriter::writeTailoring( 1048 *t, *t->settings, indexes, dest, capacity, intStatus); 1049 if(intStatus == U_BUFFER_OVERFLOW_ERROR) { 1050 intStatus = U_ZERO_ERROR; 1051 capacity = totalSize; 1052 dest = buffer.allocateInsteadAndCopy(capacity); 1053 if(dest == NULL) { 1054 fprintf(stderr, "memory allocation (%ld bytes) for file contents failed\n", 1055 (long)capacity); 1056 *status = U_MEMORY_ALLOCATION_ERROR; 1057 res_close(result); 1058 return NULL; 1059 } 1060 totalSize = icu::CollationDataWriter::writeTailoring( 1061 *t, *t->settings, indexes, dest, capacity, intStatus); 1062 } 1063 if(U_FAILURE(intStatus)) { 1064 fprintf(stderr, "CollationDataWriter::writeTailoring() failed: %s\n", 1065 u_errorName(intStatus)); 1066 res_close(result); 1067 return NULL; 1068 } 1069 if(isVerbose()) { 1070 printf("%s~%s collation tailoring part sizes:\n", state->filename, collationType); 1071 icu::CollationInfo::printSizes(totalSize, indexes); 1072 if(t->settings->hasReordering()) { 1073 printf("%s~%s collation reordering ranges:\n", state->filename, collationType); 1074 icu::CollationInfo::printReorderRanges( 1075 *t->data, t->settings->reorderCodes, t->settings->reorderCodesLength); 1076 } 1077 } 1078 struct SResource *collationBin = bin_open(state->bundle, "%%CollationBin", totalSize, dest, NULL, NULL, status); 1079 table_add(result, collationBin, line, status); 1080 if (U_FAILURE(*status)) { 1081 res_close(result); 1082 return NULL; 1083 } 1084#endif 1085 return result; 1086} 1087 1088static UBool 1089keepCollationType(const char *type) { // android-changed 1090 // BEGIN android-added 1091 if (uprv_strcmp(type, "big5han") == 0) { return FALSE; } 1092 if (uprv_strcmp(type, "gb2312han") == 0) { return FALSE; } 1093 // END android-added 1094 return TRUE; 1095} 1096 1097static struct SResource * 1098parseCollationElements(ParseState* state, char *tag, uint32_t startline, UBool newCollation, UErrorCode *status) 1099{ 1100 struct SResource *result = NULL; 1101 struct SResource *member = NULL; 1102 struct SResource *collationRes = NULL; 1103 struct UString *tokenValue; 1104 struct UString comment; 1105 enum ETokenType token; 1106 char subtag[1024], typeKeyword[1024]; 1107 uint32_t line; 1108 1109 result = table_open(state->bundle, tag, NULL, status); 1110 1111 if (result == NULL || U_FAILURE(*status)) 1112 { 1113 return NULL; 1114 } 1115 if(isVerbose()){ 1116 printf(" collation elements %s at line %i \n", (tag == NULL) ? "(null)" : tag, (int)startline); 1117 } 1118 if(!newCollation) { 1119 return addCollation(state, result, "(no type)", startline, status); 1120 } 1121 else { 1122 for(;;) { 1123 ustr_init(&comment); 1124 token = getToken(state, &tokenValue, &comment, &line, status); 1125 1126 if (token == TOK_CLOSE_BRACE) 1127 { 1128 return result; 1129 } 1130 1131 if (token != TOK_STRING) 1132 { 1133 res_close(result); 1134 *status = U_INVALID_FORMAT_ERROR; 1135 1136 if (token == TOK_EOF) 1137 { 1138 error(startline, "unterminated table"); 1139 } 1140 else 1141 { 1142 error(line, "Unexpected token %s", tokenNames[token]); 1143 } 1144 1145 return NULL; 1146 } 1147 1148 u_UCharsToChars(tokenValue->fChars, subtag, u_strlen(tokenValue->fChars) + 1); 1149 1150 if (U_FAILURE(*status)) 1151 { 1152 res_close(result); 1153 return NULL; 1154 } 1155 1156 if (uprv_strcmp(subtag, "default") == 0) 1157 { 1158 member = parseResource(state, subtag, NULL, status); 1159 1160 if (U_FAILURE(*status)) 1161 { 1162 res_close(result); 1163 return NULL; 1164 } 1165 1166 table_add(result, member, line, status); 1167 } 1168 else 1169 { 1170 token = peekToken(state, 0, &tokenValue, &line, &comment, status); 1171 /* this probably needs to be refactored or recursively use the parser */ 1172 /* first we assume that our collation table won't have the explicit type */ 1173 /* then, we cannot handle aliases */ 1174 if(token == TOK_OPEN_BRACE) { 1175 token = getToken(state, &tokenValue, &comment, &line, status); 1176 if (keepCollationType(subtag)) { 1177 collationRes = table_open(state->bundle, subtag, NULL, status); 1178 } else { 1179 collationRes = NULL; 1180 } 1181 // need to parse the collation data regardless 1182 collationRes = addCollation(state, collationRes, subtag, startline, status); 1183 if (collationRes != NULL) { 1184 table_add(result, collationRes, startline, status); 1185 } 1186 } else if(token == TOK_COLON) { /* right now, we'll just try to see if we have aliases */ 1187 /* we could have a table too */ 1188 token = peekToken(state, 1, &tokenValue, &line, &comment, status); 1189 u_UCharsToChars(tokenValue->fChars, typeKeyword, u_strlen(tokenValue->fChars) + 1); 1190 if(uprv_strcmp(typeKeyword, "alias") == 0) { 1191 member = parseResource(state, subtag, NULL, status); 1192 if (U_FAILURE(*status)) 1193 { 1194 res_close(result); 1195 return NULL; 1196 } 1197 1198 table_add(result, member, line, status); 1199 } else { 1200 res_close(result); 1201 *status = U_INVALID_FORMAT_ERROR; 1202 return NULL; 1203 } 1204 } else { 1205 res_close(result); 1206 *status = U_INVALID_FORMAT_ERROR; 1207 return NULL; 1208 } 1209 } 1210 1211 /*member = string_open(bundle, subtag, tokenValue->fChars, tokenValue->fLength, status);*/ 1212 1213 /*expect(TOK_CLOSE_BRACE, NULL, NULL, status);*/ 1214 1215 if (U_FAILURE(*status)) 1216 { 1217 res_close(result); 1218 return NULL; 1219 } 1220 } 1221 } 1222} 1223 1224/* Necessary, because CollationElements requires the bundle->fRoot member to be present which, 1225 if this weren't special-cased, wouldn't be set until the entire file had been processed. */ 1226static struct SResource * 1227realParseTable(ParseState* state, struct SResource *table, char *tag, uint32_t startline, UErrorCode *status) 1228{ 1229 struct SResource *member = NULL; 1230 struct UString *tokenValue=NULL; 1231 struct UString comment; 1232 enum ETokenType token; 1233 char subtag[1024]; 1234 uint32_t line; 1235 UBool readToken = FALSE; 1236 1237 /* '{' . (name resource)* '}' */ 1238 1239 if(isVerbose()){ 1240 printf(" parsing table %s at line %i \n", (tag == NULL) ? "(null)" : tag, (int)startline); 1241 } 1242 for (;;) 1243 { 1244 ustr_init(&comment); 1245 token = getToken(state, &tokenValue, &comment, &line, status); 1246 1247 if (token == TOK_CLOSE_BRACE) 1248 { 1249 if (!readToken) { 1250 warning(startline, "Encountered empty table"); 1251 } 1252 return table; 1253 } 1254 1255 if (token != TOK_STRING) 1256 { 1257 *status = U_INVALID_FORMAT_ERROR; 1258 1259 if (token == TOK_EOF) 1260 { 1261 error(startline, "unterminated table"); 1262 } 1263 else 1264 { 1265 error(line, "unexpected token %s", tokenNames[token]); 1266 } 1267 1268 return NULL; 1269 } 1270 1271 if(uprv_isInvariantUString(tokenValue->fChars, -1)) { 1272 u_UCharsToChars(tokenValue->fChars, subtag, u_strlen(tokenValue->fChars) + 1); 1273 } else { 1274 *status = U_INVALID_FORMAT_ERROR; 1275 error(line, "invariant characters required for table keys"); 1276 return NULL; 1277 } 1278 1279 if (U_FAILURE(*status)) 1280 { 1281 error(line, "parse error. Stopped parsing tokens with %s", u_errorName(*status)); 1282 return NULL; 1283 } 1284 1285 member = parseResource(state, subtag, &comment, status); 1286 1287 if (member == NULL || U_FAILURE(*status)) 1288 { 1289 error(line, "parse error. Stopped parsing resource with %s", u_errorName(*status)); 1290 return NULL; 1291 } 1292 1293 table_add(table, member, line, status); 1294 1295 if (U_FAILURE(*status)) 1296 { 1297 error(line, "parse error. Stopped parsing table with %s", u_errorName(*status)); 1298 return NULL; 1299 } 1300 readToken = TRUE; 1301 ustr_deinit(&comment); 1302 } 1303 1304 /* not reached */ 1305 /* A compiler warning will appear if all paths don't contain a return statement. */ 1306/* *status = U_INTERNAL_PROGRAM_ERROR; 1307 return NULL;*/ 1308} 1309 1310static struct SResource * 1311parseTable(ParseState* state, char *tag, uint32_t startline, const struct UString *comment, UErrorCode *status) 1312{ 1313 struct SResource *result; 1314 1315 if (tag != NULL && uprv_strcmp(tag, "CollationElements") == 0) 1316 { 1317 return parseCollationElements(state, tag, startline, FALSE, status); 1318 } 1319 if (tag != NULL && uprv_strcmp(tag, "collations") == 0) 1320 { 1321 return parseCollationElements(state, tag, startline, TRUE, status); 1322 } 1323 if(isVerbose()){ 1324 printf(" table %s at line %i \n", (tag == NULL) ? "(null)" : tag, (int)startline); 1325 } 1326 1327 result = table_open(state->bundle, tag, comment, status); 1328 1329 if (result == NULL || U_FAILURE(*status)) 1330 { 1331 return NULL; 1332 } 1333 return realParseTable(state, result, tag, startline, status); 1334} 1335 1336static struct SResource * 1337parseArray(ParseState* state, char *tag, uint32_t startline, const struct UString *comment, UErrorCode *status) 1338{ 1339 struct SResource *result = NULL; 1340 struct SResource *member = NULL; 1341 struct UString *tokenValue; 1342 struct UString memberComments; 1343 enum ETokenType token; 1344 UBool readToken = FALSE; 1345 1346 result = array_open(state->bundle, tag, comment, status); 1347 1348 if (result == NULL || U_FAILURE(*status)) 1349 { 1350 return NULL; 1351 } 1352 if(isVerbose()){ 1353 printf(" array %s at line %i \n", (tag == NULL) ? "(null)" : tag, (int)startline); 1354 } 1355 1356 ustr_init(&memberComments); 1357 1358 /* '{' . resource [','] '}' */ 1359 for (;;) 1360 { 1361 /* reset length */ 1362 ustr_setlen(&memberComments, 0, status); 1363 1364 /* check for end of array, but don't consume next token unless it really is the end */ 1365 token = peekToken(state, 0, &tokenValue, NULL, &memberComments, status); 1366 1367 1368 if (token == TOK_CLOSE_BRACE) 1369 { 1370 getToken(state, NULL, NULL, NULL, status); 1371 if (!readToken) { 1372 warning(startline, "Encountered empty array"); 1373 } 1374 break; 1375 } 1376 1377 if (token == TOK_EOF) 1378 { 1379 res_close(result); 1380 *status = U_INVALID_FORMAT_ERROR; 1381 error(startline, "unterminated array"); 1382 return NULL; 1383 } 1384 1385 /* string arrays are a special case */ 1386 if (token == TOK_STRING) 1387 { 1388 getToken(state, &tokenValue, &memberComments, NULL, status); 1389 member = string_open(state->bundle, NULL, tokenValue->fChars, tokenValue->fLength, &memberComments, status); 1390 } 1391 else 1392 { 1393 member = parseResource(state, NULL, &memberComments, status); 1394 } 1395 1396 if (member == NULL || U_FAILURE(*status)) 1397 { 1398 res_close(result); 1399 return NULL; 1400 } 1401 1402 array_add(result, member, status); 1403 1404 if (U_FAILURE(*status)) 1405 { 1406 res_close(result); 1407 return NULL; 1408 } 1409 1410 /* eat optional comma if present */ 1411 token = peekToken(state, 0, NULL, NULL, NULL, status); 1412 1413 if (token == TOK_COMMA) 1414 { 1415 getToken(state, NULL, NULL, NULL, status); 1416 } 1417 1418 if (U_FAILURE(*status)) 1419 { 1420 res_close(result); 1421 return NULL; 1422 } 1423 readToken = TRUE; 1424 } 1425 1426 ustr_deinit(&memberComments); 1427 return result; 1428} 1429 1430static struct SResource * 1431parseIntVector(ParseState* state, char *tag, uint32_t startline, const struct UString *comment, UErrorCode *status) 1432{ 1433 struct SResource *result = NULL; 1434 enum ETokenType token; 1435 char *string; 1436 int32_t value; 1437 UBool readToken = FALSE; 1438 char *stopstring; 1439 uint32_t len; 1440 struct UString memberComments; 1441 1442 result = intvector_open(state->bundle, tag, comment, status); 1443 1444 if (result == NULL || U_FAILURE(*status)) 1445 { 1446 return NULL; 1447 } 1448 1449 if(isVerbose()){ 1450 printf(" vector %s at line %i \n", (tag == NULL) ? "(null)" : tag, (int)startline); 1451 } 1452 ustr_init(&memberComments); 1453 /* '{' . string [','] '}' */ 1454 for (;;) 1455 { 1456 ustr_setlen(&memberComments, 0, status); 1457 1458 /* check for end of array, but don't consume next token unless it really is the end */ 1459 token = peekToken(state, 0, NULL, NULL,&memberComments, status); 1460 1461 if (token == TOK_CLOSE_BRACE) 1462 { 1463 /* it's the end, consume the close brace */ 1464 getToken(state, NULL, NULL, NULL, status); 1465 if (!readToken) { 1466 warning(startline, "Encountered empty int vector"); 1467 } 1468 ustr_deinit(&memberComments); 1469 return result; 1470 } 1471 1472 string = getInvariantString(state, NULL, NULL, status); 1473 1474 if (U_FAILURE(*status)) 1475 { 1476 res_close(result); 1477 return NULL; 1478 } 1479 1480 /* For handling illegal char in the Intvector */ 1481 value = uprv_strtoul(string, &stopstring, 0);/* make intvector support decimal,hexdigit,octal digit ranging from -2^31-2^32-1*/ 1482 len=(uint32_t)(stopstring-string); 1483 1484 if(len==uprv_strlen(string)) 1485 { 1486 intvector_add(result, value, status); 1487 uprv_free(string); 1488 token = peekToken(state, 0, NULL, NULL, NULL, status); 1489 } 1490 else 1491 { 1492 uprv_free(string); 1493 *status=U_INVALID_CHAR_FOUND; 1494 } 1495 1496 if (U_FAILURE(*status)) 1497 { 1498 res_close(result); 1499 return NULL; 1500 } 1501 1502 /* the comma is optional (even though it is required to prevent the reader from concatenating 1503 consecutive entries) so that a missing comma on the last entry isn't an error */ 1504 if (token == TOK_COMMA) 1505 { 1506 getToken(state, NULL, NULL, NULL, status); 1507 } 1508 readToken = TRUE; 1509 } 1510 1511 /* not reached */ 1512 /* A compiler warning will appear if all paths don't contain a return statement. */ 1513/* intvector_close(result, status); 1514 *status = U_INTERNAL_PROGRAM_ERROR; 1515 return NULL;*/ 1516} 1517 1518static struct SResource * 1519parseBinary(ParseState* state, char *tag, uint32_t startline, const struct UString *comment, UErrorCode *status) 1520{ 1521 struct SResource *result = NULL; 1522 uint8_t *value; 1523 char *string; 1524 char toConv[3] = {'\0', '\0', '\0'}; 1525 uint32_t count; 1526 uint32_t i; 1527 uint32_t line; 1528 char *stopstring; 1529 uint32_t len; 1530 1531 string = getInvariantString(state, &line, NULL, status); 1532 1533 if (string == NULL || U_FAILURE(*status)) 1534 { 1535 return NULL; 1536 } 1537 1538 expect(state, TOK_CLOSE_BRACE, NULL, NULL, NULL, status); 1539 1540 if (U_FAILURE(*status)) 1541 { 1542 uprv_free(string); 1543 return NULL; 1544 } 1545 1546 if(isVerbose()){ 1547 printf(" binary %s at line %i \n", (tag == NULL) ? "(null)" : tag, (int)startline); 1548 } 1549 1550 count = (uint32_t)uprv_strlen(string); 1551 if (count > 0){ 1552 if((count % 2)==0){ 1553 value = static_cast<uint8_t *>(uprv_malloc(sizeof(uint8_t) * count)); 1554 1555 if (value == NULL) 1556 { 1557 uprv_free(string); 1558 *status = U_MEMORY_ALLOCATION_ERROR; 1559 return NULL; 1560 } 1561 1562 for (i = 0; i < count; i += 2) 1563 { 1564 toConv[0] = string[i]; 1565 toConv[1] = string[i + 1]; 1566 1567 value[i >> 1] = (uint8_t) uprv_strtoul(toConv, &stopstring, 16); 1568 len=(uint32_t)(stopstring-toConv); 1569 1570 if(len!=uprv_strlen(toConv)) 1571 { 1572 uprv_free(string); 1573 *status=U_INVALID_CHAR_FOUND; 1574 return NULL; 1575 } 1576 } 1577 1578 result = bin_open(state->bundle, tag, (i >> 1), value,NULL, comment, status); 1579 1580 uprv_free(value); 1581 } 1582 else 1583 { 1584 *status = U_INVALID_CHAR_FOUND; 1585 uprv_free(string); 1586 error(line, "Encountered invalid binary string"); 1587 return NULL; 1588 } 1589 } 1590 else 1591 { 1592 result = bin_open(state->bundle, tag, 0, NULL, "",comment,status); 1593 warning(startline, "Encountered empty binary tag"); 1594 } 1595 uprv_free(string); 1596 1597 return result; 1598} 1599 1600static struct SResource * 1601parseInteger(ParseState* state, char *tag, uint32_t startline, const struct UString *comment, UErrorCode *status) 1602{ 1603 struct SResource *result = NULL; 1604 int32_t value; 1605 char *string; 1606 char *stopstring; 1607 uint32_t len; 1608 1609 string = getInvariantString(state, NULL, NULL, status); 1610 1611 if (string == NULL || U_FAILURE(*status)) 1612 { 1613 return NULL; 1614 } 1615 1616 expect(state, TOK_CLOSE_BRACE, NULL, NULL, NULL, status); 1617 1618 if (U_FAILURE(*status)) 1619 { 1620 uprv_free(string); 1621 return NULL; 1622 } 1623 1624 if(isVerbose()){ 1625 printf(" integer %s at line %i \n", (tag == NULL) ? "(null)" : tag, (int)startline); 1626 } 1627 1628 if (uprv_strlen(string) <= 0) 1629 { 1630 warning(startline, "Encountered empty integer. Default value is 0."); 1631 } 1632 1633 /* Allow integer support for hexdecimal, octal digit and decimal*/ 1634 /* and handle illegal char in the integer*/ 1635 value = uprv_strtoul(string, &stopstring, 0); 1636 len=(uint32_t)(stopstring-string); 1637 if(len==uprv_strlen(string)) 1638 { 1639 result = int_open(state->bundle, tag, value, comment, status); 1640 } 1641 else 1642 { 1643 *status=U_INVALID_CHAR_FOUND; 1644 } 1645 uprv_free(string); 1646 1647 return result; 1648} 1649 1650static struct SResource * 1651parseImport(ParseState* state, char *tag, uint32_t startline, const struct UString* comment, UErrorCode *status) 1652{ 1653 struct SResource *result; 1654 FileStream *file; 1655 int32_t len; 1656 uint8_t *data; 1657 char *filename; 1658 uint32_t line; 1659 char *fullname = NULL; 1660 filename = getInvariantString(state, &line, NULL, status); 1661 1662 if (U_FAILURE(*status)) 1663 { 1664 return NULL; 1665 } 1666 1667 expect(state, TOK_CLOSE_BRACE, NULL, NULL, NULL, status); 1668 1669 if (U_FAILURE(*status)) 1670 { 1671 uprv_free(filename); 1672 return NULL; 1673 } 1674 1675 if(isVerbose()){ 1676 printf(" import %s at line %i \n", (tag == NULL) ? "(null)" : tag, (int)startline); 1677 } 1678 1679 /* Open the input file for reading */ 1680 if (state->inputdir == NULL) 1681 { 1682#if 1 1683 /* 1684 * Always save file file name, even if there's 1685 * no input directory specified. MIGHT BREAK SOMETHING 1686 */ 1687 int32_t filenameLength = uprv_strlen(filename); 1688 1689 fullname = (char *) uprv_malloc(filenameLength + 1); 1690 uprv_strcpy(fullname, filename); 1691#endif 1692 1693 file = T_FileStream_open(filename, "rb"); 1694 } 1695 else 1696 { 1697 1698 int32_t count = (int32_t)uprv_strlen(filename); 1699 1700 if (state->inputdir[state->inputdirLength - 1] != U_FILE_SEP_CHAR) 1701 { 1702 fullname = (char *) uprv_malloc(state->inputdirLength + count + 2); 1703 1704 /* test for NULL */ 1705 if(fullname == NULL) 1706 { 1707 *status = U_MEMORY_ALLOCATION_ERROR; 1708 return NULL; 1709 } 1710 1711 uprv_strcpy(fullname, state->inputdir); 1712 1713 fullname[state->inputdirLength] = U_FILE_SEP_CHAR; 1714 fullname[state->inputdirLength + 1] = '\0'; 1715 1716 uprv_strcat(fullname, filename); 1717 } 1718 else 1719 { 1720 fullname = (char *) uprv_malloc(state->inputdirLength + count + 1); 1721 1722 /* test for NULL */ 1723 if(fullname == NULL) 1724 { 1725 *status = U_MEMORY_ALLOCATION_ERROR; 1726 return NULL; 1727 } 1728 1729 uprv_strcpy(fullname, state->inputdir); 1730 uprv_strcat(fullname, filename); 1731 } 1732 1733 file = T_FileStream_open(fullname, "rb"); 1734 1735 } 1736 1737 if (file == NULL) 1738 { 1739 error(line, "couldn't open input file %s", filename); 1740 *status = U_FILE_ACCESS_ERROR; 1741 return NULL; 1742 } 1743 1744 len = T_FileStream_size(file); 1745 data = (uint8_t*)uprv_malloc(len * sizeof(uint8_t)); 1746 /* test for NULL */ 1747 if(data == NULL) 1748 { 1749 *status = U_MEMORY_ALLOCATION_ERROR; 1750 T_FileStream_close (file); 1751 return NULL; 1752 } 1753 1754 /* int32_t numRead = */ T_FileStream_read (file, data, len); 1755 T_FileStream_close (file); 1756 1757 result = bin_open(state->bundle, tag, len, data, fullname, comment, status); 1758 1759 uprv_free(data); 1760 uprv_free(filename); 1761 uprv_free(fullname); 1762 1763 return result; 1764} 1765 1766static struct SResource * 1767parseInclude(ParseState* state, char *tag, uint32_t startline, const struct UString* comment, UErrorCode *status) 1768{ 1769 struct SResource *result; 1770 int32_t len=0; 1771 char *filename; 1772 uint32_t line; 1773 UChar *pTarget = NULL; 1774 1775 UCHARBUF *ucbuf; 1776 char *fullname = NULL; 1777 int32_t count = 0; 1778 const char* cp = NULL; 1779 const UChar* uBuffer = NULL; 1780 1781 filename = getInvariantString(state, &line, NULL, status); 1782 count = (int32_t)uprv_strlen(filename); 1783 1784 if (U_FAILURE(*status)) 1785 { 1786 return NULL; 1787 } 1788 1789 expect(state, TOK_CLOSE_BRACE, NULL, NULL, NULL, status); 1790 1791 if (U_FAILURE(*status)) 1792 { 1793 uprv_free(filename); 1794 return NULL; 1795 } 1796 1797 if(isVerbose()){ 1798 printf(" include %s at line %i \n", (tag == NULL) ? "(null)" : tag, (int)startline); 1799 } 1800 1801 fullname = (char *) uprv_malloc(state->inputdirLength + count + 2); 1802 /* test for NULL */ 1803 if(fullname == NULL) 1804 { 1805 *status = U_MEMORY_ALLOCATION_ERROR; 1806 uprv_free(filename); 1807 return NULL; 1808 } 1809 1810 if(state->inputdir!=NULL){ 1811 if (state->inputdir[state->inputdirLength - 1] != U_FILE_SEP_CHAR) 1812 { 1813 1814 uprv_strcpy(fullname, state->inputdir); 1815 1816 fullname[state->inputdirLength] = U_FILE_SEP_CHAR; 1817 fullname[state->inputdirLength + 1] = '\0'; 1818 1819 uprv_strcat(fullname, filename); 1820 } 1821 else 1822 { 1823 uprv_strcpy(fullname, state->inputdir); 1824 uprv_strcat(fullname, filename); 1825 } 1826 }else{ 1827 uprv_strcpy(fullname,filename); 1828 } 1829 1830 ucbuf = ucbuf_open(fullname, &cp,getShowWarning(),FALSE,status); 1831 1832 if (U_FAILURE(*status)) { 1833 error(line, "couldn't open input file %s\n", filename); 1834 return NULL; 1835 } 1836 1837 uBuffer = ucbuf_getBuffer(ucbuf,&len,status); 1838 result = string_open(state->bundle, tag, uBuffer, len, comment, status); 1839 1840 ucbuf_close(ucbuf); 1841 1842 uprv_free(pTarget); 1843 1844 uprv_free(filename); 1845 uprv_free(fullname); 1846 1847 return result; 1848} 1849 1850 1851 1852 1853 1854U_STRING_DECL(k_type_string, "string", 6); 1855U_STRING_DECL(k_type_binary, "binary", 6); 1856U_STRING_DECL(k_type_bin, "bin", 3); 1857U_STRING_DECL(k_type_table, "table", 5); 1858U_STRING_DECL(k_type_table_no_fallback, "table(nofallback)", 17); 1859U_STRING_DECL(k_type_int, "int", 3); 1860U_STRING_DECL(k_type_integer, "integer", 7); 1861U_STRING_DECL(k_type_array, "array", 5); 1862U_STRING_DECL(k_type_alias, "alias", 5); 1863U_STRING_DECL(k_type_intvector, "intvector", 9); 1864U_STRING_DECL(k_type_import, "import", 6); 1865U_STRING_DECL(k_type_include, "include", 7); 1866 1867/* Various non-standard processing plugins that create one or more special resources. */ 1868U_STRING_DECL(k_type_plugin_uca_rules, "process(uca_rules)", 18); 1869U_STRING_DECL(k_type_plugin_collation, "process(collation)", 18); 1870U_STRING_DECL(k_type_plugin_transliterator, "process(transliterator)", 23); 1871U_STRING_DECL(k_type_plugin_dependency, "process(dependency)", 19); 1872 1873typedef enum EResourceType 1874{ 1875 RESTYPE_UNKNOWN, 1876 RESTYPE_STRING, 1877 RESTYPE_BINARY, 1878 RESTYPE_TABLE, 1879 RESTYPE_TABLE_NO_FALLBACK, 1880 RESTYPE_INTEGER, 1881 RESTYPE_ARRAY, 1882 RESTYPE_ALIAS, 1883 RESTYPE_INTVECTOR, 1884 RESTYPE_IMPORT, 1885 RESTYPE_INCLUDE, 1886 RESTYPE_PROCESS_UCA_RULES, 1887 RESTYPE_PROCESS_COLLATION, 1888 RESTYPE_PROCESS_TRANSLITERATOR, 1889 RESTYPE_PROCESS_DEPENDENCY, 1890 RESTYPE_RESERVED 1891} EResourceType; 1892 1893static struct { 1894 const char *nameChars; /* only used for debugging */ 1895 const UChar *nameUChars; 1896 ParseResourceFunction *parseFunction; 1897} gResourceTypes[] = { 1898 {"Unknown", NULL, NULL}, 1899 {"string", k_type_string, parseString}, 1900 {"binary", k_type_binary, parseBinary}, 1901 {"table", k_type_table, parseTable}, 1902 {"table(nofallback)", k_type_table_no_fallback, NULL}, /* parseFunction will never be called */ 1903 {"integer", k_type_integer, parseInteger}, 1904 {"array", k_type_array, parseArray}, 1905 {"alias", k_type_alias, parseAlias}, 1906 {"intvector", k_type_intvector, parseIntVector}, 1907 {"import", k_type_import, parseImport}, 1908 {"include", k_type_include, parseInclude}, 1909 {"process(uca_rules)", k_type_plugin_uca_rules, parseUCARules}, 1910 {"process(collation)", k_type_plugin_collation, NULL /* not implemented yet */}, 1911 {"process(transliterator)", k_type_plugin_transliterator, parseTransliterator}, 1912 {"process(dependency)", k_type_plugin_dependency, parseDependency}, 1913 {"reserved", NULL, NULL} 1914}; 1915 1916void initParser() 1917{ 1918 U_STRING_INIT(k_type_string, "string", 6); 1919 U_STRING_INIT(k_type_binary, "binary", 6); 1920 U_STRING_INIT(k_type_bin, "bin", 3); 1921 U_STRING_INIT(k_type_table, "table", 5); 1922 U_STRING_INIT(k_type_table_no_fallback, "table(nofallback)", 17); 1923 U_STRING_INIT(k_type_int, "int", 3); 1924 U_STRING_INIT(k_type_integer, "integer", 7); 1925 U_STRING_INIT(k_type_array, "array", 5); 1926 U_STRING_INIT(k_type_alias, "alias", 5); 1927 U_STRING_INIT(k_type_intvector, "intvector", 9); 1928 U_STRING_INIT(k_type_import, "import", 6); 1929 U_STRING_INIT(k_type_include, "include", 7); 1930 1931 U_STRING_INIT(k_type_plugin_uca_rules, "process(uca_rules)", 18); 1932 U_STRING_INIT(k_type_plugin_collation, "process(collation)", 18); 1933 U_STRING_INIT(k_type_plugin_transliterator, "process(transliterator)", 23); 1934 U_STRING_INIT(k_type_plugin_dependency, "process(dependency)", 19); 1935} 1936 1937static inline UBool isTable(enum EResourceType type) { 1938 return (UBool)(type==RESTYPE_TABLE || type==RESTYPE_TABLE_NO_FALLBACK); 1939} 1940 1941static enum EResourceType 1942parseResourceType(ParseState* state, UErrorCode *status) 1943{ 1944 struct UString *tokenValue; 1945 struct UString comment; 1946 enum EResourceType result = RESTYPE_UNKNOWN; 1947 uint32_t line=0; 1948 ustr_init(&comment); 1949 expect(state, TOK_STRING, &tokenValue, &comment, &line, status); 1950 1951 if (U_FAILURE(*status)) 1952 { 1953 return RESTYPE_UNKNOWN; 1954 } 1955 1956 *status = U_ZERO_ERROR; 1957 1958 /* Search for normal types */ 1959 result=RESTYPE_UNKNOWN; 1960 while ((result=(EResourceType)(result+1)) < RESTYPE_RESERVED) { 1961 if (u_strcmp(tokenValue->fChars, gResourceTypes[result].nameUChars) == 0) { 1962 break; 1963 } 1964 } 1965 /* Now search for the aliases */ 1966 if (u_strcmp(tokenValue->fChars, k_type_int) == 0) { 1967 result = RESTYPE_INTEGER; 1968 } 1969 else if (u_strcmp(tokenValue->fChars, k_type_bin) == 0) { 1970 result = RESTYPE_BINARY; 1971 } 1972 else if (result == RESTYPE_RESERVED) { 1973 char tokenBuffer[1024]; 1974 u_austrncpy(tokenBuffer, tokenValue->fChars, sizeof(tokenBuffer)); 1975 tokenBuffer[sizeof(tokenBuffer) - 1] = 0; 1976 *status = U_INVALID_FORMAT_ERROR; 1977 error(line, "unknown resource type '%s'", tokenBuffer); 1978 } 1979 1980 return result; 1981} 1982 1983/* parse a non-top-level resource */ 1984static struct SResource * 1985parseResource(ParseState* state, char *tag, const struct UString *comment, UErrorCode *status) 1986{ 1987 enum ETokenType token; 1988 enum EResourceType resType = RESTYPE_UNKNOWN; 1989 ParseResourceFunction *parseFunction = NULL; 1990 struct UString *tokenValue; 1991 uint32_t startline; 1992 uint32_t line; 1993 1994 1995 token = getToken(state, &tokenValue, NULL, &startline, status); 1996 1997 if(isVerbose()){ 1998 printf(" resource %s at line %i \n", (tag == NULL) ? "(null)" : tag, (int)startline); 1999 } 2000 2001 /* name . [ ':' type ] '{' resource '}' */ 2002 /* This function parses from the colon onwards. If the colon is present, parse the 2003 type then try to parse a resource of that type. If there is no explicit type, 2004 work it out using the lookahead tokens. */ 2005 switch (token) 2006 { 2007 case TOK_EOF: 2008 *status = U_INVALID_FORMAT_ERROR; 2009 error(startline, "Unexpected EOF encountered"); 2010 return NULL; 2011 2012 case TOK_ERROR: 2013 *status = U_INVALID_FORMAT_ERROR; 2014 return NULL; 2015 2016 case TOK_COLON: 2017 resType = parseResourceType(state, status); 2018 expect(state, TOK_OPEN_BRACE, &tokenValue, NULL, &startline, status); 2019 2020 if (U_FAILURE(*status)) 2021 { 2022 return NULL; 2023 } 2024 2025 break; 2026 2027 case TOK_OPEN_BRACE: 2028 break; 2029 2030 default: 2031 *status = U_INVALID_FORMAT_ERROR; 2032 error(startline, "syntax error while reading a resource, expected '{' or ':'"); 2033 return NULL; 2034 } 2035 2036 2037 if (resType == RESTYPE_UNKNOWN) 2038 { 2039 /* No explicit type, so try to work it out. At this point, we've read the first '{'. 2040 We could have any of the following: 2041 { { => array (nested) 2042 { :/} => array 2043 { string , => string array 2044 2045 { string { => table 2046 2047 { string :/{ => table 2048 { string } => string 2049 */ 2050 2051 token = peekToken(state, 0, NULL, &line, NULL,status); 2052 2053 if (U_FAILURE(*status)) 2054 { 2055 return NULL; 2056 } 2057 2058 if (token == TOK_OPEN_BRACE || token == TOK_COLON ||token ==TOK_CLOSE_BRACE ) 2059 { 2060 resType = RESTYPE_ARRAY; 2061 } 2062 else if (token == TOK_STRING) 2063 { 2064 token = peekToken(state, 1, NULL, &line, NULL, status); 2065 2066 if (U_FAILURE(*status)) 2067 { 2068 return NULL; 2069 } 2070 2071 switch (token) 2072 { 2073 case TOK_COMMA: resType = RESTYPE_ARRAY; break; 2074 case TOK_OPEN_BRACE: resType = RESTYPE_TABLE; break; 2075 case TOK_CLOSE_BRACE: resType = RESTYPE_STRING; break; 2076 case TOK_COLON: resType = RESTYPE_TABLE; break; 2077 default: 2078 *status = U_INVALID_FORMAT_ERROR; 2079 error(line, "Unexpected token after string, expected ',', '{' or '}'"); 2080 return NULL; 2081 } 2082 } 2083 else 2084 { 2085 *status = U_INVALID_FORMAT_ERROR; 2086 error(line, "Unexpected token after '{'"); 2087 return NULL; 2088 } 2089 2090 /* printf("Type guessed as %s\n", resourceNames[resType]); */ 2091 } else if(resType == RESTYPE_TABLE_NO_FALLBACK) { 2092 *status = U_INVALID_FORMAT_ERROR; 2093 error(startline, "error: %s resource type not valid except on top bundle level", gResourceTypes[resType].nameChars); 2094 return NULL; 2095 } 2096 2097 2098 /* We should now know what we need to parse next, so call the appropriate parser 2099 function and return. */ 2100 parseFunction = gResourceTypes[resType].parseFunction; 2101 if (parseFunction != NULL) { 2102 return parseFunction(state, tag, startline, comment, status); 2103 } 2104 else { 2105 *status = U_INTERNAL_PROGRAM_ERROR; 2106 error(startline, "internal error: %s resource type found and not handled", gResourceTypes[resType].nameChars); 2107 } 2108 2109 return NULL; 2110} 2111 2112/* parse the top-level resource */ 2113struct SRBRoot * 2114parse(UCHARBUF *buf, const char *inputDir, const char *outputDir, const char *filename, 2115 UBool makeBinaryCollation, UBool omitCollationRules, UErrorCode *status) 2116{ 2117 struct UString *tokenValue; 2118 struct UString comment; 2119 uint32_t line; 2120 enum EResourceType bundleType; 2121 enum ETokenType token; 2122 ParseState state; 2123 uint32_t i; 2124 2125 2126 for (i = 0; i < MAX_LOOKAHEAD + 1; i++) 2127 { 2128 ustr_init(&state.lookahead[i].value); 2129 ustr_init(&state.lookahead[i].comment); 2130 } 2131 2132 initLookahead(&state, buf, status); 2133 2134 state.inputdir = inputDir; 2135 state.inputdirLength = (state.inputdir != NULL) ? (uint32_t)uprv_strlen(state.inputdir) : 0; 2136 state.outputdir = outputDir; 2137 state.outputdirLength = (state.outputdir != NULL) ? (uint32_t)uprv_strlen(state.outputdir) : 0; 2138 state.filename = filename; 2139 state.makeBinaryCollation = makeBinaryCollation; 2140 state.omitCollationRules = omitCollationRules; 2141 2142 ustr_init(&comment); 2143 expect(&state, TOK_STRING, &tokenValue, &comment, NULL, status); 2144 2145 state.bundle = bundle_open(&comment, FALSE, status); 2146 2147 if (state.bundle == NULL || U_FAILURE(*status)) 2148 { 2149 return NULL; 2150 } 2151 2152 2153 bundle_setlocale(state.bundle, tokenValue->fChars, status); 2154 2155 /* The following code is to make Empty bundle work no matter with :table specifer or not */ 2156 token = getToken(&state, NULL, NULL, &line, status); 2157 if(token==TOK_COLON) { 2158 *status=U_ZERO_ERROR; 2159 bundleType=parseResourceType(&state, status); 2160 2161 if(isTable(bundleType)) 2162 { 2163 expect(&state, TOK_OPEN_BRACE, NULL, NULL, &line, status); 2164 } 2165 else 2166 { 2167 *status=U_PARSE_ERROR; 2168 error(line, "parse error. Stopped parsing with %s", u_errorName(*status)); 2169 } 2170 } 2171 else 2172 { 2173 /* not a colon */ 2174 if(token==TOK_OPEN_BRACE) 2175 { 2176 *status=U_ZERO_ERROR; 2177 bundleType=RESTYPE_TABLE; 2178 } 2179 else 2180 { 2181 /* neither colon nor open brace */ 2182 *status=U_PARSE_ERROR; 2183 bundleType=RESTYPE_UNKNOWN; 2184 error(line, "parse error, did not find open-brace '{' or colon ':', stopped with %s", u_errorName(*status)); 2185 } 2186 } 2187 2188 if (U_FAILURE(*status)) 2189 { 2190 bundle_close(state.bundle, status); 2191 return NULL; 2192 } 2193 2194 if(bundleType==RESTYPE_TABLE_NO_FALLBACK) { 2195 /* 2196 * Parse a top-level table with the table(nofallback) declaration. 2197 * This is the same as a regular table, but also sets the 2198 * URES_ATT_NO_FALLBACK flag in indexes[URES_INDEX_ATTRIBUTES] . 2199 */ 2200 state.bundle->noFallback=TRUE; 2201 } 2202 /* top-level tables need not handle special table names like "collations" */ 2203 realParseTable(&state, state.bundle->fRoot, NULL, line, status); 2204 if(dependencyArray!=NULL){ 2205 table_add(state.bundle->fRoot, dependencyArray, 0, status); 2206 dependencyArray = NULL; 2207 } 2208 if (U_FAILURE(*status)) 2209 { 2210 bundle_close(state.bundle, status); 2211 res_close(dependencyArray); 2212 return NULL; 2213 } 2214 2215 if (getToken(&state, NULL, NULL, &line, status) != TOK_EOF) 2216 { 2217 warning(line, "extraneous text after resource bundle (perhaps unmatched braces)"); 2218 if(isStrict()){ 2219 *status = U_INVALID_FORMAT_ERROR; 2220 return NULL; 2221 } 2222 } 2223 2224 cleanupLookahead(&state); 2225 ustr_deinit(&comment); 2226 return state.bundle; 2227} 2228