1 /*++ 2 3 Copyright (c) 2004 - 2010, Intel Corporation. All rights reserved.<BR> 4 This program and the accompanying materials 5 are licensed and made available under the terms and conditions of the BSD License 6 which accompanies this distribution. The full text of the license may be found at 7 http://opensource.org/licenses/bsd-license.php 8 9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 11 12 Module Name: 13 14 StringDB.c 15 16 Abstract: 17 18 String database implementation 19 20 --*/ 21 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <ctype.h> // for tolower() 26 #include "Tiano.h" 27 #include "EfiUtilityMsgs.h" 28 #include "StrGather.h" 29 #include "StringDb.h" 30 #include "EfiInternalFormRepresentation.h" 31 32 #include EFI_PROTOCOL_DEFINITION (Hii) 33 34 typedef CHAR16 WCHAR; 35 #define STRING_OFFSET RELOFST 36 37 #define STRING_DB_KEY (('S' << 24) | ('D' << 16) | ('B' << 8) | 'K') 38 // 39 // Version supported by this tool 40 // 41 #define STRING_DB_VERSION 0x00010000 42 43 #define STRING_DB_MAJOR_VERSION_MASK 0xFFFF0000 44 #define STRING_DB_MINOR_VERSION_MASK 0x0000FFFF 45 46 #define DEFINE_STR L"// #define" 47 48 #define LANGUAGE_CODE_WIDTH 4 49 // 50 // This is the header that gets written to the top of the 51 // output binary database file. 52 // 53 typedef struct { 54 UINT32 Key; 55 UINT32 HeaderSize; 56 UINT32 Version; 57 UINT32 NumStringIdenfiers; 58 UINT32 StringIdentifiersSize; 59 UINT32 NumLanguages; 60 } STRING_DB_HEADER; 61 62 // 63 // When we write out data to the database, we have a UINT16 identifier, which 64 // indicates what follows, followed by the data. Here's the structure. 65 // 66 typedef struct { 67 UINT16 DataType; 68 UINT16 Reserved; 69 } DB_DATA_ITEM_HEADER; 70 71 #define DB_DATA_TYPE_INVALID 0x0000 72 #define DB_DATA_TYPE_STRING_IDENTIFIER 0x0001 73 #define DB_DATA_TYPE_LANGUAGE_DEFINITION 0x0002 74 #define DB_DATA_TYPE_STRING_DEFINITION 0x0003 75 #define DB_DATA_TYPE_LAST DB_DATA_TYPE_STRING_DEFINITION 76 77 // 78 // We have to keep track of a list of languages, each of which has its own 79 // list of strings. Define a structure to keep track of all languages and 80 // their list of strings. 81 // 82 typedef struct _STRING_LIST { 83 struct _STRING_LIST *Next; 84 UINT32 Size; // number of bytes in string, including null terminator 85 WCHAR *LanguageName; 86 WCHAR *StringName; // for example STR_ID_TEXT1 87 WCHAR *Scope; // 88 WCHAR *Str; // the actual string 89 UINT16 Flags; // properties of this string (used, undefined) 90 } STRING_LIST; 91 92 typedef struct _LANGUAGE_LIST { 93 struct _LANGUAGE_LIST *Next; 94 WCHAR LanguageName[4]; 95 WCHAR *PrintableLanguageName; 96 STRING_LIST *String; 97 STRING_LIST *LastString; 98 } LANGUAGE_LIST; 99 100 // 101 // We also keep track of all the string identifier names, which we assign unique 102 // values to. Create a structure to keep track of them all. 103 // 104 typedef struct _STRING_IDENTIFIER { 105 struct _STRING_IDENTIFIER *Next; 106 UINT32 Index; // only need 16 bits, but makes it easier with UINT32 107 WCHAR *StringName; 108 UINT16 Flags; // if someone referenced it via STRING_TOKEN() 109 } STRING_IDENTIFIER; 110 // 111 // Keep our globals in this structure to be as modular as possible. 112 // 113 typedef struct { 114 FILE *StringDBFptr; 115 LANGUAGE_LIST *LanguageList; 116 LANGUAGE_LIST *LastLanguageList; 117 LANGUAGE_LIST *CurrentLanguage; // keep track of the last language they used 118 STRING_IDENTIFIER *StringIdentifier; 119 STRING_IDENTIFIER *LastStringIdentifier; 120 UINT8 *StringDBFileName; 121 UINT32 NumStringIdentifiers; 122 UINT32 NumStringIdentifiersReferenced; 123 STRING_IDENTIFIER *CurrentStringIdentifier; // keep track of the last string identifier they added 124 WCHAR *CurrentScope; 125 } STRING_DB_DATA; 126 127 static STRING_DB_DATA mDBData; 128 129 static const char *mSourceFileHeader[] = { 130 "//", 131 "// DO NOT EDIT -- auto-generated file", 132 "//", 133 "// This file is generated by the string gather utility", 134 "//", 135 NULL 136 }; 137 138 static 139 STRING_LIST * 140 StringDBFindString ( 141 WCHAR *LanguageName, 142 WCHAR *StringName, 143 WCHAR *Scope, 144 WCHAR_STRING_LIST *LanguagesOfInterest, 145 WCHAR_MATCHING_STRING_LIST *IndirectionList 146 ); 147 148 static 149 STRING_IDENTIFIER * 150 StringDBFindStringIdentifierByName ( 151 WCHAR *Name 152 ); 153 154 static 155 STRING_IDENTIFIER * 156 StringDBFindStringIdentifierByIndex ( 157 UINT32 Index 158 ); 159 160 static 161 LANGUAGE_LIST * 162 StringDBFindLanguageList ( 163 WCHAR *LanguageName 164 ); 165 166 static 167 void 168 StringDBWriteStandardFileHeader ( 169 FILE *OutFptr 170 ); 171 172 static 173 WCHAR * 174 AsciiToWchar ( 175 INT8 *Str 176 ); 177 178 static 179 WCHAR * 180 DuplicateString ( 181 WCHAR *Str 182 ); 183 184 static 185 STATUS 186 StringDBWriteStringIdentifier ( 187 FILE *DBFptr, 188 UINT16 StringId, 189 UINT16 Flags, 190 WCHAR *IdentifierName 191 ); 192 193 static 194 STATUS 195 StringDBReadStringIdentifier ( 196 FILE *DBFptr 197 ); 198 199 static 200 STATUS 201 StringDBWriteLanguageDefinition ( 202 FILE *DBFptr, 203 WCHAR *LanguageName, 204 WCHAR *PrintableLanguageName 205 ); 206 207 static 208 STATUS 209 StringDBReadLanguageDefinition ( 210 FILE *DBFptr 211 ); 212 213 static 214 STATUS 215 StringDBWriteString ( 216 FILE *DBFptr, 217 UINT16 Flags, 218 WCHAR *Language, 219 WCHAR *StringName, 220 WCHAR *Scope, 221 WCHAR *Str 222 ); 223 224 static 225 STATUS 226 StringDBReadString ( 227 FILE *DBFptr 228 ); 229 230 static 231 STATUS 232 StringDBReadGenericString ( 233 FILE *DBFptr, 234 UINT16 *Size, 235 WCHAR **Str 236 ); 237 238 static 239 STATUS 240 StringDBWriteGenericString ( 241 FILE *DBFptr, 242 WCHAR *Str 243 ); 244 245 static 246 void 247 StringDBAssignStringIndexes ( 248 VOID 249 ); 250 251 /*****************************************************************************/ 252 253 /*++ 254 255 Routine Description: 256 Constructor function for the string database handler. 257 258 Arguments: 259 None. 260 261 Returns: 262 None. 263 264 --*/ 265 void 266 StringDBConstructor ( 267 VOID 268 ) 269 { 270 memset ((char *) &mDBData, 0, sizeof (STRING_DB_DATA)); 271 mDBData.CurrentScope = DuplicateString (L"NULL"); 272 } 273 274 /*****************************************************************************/ 275 276 /*++ 277 278 Routine Description: 279 Destructor function for the string database handler. 280 281 Arguments: 282 None. 283 284 Returns: 285 None. 286 287 --*/ 288 void 289 StringDBDestructor ( 290 VOID 291 ) 292 { 293 LANGUAGE_LIST *NextLang; 294 STRING_LIST *NextStr; 295 STRING_IDENTIFIER *NextIdentifier; 296 // 297 // Close the database file if it's open 298 // 299 if (mDBData.StringDBFptr != NULL) { 300 fclose (mDBData.StringDBFptr); 301 mDBData.StringDBFptr = NULL; 302 } 303 // 304 // If we've allocated any strings/languages, free them up 305 // 306 while (mDBData.LanguageList != NULL) { 307 NextLang = mDBData.LanguageList->Next; 308 // 309 // Free up all strings for this language 310 // 311 while (mDBData.LanguageList->String != NULL) { 312 NextStr = mDBData.LanguageList->String->Next; 313 FREE (mDBData.LanguageList->String->Str); 314 FREE (mDBData.LanguageList->String); 315 mDBData.LanguageList->String = NextStr; 316 } 317 318 FREE (mDBData.LanguageList->PrintableLanguageName); 319 FREE (mDBData.LanguageList); 320 mDBData.LanguageList = NextLang; 321 } 322 // 323 // Free up string identifiers 324 // 325 while (mDBData.StringIdentifier != NULL) { 326 NextIdentifier = mDBData.StringIdentifier->Next; 327 FREE (mDBData.StringIdentifier->StringName); 328 FREE (mDBData.StringIdentifier); 329 mDBData.StringIdentifier = NextIdentifier; 330 } 331 // 332 // Free the filename 333 // 334 if (mDBData.StringDBFileName != NULL) { 335 FREE (mDBData.StringDBFileName); 336 mDBData.StringDBFileName = NULL; 337 } 338 // 339 // We save a copy of the scope, so free it up if we 340 // have one. 341 // 342 if (mDBData.CurrentScope != NULL) { 343 FREE (mDBData.CurrentScope); 344 mDBData.CurrentScope = NULL; 345 } 346 } 347 348 /*****************************************************************************/ 349 350 /*++ 351 352 Routine Description: 353 354 Dump the contents of a database to an output C file. 355 356 Arguments: 357 358 FileName - name of the output file to write 359 BaseName - used for the name of the C array defined 360 Languages - list of languages of interest 361 362 Returns: 363 364 STATUS 365 366 Notes: 367 368 Languages is a pointer to a linked list of languages specified on 369 the command line. Format is "eng" and "spa+cat". For this, print 370 the strings for eng. Print the strings for spa too, but if one is 371 missing look for a cat string and print if it it exists. 372 373 --*/ 374 STATUS 375 StringDBDumpCStrings ( 376 INT8 *FileName, 377 INT8 *BaseName, 378 WCHAR_STRING_LIST *LanguagesOfInterest, 379 WCHAR_MATCHING_STRING_LIST *IndirectionList 380 ) 381 { 382 FILE *Fptr; 383 LANGUAGE_LIST *Lang; 384 STRING_LIST *CurrString; 385 STRING_LIST EmptyString; 386 UINT32 Offset; 387 UINT32 StringIndex; 388 UINT32 TempIndex; 389 UINT32 BytesThisLine; 390 EFI_HII_STRING_PACK_HEADER StringPack; 391 UINT8 *Ptr; 392 UINT32 Len; 393 WCHAR ZeroString[1]; 394 WCHAR_STRING_LIST *LOIPtr; 395 BOOLEAN LanguageOk; 396 WCHAR *TempStringPtr; 397 WCHAR *LangName; 398 STRING_IDENTIFIER *StringIdentifier; 399 400 if ((Fptr = fopen (FileName, "w")) == NULL) { 401 Error (NULL, 0, 0, FileName, "failed to open output C string file"); 402 return STATUS_ERROR; 403 } 404 // 405 // Assign index values to the string identifiers 406 // 407 StringDBAssignStringIndexes (); 408 // 409 // Write the standard header to the output file, then the structure 410 // definition header. 411 // 412 StringDBWriteStandardFileHeader (Fptr); 413 fprintf (Fptr, "\nunsigned char %s[] = {\n", BaseName); 414 // 415 // If a given string is not defined, then we'll use this one. 416 // 417 memset (&EmptyString, 0, sizeof (EmptyString)); 418 EmptyString.Size = sizeof (ZeroString); 419 EmptyString.Str = ZeroString; 420 // 421 // Process each language, then each string for each langage 422 // 423 ZeroString[0] = 0; 424 for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) { 425 // 426 // If we have a language list, then make sure this language is in that 427 // list. 428 // 429 LanguageOk = TRUE; 430 LangName = Lang->LanguageName; 431 if (LanguagesOfInterest != NULL) { 432 LanguageOk = FALSE; 433 for (LOIPtr = LanguagesOfInterest; LOIPtr != NULL; LOIPtr = LOIPtr->Next) { 434 if (wcsncmp (LOIPtr->Str, Lang->LanguageName, LANGUAGE_IDENTIFIER_NAME_LEN) == 0) { 435 LangName = LOIPtr->Str; 436 LanguageOk = TRUE; 437 break; 438 } 439 } 440 } 441 442 if (!LanguageOk) { 443 continue; 444 } 445 // 446 // Process each string for this language. We have to make 3 passes on the strings: 447 // Pass1: computes sizes and fill in the string pack header 448 // Pass2: write the array of offsets 449 // Pass3: write the strings 450 // 451 // 452 // PASS 1: Fill in and print the HII string pack header 453 // 454 // Compute the size for this language package and write 455 // the header out. Each string package contains: 456 // Header 457 // Offset[] -- an array of offsets to strings, of type RELOFST each 458 // String[] -- the actual strings themselves 459 // 460 fprintf ( 461 Fptr, 462 "\n//******************************************************************************" 463 "\n// Start of string definitions for %S/%S", 464 Lang->LanguageName, 465 Lang->PrintableLanguageName 466 ); 467 memset ((char *) &StringPack, 0, sizeof (EFI_HII_STRING_PACK_HEADER)); 468 StringPack.Header.Type = EFI_HII_STRING; 469 StringPack.NumStringPointers = (UINT16) mDBData.NumStringIdentifiersReferenced; 470 // 471 // First string is the language name. If we're printing all languages, then 472 // it's just the "spa". If we were given a list of languages to print, then it's 473 // the "spacat" string. Compute its offset and fill in 474 // the info in the header. Since we know the language name string's length, 475 // and the printable language name follows it, use that info to fill in the 476 // entry for the printable language name as well. 477 // 478 StringPack.LanguageNameString = (STRING_OFFSET) (sizeof (EFI_HII_STRING_PACK_HEADER) + (mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET))); 479 StringPack.PrintableLanguageName = (STRING_OFFSET) (StringPack.LanguageNameString + (wcslen (LangName) + 1) * sizeof (WCHAR)); 480 // 481 // Add up the size of all strings so we can fill in our header. 482 // 483 Len = 0; 484 for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) { 485 // 486 // For the first string (language name), we print out the "spacat" if they 487 // requested it. We set LangName to point to the proper language name string above. 488 // 489 if (StringIndex == STRING_ID_LANGUAGE_NAME) { 490 Len += (wcslen (LangName) + 1) * sizeof (WCHAR); 491 } else { 492 // 493 // Find a string with this language.stringname 494 // 495 StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex); 496 if (StringIdentifier == NULL) { 497 Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex); 498 return STATUS_ERROR; 499 } 500 // 501 // Find a matching string if this string identifier was referenced 502 // 503 EmptyString.Flags = STRING_FLAGS_UNDEFINED; 504 CurrString = NULL; 505 if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) { 506 CurrString = StringDBFindString ( 507 Lang->LanguageName, 508 StringIdentifier->StringName, 509 NULL, 510 LanguagesOfInterest, 511 IndirectionList 512 ); 513 if (NULL == CurrString) { 514 // 515 // If string for Lang->LanguageName is not found, try to get an English version 516 // 517 CurrString = StringDBFindString ( 518 L"eng", 519 StringIdentifier->StringName, 520 NULL, 521 LanguagesOfInterest, 522 IndirectionList 523 ); 524 } 525 } 526 527 if (CurrString == NULL) { 528 CurrString = &EmptyString; 529 EmptyString.Flags |= StringIdentifier->Flags; 530 } 531 532 Len += CurrString->Size; 533 } 534 } 535 StringPack.Header.Length = sizeof (EFI_HII_STRING_PACK_HEADER) 536 + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET) 537 + Len; 538 // 539 // Write out the header one byte at a time 540 // 541 Ptr = (UINT8 *) &StringPack; 542 for (TempIndex = 0; TempIndex < sizeof (EFI_HII_STRING_PACK_HEADER); TempIndex++, Ptr++) { 543 if ((TempIndex & 0x07) == 0) { 544 fprintf (Fptr, "\n "); 545 } 546 547 fprintf (Fptr, "0x%02X, ", (UINT32) *Ptr); 548 } 549 550 fprintf (Fptr, "\n // offset 0x%X\n", sizeof (StringPack)); 551 // 552 // PASS2 : write the offsets 553 // 554 // Traverse the list of strings again and write the array of offsets. The 555 // offset to the first string is the size of the string pack header 556 // plus the size of the offsets array. The other strings follow it. 557 // 558 StringIndex = 0; 559 Offset = sizeof (StringPack) + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET); 560 for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) { 561 // 562 // Write the offset, followed by a useful comment 563 // 564 fprintf (Fptr, " "); 565 Ptr = (UINT8 *) &Offset; 566 for (TempIndex = 0; TempIndex < sizeof (STRING_OFFSET); TempIndex++) { 567 fprintf (Fptr, "0x%02X, ", (UINT32) Ptr[TempIndex]); 568 } 569 // 570 // Find the string name 571 // 572 StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex); 573 if (StringIdentifier == NULL) { 574 Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex); 575 return STATUS_ERROR; 576 } 577 578 fprintf (Fptr, " // offset to string %S (0x%04X)", StringIdentifier->StringName, StringIndex); 579 // 580 // For the first string (language name), we print out the "spacat" if they 581 // requested it. We set LangName to point to the proper language name string above. 582 // 583 if (StringIndex == STRING_ID_LANGUAGE_NAME) { 584 Offset += (wcslen (LangName) + 1) * sizeof (WCHAR); 585 CurrString = StringDBFindString ( 586 Lang->LanguageName, 587 StringIdentifier->StringName, 588 NULL, // scope 589 NULL, 590 NULL 591 ); 592 } else { 593 // 594 // Find a matching string 595 // 596 CurrString = StringDBFindString ( 597 Lang->LanguageName, 598 StringIdentifier->StringName, 599 NULL, // scope 600 LanguagesOfInterest, 601 IndirectionList 602 ); 603 604 if (NULL == CurrString) { 605 CurrString = StringDBFindString ( 606 L"eng", 607 StringIdentifier->StringName, 608 NULL, // scope 609 LanguagesOfInterest, 610 IndirectionList 611 ); 612 } 613 614 EmptyString.LanguageName = Lang->LanguageName; 615 if (CurrString == NULL) { 616 CurrString = &EmptyString; 617 EmptyString.Flags = STRING_FLAGS_UNDEFINED; 618 } else if ((StringIdentifier->Flags & STRING_FLAGS_REFERENCED) == 0) { 619 CurrString = &EmptyString; 620 EmptyString.Flags = 0; 621 } 622 623 Offset += CurrString->Size; 624 } 625 // 626 // Print useful info about this string 627 // 628 if ((StringIdentifier->Flags & STRING_FLAGS_REFERENCED) == 0) { 629 fprintf (Fptr, " - not referenced"); 630 } 631 632 if (CurrString->Flags & STRING_FLAGS_UNDEFINED) { 633 fprintf (Fptr, " - not defined for this language"); 634 } else if (wcscmp (CurrString->LanguageName, Lang->LanguageName) != 0) { 635 fprintf ( 636 Fptr, 637 " - not defined for this language -- using secondary language %S definition", 638 CurrString->LanguageName 639 ); 640 } 641 642 fprintf (Fptr, "\n"); 643 } 644 // 645 // For unreferenced string identifiers, print a message that they are not referenced anywhere 646 // 647 while (StringIndex < mDBData.NumStringIdentifiers) { 648 StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex); 649 if (StringIdentifier != NULL) { 650 fprintf (Fptr, " // %S not referenced\n", StringIdentifier->StringName); 651 } 652 653 StringIndex++; 654 } 655 656 // 657 // PASS 3: write the strings themselves. 658 // Keep track of how many bytes we write per line because some editors 659 // (Visual Studio for instance) can't handle too long of lines. 660 // 661 Offset = sizeof (StringPack) + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET); 662 for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) { 663 StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex); 664 if (StringIdentifier == NULL) { 665 Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex); 666 return STATUS_ERROR; 667 } 668 669 fprintf (Fptr, " // string %S offset 0x%08X\n ", StringIdentifier->StringName, Offset); 670 // 671 // For the first string (language name), we print out the "spacat" if they 672 // requested it. We set LangName to point to the proper language name string above. 673 // 674 if (StringIndex == STRING_ID_LANGUAGE_NAME) { 675 TempStringPtr = LangName; 676 } else { 677 // 678 // Find a matching string if this string identifier was referenced 679 // 680 CurrString = NULL; 681 if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) { 682 CurrString = StringDBFindString ( 683 Lang->LanguageName, 684 StringIdentifier->StringName, 685 NULL, // scope 686 LanguagesOfInterest, 687 IndirectionList 688 ); 689 if (NULL == CurrString) { 690 CurrString = StringDBFindString ( 691 L"eng", 692 StringIdentifier->StringName, 693 NULL, // scope 694 LanguagesOfInterest, 695 IndirectionList 696 ); 697 } 698 } 699 700 if (CurrString == NULL) { 701 CurrString = &EmptyString; 702 } 703 704 TempStringPtr = CurrString->Str; 705 } 706 707 BytesThisLine = 0; 708 for (TempIndex = 0; TempStringPtr[TempIndex] != 0; TempIndex++) { 709 fprintf ( 710 Fptr, 711 "0x%02X, 0x%02X, ", 712 (UINT32) TempStringPtr[TempIndex] & 0xFF, 713 (UINT32) ((TempStringPtr[TempIndex] >> 8) & 0xFF) 714 ); 715 BytesThisLine += 2; 716 Offset += 2; 717 // 718 // Let's say we only allow 14 per line 719 // 720 if (BytesThisLine > 14) { 721 fprintf (Fptr, "\n "); 722 BytesThisLine = 0; 723 } 724 } 725 // 726 // Print NULL WCHAR at the end of this string. 727 // 728 fprintf (Fptr, "0x00, 0x00,\n"); 729 Offset += 2; 730 } 731 // 732 // Sanity check the offset. Make sure our running offset is what we put in the 733 // string pack header. 734 // 735 if (StringPack.Header.Length != Offset) { 736 Error ( 737 __FILE__, 738 __LINE__, 739 0, 740 "application error", 741 "stringpack size 0x%X does not match final size 0x%X", 742 StringPack.Header.Length, 743 Offset 744 ); 745 } 746 } 747 // 748 // Print terminator string pack, closing brace and close the file. 749 // The size of 0 triggers to the consumer that this is the end. 750 // 751 memset ((char *) &StringPack, 0, sizeof (EFI_HII_STRING_PACK_HEADER)); 752 StringPack.Header.Type = EFI_HII_STRING; 753 Ptr = (UINT8 *) &StringPack; 754 fprintf (Fptr, "\n // strings terminator pack"); 755 for (TempIndex = 0; TempIndex < sizeof (StringPack); TempIndex++, Ptr++) { 756 if ((TempIndex & 0x0F) == 0) { 757 fprintf (Fptr, "\n "); 758 } 759 760 fprintf (Fptr, "0x%02X, ", (UINT32) *Ptr); 761 } 762 763 fprintf (Fptr, "\n};\n"); 764 fclose (Fptr); 765 return STATUS_SUCCESS; 766 } 767 768 /*****************************************************************************/ 769 770 /*++ 771 772 Routine Description: 773 774 Dump the #define string names 775 776 Arguments: 777 778 FileName - name of the output file to write 779 BaseName - used for the protection #ifndef/#endif 780 781 Returns: 782 783 STATUS 784 785 --*/ 786 STATUS 787 StringDBDumpStringDefines ( 788 INT8 *FileName, 789 INT8 *BaseName 790 ) 791 { 792 FILE *Fptr; 793 STRING_IDENTIFIER *Identifier; 794 INT8 CopyBaseName[100]; 795 UINT32 Index; 796 const INT8 *StrDefHeader[] = { 797 "#ifndef _%s_STRINGS_DEFINE_H_\n", 798 "#define _%s_STRINGS_DEFINE_H_\n\n", 799 NULL 800 }; 801 802 if ((Fptr = fopen (FileName, "w")) == NULL) { 803 Error (NULL, 0, 0, FileName, "failed to open output string defines file"); 804 return STATUS_ERROR; 805 } 806 // 807 // Get the base source filename and convert to uppercase. 808 // 809 if (sizeof (CopyBaseName) <= strlen (BaseName) + 1) { 810 Error (NULL, 0, 0, "application error", "StringDBDumpStringDefines() string length insufficient"); 811 return STATUS_ERROR; 812 } 813 814 strcpy (CopyBaseName, BaseName); 815 for (Index = 0; CopyBaseName[Index] != 0; Index++) { 816 if (islower (CopyBaseName[Index])) { 817 CopyBaseName[Index] = (INT8) toupper (CopyBaseName[Index]); 818 } 819 } 820 // 821 // Assign index values to the string identifiers 822 // 823 StringDBAssignStringIndexes (); 824 // 825 // Write the standard header to the output file, and then the 826 // protective #ifndef. 827 // 828 StringDBWriteStandardFileHeader (Fptr); 829 for (Index = 0; StrDefHeader[Index] != NULL; Index++) { 830 fprintf (Fptr, StrDefHeader[Index], CopyBaseName); 831 } 832 // 833 // Print all the #defines for the string identifiers. Print identifiers 834 // whose names start with '$' as comments. Add comments for string 835 // identifiers not used as well. 836 // 837 Identifier = mDBData.StringIdentifier; 838 while (Identifier != NULL) { 839 if (Identifier->StringName[0] == L'$') { 840 fprintf (Fptr, "// "); 841 } 842 843 if (Identifier->Flags & STRING_FLAGS_REFERENCED) { 844 fprintf (Fptr, "#define %-40S 0x%04X\n", Identifier->StringName, Identifier->Index); 845 } else { 846 fprintf (Fptr, "//#define %-40S 0x%04X // not referenced\n", Identifier->StringName, Identifier->Index); 847 } 848 849 Identifier = Identifier->Next; 850 } 851 852 fprintf (Fptr, "\n#endif\n"); 853 fclose (Fptr); 854 return STATUS_SUCCESS; 855 } 856 857 /*****************************************************************************/ 858 859 /*++ 860 861 Routine Description: 862 863 Add a string identifier to the database. 864 865 Arguments: 866 867 StringName - name of the string identifier. For example "STR_MY_STRING" 868 NewId - if an ID has been assigned 869 Flags - characteristics for the identifier 870 871 Returns: 872 873 STATUS 874 875 --*/ 876 STATUS 877 StringDBAddStringIdentifier ( 878 WCHAR *StringName, 879 UINT16 *NewId, 880 UINT16 Flags 881 ) 882 { 883 STRING_IDENTIFIER *StringIdentifier; 884 STATUS Status; 885 // 886 // If it was already used for some other language, then we don't 887 // need to add it. But set it to the current string identifier. 888 // The referenced bit is sticky. 889 // 890 Status = STATUS_SUCCESS; 891 StringIdentifier = StringDBFindStringIdentifierByName (StringName); 892 if (StringIdentifier != NULL) { 893 if (Flags & STRING_FLAGS_REFERENCED) { 894 StringIdentifier->Flags |= STRING_FLAGS_REFERENCED; 895 } 896 897 mDBData.CurrentStringIdentifier = StringIdentifier; 898 *NewId = (UINT16) StringIdentifier->Index; 899 return Status; 900 } 901 902 StringIdentifier = (STRING_IDENTIFIER *) MALLOC (sizeof (STRING_IDENTIFIER)); 903 if (StringIdentifier == NULL) { 904 Error (NULL, 0, 0, NULL, "memory allocation error"); 905 return STATUS_ERROR; 906 } 907 908 memset ((char *) StringIdentifier, 0, sizeof (STRING_IDENTIFIER)); 909 StringIdentifier->StringName = (WCHAR *) malloc ((wcslen (StringName) + 1) * sizeof (WCHAR)); 910 if (StringIdentifier->StringName == NULL) { 911 Error (NULL, 0, 0, NULL, "memory allocation error"); 912 return STATUS_ERROR; 913 } 914 915 wcscpy (StringIdentifier->StringName, StringName); 916 if (*NewId != STRING_ID_INVALID) { 917 StringIdentifier->Index = *NewId; 918 StringIdentifier->Flags |= STRING_FLAGS_INDEX_ASSIGNED; 919 if (mDBData.NumStringIdentifiers <= StringIdentifier->Index) { 920 mDBData.NumStringIdentifiers = StringIdentifier->Index + 1; 921 } 922 } else { 923 StringIdentifier->Index = mDBData.NumStringIdentifiers++; 924 } 925 926 StringIdentifier->Flags |= Flags; 927 // 928 // Add it to our list of string identifiers 929 // 930 if (mDBData.StringIdentifier == NULL) { 931 mDBData.StringIdentifier = StringIdentifier; 932 } else { 933 mDBData.LastStringIdentifier->Next = StringIdentifier; 934 } 935 936 mDBData.LastStringIdentifier = StringIdentifier; 937 mDBData.CurrentStringIdentifier = StringIdentifier; 938 *NewId = (UINT16) StringIdentifier->Index; 939 return Status; 940 } 941 942 /*****************************************************************************/ 943 944 /*++ 945 946 Routine Description: 947 948 Add a new string to the database. 949 950 Arguments: 951 952 LanguageName - "eng" or "spa" language name 953 StringName - "STR_MY_TEXT" string name 954 Scope - from the #scope statements in the string file 955 Format - if we should format the string 956 Flags - characteristic flags for the string 957 958 Returns: 959 960 STATUS 961 962 Notes: 963 964 Several of the fields can be "inherited" from the previous calls to 965 our database functions. For example, if scope is NULL here, then 966 we'll use the previous setting. 967 968 --*/ 969 STATUS 970 StringDBAddString ( 971 WCHAR *LanguageName, 972 WCHAR *StringName, 973 WCHAR *Scope, 974 WCHAR *String, 975 BOOLEAN Format, 976 UINT16 Flags 977 ) 978 { 979 LANGUAGE_LIST *Lang; 980 UINT32 Size; 981 STRING_LIST *Str; 982 UINT16 StringIndex; 983 WCHAR TempLangName[4]; 984 STRING_IDENTIFIER *StringIdentifier; 985 986 // 987 // Check that language name is exactly 3 characters, or emit an error. 988 // Truncate at 3 if it's longer, or make it 3 if it's shorter. 989 // 990 if (LanguageName != NULL) { 991 Size = wcslen (LanguageName); 992 if (Size != 3) { 993 ParserError (0, "invalid length for language name", "%S", LanguageName); 994 if (Size > 3) { 995 LanguageName[3] = 0; 996 } else { 997 // 998 // Make a local copy of the language name string, and extend to 999 // 3 characters since we make assumptions elsewhere in this program 1000 // on the length. 1001 // 1002 wcscpy (TempLangName, LanguageName); 1003 for (; Size < 3; Size++) { 1004 TempLangName[Size] = L'?'; 1005 } 1006 1007 TempLangName[3] = 0; 1008 LanguageName = TempLangName; 1009 } 1010 } 1011 } 1012 // 1013 // If they specified a language, make sure they've defined it already 1014 // via a #langdef statement. Otherwise use the current default language. 1015 // 1016 if (LanguageName != NULL) { 1017 Lang = StringDBFindLanguageList (LanguageName); 1018 if (Lang == NULL) { 1019 ParserError (0, "language not defined", "%S", LanguageName); 1020 return STATUS_ERROR; 1021 } else { 1022 StringDBSetCurrentLanguage (LanguageName); 1023 } 1024 } else { 1025 Lang = mDBData.CurrentLanguage; 1026 if (Lang == NULL) { 1027 // 1028 // Have to call SetLanguage() first 1029 // 1030 ParserError (0, "no language defined", "%S", StringName); 1031 return STATUS_ERROR; 1032 } 1033 } 1034 // 1035 // If they didn't define a string identifier, use the last string identifier 1036 // added. 1037 // 1038 if (StringName == NULL) { 1039 StringName = mDBData.CurrentStringIdentifier->StringName; 1040 if (StringName == NULL) { 1041 ParserError (0, "no string identifier previously specified", NULL); 1042 return STATUS_ERROR; 1043 } 1044 } 1045 // 1046 // If scope was not specified, use the default setting 1047 // 1048 if (Scope != NULL) { 1049 Scope = DuplicateString (Scope); 1050 } else { 1051 Scope = DuplicateString (mDBData.CurrentScope); 1052 } 1053 // 1054 // printf ("Adding string: %S.%S.%S\n", Lang->LanguageName, StringName, Scope); 1055 // 1056 // Check for duplicates for this Language.StringName.Scope. Allow multiple 1057 // definitions of the language name and printable language name, since the 1058 // user does not specifically define them. 1059 // 1060 if (StringDBFindString (Lang->LanguageName, StringName, Scope, NULL, NULL) != NULL) { 1061 if ((wcscmp (StringName, LANGUAGE_NAME_STRING_NAME) == 0) && 1062 (wcscmp (StringName, PRINTABLE_LANGUAGE_NAME_STRING_NAME) == 0) 1063 ) { 1064 ParserError ( 1065 0, 1066 "string multiply defined", 1067 "Language.Name.Scope = %S.%S.%S", 1068 Lang->LanguageName, 1069 StringName, 1070 Scope 1071 ); 1072 return STATUS_ERROR; 1073 } 1074 } 1075 1076 StringIndex = STRING_ID_INVALID; 1077 if (StringDBAddStringIdentifier (StringName, &StringIndex, Flags) != STATUS_SUCCESS) { 1078 return STATUS_ERROR; 1079 } 1080 1081 StringIdentifier = StringDBFindStringIdentifierByName (StringName); 1082 // 1083 // Add this string to the end of the strings for this language. 1084 // 1085 Str = (STRING_LIST *) malloc (sizeof (STRING_LIST)); 1086 if (Str == NULL) { 1087 Error (NULL, 0, 0, NULL, "memory allocation error"); 1088 return STATUS_ERROR; 1089 } 1090 1091 memset ((char *) Str, 0, sizeof (STRING_LIST)); 1092 Size = (wcslen (String) + 1) * sizeof (WCHAR); 1093 Str->Flags = Flags; 1094 Str->Scope = Scope; 1095 Str->StringName = StringIdentifier->StringName; 1096 Str->LanguageName = DuplicateString (LanguageName); 1097 Str->Str = (WCHAR *) MALLOC (Size); 1098 if (Str->Str == NULL) { 1099 Error (NULL, 0, 0, NULL, "memory allocation error"); 1100 return STATUS_ERROR; 1101 } 1102 // 1103 // If not formatting, just copy the string. 1104 // 1105 wcscpy (Str->Str, String); 1106 if (Format) { 1107 StringDBFormatString (Str->Str); 1108 } 1109 // 1110 // Size may change after formatting. We set the size to 1111 // the actual size of the string, including the null for 1112 // easier processing later. 1113 // 1114 Str->Size = (wcslen (Str->Str) + 1) * sizeof (WCHAR); 1115 if (Lang->String == NULL) { 1116 Lang->String = Str; 1117 } else { 1118 Lang->LastString->Next = Str; 1119 } 1120 1121 Lang->LastString = Str; 1122 return STATUS_SUCCESS; 1123 } 1124 1125 /*****************************************************************************/ 1126 1127 /*++ 1128 1129 Routine Description: 1130 1131 Given a language name, see if a language list for it has been defined 1132 1133 Arguments: 1134 1135 LanguageName - like "eng" 1136 1137 Returns: 1138 1139 A pointer to the language list 1140 1141 --*/ 1142 static 1143 LANGUAGE_LIST * 1144 StringDBFindLanguageList ( 1145 WCHAR *LanguageName 1146 ) 1147 { 1148 LANGUAGE_LIST *Lang; 1149 1150 Lang = mDBData.LanguageList; 1151 while (Lang != NULL) { 1152 if (wcscmp (LanguageName, Lang->LanguageName) == 0) { 1153 break; 1154 } 1155 1156 Lang = Lang->Next; 1157 } 1158 1159 return Lang; 1160 } 1161 1162 /*****************************************************************************/ 1163 STATUS 1164 StringDBSetCurrentLanguage ( 1165 WCHAR *LanguageName 1166 ) 1167 { 1168 LANGUAGE_LIST *Lang; 1169 1170 Lang = StringDBFindLanguageList (LanguageName); 1171 if (Lang == NULL) { 1172 ParserError (0, "language not previously defined", "%S", LanguageName); 1173 return STATUS_ERROR; 1174 } 1175 1176 mDBData.CurrentLanguage = Lang; 1177 return STATUS_SUCCESS; 1178 } 1179 1180 /*****************************************************************************/ 1181 STATUS 1182 StringDBAddLanguage ( 1183 WCHAR *LanguageName, 1184 WCHAR *PrintableLanguageName 1185 ) 1186 { 1187 LANGUAGE_LIST *Lang; 1188 // 1189 // Check for redefinitions 1190 // 1191 Lang = StringDBFindLanguageList (LanguageName); 1192 if (Lang != NULL) { 1193 // 1194 // Better be the same printable name 1195 // 1196 if (wcscmp (PrintableLanguageName, Lang->PrintableLanguageName) != 0) { 1197 ParserError ( 1198 0, 1199 "language redefinition", 1200 "%S:%S != %S:%S", 1201 Lang->LanguageName, 1202 Lang->PrintableLanguageName, 1203 LanguageName, 1204 PrintableLanguageName 1205 ); 1206 return STATUS_ERROR; 1207 // 1208 // } else { 1209 // ParserWarning (0, "benign language redefinition", "%S", PrintableLanguageName); 1210 // return STATUS_WARNING; 1211 // 1212 } 1213 } else { 1214 // 1215 // Allocate memory to keep track of this new language 1216 // 1217 Lang = (LANGUAGE_LIST *) malloc (sizeof (LANGUAGE_LIST)); 1218 if (Lang == NULL) { 1219 Error (NULL, 0, 0, NULL, "memory allocation error"); 1220 return STATUS_ERROR; 1221 } 1222 1223 memset ((char *) Lang, 0, sizeof (LANGUAGE_LIST)); 1224 // 1225 // Save the language name, then allocate memory to save the 1226 // printable language name 1227 // 1228 Lang->LanguageName[3] = 0; 1229 wcsncpy (Lang->LanguageName, LanguageName, 3); 1230 Lang->PrintableLanguageName = (WCHAR *) malloc ((wcslen (PrintableLanguageName) + 1) * sizeof (WCHAR)); 1231 if (Lang->PrintableLanguageName == NULL) { 1232 Error (NULL, 0, 0, NULL, "memory allocation error"); 1233 return STATUS_ERROR; 1234 } 1235 1236 wcscpy (Lang->PrintableLanguageName, PrintableLanguageName); 1237 1238 if (mDBData.LanguageList == NULL) { 1239 mDBData.LanguageList = Lang; 1240 } else { 1241 mDBData.LastLanguageList->Next = Lang; 1242 } 1243 1244 mDBData.LastLanguageList = Lang; 1245 } 1246 // 1247 // Default is to make our active language this new one 1248 // 1249 StringDBSetCurrentLanguage (LanguageName); 1250 // 1251 // The first two strings for any language are the language name, 1252 // followed by the printable language name. Add them and set them 1253 // to referenced so they never get stripped out. 1254 // 1255 StringDBAddString ( 1256 LanguageName, 1257 LANGUAGE_NAME_STRING_NAME, 1258 NULL, 1259 LanguageName, 1260 FALSE, 1261 STRING_FLAGS_REFERENCED 1262 ); 1263 StringDBAddString ( 1264 LanguageName, 1265 PRINTABLE_LANGUAGE_NAME_STRING_NAME, 1266 NULL, 1267 PrintableLanguageName, 1268 FALSE, 1269 STRING_FLAGS_REFERENCED 1270 ); 1271 return STATUS_SUCCESS; 1272 } 1273 1274 /*****************************************************************************/ 1275 static 1276 STRING_IDENTIFIER * 1277 StringDBFindStringIdentifierByName ( 1278 WCHAR *StringName 1279 ) 1280 { 1281 STRING_IDENTIFIER *Identifier; 1282 1283 Identifier = mDBData.StringIdentifier; 1284 while (Identifier != NULL) { 1285 if (wcscmp (StringName, Identifier->StringName) == 0) { 1286 return Identifier; 1287 } 1288 1289 Identifier = Identifier->Next; 1290 } 1291 1292 return NULL; 1293 } 1294 1295 static 1296 STRING_IDENTIFIER * 1297 StringDBFindStringIdentifierByIndex ( 1298 UINT32 StringIndex 1299 ) 1300 { 1301 STRING_IDENTIFIER *Identifier; 1302 1303 Identifier = mDBData.StringIdentifier; 1304 while (Identifier != NULL) { 1305 if (Identifier->Index == StringIndex) { 1306 return Identifier; 1307 } 1308 1309 Identifier = Identifier->Next; 1310 } 1311 1312 return NULL; 1313 } 1314 1315 /*****************************************************************************/ 1316 static 1317 void 1318 StringDBWriteStandardFileHeader ( 1319 FILE *OutFptr 1320 ) 1321 { 1322 UINT32 TempIndex; 1323 for (TempIndex = 0; mSourceFileHeader[TempIndex] != NULL; TempIndex++) { 1324 fprintf (OutFptr, "%s\n", mSourceFileHeader[TempIndex]); 1325 } 1326 } 1327 1328 /*****************************************************************************/ 1329 1330 /*++ 1331 1332 Routine Description: 1333 1334 Given a Unicode string from an input file, reformat the string to replace 1335 backslash control sequences with the appropriate encoding. 1336 1337 Arguments: 1338 1339 String - pointer to string to reformat 1340 1341 Returns: 1342 1343 Nothing 1344 1345 --*/ 1346 void 1347 StringDBFormatString ( 1348 WCHAR *String 1349 ) 1350 { 1351 WCHAR *From; 1352 WCHAR *To; 1353 int HexNibbles; 1354 WCHAR HexValue; 1355 // 1356 // Go through the string and process any formatting characters 1357 // 1358 From = String; 1359 To = String; 1360 while (*From) { 1361 if (*From == UNICODE_BACKSLASH) { 1362 // 1363 // First look for \wide and replace with the appropriate control character. Note that 1364 // when you have "define STR L"ABC"", then sizeof(ABC) is 8 because the null char is 1365 // counted. Make adjustments for this. We advance From below, so subtract 2 each time. 1366 // 1367 if (wcsncmp (From, UNICODE_WIDE_STRING, sizeof (UNICODE_WIDE_STRING) / sizeof (WCHAR) - 1) == 0) { 1368 *To = WIDE_CHAR; 1369 From += sizeof (UNICODE_WIDE_STRING) / sizeof (WCHAR) - 2; 1370 } else if (wcsncmp (From, UNICODE_NARROW_STRING, sizeof (UNICODE_NARROW_STRING) / sizeof (WCHAR) - 1) == 0) { 1371 // 1372 // Found: \narrow 1373 // 1374 *To = NARROW_CHAR; 1375 From += sizeof (UNICODE_NARROW_STRING) / sizeof (WCHAR) - 2; 1376 } else if (wcsncmp (From, UNICODE_NBR_STRING, sizeof (UNICODE_NBR_STRING) / sizeof (WCHAR) - 1) == 0) { 1377 // 1378 // Found: \nbr 1379 // 1380 *To = NON_BREAKING_CHAR; 1381 From += sizeof (UNICODE_NBR_STRING) / sizeof (WCHAR) - 2; 1382 } else if (wcsncmp (From, UNICODE_BR_STRING, sizeof (UNICODE_BR_STRING) / sizeof (WCHAR) - 1) == 0) { 1383 // 1384 // Found: \br -- pass through untouched 1385 // 1386 *To = *From; 1387 } else { 1388 // 1389 // Standard one-character control sequences such as \n, \r, \\, or \x 1390 // 1391 From++; 1392 switch (*From) { 1393 case ASCII_TO_UNICODE ('n'): 1394 *To = UNICODE_CR; 1395 To++; 1396 *To = UNICODE_LF; 1397 break; 1398 1399 // 1400 // carriage return 1401 // 1402 case ASCII_TO_UNICODE ('r'): 1403 *To = UNICODE_CR; 1404 break; 1405 1406 // 1407 // backslash 1408 // 1409 case UNICODE_BACKSLASH: 1410 *To = UNICODE_BACKSLASH; 1411 break; 1412 1413 // 1414 // Tab 1415 // 1416 case ASCII_TO_UNICODE ('t'): 1417 *To = UNICODE_TAB; 1418 break; 1419 1420 // 1421 // embedded double-quote 1422 // 1423 case UNICODE_DOUBLE_QUOTE: 1424 *To = UNICODE_DOUBLE_QUOTE; 1425 break; 1426 1427 // 1428 // Hex Unicode character \x1234. We'll process up to 4 hex characters 1429 // 1430 case ASCII_TO_UNICODE ('x'): 1431 HexValue = 0; 1432 for (HexNibbles = 0; HexNibbles < 4; HexNibbles++) { 1433 if ((From[1] >= UNICODE_0) && (From[1] <= UNICODE_9)) { 1434 HexValue = (HexValue << 4) | (From[1] - UNICODE_0); 1435 } else if ((From[1] >= UNICODE_a) && (From[1] <= UNICODE_f)) { 1436 HexValue = (HexValue << 4) | (10 + From[1] - UNICODE_a); 1437 } else if ((From[1] >= UNICODE_A) && (From[1] <= UNICODE_F)) { 1438 HexValue = (HexValue << 4) | (10 + From[1] - UNICODE_A); 1439 } else { 1440 break; 1441 } 1442 1443 From++; 1444 } 1445 1446 if (HexNibbles == 0) { 1447 ParserWarning ( 1448 0, 1449 "expected at least one valid hex digit with \\x escaped character in string", 1450 "\\%C", 1451 *From 1452 ); 1453 } else { 1454 *To = HexValue; 1455 } 1456 break; 1457 1458 default: 1459 *To = UNICODE_SPACE; 1460 ParserWarning (0, "invalid escaped character in string", "\\%C", *From); 1461 break; 1462 } 1463 } 1464 } else { 1465 *To = *From; 1466 } 1467 1468 From++; 1469 To++; 1470 } 1471 1472 *To = 0; 1473 } 1474 1475 /*****************************************************************************/ 1476 STATUS 1477 StringDBReadDatabase ( 1478 INT8 *DBFileName, 1479 BOOLEAN IgnoreIfNotExist, 1480 BOOLEAN Verbose 1481 ) 1482 { 1483 STRING_DB_HEADER DbHeader; 1484 STATUS Status; 1485 FILE *DBFptr; 1486 DB_DATA_ITEM_HEADER DataItemHeader; 1487 1488 Status = STATUS_SUCCESS; 1489 DBFptr = NULL; 1490 // 1491 // if (Verbose) { 1492 // fprintf (stdout, "Reading database file %s\n", DBFileName); 1493 // } 1494 // 1495 // Try to open the input file 1496 // 1497 if ((DBFptr = fopen (DBFileName, "rb")) == NULL) { 1498 if (IgnoreIfNotExist) { 1499 return STATUS_SUCCESS; 1500 } 1501 1502 Error (NULL, 0, 0, DBFileName, "failed to open input database file for reading"); 1503 return STATUS_ERROR; 1504 } 1505 // 1506 // Read and verify the database header 1507 // 1508 if (fread ((void *) &DbHeader, sizeof (STRING_DB_HEADER), 1, DBFptr) != 1) { 1509 Error (NULL, 0, 0, DBFileName, "failed to read header from database file"); 1510 Status = STATUS_ERROR; 1511 goto Finish; 1512 } 1513 1514 if (DbHeader.Key != STRING_DB_KEY) { 1515 Error (NULL, 0, 0, DBFileName, "invalid header in database file"); 1516 Status = STATUS_ERROR; 1517 goto Finish; 1518 } 1519 1520 if ((DbHeader.Version & STRING_DB_MAJOR_VERSION_MASK) != (STRING_DB_VERSION & STRING_DB_MAJOR_VERSION_MASK)) { 1521 Error (NULL, 0, 0, DBFileName, "incompatible database file version -- rebuild clean"); 1522 Status = STATUS_ERROR; 1523 goto Finish; 1524 } 1525 // 1526 // Read remaining items 1527 // 1528 while (fread (&DataItemHeader, sizeof (DataItemHeader), 1, DBFptr) == 1) { 1529 switch (DataItemHeader.DataType) { 1530 case DB_DATA_TYPE_STRING_IDENTIFIER: 1531 StringDBReadStringIdentifier (DBFptr); 1532 break; 1533 1534 case DB_DATA_TYPE_LANGUAGE_DEFINITION: 1535 StringDBReadLanguageDefinition (DBFptr); 1536 break; 1537 1538 case DB_DATA_TYPE_STRING_DEFINITION: 1539 StringDBReadString (DBFptr); 1540 break; 1541 1542 default: 1543 Error ( 1544 NULL, 1545 0, 1546 0, 1547 "database corrupted", 1548 "invalid data item type 0x%X at offset 0x%X", 1549 (UINT32) DataItemHeader.DataType, 1550 ftell (DBFptr) - sizeof (DataItemHeader) 1551 ); 1552 Status = STATUS_ERROR; 1553 goto Finish; 1554 } 1555 } 1556 1557 Finish: 1558 if (DBFptr != NULL) { 1559 fclose (DBFptr); 1560 } 1561 1562 return Status; 1563 } 1564 1565 /*****************************************************************************/ 1566 1567 /*++ 1568 1569 Routine Description: 1570 1571 Write everything we know to the output database file. Write: 1572 1573 Database header 1574 String identifiers[] 1575 StringPacks[] 1576 1577 Arguments: 1578 1579 DBFileName - name of the file to write to 1580 Verbose - for debug purposes, print info messages along the way. 1581 1582 Returns: 1583 1584 STATUS 1585 1586 --*/ 1587 STATUS 1588 StringDBWriteDatabase ( 1589 INT8 *DBFileName, 1590 BOOLEAN Verbose 1591 ) 1592 { 1593 STRING_DB_HEADER DbHeader; 1594 UINT32 Counter; 1595 UINT32 StrLen; 1596 LANGUAGE_LIST *Lang; 1597 STRING_IDENTIFIER *StringIdentifier; 1598 STRING_LIST *StrList; 1599 FILE *DBFptr; 1600 1601 if (Verbose) { 1602 fprintf (stdout, "Writing database %s\n", DBFileName); 1603 } 1604 1605 if ((DBFptr = fopen (DBFileName, "wb")) == NULL) { 1606 Error (NULL, 0, 0, DBFileName, "failed to open output database file for writing"); 1607 return STATUS_ERROR; 1608 } 1609 // 1610 // Fill in and write the database header 1611 // 1612 memset (&DbHeader, 0, sizeof (STRING_DB_HEADER)); 1613 DbHeader.HeaderSize = sizeof (STRING_DB_HEADER); 1614 DbHeader.Key = STRING_DB_KEY; 1615 DbHeader.Version = STRING_DB_VERSION; 1616 // 1617 // Count the number of languages we have 1618 // 1619 for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) { 1620 DbHeader.NumLanguages++; 1621 } 1622 // 1623 // Count up how many string identifiers we have, and total up the 1624 // size of the names plus the size of the flags field we will 1625 // write out too. 1626 // 1627 DbHeader.NumStringIdenfiers = mDBData.NumStringIdentifiers; 1628 StringIdentifier = mDBData.StringIdentifier; 1629 for (Counter = 0; Counter < mDBData.NumStringIdentifiers; Counter++) { 1630 StrLen = wcslen (StringIdentifier->StringName) + 1; 1631 DbHeader.StringIdentifiersSize += StrLen * sizeof (WCHAR) + sizeof (StringIdentifier->Flags); 1632 StringIdentifier = StringIdentifier->Next; 1633 } 1634 1635 // 1636 // Write the header 1637 // 1638 fwrite (&DbHeader, sizeof (STRING_DB_HEADER), 1, DBFptr); 1639 if (Verbose) { 1640 fprintf (stdout, " Number of string identifiers 0x%04X\n", DbHeader.NumStringIdenfiers); 1641 fprintf (stdout, " Number of languages %d\n", DbHeader.NumLanguages); 1642 } 1643 // 1644 // Write the string identifiers 1645 // 1646 for (StringIdentifier = mDBData.StringIdentifier; StringIdentifier != NULL; StringIdentifier = StringIdentifier->Next) { 1647 StringDBWriteStringIdentifier ( 1648 DBFptr, 1649 (UINT16) StringIdentifier->Index, 1650 StringIdentifier->Flags, 1651 StringIdentifier->StringName 1652 ); 1653 } 1654 // 1655 // Now write all the strings for each language 1656 // 1657 for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) { 1658 StringDBWriteLanguageDefinition (DBFptr, Lang->LanguageName, Lang->PrintableLanguageName); 1659 for (StrList = Lang->String; StrList != NULL; StrList = StrList->Next) { 1660 StringDBWriteString ( 1661 DBFptr, 1662 StrList->Flags, 1663 Lang->LanguageName, 1664 StrList->StringName, 1665 StrList->Scope, 1666 StrList->Str 1667 ); 1668 } 1669 } 1670 1671 fclose (DBFptr); 1672 return STATUS_SUCCESS; 1673 } 1674 1675 STATUS 1676 StringDBSetStringReferenced ( 1677 INT8 *StringIdentifierName, 1678 BOOLEAN IgnoreNotFound 1679 ) 1680 { 1681 STRING_IDENTIFIER *Id; 1682 WCHAR *WName; 1683 STATUS Status; 1684 // 1685 // See if it's already been defined. 1686 // 1687 Status = STATUS_SUCCESS; 1688 WName = (WCHAR *) malloc ((strlen (StringIdentifierName) + 1) * sizeof (WCHAR)); 1689 #ifdef USE_VC8 1690 swprintf (WName, (strlen (StringIdentifierName) + 1) * sizeof (WCHAR), L"%S", StringIdentifierName); 1691 #else 1692 swprintf (WName, L"%S", StringIdentifierName); 1693 #endif 1694 Id = StringDBFindStringIdentifierByName (WName); 1695 if (Id != NULL) { 1696 Id->Flags |= STRING_FLAGS_REFERENCED; 1697 } else { 1698 if (IgnoreNotFound == 0) { 1699 ParserWarning (0, StringIdentifierName, "string identifier not found in database"); 1700 Status = STATUS_WARNING; 1701 } 1702 } 1703 1704 free (WName); 1705 return Status; 1706 } 1707 1708 /*****************************************************************************/ 1709 1710 /*++ 1711 1712 Routine Description: 1713 1714 Dump the contents of a database to an output unicode file. 1715 1716 Arguments: 1717 1718 DBFileName - name of the pre-existing database file to read 1719 OutputFileName - name of the file to dump the database contents to 1720 Verbose - for printing of additional info useful for debugging 1721 1722 Returns: 1723 1724 STATUS 1725 1726 Notes: 1727 1728 There's some issue with the unicode printing routines. Therefore to 1729 write to the output file properly, open it as binary and use fwrite. 1730 Ideally we could open it with just L"w" and use fwprintf(). 1731 1732 --*/ 1733 STATUS 1734 StringDBDumpDatabase ( 1735 INT8 *DBFileName, 1736 INT8 *OutputFileName, 1737 BOOLEAN Verbose 1738 ) 1739 { 1740 LANGUAGE_LIST *Lang; 1741 STRING_IDENTIFIER *StringIdentifier; 1742 STRING_LIST *StrList; 1743 FILE *OutFptr; 1744 WCHAR WChar; 1745 WCHAR *WOutputFileName; 1746 WCHAR CrLf[2]; 1747 WCHAR Line[200]; 1748 WCHAR *Scope; 1749 // 1750 // This function assumes the database has already been read, and 1751 // we're just dumping our internal data structures to a unicode file. 1752 // 1753 if (Verbose) { 1754 fprintf (stdout, "Dumping database file %s\n", DBFileName); 1755 } 1756 1757 WOutputFileName = AsciiToWchar (OutputFileName); 1758 OutFptr = _wfopen (WOutputFileName, L"wb"); 1759 free (WOutputFileName); 1760 if (OutFptr == NULL) { 1761 Error (NULL, 0, 0, OutputFileName, "failed to open output file for writing"); 1762 return STATUS_ERROR; 1763 } 1764 1765 WChar = UNICODE_FILE_START; 1766 fwrite (&WChar, sizeof (WCHAR), 1, OutFptr); 1767 CrLf[1] = UNICODE_LF; 1768 CrLf[0] = UNICODE_CR; 1769 // 1770 // The default control character is '/'. Make it '#' by writing 1771 // "/=#" to the output file. 1772 // 1773 #ifdef USE_VC8 1774 swprintf (Line, wcslen(Line) * sizeof (WCHAR), L"/=#"); 1775 #else 1776 swprintf (Line, L"/=#"); 1777 #endif 1778 fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr); 1779 fwrite (&CrLf, sizeof (CrLf), 1, OutFptr); 1780 fwrite (&CrLf, sizeof (CrLf), 1, OutFptr); 1781 // 1782 // Dump all the string identifiers and their values 1783 // 1784 StringDBAssignStringIndexes (); 1785 for (StringIdentifier = mDBData.StringIdentifier; StringIdentifier != NULL; StringIdentifier = StringIdentifier->Next) { 1786 // 1787 // Write the "#define " string 1788 // 1789 if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) { 1790 #ifdef USE_VC8 1791 swprintf ( 1792 Line, 1793 wcslen(Line) * sizeof (WCHAR), 1794 L"%s %-60.60s 0x%04X", 1795 DEFINE_STR, 1796 StringIdentifier->StringName, 1797 StringIdentifier->Index 1798 ); 1799 #else 1800 swprintf ( 1801 Line, 1802 L"%s %-60.60s 0x%04X", 1803 DEFINE_STR, 1804 StringIdentifier->StringName, 1805 StringIdentifier->Index 1806 ); 1807 #endif 1808 } else { 1809 #ifdef USE_VC8 1810 swprintf ( 1811 Line, 1812 wcslen(Line) * sizeof (WCHAR), 1813 L"%s %-60.60s 0x%04X // NOT REFERENCED", 1814 DEFINE_STR, 1815 StringIdentifier->StringName, 1816 StringIdentifier->Index 1817 ); 1818 #else 1819 swprintf ( 1820 Line, 1821 L"%s %-60.60s 0x%04X // NOT REFERENCED", 1822 DEFINE_STR, 1823 StringIdentifier->StringName, 1824 StringIdentifier->Index 1825 ); 1826 #endif 1827 } 1828 1829 fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr); 1830 fwrite (&CrLf, sizeof (CrLf), 1, OutFptr); 1831 } 1832 1833 fwrite (&CrLf, sizeof (CrLf), 1, OutFptr); 1834 // 1835 // Now write all the strings for each language. 1836 // 1837 WChar = UNICODE_DOUBLE_QUOTE; 1838 Scope = NULL; 1839 for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) { 1840 fwrite (&CrLf, sizeof (CrLf), 1, OutFptr); 1841 #ifdef USE_VC8 1842 swprintf (Line, wcslen(Line) * sizeof (WCHAR), L"#langdef %s \"%s\"", Lang->LanguageName, Lang->PrintableLanguageName); 1843 #else 1844 swprintf (Line, L"#langdef %s \"%s\"", Lang->LanguageName, Lang->PrintableLanguageName); 1845 #endif 1846 fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr); 1847 fwrite (&CrLf, sizeof (CrLf), 1, OutFptr); 1848 fwrite (&CrLf, sizeof (CrLf), 1, OutFptr); 1849 // 1850 // Now the strings (in double-quotes) for this language. Write 1851 // #string STR_NAME #language eng "string" 1852 // 1853 for (StrList = Lang->String; StrList != NULL; StrList = StrList->Next) { 1854 // 1855 // Print the internal flags for debug 1856 // 1857 #ifdef USE_VC8 1858 swprintf (Line, wcslen(Line) * sizeof (WCHAR), L"// flags=0x%02X", (UINT32) StrList->Flags); 1859 #else 1860 swprintf (Line, L"// flags=0x%02X", (UINT32) StrList->Flags); 1861 #endif 1862 fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr); 1863 fwrite (&CrLf, sizeof (CrLf), 1, OutFptr); 1864 // 1865 // Print the scope if changed 1866 // 1867 if ((Scope == NULL) || (wcscmp (Scope, StrList->Scope) != 0)) { 1868 #ifdef USE_VC8 1869 swprintf (Line, wcslen(Line) * sizeof (WCHAR), L"#scope %s", StrList->Scope); 1870 #else 1871 swprintf (Line, L"#scope %s", StrList->Scope); 1872 #endif 1873 fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr); 1874 fwrite (&CrLf, sizeof (CrLf), 1, OutFptr); 1875 Scope = StrList->Scope; 1876 } 1877 1878 #ifdef USE_VC8 1879 swprintf ( 1880 Line, 1881 wcslen(Line) * sizeof (WCHAR), 1882 L"#string %-50.50s #language %s \"", 1883 StrList->StringName, 1884 Lang->LanguageName 1885 ); 1886 #else 1887 swprintf ( 1888 Line, 1889 L"#string %-50.50s #language %s \"", 1890 StrList->StringName, 1891 Lang->LanguageName 1892 ); 1893 #endif 1894 fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr); 1895 fwrite (StrList->Str, StrList->Size - sizeof (WCHAR), 1, OutFptr); 1896 #ifdef USE_VC8 1897 swprintf (Line, wcslen(Line) * sizeof (WCHAR), L"\""); 1898 #else 1899 swprintf (Line, L"\""); 1900 #endif 1901 fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr); 1902 fwrite (&CrLf, sizeof (CrLf), 1, OutFptr); 1903 } 1904 } 1905 1906 fclose (OutFptr); 1907 return STATUS_SUCCESS; 1908 } 1909 1910 /*****************************************************************************/ 1911 1912 /*++ 1913 1914 Routine Description: 1915 1916 Given a primary language, a string identifier number, and a list of 1917 languages, find a secondary string. 1918 1919 Arguments: 1920 1921 LanguageName - primary language, like "spa" 1922 StringId - string index value 1923 LanguageList - linked list of "eng", "spa+cat",... 1924 1925 Returns: 1926 1927 Pointer to a secondary string if found. NULL otherwise. 1928 1929 Notes: 1930 1931 Given: LanguageName "spa" and LanguageList "spa+cat", match the 1932 "spa" and extract the "cat" and see if there is a string defined 1933 for "cat".StringId. 1934 1935 --*/ 1936 static 1937 STATUS 1938 StringDBWriteStringIdentifier ( 1939 FILE *DBFptr, 1940 UINT16 StringId, 1941 UINT16 Flags, 1942 WCHAR *IdentifierName 1943 ) 1944 { 1945 DB_DATA_ITEM_HEADER Hdr; 1946 memset (&Hdr, 0, sizeof (DB_DATA_ITEM_HEADER)); 1947 Hdr.DataType = DB_DATA_TYPE_STRING_IDENTIFIER; 1948 if (fwrite (&Hdr, sizeof (DB_DATA_ITEM_HEADER), 1, DBFptr) != 1) { 1949 Error (NULL, 0, 0, "failed to write string to output database file", NULL); 1950 return STATUS_ERROR; 1951 } 1952 1953 if (fwrite (&StringId, sizeof (StringId), 1, DBFptr) != 1) { 1954 Error (NULL, 0, 0, "failed to write StringId to output database", NULL); 1955 return STATUS_ERROR; 1956 } 1957 1958 if (fwrite (&Flags, sizeof (Flags), 1, DBFptr) != 1) { 1959 Error (NULL, 0, 0, "failed to write StringId flags to output database", NULL); 1960 return STATUS_ERROR; 1961 } 1962 1963 if (StringDBWriteGenericString (DBFptr, IdentifierName) != STATUS_SUCCESS) { 1964 return STATUS_ERROR; 1965 } 1966 1967 return STATUS_SUCCESS; 1968 } 1969 1970 static 1971 STATUS 1972 StringDBReadStringIdentifier ( 1973 FILE *DBFptr 1974 ) 1975 { 1976 WCHAR *IdentifierName; 1977 UINT16 Flags; 1978 UINT16 StringId; 1979 UINT16 Size; 1980 1981 if (fread (&StringId, sizeof (StringId), 1, DBFptr) != 1) { 1982 Error (NULL, 0, 0, "failed to read StringId from database", NULL); 1983 return STATUS_ERROR; 1984 } 1985 1986 if (fread (&Flags, sizeof (Flags), 1, DBFptr) != 1) { 1987 Error (NULL, 0, 0, "failed to read StringId flags from database", NULL); 1988 return STATUS_ERROR; 1989 } 1990 1991 if (StringDBReadGenericString (DBFptr, &Size, &IdentifierName) != STATUS_SUCCESS) { 1992 return STATUS_ERROR; 1993 } 1994 1995 StringDBAddStringIdentifier (IdentifierName, &StringId, Flags); 1996 // 1997 // printf ("STRID: 0x%04X %S\n", (UINT32)StringId, IdentifierName); 1998 // 1999 FREE (IdentifierName); 2000 return STATUS_SUCCESS; 2001 } 2002 2003 static 2004 STATUS 2005 StringDBWriteString ( 2006 FILE *DBFptr, 2007 UINT16 Flags, 2008 WCHAR *Language, 2009 WCHAR *StringName, 2010 WCHAR *Scope, 2011 WCHAR *Str 2012 ) 2013 { 2014 DB_DATA_ITEM_HEADER Hdr; 2015 memset (&Hdr, 0, sizeof (DB_DATA_ITEM_HEADER)); 2016 Hdr.DataType = DB_DATA_TYPE_STRING_DEFINITION; 2017 if (fwrite (&Hdr, sizeof (DB_DATA_ITEM_HEADER), 1, DBFptr) != 1) { 2018 Error (NULL, 0, 0, "failed to write string header to output database file", NULL); 2019 return STATUS_ERROR; 2020 } 2021 2022 if (fwrite (&Flags, sizeof (Flags), 1, DBFptr) != 1) { 2023 Error (NULL, 0, 0, "failed to write string flags to output database", NULL); 2024 return STATUS_ERROR; 2025 } 2026 2027 if (StringDBWriteGenericString (DBFptr, Language) != STATUS_SUCCESS) { 2028 return STATUS_ERROR; 2029 } 2030 2031 if (StringDBWriteGenericString (DBFptr, StringName) != STATUS_SUCCESS) { 2032 return STATUS_ERROR; 2033 } 2034 2035 if (StringDBWriteGenericString (DBFptr, Scope) != STATUS_SUCCESS) { 2036 return STATUS_ERROR; 2037 } 2038 2039 if (StringDBWriteGenericString (DBFptr, Str) != STATUS_SUCCESS) { 2040 return STATUS_ERROR; 2041 } 2042 // 2043 // printf ("DBWriteString: %S.%S.%S\n", Language, StringName, Scope); 2044 // 2045 return STATUS_SUCCESS; 2046 } 2047 2048 static 2049 STATUS 2050 StringDBReadString ( 2051 FILE *DBFptr 2052 ) 2053 { 2054 UINT16 Flags; 2055 UINT16 Size; 2056 WCHAR *Language; 2057 WCHAR *StringName; 2058 WCHAR *Scope; 2059 WCHAR *Str; 2060 2061 if (fread (&Flags, sizeof (Flags), 1, DBFptr) != 1) { 2062 Error (NULL, 0, 0, "failed to read string flags from database", NULL); 2063 return STATUS_ERROR; 2064 } 2065 2066 if (StringDBReadGenericString (DBFptr, &Size, &Language) != STATUS_SUCCESS) { 2067 return STATUS_ERROR; 2068 } 2069 2070 if (StringDBReadGenericString (DBFptr, &Size, &StringName) != STATUS_SUCCESS) { 2071 return STATUS_ERROR; 2072 } 2073 2074 if (StringDBReadGenericString (DBFptr, &Size, &Scope) != STATUS_SUCCESS) { 2075 return STATUS_ERROR; 2076 } 2077 2078 if (StringDBReadGenericString (DBFptr, &Size, &Str) != STATUS_SUCCESS) { 2079 return STATUS_ERROR; 2080 } 2081 // 2082 // If the first or second string (language name and printable language name), 2083 // then skip them. They're added via language definitions data items in 2084 // the database. 2085 // 2086 if (StringName[0] != L'$') { 2087 StringDBAddString (Language, StringName, Scope, Str, FALSE, Flags); 2088 } 2089 // 2090 // printf ("DBReadString: %S.%S.%S\n", Language, StringName, Scope); 2091 // 2092 FREE (Language); 2093 FREE (StringName); 2094 if (Str != NULL) { 2095 FREE (Str); 2096 } 2097 2098 if (Scope != NULL) { 2099 FREE (Scope); 2100 } 2101 2102 return STATUS_SUCCESS; 2103 } 2104 2105 static 2106 STATUS 2107 StringDBWriteLanguageDefinition ( 2108 FILE *DBFptr, 2109 WCHAR *LanguageName, 2110 WCHAR *PrintableLanguageName 2111 ) 2112 { 2113 DB_DATA_ITEM_HEADER Hdr; 2114 memset (&Hdr, 0, sizeof (DB_DATA_ITEM_HEADER)); 2115 Hdr.DataType = DB_DATA_TYPE_LANGUAGE_DEFINITION; 2116 if (fwrite (&Hdr, sizeof (DB_DATA_ITEM_HEADER), 1, DBFptr) != 1) { 2117 Error (NULL, 0, 0, "failed to write string to output database file", NULL); 2118 return STATUS_ERROR; 2119 } 2120 2121 if (StringDBWriteGenericString (DBFptr, LanguageName) != STATUS_SUCCESS) { 2122 return STATUS_ERROR; 2123 } 2124 2125 if (StringDBWriteGenericString (DBFptr, PrintableLanguageName) != STATUS_SUCCESS) { 2126 return STATUS_ERROR; 2127 } 2128 2129 return STATUS_SUCCESS; 2130 } 2131 2132 static 2133 STATUS 2134 StringDBReadLanguageDefinition ( 2135 FILE *DBFptr 2136 ) 2137 { 2138 WCHAR *LanguageName; 2139 WCHAR *PrintableLanguageName; 2140 UINT16 Size; 2141 STATUS Status; 2142 2143 if (StringDBReadGenericString (DBFptr, &Size, &LanguageName) != STATUS_SUCCESS) { 2144 return STATUS_ERROR; 2145 } 2146 2147 if (StringDBReadGenericString (DBFptr, &Size, &PrintableLanguageName) != STATUS_SUCCESS) { 2148 return STATUS_ERROR; 2149 } 2150 // 2151 // printf("LANG: %S %S\n", LanguageName, PrintableLanguageName); 2152 // 2153 Status = StringDBAddLanguage (LanguageName, PrintableLanguageName); 2154 FREE (LanguageName); 2155 FREE (PrintableLanguageName); 2156 return Status; 2157 } 2158 // 2159 // All unicode strings in the database consist of a UINT16 length 2160 // field, followed by the string itself. This routine reads one 2161 // of those and returns the info. 2162 // 2163 static 2164 STATUS 2165 StringDBReadGenericString ( 2166 FILE *DBFptr, 2167 UINT16 *Size, 2168 WCHAR **Str 2169 ) 2170 { 2171 UINT16 LSize; 2172 UINT16 Flags; 2173 WCHAR *LStr; 2174 2175 if (fread (&LSize, sizeof (UINT16), 1, DBFptr) != 1) { 2176 Error (NULL, 0, 0, "failed to read a string length field from the database", NULL); 2177 return STATUS_ERROR; 2178 } 2179 2180 if (fread (&Flags, sizeof (UINT16), 1, DBFptr) != 1) { 2181 Error (NULL, 0, 0, "failed to read a string flags field from the database", NULL); 2182 return STATUS_ERROR; 2183 } 2184 2185 LStr = MALLOC (LSize); 2186 if (LStr == NULL) { 2187 Error (__FILE__, __LINE__, 0, "memory allocation failed reading the database", NULL); 2188 return STATUS_ERROR; 2189 } 2190 2191 if (fread (LStr, sizeof (WCHAR), (UINT32) LSize / sizeof (WCHAR), DBFptr) != (UINT32) LSize / sizeof (WCHAR)) { 2192 Error (NULL, 0, 0, "failed to read string from database", NULL); 2193 Error (NULL, 0, 0, "database read failure", "offset 0x%X", ftell (DBFptr)); 2194 free (LStr); 2195 return STATUS_ERROR; 2196 } 2197 // 2198 // printf ("DBR: %S\n", LStr); 2199 // 2200 // If the flags field indicated we were asked to write a NULL string, then 2201 // return them a NULL pointer. 2202 // 2203 if (Flags & STRING_FLAGS_UNDEFINED) { 2204 *Size = 0; 2205 *Str = NULL; 2206 } else { 2207 *Size = LSize; 2208 *Str = LStr; 2209 } 2210 2211 return STATUS_SUCCESS; 2212 } 2213 2214 static 2215 STATUS 2216 StringDBWriteGenericString ( 2217 FILE *DBFptr, 2218 WCHAR *Str 2219 ) 2220 { 2221 UINT16 Size; 2222 UINT16 Flags; 2223 WCHAR ZeroString[1]; 2224 // 2225 // Strings in the database consist of a size UINT16 followed 2226 // by the string itself. 2227 // 2228 if (Str == NULL) { 2229 ZeroString[0] = 0; 2230 Str = ZeroString; 2231 Size = sizeof (ZeroString); 2232 Flags = STRING_FLAGS_UNDEFINED; 2233 } else { 2234 Flags = 0; 2235 Size = (UINT16) ((wcslen (Str) + 1) * sizeof (WCHAR)); 2236 } 2237 2238 if (fwrite (&Size, sizeof (UINT16), 1, DBFptr) != 1) { 2239 Error (NULL, 0, 0, "failed to write string size to database", NULL); 2240 return STATUS_ERROR; 2241 } 2242 2243 if (fwrite (&Flags, sizeof (UINT16), 1, DBFptr) != 1) { 2244 Error (NULL, 0, 0, "failed to write string flags to database", NULL); 2245 return STATUS_ERROR; 2246 } 2247 2248 if (fwrite (Str, sizeof (WCHAR), Size / sizeof (WCHAR), DBFptr) != Size / sizeof (WCHAR)) { 2249 Error (NULL, 0, 0, "failed to write string to database", NULL); 2250 return STATUS_ERROR; 2251 } 2252 2253 return STATUS_SUCCESS; 2254 } 2255 2256 static 2257 STRING_LIST * 2258 StringDBFindString ( 2259 WCHAR *LanguageName, 2260 WCHAR *StringName, 2261 WCHAR *Scope, 2262 WCHAR_STRING_LIST *LanguagesOfInterest, 2263 WCHAR_MATCHING_STRING_LIST *IndirectionList 2264 ) 2265 { 2266 LANGUAGE_LIST *Lang; 2267 STRING_LIST *CurrString; 2268 WCHAR_MATCHING_STRING_LIST *IndListPtr; 2269 WCHAR TempLangName[LANGUAGE_IDENTIFIER_NAME_LEN + 1]; 2270 WCHAR *WCharPtr; 2271 2272 // 2273 // If we were given an indirection list, then see if one was specified for this 2274 // string identifier. That is to say, if the indirection says "STR_ID_MY_FAVORITE MyScope", 2275 // then if this string name matches one in the list, then do a lookup with the 2276 // specified scope and return that value. 2277 // 2278 if (IndirectionList != NULL) { 2279 for (IndListPtr = IndirectionList; IndListPtr != NULL; IndListPtr = IndListPtr->Next) { 2280 if (wcscmp (StringName, IndListPtr->Str1) == 0) { 2281 CurrString = StringDBFindString (LanguageName, StringName, IndListPtr->Str2, LanguagesOfInterest, NULL); 2282 if (CurrString != NULL) { 2283 return CurrString; 2284 } 2285 } 2286 } 2287 } 2288 // 2289 // First look for exact match language.stringname 2290 // 2291 for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) { 2292 if (wcscmp (LanguageName, Lang->LanguageName) == 0) { 2293 // 2294 // Found language match. Try to find string name match 2295 // 2296 for (CurrString = Lang->String; CurrString != NULL; CurrString = CurrString->Next) { 2297 if (wcscmp (StringName, CurrString->StringName) == 0) { 2298 // 2299 // Found a string name match. See if we're supposed to find 2300 // a scope match. 2301 // 2302 if (Scope != NULL) { 2303 if (wcscmp (CurrString->Scope, Scope) == 0) { 2304 return CurrString; 2305 } 2306 } else { 2307 return CurrString; 2308 } 2309 } 2310 } 2311 } 2312 } 2313 // 2314 // If we got here, then we didn't find a match. Look for secondary string 2315 // matches. That is to say, if we're processing "spa", and they requested 2316 // "spa+cat", then recursively call with "cat" 2317 // 2318 while (LanguagesOfInterest != NULL) { 2319 // 2320 // If this is the language we're looking for, then process the 2321 // languages of interest list for it. 2322 // 2323 if (wcsncmp (LanguageName, LanguagesOfInterest->Str, LANGUAGE_IDENTIFIER_NAME_LEN) == 0) { 2324 WCharPtr = LanguagesOfInterest->Str + LANGUAGE_IDENTIFIER_NAME_LEN; 2325 while (*WCharPtr) { 2326 // 2327 // Double-check the length, though it should have been checked on the 2328 // command line. 2329 // 2330 if (wcslen (WCharPtr) < LANGUAGE_IDENTIFIER_NAME_LEN) { 2331 Error (NULL, 0, 0, "malformed alternate language list", "%S", LanguagesOfInterest->Str); 2332 return NULL; 2333 } 2334 2335 wcsncpy (TempLangName, WCharPtr, LANGUAGE_IDENTIFIER_NAME_LEN); 2336 TempLangName[LANGUAGE_IDENTIFIER_NAME_LEN] = 0; 2337 CurrString = StringDBFindString (TempLangName, StringName, NULL, NULL, IndirectionList); 2338 if (CurrString != NULL) { 2339 return CurrString; 2340 } 2341 2342 WCharPtr += LANGUAGE_IDENTIFIER_NAME_LEN; 2343 } 2344 } 2345 2346 LanguagesOfInterest = LanguagesOfInterest->Next; 2347 } 2348 2349 return NULL; 2350 } 2351 2352 STATUS 2353 StringDBSetScope ( 2354 WCHAR *Scope 2355 ) 2356 { 2357 // 2358 // Free up existing scope memory. 2359 // 2360 if (mDBData.CurrentScope != NULL) { 2361 FREE (mDBData.CurrentScope); 2362 } 2363 2364 mDBData.CurrentScope = DuplicateString (Scope); 2365 return STATUS_SUCCESS; 2366 } 2367 // 2368 // We typically don't assign index values to string identifiers 2369 // until we're ready to write out files. To reduce the size of 2370 // the output file, re-order the string identifiers to move any 2371 // unreferenced ones to the end. Then we'll walk the list 2372 // again to assign string indexes, keeping track of the last 2373 // one referenced. 2374 // 2375 static 2376 void 2377 StringDBAssignStringIndexes ( 2378 VOID 2379 ) 2380 { 2381 STRING_IDENTIFIER *StrId; 2382 STRING_IDENTIFIER *FirstUsed; 2383 STRING_IDENTIFIER *LastUsed; 2384 STRING_IDENTIFIER *FirstUnused; 2385 STRING_IDENTIFIER *LastUnused; 2386 UINT32 Index; 2387 UINT32 MaxReferenced; 2388 2389 // 2390 // Create two lists -- used and unused. Then put them together with 2391 // the unused ones on the end. 2392 // 2393 FirstUsed = NULL; 2394 LastUsed = NULL; 2395 FirstUnused = NULL; 2396 LastUnused = NULL; 2397 StrId = mDBData.StringIdentifier; 2398 while (StrId != NULL) { 2399 if ((StrId->Flags & STRING_FLAGS_REFERENCED) == 0) { 2400 // 2401 // Put it on the unused list 2402 // 2403 if (FirstUnused == NULL) { 2404 FirstUnused = StrId; 2405 } else { 2406 LastUnused->Next = StrId; 2407 } 2408 2409 LastUnused = StrId; 2410 StrId = StrId->Next; 2411 LastUnused->Next = NULL; 2412 } else { 2413 // 2414 // Put it on the used list 2415 // 2416 if (FirstUsed == NULL) { 2417 FirstUsed = StrId; 2418 } else { 2419 LastUsed->Next = StrId; 2420 } 2421 2422 LastUsed = StrId; 2423 StrId = StrId->Next; 2424 LastUsed->Next = NULL; 2425 } 2426 } 2427 // 2428 // Join the lists 2429 // 2430 if (FirstUsed != NULL) { 2431 mDBData.StringIdentifier = FirstUsed; 2432 LastUsed->Next = FirstUnused; 2433 } else { 2434 mDBData.StringIdentifier = FirstUnused; 2435 } 2436 2437 MaxReferenced = 0; 2438 Index = 0; 2439 for (StrId = mDBData.StringIdentifier; StrId != NULL; StrId = StrId->Next) { 2440 StrId->Index = Index; 2441 Index++; 2442 if (StrId->Flags & STRING_FLAGS_REFERENCED) { 2443 mDBData.NumStringIdentifiersReferenced = Index; 2444 } 2445 } 2446 2447 mDBData.NumStringIdentifiers = Index; 2448 } 2449 2450 static 2451 WCHAR * 2452 DuplicateString ( 2453 WCHAR *Str 2454 ) 2455 { 2456 WCHAR *NewStr; 2457 if (Str == NULL) { 2458 return NULL; 2459 } 2460 2461 NewStr = MALLOC ((wcslen (Str) + 1) * sizeof (WCHAR)); 2462 if (NewStr == NULL) { 2463 Error (NULL, 0, 0, "memory allocation failure", NULL); 2464 return NULL; 2465 } 2466 2467 wcscpy (NewStr, Str); 2468 return NewStr; 2469 } 2470 2471 static 2472 WCHAR * 2473 AsciiToWchar ( 2474 INT8 *Str 2475 ) 2476 { 2477 UINT32 Len; 2478 WCHAR *NewStr; 2479 WCHAR *Ptr; 2480 2481 Len = strlen (Str) + 1; 2482 NewStr = (WCHAR *) malloc (Len * sizeof (WCHAR)); 2483 for (Ptr = NewStr; *Str != 0; Str++, Ptr++) { 2484 *Ptr = (UINT16) (UINT8) *Str; 2485 } 2486 2487 *Ptr = 0; 2488 return NewStr; 2489 } 2490 2491 /*****************************************************************************/ 2492 2493 /*++ 2494 2495 Routine Description: 2496 2497 Create an HII export string pack for the strings in our database. 2498 2499 Arguments: 2500 2501 FileName - name of the output file to write 2502 2503 Returns: 2504 2505 STATUS 2506 2507 2508 --*/ 2509 STATUS 2510 StringDBCreateHiiExportPack ( 2511 INT8 *FileName, 2512 WCHAR_STRING_LIST *LanguagesOfInterest 2513 ) 2514 { 2515 FILE *Fptr; 2516 LANGUAGE_LIST *Lang; 2517 STRING_LIST *CurrString; 2518 STRING_LIST EmptyString; 2519 UINT32 Offset; 2520 UINT32 StringIndex; 2521 UINT32 TempIndex; 2522 EFI_HII_STRING_PACK_HEADER StringPack; 2523 UINT32 Len; 2524 WCHAR ZeroString[1]; 2525 WCHAR *TempStringPtr; 2526 WCHAR *LangName; 2527 STRING_IDENTIFIER *StringIdentifier; 2528 WCHAR_STRING_LIST *LOIPtr; 2529 BOOLEAN LanguageOk; 2530 2531 2532 if ((Fptr = fopen (FileName, "wb")) == NULL) { 2533 Error (NULL, 0, 0, FileName, "failed to open output HII export file"); 2534 return STATUS_ERROR; 2535 } 2536 // 2537 // Assign index values to the string identifiers 2538 // 2539 StringDBAssignStringIndexes (); 2540 // 2541 // If a given string is not defined, then we'll use this one. 2542 // 2543 memset (&EmptyString, 0, sizeof (EmptyString)); 2544 EmptyString.Size = sizeof (ZeroString); 2545 EmptyString.Str = ZeroString; 2546 // 2547 // Process each language, then each string for each langage 2548 // 2549 ZeroString[0] = 0; 2550 for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) { 2551 // 2552 // If we have a language list, then make sure this language is in that 2553 // list. 2554 // 2555 LanguageOk = TRUE; 2556 if (LanguagesOfInterest != NULL) { 2557 LanguageOk = FALSE; 2558 for (LOIPtr = LanguagesOfInterest; LOIPtr != NULL; LOIPtr = LOIPtr->Next) { 2559 if (wcsncmp (LOIPtr->Str, Lang->LanguageName, LANGUAGE_IDENTIFIER_NAME_LEN) == 0) { 2560 LanguageOk = TRUE; 2561 break; 2562 } 2563 } 2564 } 2565 2566 if (!LanguageOk) { 2567 continue; 2568 } 2569 2570 // 2571 // Process each string for this language. We have to make 3 passes on the strings: 2572 // Pass1: computes sizes and fill in the string pack header 2573 // Pass2: write the array of offsets 2574 // Pass3: write the strings 2575 // 2576 // 2577 // PASS 1: Fill in and print the HII string pack header 2578 // 2579 // Compute the size for this language package and write 2580 // the header out. Each string package contains: 2581 // Header 2582 // Offset[] -- an array of offsets to strings, of type RELOFST each 2583 // String[] -- the actual strings themselves 2584 // 2585 memset ((char *) &StringPack, 0, sizeof (EFI_HII_STRING_PACK_HEADER)); 2586 StringPack.Header.Type = EFI_HII_STRING; 2587 StringPack.NumStringPointers = (UINT16) mDBData.NumStringIdentifiersReferenced; 2588 LangName = Lang->LanguageName; 2589 // 2590 // First string is the language name. If we're printing all languages, then 2591 // it's just the "spa". If we were given a list of languages to print, then it's 2592 // the "spacat" string. Compute its offset and fill in 2593 // the info in the header. Since we know the language name string's length, 2594 // and the printable language name follows it, use that info to fill in the 2595 // entry for the printable language name as well. 2596 // 2597 StringPack.LanguageNameString = (STRING_OFFSET) (sizeof (EFI_HII_STRING_PACK_HEADER) + (mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET))); 2598 StringPack.PrintableLanguageName = (STRING_OFFSET) (StringPack.LanguageNameString + (wcslen (LangName) + 1) * sizeof (WCHAR)); 2599 // 2600 // Add up the size of all strings so we can fill in our header. 2601 // 2602 Len = 0; 2603 for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) { 2604 // 2605 // For the first string (language name), we print out the "spacat" if they 2606 // requested it. We set LangName to point to the proper language name string above. 2607 // 2608 if (StringIndex == STRING_ID_LANGUAGE_NAME) { 2609 Len += (wcslen (LangName) + 1) * sizeof (WCHAR); 2610 } else { 2611 // 2612 // Find a string with this language.stringname 2613 // 2614 StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex); 2615 if (StringIdentifier == NULL) { 2616 Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex); 2617 return STATUS_ERROR; 2618 } 2619 // 2620 // Find a matching string if this string identifier was referenced 2621 // 2622 EmptyString.Flags = STRING_FLAGS_UNDEFINED; 2623 CurrString = NULL; 2624 if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) { 2625 CurrString = StringDBFindString ( 2626 Lang->LanguageName, 2627 StringIdentifier->StringName, 2628 NULL, 2629 NULL, // LanguagesOfInterest, 2630 NULL 2631 ); 2632 // 2633 // IndirectionList); 2634 // 2635 if (NULL == CurrString) { 2636 // 2637 // If string for Lang->LanguageName is not found, try to get an English version 2638 // 2639 CurrString = StringDBFindString ( 2640 L"eng", 2641 StringIdentifier->StringName, 2642 NULL, 2643 NULL, // LanguagesOfInterest, 2644 NULL 2645 ); 2646 // 2647 // IndirectionList); 2648 // 2649 } 2650 } 2651 2652 if (CurrString == NULL) { 2653 CurrString = &EmptyString; 2654 EmptyString.Flags |= StringIdentifier->Flags; 2655 } 2656 2657 Len += CurrString->Size; 2658 } 2659 } 2660 StringPack.Header.Length = sizeof (EFI_HII_STRING_PACK_HEADER) 2661 + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET) 2662 + Len; 2663 // 2664 // Write out the string pack header 2665 // 2666 fwrite ((void *) &StringPack, sizeof (StringPack), 1, Fptr); 2667 // 2668 // PASS2 : write the offsets 2669 // 2670 // Traverse the list of strings again and write the array of offsets. The 2671 // offset to the first string is the size of the string pack header 2672 // plus the size of the offsets array. The other strings follow it. 2673 // 2674 StringIndex = 0; 2675 Offset = sizeof (StringPack) + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET); 2676 for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) { 2677 // 2678 // Write the offset 2679 // 2680 fwrite (&Offset, sizeof (STRING_OFFSET), 1, Fptr); 2681 // 2682 // Find the string name 2683 // 2684 StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex); 2685 if (StringIdentifier == NULL) { 2686 Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex); 2687 return STATUS_ERROR; 2688 } 2689 // 2690 // For the first string (language name), we print out the "spacat" if they 2691 // requested it. We set LangName to point to the proper language name string above. 2692 // 2693 if (StringIndex == STRING_ID_LANGUAGE_NAME) { 2694 Offset += (wcslen (LangName) + 1) * sizeof (WCHAR); 2695 CurrString = StringDBFindString ( 2696 Lang->LanguageName, 2697 StringIdentifier->StringName, 2698 NULL, // scope 2699 NULL, 2700 NULL 2701 ); 2702 } else { 2703 // 2704 // Find a matching string 2705 // 2706 CurrString = StringDBFindString ( 2707 Lang->LanguageName, 2708 StringIdentifier->StringName, 2709 NULL, // scope 2710 NULL, // LanguagesOfInterest, 2711 NULL 2712 ); 2713 // 2714 // IndirectionList); 2715 // 2716 if (NULL == CurrString) { 2717 CurrString = StringDBFindString ( 2718 L"eng", 2719 StringIdentifier->StringName, 2720 NULL, // scope 2721 NULL, // LanguagesOfInterest, 2722 NULL 2723 ); 2724 // 2725 // IndirectionList); 2726 // 2727 } 2728 2729 EmptyString.LanguageName = Lang->LanguageName; 2730 if (CurrString == NULL) { 2731 CurrString = &EmptyString; 2732 EmptyString.Flags = STRING_FLAGS_UNDEFINED; 2733 } else if ((StringIdentifier->Flags & STRING_FLAGS_REFERENCED) == 0) { 2734 CurrString = &EmptyString; 2735 EmptyString.Flags = 0; 2736 } 2737 2738 Offset += CurrString->Size; 2739 } 2740 } 2741 2742 // 2743 // PASS 3: write the strings themselves. 2744 // 2745 Offset = sizeof (StringPack) + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET); 2746 for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) { 2747 StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex); 2748 if (StringIdentifier == NULL) { 2749 Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex); 2750 return STATUS_ERROR; 2751 } 2752 // 2753 // For the first string (language name), we print out the "spacat" if they 2754 // requested it. We set LangName to point to the proper language name string above. 2755 // 2756 if (StringIndex == STRING_ID_LANGUAGE_NAME) { 2757 TempStringPtr = LangName; 2758 } else { 2759 // 2760 // Find a matching string if this string identifier was referenced 2761 // 2762 CurrString = NULL; 2763 if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) { 2764 CurrString = StringDBFindString ( 2765 Lang->LanguageName, 2766 StringIdentifier->StringName, 2767 NULL, // scope 2768 NULL, // LanguagesOfInterest, 2769 NULL 2770 ); 2771 // 2772 // IndirectionList); 2773 // 2774 if (NULL == CurrString) { 2775 CurrString = StringDBFindString ( 2776 L"eng", 2777 StringIdentifier->StringName, 2778 NULL, // scope 2779 NULL, // LanguagesOfInterest, 2780 NULL 2781 ); 2782 // 2783 // IndirectionList); 2784 // 2785 } 2786 } 2787 2788 if (CurrString == NULL) { 2789 CurrString = &EmptyString; 2790 } 2791 2792 TempStringPtr = CurrString->Str; 2793 } 2794 2795 for (TempIndex = 0; TempStringPtr[TempIndex] != 0; TempIndex++) { 2796 fwrite (&TempStringPtr[TempIndex], sizeof (CHAR16), 1, Fptr); 2797 Offset += 2; 2798 } 2799 // 2800 // Print NULL WCHAR at the end of this string. 2801 // 2802 TempIndex = 0; 2803 fwrite (&TempIndex, sizeof (CHAR16), 1, Fptr); 2804 Offset += 2; 2805 } 2806 // 2807 // Sanity check the offset. Make sure our running offset is what we put in the 2808 // string pack header. 2809 // 2810 if (StringPack.Header.Length != Offset) { 2811 Error ( 2812 __FILE__, 2813 __LINE__, 2814 0, 2815 "application error", 2816 "stringpack size 0x%X does not match final size 0x%X", 2817 StringPack.Header.Length, 2818 Offset 2819 ); 2820 } 2821 } 2822 // 2823 // Print terminator string pack, closing brace and close the file. 2824 // The size of 0 triggers to the consumer that this is the end. 2825 // 2826 memset ((char *) &StringPack, 0, sizeof (EFI_HII_STRING_PACK_HEADER)); 2827 StringPack.Header.Type = EFI_HII_STRING; 2828 fwrite ((void *) &StringPack, sizeof (StringPack), 1, Fptr); 2829 fclose (Fptr); 2830 return STATUS_SUCCESS; 2831 } 2832