3 Copyright (c) 2004, Intel Corporation
4 All rights reserved. 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
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.
18 String database implementation
25 #include <ctype.h> // for tolower()
27 #include <UefiBaseTypes.h>
28 #include <MultiPhase.h>
30 #include "EfiUtilityMsgs.h"
31 #include "StrGather.h"
33 #include "InternalFormRepresentation.h"
35 // #include EFI_PROTOCOL_DEFINITION (Hii)
39 #define STRING_OFFSET RELOFST
41 #define STRING_DB_KEY (('S' << 24) | ('D' << 16) | ('B' << 8) | 'K')
43 // Version supported by this tool
45 #define STRING_DB_VERSION 0x00010000
47 #define STRING_DB_MAJOR_VERSION_MASK 0xFFFF0000
48 #define STRING_DB_MINOR_VERSION_MASK 0x0000FFFF
50 #define DEFINE_STR L"// #define"
52 #define LANGUAGE_CODE_WIDTH 4
54 // This is the header that gets written to the top of the
55 // output binary database file.
61 UINT32 NumStringIdenfiers
;
62 UINT32 StringIdentifiersSize
;
67 // When we write out data to the database, we have a UINT16 identifier, which
68 // indicates what follows, followed by the data. Here's the structure.
73 } DB_DATA_ITEM_HEADER
;
75 #define DB_DATA_TYPE_INVALID 0x0000
76 #define DB_DATA_TYPE_STRING_IDENTIFIER 0x0001
77 #define DB_DATA_TYPE_LANGUAGE_DEFINITION 0x0002
78 #define DB_DATA_TYPE_STRING_DEFINITION 0x0003
79 #define DB_DATA_TYPE_LAST DB_DATA_TYPE_STRING_DEFINITION
82 // We have to keep track of a list of languages, each of which has its own
83 // list of strings. Define a structure to keep track of all languages and
84 // their list of strings.
86 typedef struct _STRING_LIST
{
87 struct _STRING_LIST
*Next
;
88 UINT32 Size
; // number of bytes in string, including null terminator
90 WCHAR
*StringName
; // for example STR_ID_TEXT1
92 WCHAR
*Str
; // the actual string
93 UINT16 Flags
; // properties of this string (used, undefined)
96 typedef struct _LANGUAGE_LIST
{
97 struct _LANGUAGE_LIST
*Next
;
98 WCHAR LanguageName
[4];
99 WCHAR
*PrintableLanguageName
;
101 STRING_LIST
*LastString
;
105 // We also keep track of all the string identifier names, which we assign unique
106 // values to. Create a structure to keep track of them all.
108 typedef struct _STRING_IDENTIFIER
{
109 struct _STRING_IDENTIFIER
*Next
;
110 UINT32 Index
; // only need 16 bits, but makes it easier with UINT32
112 UINT16 Flags
; // if someone referenced it via STRING_TOKEN()
115 // Keep our globals in this structure to be as modular as possible.
119 LANGUAGE_LIST
*LanguageList
;
120 LANGUAGE_LIST
*LastLanguageList
;
121 LANGUAGE_LIST
*CurrentLanguage
; // keep track of the last language they used
122 STRING_IDENTIFIER
*StringIdentifier
;
123 STRING_IDENTIFIER
*LastStringIdentifier
;
124 UINT8
*StringDBFileName
;
125 UINT32 NumStringIdentifiers
;
126 UINT32 NumStringIdentifiersReferenced
;
127 STRING_IDENTIFIER
*CurrentStringIdentifier
; // keep track of the last string identifier they added
131 static STRING_DB_DATA mDBData
;
133 static const char *mSourceFileHeader
[] = {
135 "// DO NOT EDIT -- auto-generated file",
137 "// This file is generated by the string gather utility",
148 WCHAR_STRING_LIST
*LanguagesOfInterest
,
149 WCHAR_MATCHING_STRING_LIST
*IndirectionList
154 StringDBFindStringIdentifierByName (
160 StringDBFindStringIdentifierByIndex (
166 StringDBFindLanguageList (
172 StringDBWriteStandardFileHeader (
190 StringDBWriteStringIdentifier (
194 WCHAR
*IdentifierName
199 StringDBReadStringIdentifier (
205 StringDBWriteLanguageDefinition (
208 WCHAR
*PrintableLanguageName
213 StringDBReadLanguageDefinition (
219 StringDBWriteString (
236 StringDBReadGenericString (
244 StringDBWriteGenericString (
251 StringDBAssignStringIndexes (
255 /*****************************************************************************/
260 Constructor function for the string database handler.
270 StringDBConstructor (
274 memset ((char *) &mDBData
, 0, sizeof (STRING_DB_DATA
));
275 mDBData
.CurrentScope
= DuplicateString (L
"NULL");
278 /*****************************************************************************/
283 Destructor function for the string database handler.
297 LANGUAGE_LIST
*NextLang
;
298 STRING_LIST
*NextStr
;
299 STRING_IDENTIFIER
*NextIdentifier
;
301 // Close the database file if it's open
303 if (mDBData
.StringDBFptr
!= NULL
) {
304 fclose (mDBData
.StringDBFptr
);
305 mDBData
.StringDBFptr
= NULL
;
308 // If we've allocated any strings/languages, free them up
310 while (mDBData
.LanguageList
!= NULL
) {
311 NextLang
= mDBData
.LanguageList
->Next
;
313 // Free up all strings for this language
315 while (mDBData
.LanguageList
->String
!= NULL
) {
316 NextStr
= mDBData
.LanguageList
->String
->Next
;
317 FREE (mDBData
.LanguageList
->String
->Str
);
318 FREE (mDBData
.LanguageList
->String
);
319 mDBData
.LanguageList
->String
= NextStr
;
322 FREE (mDBData
.LanguageList
->PrintableLanguageName
);
323 FREE (mDBData
.LanguageList
);
324 mDBData
.LanguageList
= NextLang
;
327 // Free up string identifiers
329 while (mDBData
.StringIdentifier
!= NULL
) {
330 NextIdentifier
= mDBData
.StringIdentifier
->Next
;
331 FREE (mDBData
.StringIdentifier
->StringName
);
332 FREE (mDBData
.StringIdentifier
);
333 mDBData
.StringIdentifier
= NextIdentifier
;
338 if (mDBData
.StringDBFileName
!= NULL
) {
339 FREE (mDBData
.StringDBFileName
);
340 mDBData
.StringDBFileName
= NULL
;
343 // We save a copy of the scope, so free it up if we
346 if (mDBData
.CurrentScope
!= NULL
) {
347 FREE (mDBData
.CurrentScope
);
348 mDBData
.CurrentScope
= NULL
;
352 /*****************************************************************************/
358 Dump the contents of a database to an output C file.
362 FileName - name of the output file to write
363 BaseName - used for the name of the C array defined
364 Languages - list of languages of interest
372 Languages is a pointer to a linked list of languages specified on
373 the command line. Format is "eng" and "spa+cat". For this, print
374 the strings for eng. Print the strings for spa too, but if one is
375 missing look for a cat string and print if it it exists.
379 StringDBDumpCStrings (
382 WCHAR_STRING_LIST
*LanguagesOfInterest
,
383 WCHAR_MATCHING_STRING_LIST
*IndirectionList
388 STRING_LIST
*CurrString
;
389 STRING_LIST EmptyString
;
393 UINT32 BytesThisLine
;
394 EFI_HII_STRING_PACK StringPack
;
398 WCHAR_STRING_LIST
*LOIPtr
;
400 WCHAR
*TempStringPtr
;
402 STRING_IDENTIFIER
*StringIdentifier
;
405 if ((Fptr
= fopen (FileName
, "w")) == NULL
) {
406 Error (NULL
, 0, 0, FileName
, "failed to open output C string file");
410 // Assign index values to the string identifiers
412 StringDBAssignStringIndexes ();
414 // Write the standard header to the output file, then the structure
415 // definition header.
417 StringDBWriteStandardFileHeader (Fptr
);
418 fprintf (Fptr
, "\nunsigned char %s[] = {\n", BaseName
);
420 // If a given string is not defined, then we'll use this one.
422 memset (&EmptyString
, 0, sizeof (EmptyString
));
423 EmptyString
.Size
= sizeof (ZeroString
);
424 EmptyString
.Str
= ZeroString
;
426 // Process each language, then each string for each langage
429 for (Lang
= mDBData
.LanguageList
; Lang
!= NULL
; Lang
= Lang
->Next
) {
431 // If we have a language list, then make sure this language is in that
435 LangName
= Lang
->LanguageName
;
436 if (LanguagesOfInterest
!= NULL
) {
438 for (LOIPtr
= LanguagesOfInterest
; LOIPtr
!= NULL
; LOIPtr
= LOIPtr
->Next
) {
439 if (StrnCmp (LOIPtr
->Str
, Lang
->LanguageName
, LANGUAGE_IDENTIFIER_NAME_LEN
) == 0) {
440 LangName
= LOIPtr
->Str
;
451 // Process each string for this language. We have to make 3 passes on the strings:
452 // Pass1: computes sizes and fill in the string pack header
453 // Pass2: write the array of offsets
454 // Pass3: write the strings
457 // PASS 1: Fill in and print the HII string pack header
459 // Compute the size for this language package and write
460 // the header out. Each string package contains:
462 // Offset[] -- an array of offsets to strings, of type RELOFST each
463 // String[] -- the actual strings themselves
465 AsciiSPrint ( Line
, sizeof(Line
),
466 "\n//******************************************************************************"
467 "\n// Start of string definitions for %s/%s",
469 Lang
->PrintableLanguageName
471 fprintf (Fptr
, "%s", Line
);
472 memset ((char *) &StringPack
, 0, sizeof (EFI_HII_STRING_PACK
));
473 StringPack
.Header
.Type
= EFI_HII_STRING
;
474 StringPack
.NumStringPointers
= (UINT16
) mDBData
.NumStringIdentifiersReferenced
;
476 // First string is the language name. If we're printing all languages, then
477 // it's just the "spa". If we were given a list of languages to print, then it's
478 // the "spacat" string. Compute its offset and fill in
479 // the info in the header. Since we know the language name string's length,
480 // and the printable language name follows it, use that info to fill in the
481 // entry for the printable language name as well.
483 StringPack
.LanguageNameString
= (STRING_OFFSET
) (sizeof (EFI_HII_STRING_PACK
) + (mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
)));
484 StringPack
.PrintableLanguageName
= (STRING_OFFSET
) (StringPack
.LanguageNameString
+ (StrLen (LangName
) + 1) * sizeof (WCHAR
));
486 // Add up the size of all strings so we can fill in our header.
489 for (StringIndex
= 0; StringIndex
< mDBData
.NumStringIdentifiersReferenced
; StringIndex
++) {
491 // For the first string (language name), we print out the "spacat" if they
492 // requested it. We set LangName to point to the proper language name string above.
494 if (StringIndex
== STRING_ID_LANGUAGE_NAME
) {
495 Len
+= (StrLen (LangName
) + 1) * sizeof (WCHAR
);
498 // Find a string with this language.stringname
500 StringIdentifier
= StringDBFindStringIdentifierByIndex (StringIndex
);
501 if (StringIdentifier
== NULL
) {
502 Error (NULL
, 0, 0, "internal error", "invalid string index 0x%X", StringIndex
);
506 // Find a matching string if this string identifier was referenced
508 EmptyString
.Flags
= STRING_FLAGS_UNDEFINED
;
510 if (StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) {
511 CurrString
= StringDBFindString (
513 StringIdentifier
->StringName
,
518 if (NULL
== CurrString
) {
520 // If string for Lang->LanguageName is not found, try to get an English version
522 CurrString
= StringDBFindString (
524 StringIdentifier
->StringName
,
532 if (CurrString
== NULL
) {
533 CurrString
= &EmptyString
;
534 EmptyString
.Flags
|= StringIdentifier
->Flags
;
537 Len
+= CurrString
->Size
;
540 StringPack
.Header
.Length
= sizeof (EFI_HII_STRING_PACK
)
541 + mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
)
544 // Write out the header one byte at a time
546 Ptr
= (UINT8
*) &StringPack
;
547 for (TempIndex
= 0; TempIndex
< sizeof (EFI_HII_STRING_PACK
); TempIndex
++, Ptr
++) {
548 if ((TempIndex
& 0x07) == 0) {
549 fprintf (Fptr
, "\n ");
552 fprintf (Fptr
, "0x%02X, ", (UINT32
) *Ptr
);
555 fprintf (Fptr
, "\n // offset 0x%X\n", sizeof (StringPack
));
557 // PASS2 : write the offsets
559 // Traverse the list of strings again and write the array of offsets. The
560 // offset to the first string is the size of the string pack header
561 // plus the size of the offsets array. The other strings follow it.
564 Offset
= sizeof (StringPack
) + mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
);
565 for (StringIndex
= 0; StringIndex
< mDBData
.NumStringIdentifiersReferenced
; StringIndex
++) {
567 // Write the offset, followed by a useful comment
570 Ptr
= (UINT8
*) &Offset
;
571 for (TempIndex
= 0; TempIndex
< sizeof (STRING_OFFSET
); TempIndex
++) {
572 fprintf (Fptr
, "0x%02X, ", (UINT32
) Ptr
[TempIndex
]);
575 // Find the string name
577 StringIdentifier
= StringDBFindStringIdentifierByIndex (StringIndex
);
578 if (StringIdentifier
== NULL
) {
579 Error (NULL
, 0, 0, "internal error", "invalid string index 0x%X", StringIndex
);
583 AsciiSPrint (Line
, sizeof(Line
) , " // offset to string %s (0x%04X)", StringIdentifier
->StringName
, StringIndex
);
584 fprintf (Fptr
, "%s", Line
);
586 // For the first string (language name), we print out the "spacat" if they
587 // requested it. We set LangName to point to the proper language name string above.
589 if (StringIndex
== STRING_ID_LANGUAGE_NAME
) {
590 Offset
+= (StrLen (LangName
) + 1) * sizeof (WCHAR
);
591 CurrString
= StringDBFindString (
593 StringIdentifier
->StringName
,
600 // Find a matching string
602 CurrString
= StringDBFindString (
604 StringIdentifier
->StringName
,
610 if (NULL
== CurrString
) {
611 CurrString
= StringDBFindString (
613 StringIdentifier
->StringName
,
620 EmptyString
.LanguageName
= Lang
->LanguageName
;
621 if (CurrString
== NULL
) {
622 CurrString
= &EmptyString
;
623 EmptyString
.Flags
= STRING_FLAGS_UNDEFINED
;
624 } else if ((StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) == 0) {
625 CurrString
= &EmptyString
;
626 EmptyString
.Flags
= 0;
629 Offset
+= CurrString
->Size
;
632 // Print useful info about this string
634 if ((StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) == 0) {
635 fprintf (Fptr
, " - not referenced");
638 if (CurrString
->Flags
& STRING_FLAGS_UNDEFINED
) {
639 fprintf (Fptr
, " - not defined for this language");
640 } else if (StrCmp (CurrString
->LanguageName
, Lang
->LanguageName
) != 0) {
643 " - not defined for this language -- using secondary language %s definition",
644 CurrString
->LanguageName
646 fprintf ( Fptr
, "%s", Line
);
649 fprintf (Fptr
, "\n");
652 // For unreferenced string identifiers, print a message that they are not referenced anywhere
654 while (StringIndex
< mDBData
.NumStringIdentifiers
) {
655 StringIdentifier
= StringDBFindStringIdentifierByIndex (StringIndex
);
656 if (StringIdentifier
!= NULL
) {
657 AsciiSPrint (Line
, sizeof(Line
), " // %s not referenced\n", StringIdentifier
->StringName
);
658 fprintf (Fptr
, "%s", Line
);
665 // PASS 3: write the strings themselves.
666 // Keep track of how many bytes we write per line because some editors
667 // (Visual Studio for instance) can't handle too long of lines.
669 Offset
= sizeof (StringPack
) + mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
);
670 for (StringIndex
= 0; StringIndex
< mDBData
.NumStringIdentifiersReferenced
; StringIndex
++) {
671 StringIdentifier
= StringDBFindStringIdentifierByIndex (StringIndex
);
672 if (StringIdentifier
== NULL
) {
673 Error (NULL
, 0, 0, "internal error", "invalid string index 0x%X", StringIndex
);
677 AsciiSPrint (Line
, sizeof(Line
), " // string %s offset 0x%08X\n ", StringIdentifier
->StringName
, Offset
);
678 fprintf (Fptr
, "%s", Line
);
680 // For the first string (language name), we print out the "spacat" if they
681 // requested it. We set LangName to point to the proper language name string above.
683 if (StringIndex
== STRING_ID_LANGUAGE_NAME
) {
684 TempStringPtr
= LangName
;
687 // Find a matching string if this string identifier was referenced
690 if (StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) {
691 CurrString
= StringDBFindString (
693 StringIdentifier
->StringName
,
698 if (NULL
== CurrString
) {
699 CurrString
= StringDBFindString (
701 StringIdentifier
->StringName
,
709 if (CurrString
== NULL
) {
710 CurrString
= &EmptyString
;
713 TempStringPtr
= CurrString
->Str
;
717 for (TempIndex
= 0; TempStringPtr
[TempIndex
] != 0; TempIndex
++) {
721 (UINT32
) TempStringPtr
[TempIndex
] & 0xFF,
722 (UINT32
) ((TempStringPtr
[TempIndex
] >> 8) & 0xFF)
727 // Let's say we only allow 14 per line
729 if (BytesThisLine
> 14) {
730 fprintf (Fptr
, "\n ");
735 // Print NULL WCHAR at the end of this string.
737 fprintf (Fptr
, "0x00, 0x00,\n");
741 // Sanity check the offset. Make sure our running offset is what we put in the
742 // string pack header.
744 if (StringPack
.Header
.Length
!= Offset
) {
750 "stringpack size 0x%X does not match final size 0x%X",
751 StringPack
.Header
.Length
,
757 // Print terminator string pack, closing brace and close the file.
758 // The size of 0 triggers to the consumer that this is the end.
760 memset ((char *) &StringPack
, 0, sizeof (EFI_HII_STRING_PACK
));
761 StringPack
.Header
.Type
= EFI_HII_STRING
;
762 Ptr
= (UINT8
*) &StringPack
;
763 fprintf (Fptr
, "\n // strings terminator pack");
764 for (TempIndex
= 0; TempIndex
< sizeof (StringPack
); TempIndex
++, Ptr
++) {
765 if ((TempIndex
& 0x0F) == 0) {
766 fprintf (Fptr
, "\n ");
769 fprintf (Fptr
, "0x%02X, ", (UINT32
) *Ptr
);
772 fprintf (Fptr
, "\n};\n");
774 return STATUS_SUCCESS
;
777 /*****************************************************************************/
783 Dump the #define string names
787 FileName - name of the output file to write
788 BaseName - used for the protection #ifndef/#endif
796 StringDBDumpStringDefines (
802 STRING_IDENTIFIER
*Identifier
;
803 INT8 CopyBaseName
[100];
806 const INT8
*StrDefHeader
[] = {
807 "#ifndef _%s_STRINGS_DEFINE_H_\n",
808 "#define _%s_STRINGS_DEFINE_H_\n\n",
812 if ((Fptr
= fopen (FileName
, "w")) == NULL
) {
813 Error (NULL
, 0, 0, FileName
, "failed to open output string defines file");
817 // Get the base source filename and convert to uppercase.
819 if (sizeof (CopyBaseName
) <= strlen (BaseName
) + 1) {
820 Error (NULL
, 0, 0, "application error", "StringDBDumpStringDefines() string length insufficient");
824 strcpy (CopyBaseName
, BaseName
);
825 for (Index
= 0; CopyBaseName
[Index
] != 0; Index
++) {
826 if (islower (CopyBaseName
[Index
])) {
827 CopyBaseName
[Index
] = (INT8
) toupper (CopyBaseName
[Index
]);
831 // Assign index values to the string identifiers
833 StringDBAssignStringIndexes ();
835 // Write the standard header to the output file, and then the
836 // protective #ifndef.
838 StringDBWriteStandardFileHeader (Fptr
);
839 for (Index
= 0; StrDefHeader
[Index
] != NULL
; Index
++) {
840 fprintf (Fptr
, StrDefHeader
[Index
], CopyBaseName
);
843 // Print all the #defines for the string identifiers. Print identifiers
844 // whose names start with '$' as comments. Add comments for string
845 // identifiers not used as well.
847 Identifier
= mDBData
.StringIdentifier
;
848 while (Identifier
!= NULL
) {
849 if (Identifier
->StringName
[0] == L
'$') {
850 fprintf (Fptr
, "// ");
853 if (Identifier
->Flags
& STRING_FLAGS_REFERENCED
) {
854 AsciiSPrint (Line
, sizeof(Line
), "#define %-40s 0x%04X\n", Identifier
->StringName
, Identifier
->Index
);
855 fprintf (Fptr
, "%s", Line
);
857 AsciiSPrint (Line
, sizeof(Line
), "//#define %-40s 0x%04X // not referenced\n", Identifier
->StringName
, Identifier
->Index
);
858 fprintf (Fptr
, "%s", Line
);
861 Identifier
= Identifier
->Next
;
864 fprintf (Fptr
, "\n#endif\n");
866 return STATUS_SUCCESS
;
869 /*****************************************************************************/
875 Add a string identifier to the database.
879 StringName - name of the string identifier. For example "STR_MY_STRING"
880 NewId - if an ID has been assigned
881 Flags - characteristics for the identifier
889 StringDBAddStringIdentifier (
895 STRING_IDENTIFIER
*StringIdentifier
;
898 // If it was already used for some other language, then we don't
899 // need to add it. But set it to the current string identifier.
900 // The referenced bit is sticky.
902 Status
= STATUS_SUCCESS
;
903 StringIdentifier
= StringDBFindStringIdentifierByName (StringName
);
904 if (StringIdentifier
!= NULL
) {
905 if (Flags
& STRING_FLAGS_REFERENCED
) {
906 StringIdentifier
->Flags
|= STRING_FLAGS_REFERENCED
;
909 mDBData
.CurrentStringIdentifier
= StringIdentifier
;
910 *NewId
= (UINT16
) StringIdentifier
->Index
;
914 StringIdentifier
= (STRING_IDENTIFIER
*) MALLOC (sizeof (STRING_IDENTIFIER
));
915 if (StringIdentifier
== NULL
) {
916 Error (NULL
, 0, 0, NULL
, "memory allocation error");
920 memset ((char *) StringIdentifier
, 0, sizeof (STRING_IDENTIFIER
));
921 StringIdentifier
->StringName
= (WCHAR
*) malloc ((StrLen (StringName
) + 1) * sizeof (WCHAR
));
922 if (StringIdentifier
->StringName
== NULL
) {
923 Error (NULL
, 0, 0, NULL
, "memory allocation error");
927 StrCpy (StringIdentifier
->StringName
, StringName
);
928 if (*NewId
!= STRING_ID_INVALID
) {
929 StringIdentifier
->Index
= *NewId
;
930 StringIdentifier
->Flags
|= STRING_FLAGS_INDEX_ASSIGNED
;
931 if (mDBData
.NumStringIdentifiers
<= StringIdentifier
->Index
) {
932 mDBData
.NumStringIdentifiers
= StringIdentifier
->Index
+ 1;
935 StringIdentifier
->Index
= mDBData
.NumStringIdentifiers
++;
938 StringIdentifier
->Flags
|= Flags
;
940 // Add it to our list of string identifiers
942 if (mDBData
.StringIdentifier
== NULL
) {
943 mDBData
.StringIdentifier
= StringIdentifier
;
945 mDBData
.LastStringIdentifier
->Next
= StringIdentifier
;
948 mDBData
.LastStringIdentifier
= StringIdentifier
;
949 mDBData
.CurrentStringIdentifier
= StringIdentifier
;
950 *NewId
= (UINT16
) StringIdentifier
->Index
;
954 /*****************************************************************************/
960 Add a new string to the database.
964 LanguageName - "eng" or "spa" language name
965 StringName - "STR_MY_TEXT" string name
966 Scope - from the #scope statements in the string file
967 Format - if we should format the string
968 Flags - characteristic flags for the string
976 Several of the fields can be "inherited" from the previous calls to
977 our database functions. For example, if scope is NULL here, then
978 we'll use the previous setting.
995 WCHAR TempLangName
[4];
996 STRING_IDENTIFIER
*StringIdentifier
;
999 // Check that language name is exactly 3 characters, or emit an error.
1000 // Truncate at 3 if it's longer, or make it 3 if it's shorter.
1002 if (LanguageName
!= NULL
) {
1003 Size
= StrLen (LanguageName
);
1005 ParserError (0, "invalid length for language name", "%S", LanguageName
);
1007 LanguageName
[3] = 0;
1010 // Make a local copy of the language name string, and extend to
1011 // 3 characters since we make assumptions elsewhere in this program
1014 StrCpy (TempLangName
, LanguageName
);
1015 for (; Size
< 3; Size
++) {
1016 TempLangName
[Size
] = L
'?';
1019 TempLangName
[4] = 0;
1020 LanguageName
= TempLangName
;
1025 // If they specified a language, make sure they've defined it already
1026 // via a #langdef statement. Otherwise use the current default language.
1028 if (LanguageName
!= NULL
) {
1029 Lang
= StringDBFindLanguageList (LanguageName
);
1031 ParserError (0, "language not defined", "%S", LanguageName
);
1032 return STATUS_ERROR
;
1034 StringDBSetCurrentLanguage (LanguageName
);
1037 Lang
= mDBData
.CurrentLanguage
;
1040 // Have to call SetLanguage() first
1042 ParserError (0, "no language defined", "%S", StringName
);
1043 return STATUS_ERROR
;
1047 // If they didn't define a string identifier, use the last string identifier
1050 if (StringName
== NULL
) {
1051 StringName
= mDBData
.CurrentStringIdentifier
->StringName
;
1052 if (StringName
== NULL
) {
1053 ParserError (0, "no string identifier previously specified", NULL
);
1054 return STATUS_ERROR
;
1058 // If scope was not specified, use the default setting
1060 if (Scope
!= NULL
) {
1061 Scope
= DuplicateString (Scope
);
1063 Scope
= DuplicateString (mDBData
.CurrentScope
);
1066 // printf ("Adding string: %S.%S.%S\n", Lang->LanguageName, StringName, Scope);
1068 // Check for duplicates for this Language.StringName.Scope. Allow multiple
1069 // definitions of the language name and printable language name, since the
1070 // user does not specifically define them.
1072 if (StringDBFindString (Lang
->LanguageName
, StringName
, Scope
, NULL
, NULL
) != NULL
) {
1073 if ((StrCmp (StringName
, LANGUAGE_NAME_STRING_NAME
) == 0) &&
1074 (StrCmp (StringName
, PRINTABLE_LANGUAGE_NAME_STRING_NAME
) == 0)
1078 "string multiply defined",
1079 "Language.Name.Scope = %S.%S.%S",
1084 return STATUS_ERROR
;
1088 StringIndex
= STRING_ID_INVALID
;
1089 if (StringDBAddStringIdentifier (StringName
, &StringIndex
, Flags
) != STATUS_SUCCESS
) {
1090 return STATUS_ERROR
;
1093 StringIdentifier
= StringDBFindStringIdentifierByName (StringName
);
1095 // Add this string to the end of the strings for this language.
1097 Str
= (STRING_LIST
*) malloc (sizeof (STRING_LIST
));
1099 Error (NULL
, 0, 0, NULL
, "memory allocation error");
1100 return STATUS_ERROR
;
1103 memset ((char *) Str
, 0, sizeof (STRING_LIST
));
1104 Size
= (StrLen (String
) + 1) * sizeof (WCHAR
);
1107 Str
->StringName
= StringIdentifier
->StringName
;
1108 Str
->LanguageName
= DuplicateString (LanguageName
);
1109 Str
->Str
= (WCHAR
*) MALLOC (Size
);
1110 if (Str
->Str
== NULL
) {
1111 Error (NULL
, 0, 0, NULL
, "memory allocation error");
1112 return STATUS_ERROR
;
1115 // If not formatting, just copy the string.
1117 StrCpy (Str
->Str
, String
);
1119 StringDBFormatString (Str
->Str
);
1122 // Size may change after formatting. We set the size to
1123 // the actual size of the string, including the null for
1124 // easier processing later.
1126 Str
->Size
= (StrLen (Str
->Str
) + 1) * sizeof (WCHAR
);
1127 if (Lang
->String
== NULL
) {
1130 Lang
->LastString
->Next
= Str
;
1133 Lang
->LastString
= Str
;
1134 return STATUS_SUCCESS
;
1137 /*****************************************************************************/
1141 Routine Description:
1143 Given a language name, see if a language list for it has been defined
1147 LanguageName - like "eng"
1151 A pointer to the language list
1156 StringDBFindLanguageList (
1160 LANGUAGE_LIST
*Lang
;
1162 Lang
= mDBData
.LanguageList
;
1163 while (Lang
!= NULL
) {
1164 if (StrCmp (LanguageName
, Lang
->LanguageName
) == 0) {
1174 /*****************************************************************************/
1176 StringDBSetCurrentLanguage (
1180 LANGUAGE_LIST
*Lang
;
1182 Lang
= StringDBFindLanguageList (LanguageName
);
1184 ParserError (0, "language not previously defined", "%S", LanguageName
);
1185 return STATUS_ERROR
;
1188 mDBData
.CurrentLanguage
= Lang
;
1189 return STATUS_SUCCESS
;
1192 /*****************************************************************************/
1194 StringDBAddLanguage (
1195 WCHAR
*LanguageName
,
1196 WCHAR
*PrintableLanguageName
1199 LANGUAGE_LIST
*Lang
;
1201 // Check for redefinitions
1203 Lang
= StringDBFindLanguageList (LanguageName
);
1206 // Better be the same printable name
1208 if (StrCmp (PrintableLanguageName
, Lang
->PrintableLanguageName
) != 0) {
1211 "language redefinition",
1214 Lang
->PrintableLanguageName
,
1216 PrintableLanguageName
1218 return STATUS_ERROR
;
1221 // ParserWarning (0, "benign language redefinition", "%S", PrintableLanguageName);
1222 // return STATUS_WARNING;
1227 // Allocate memory to keep track of this new language
1229 Lang
= (LANGUAGE_LIST
*) malloc (sizeof (LANGUAGE_LIST
));
1231 Error (NULL
, 0, 0, NULL
, "memory allocation error");
1232 return STATUS_ERROR
;
1235 memset ((char *) Lang
, 0, sizeof (LANGUAGE_LIST
));
1237 // Save the language name, then allocate memory to save the
1238 // printable language name
1240 StrCpy (Lang
->LanguageName
, LanguageName
);
1241 Lang
->PrintableLanguageName
= (WCHAR
*) malloc ((StrLen (PrintableLanguageName
) + 1) * sizeof (WCHAR
));
1242 if (Lang
->PrintableLanguageName
== NULL
) {
1243 Error (NULL
, 0, 0, NULL
, "memory allocation error");
1244 return STATUS_ERROR
;
1247 StrCpy (Lang
->PrintableLanguageName
, PrintableLanguageName
);
1249 if (mDBData
.LanguageList
== NULL
) {
1250 mDBData
.LanguageList
= Lang
;
1252 mDBData
.LastLanguageList
->Next
= Lang
;
1255 mDBData
.LastLanguageList
= Lang
;
1258 // Default is to make our active language this new one
1260 StringDBSetCurrentLanguage (LanguageName
);
1262 // The first two strings for any language are the language name,
1263 // followed by the printable language name. Add them and set them
1264 // to referenced so they never get stripped out.
1268 LANGUAGE_NAME_STRING_NAME
,
1272 STRING_FLAGS_REFERENCED
1276 PRINTABLE_LANGUAGE_NAME_STRING_NAME
,
1278 PrintableLanguageName
,
1280 STRING_FLAGS_REFERENCED
1282 return STATUS_SUCCESS
;
1285 /*****************************************************************************/
1288 StringDBFindStringIdentifierByName (
1292 STRING_IDENTIFIER
*Identifier
;
1294 Identifier
= mDBData
.StringIdentifier
;
1295 while (Identifier
!= NULL
) {
1296 if (StrCmp (StringName
, Identifier
->StringName
) == 0) {
1300 Identifier
= Identifier
->Next
;
1308 StringDBFindStringIdentifierByIndex (
1312 STRING_IDENTIFIER
*Identifier
;
1314 Identifier
= mDBData
.StringIdentifier
;
1315 while (Identifier
!= NULL
) {
1316 if (Identifier
->Index
== StringIndex
) {
1320 Identifier
= Identifier
->Next
;
1326 /*****************************************************************************/
1329 StringDBWriteStandardFileHeader (
1334 for (TempIndex
= 0; mSourceFileHeader
[TempIndex
] != NULL
; TempIndex
++) {
1335 fprintf (OutFptr
, "%s\n", mSourceFileHeader
[TempIndex
]);
1339 /*****************************************************************************/
1343 Routine Description:
1345 Given a Unicode string from an input file, reformat the string to replace
1346 backslash control sequences with the appropriate encoding.
1350 String - pointer to string to reformat
1358 StringDBFormatString (
1367 // Go through the string and process any formatting characters
1372 if (*From
== UNICODE_BACKSLASH
) {
1374 // First look for \wide and replace with the appropriate control character. Note that
1375 // when you have "define STR L"ABC"", then sizeof(ABC) is 8 because the null char is
1376 // counted. Make adjustments for this. We advance From below, so subtract 2 each time.
1378 if (StrnCmp (From
, UNICODE_WIDE_STRING
, sizeof (UNICODE_WIDE_STRING
) / sizeof (WCHAR
) - 1) == 0) {
1380 From
+= sizeof (UNICODE_WIDE_STRING
) / sizeof (WCHAR
) - 2;
1381 } else if (StrnCmp (From
, UNICODE_NARROW_STRING
, sizeof (UNICODE_NARROW_STRING
) / sizeof (WCHAR
) - 1) == 0) {
1386 From
+= sizeof (UNICODE_NARROW_STRING
) / sizeof (WCHAR
) - 2;
1387 } else if (StrnCmp (From
, UNICODE_NBR_STRING
, sizeof (UNICODE_NBR_STRING
) / sizeof (WCHAR
) - 1) == 0) {
1391 *To
= NON_BREAKING_CHAR
;
1392 From
+= sizeof (UNICODE_NBR_STRING
) / sizeof (WCHAR
) - 2;
1393 } else if (StrnCmp (From
, UNICODE_BR_STRING
, sizeof (UNICODE_BR_STRING
) / sizeof (WCHAR
) - 1) == 0) {
1395 // Found: \br -- pass through untouched
1400 // Standard one-character control sequences such as \n, \r, \\, or \x
1404 case ASCII_TO_UNICODE ('n'):
1413 case ASCII_TO_UNICODE ('r'):
1420 case UNICODE_BACKSLASH
:
1421 *To
= UNICODE_BACKSLASH
;
1427 case ASCII_TO_UNICODE ('t'):
1432 // embedded double-quote
1434 case UNICODE_DOUBLE_QUOTE
:
1435 *To
= UNICODE_DOUBLE_QUOTE
;
1439 // Hex Unicode character \x1234. We'll process up to 4 hex characters
1441 case ASCII_TO_UNICODE ('x'):
1443 for (HexNibbles
= 0; HexNibbles
< 4; HexNibbles
++) {
1444 if ((From
[1] >= UNICODE_0
) && (From
[1] <= UNICODE_9
)) {
1445 HexValue
= (HexValue
<< 4) | (From
[1] - UNICODE_0
);
1446 } else if ((From
[1] >= UNICODE_a
) && (From
[1] <= UNICODE_f
)) {
1447 HexValue
= (HexValue
<< 4) | (10 + From
[1] - UNICODE_a
);
1448 } else if ((From
[1] >= UNICODE_A
) && (From
[1] <= UNICODE_F
)) {
1449 HexValue
= (HexValue
<< 4) | (10 + From
[1] - UNICODE_A
);
1457 if (HexNibbles
== 0) {
1460 "expected at least one valid hex digit with \\x escaped character in string",
1470 *To
= UNICODE_SPACE
;
1471 ParserWarning (0, "invalid escaped character in string", "\\%C", *From
);
1486 /*****************************************************************************/
1488 StringDBReadDatabase (
1490 BOOLEAN IgnoreIfNotExist
,
1494 STRING_DB_HEADER DbHeader
;
1497 DB_DATA_ITEM_HEADER DataItemHeader
;
1499 Status
= STATUS_SUCCESS
;
1503 // fprintf (stdout, "Reading database file %s\n", DBFileName);
1506 // Try to open the input file
1508 if ((DBFptr
= fopen (DBFileName
, "rb")) == NULL
) {
1509 if (IgnoreIfNotExist
) {
1510 return STATUS_SUCCESS
;
1513 Error (NULL
, 0, 0, DBFileName
, "failed to open input database file for reading");
1514 return STATUS_ERROR
;
1517 // Read and verify the database header
1519 if (fread ((void *) &DbHeader
, sizeof (STRING_DB_HEADER
), 1, DBFptr
) != 1) {
1520 Error (NULL
, 0, 0, DBFileName
, "failed to read header from database file");
1521 Status
= STATUS_ERROR
;
1525 if (DbHeader
.Key
!= STRING_DB_KEY
) {
1526 Error (NULL
, 0, 0, DBFileName
, "invalid header in database file");
1527 Status
= STATUS_ERROR
;
1531 if ((DbHeader
.Version
& STRING_DB_MAJOR_VERSION_MASK
) != (STRING_DB_VERSION
& STRING_DB_MAJOR_VERSION_MASK
)) {
1532 Error (NULL
, 0, 0, DBFileName
, "incompatible database file version -- rebuild clean");
1533 Status
= STATUS_ERROR
;
1537 // Read remaining items
1539 while (fread (&DataItemHeader
, sizeof (DataItemHeader
), 1, DBFptr
) == 1) {
1540 switch (DataItemHeader
.DataType
) {
1541 case DB_DATA_TYPE_STRING_IDENTIFIER
:
1542 StringDBReadStringIdentifier (DBFptr
);
1545 case DB_DATA_TYPE_LANGUAGE_DEFINITION
:
1546 StringDBReadLanguageDefinition (DBFptr
);
1549 case DB_DATA_TYPE_STRING_DEFINITION
:
1550 StringDBReadString (DBFptr
);
1558 "database corrupted",
1559 "invalid data item type 0x%X at offset 0x%X",
1560 (UINT32
) DataItemHeader
.DataType
,
1561 ftell (DBFptr
) - sizeof (DataItemHeader
)
1563 Status
= STATUS_ERROR
;
1569 if (DBFptr
!= NULL
) {
1576 /*****************************************************************************/
1580 Routine Description:
1582 Write everything we know to the output database file. Write:
1585 String identifiers[]
1590 DBFileName - name of the file to write to
1591 Verbose - for debug purposes, print info messages along the way.
1599 StringDBWriteDatabase (
1604 STRING_DB_HEADER DbHeader
;
1607 LANGUAGE_LIST
*Lang
;
1608 STRING_IDENTIFIER
*StringIdentifier
;
1609 STRING_LIST
*StrList
;
1613 fprintf (stdout
, "Writing database %s\n", DBFileName
);
1616 if ((DBFptr
= fopen (DBFileName
, "wb")) == NULL
) {
1617 Error (NULL
, 0, 0, DBFileName
, "failed to open output database file for writing");
1618 return STATUS_ERROR
;
1621 // Fill in and write the database header
1623 memset (&DbHeader
, 0, sizeof (STRING_DB_HEADER
));
1624 DbHeader
.HeaderSize
= sizeof (STRING_DB_HEADER
);
1625 DbHeader
.Key
= STRING_DB_KEY
;
1626 DbHeader
.Version
= STRING_DB_VERSION
;
1628 // Count the number of languages we have
1630 for (Lang
= mDBData
.LanguageList
; Lang
!= NULL
; Lang
= Lang
->Next
) {
1631 DbHeader
.NumLanguages
++;
1634 // Count up how many string identifiers we have, and total up the
1635 // size of the names plus the size of the flags field we will
1638 DbHeader
.NumStringIdenfiers
= mDBData
.NumStringIdentifiers
;
1639 StringIdentifier
= mDBData
.StringIdentifier
;
1640 for (Counter
= 0; Counter
< mDBData
.NumStringIdentifiers
; Counter
++) {
1641 StrLength
= StrLen (StringIdentifier
->StringName
) + 1;
1642 DbHeader
.StringIdentifiersSize
+= StrLength
* sizeof (WCHAR
) + sizeof (StringIdentifier
->Flags
);
1643 StringIdentifier
= StringIdentifier
->Next
;
1649 fwrite (&DbHeader
, sizeof (STRING_DB_HEADER
), 1, DBFptr
);
1651 fprintf (stdout
, " Number of string identifiers 0x%04X\n", DbHeader
.NumStringIdenfiers
);
1652 fprintf (stdout
, " Number of languages %d\n", DbHeader
.NumLanguages
);
1655 // Write the string identifiers
1657 for (StringIdentifier
= mDBData
.StringIdentifier
; StringIdentifier
!= NULL
; StringIdentifier
= StringIdentifier
->Next
) {
1658 StringDBWriteStringIdentifier (
1660 (UINT16
) StringIdentifier
->Index
,
1661 StringIdentifier
->Flags
,
1662 StringIdentifier
->StringName
1666 // Now write all the strings for each language
1668 for (Lang
= mDBData
.LanguageList
; Lang
!= NULL
; Lang
= Lang
->Next
) {
1669 StringDBWriteLanguageDefinition (DBFptr
, Lang
->LanguageName
, Lang
->PrintableLanguageName
);
1670 for (StrList
= Lang
->String
; StrList
!= NULL
; StrList
= StrList
->Next
) {
1671 StringDBWriteString (
1675 StrList
->StringName
,
1683 return STATUS_SUCCESS
;
1687 StringDBSetStringReferenced (
1688 INT8
*StringIdentifierName
,
1689 BOOLEAN IgnoreNotFound
1692 STRING_IDENTIFIER
*Id
;
1696 // See if it's already been defined.
1698 Status
= STATUS_SUCCESS
;
1699 WName
= (WCHAR
*) malloc ((strlen (StringIdentifierName
) + 1) * sizeof (WCHAR
));
1700 UnicodeSPrint (WName
, (strlen (StringIdentifierName
) + 1) * sizeof (WCHAR
), L
"%a", StringIdentifierName
);
1701 Id
= StringDBFindStringIdentifierByName (WName
);
1703 Id
->Flags
|= STRING_FLAGS_REFERENCED
;
1705 if (IgnoreNotFound
== 0) {
1706 ParserWarning (0, StringIdentifierName
, "string identifier not found in database");
1707 Status
= STATUS_WARNING
;
1715 /*****************************************************************************/
1719 Routine Description:
1721 Dump the contents of a database to an output unicode file.
1725 DBFileName - name of the pre-existing database file to read
1726 OutputFileName - name of the file to dump the database contents to
1727 Verbose - for printing of additional info useful for debugging
1735 There's some issue with the unicode printing routines. Therefore to
1736 write to the output file properly, open it as binary and use fwrite.
1737 Ideally we could open it with just L"w" and use fwprintf().
1741 StringDBDumpDatabase (
1743 INT8
*OutputFileName
,
1747 LANGUAGE_LIST
*Lang
;
1748 STRING_IDENTIFIER
*StringIdentifier
;
1749 STRING_LIST
*StrList
;
1756 // This function assumes the database has already been read, and
1757 // we're just dumping our internal data structures to a unicode file.
1760 fprintf (stdout
, "Dumping database file %s\n", DBFileName
);
1763 OutFptr
= fopen (OutputFileName
, "wb");
1764 if (OutFptr
== NULL
) {
1765 Error (NULL
, 0, 0, OutputFileName
, "failed to open output file for writing");
1766 return STATUS_ERROR
;
1769 WChar
= UNICODE_FILE_START
;
1770 fwrite (&WChar
, sizeof (WCHAR
), 1, OutFptr
);
1771 CrLf
[1] = UNICODE_LF
;
1772 CrLf
[0] = UNICODE_CR
;
1774 // The default control character is '/'. Make it '#' by writing
1775 // "/=#" to the output file.
1777 UnicodeSPrint (Line
, sizeof(Line
), L
"/=#");
1778 fwrite (Line
, StrLen (Line
) * sizeof (WCHAR
), 1, OutFptr
);
1779 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1780 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1782 // Dump all the string identifiers and their values
1784 StringDBAssignStringIndexes ();
1785 for (StringIdentifier
= mDBData
.StringIdentifier
; StringIdentifier
!= NULL
; StringIdentifier
= StringIdentifier
->Next
) {
1787 // Write the "#define " string
1789 if (StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) {
1792 sizeof(Line
), L
"%s %-60.60s 0x%04X",
1794 StringIdentifier
->StringName
,
1795 StringIdentifier
->Index
1800 sizeof(Line
), L
"%s %-60.60s 0x%04X // NOT REFERENCED",
1802 StringIdentifier
->StringName
,
1803 StringIdentifier
->Index
1807 fwrite (Line
, StrLen (Line
) * sizeof (WCHAR
), 1, OutFptr
);
1808 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1811 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1813 // Now write all the strings for each language.
1815 WChar
= UNICODE_DOUBLE_QUOTE
;
1817 for (Lang
= mDBData
.LanguageList
; Lang
!= NULL
; Lang
= Lang
->Next
) {
1818 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1819 UnicodeSPrint (Line
, sizeof(Line
), L
"#langdef %s \"%s\"", Lang
->LanguageName
, Lang
->PrintableLanguageName
);
1820 fwrite (Line
, StrLen (Line
) * sizeof (WCHAR
), 1, OutFptr
);
1821 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1822 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1824 // Now the strings (in double-quotes) for this language. Write
1825 // #string STR_NAME #language eng "string"
1827 for (StrList
= Lang
->String
; StrList
!= NULL
; StrList
= StrList
->Next
) {
1829 // Print the internal flags for debug
1831 UnicodeSPrint (Line
, sizeof(Line
), L
"// flags=0x%02X", (UINT32
) StrList
->Flags
);
1832 fwrite (Line
, StrLen (Line
) * sizeof (WCHAR
), 1, OutFptr
);
1833 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1835 // Print the scope if changed
1837 if ((Scope
== NULL
) || (StrCmp (Scope
, StrList
->Scope
) != 0)) {
1838 UnicodeSPrint (Line
, sizeof(Line
), L
"#scope %s", StrList
->Scope
);
1839 fwrite (Line
, StrLen (Line
) * sizeof (WCHAR
), 1, OutFptr
);
1840 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1841 Scope
= StrList
->Scope
;
1846 sizeof(Line
), L
"#string %-50.50s #language %s \"",
1847 StrList
->StringName
,
1850 fwrite (Line
, StrLen (Line
) * sizeof (WCHAR
), 1, OutFptr
);
1851 fwrite (StrList
->Str
, StrList
->Size
- sizeof (WCHAR
), 1, OutFptr
);
1852 UnicodeSPrint (Line
, sizeof(Line
), L
"\"");
1853 fwrite (Line
, StrLen (Line
) * sizeof (WCHAR
), 1, OutFptr
);
1854 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1859 return STATUS_SUCCESS
;
1862 /*****************************************************************************/
1866 Routine Description:
1868 Given a primary language, a string identifier number, and a list of
1869 languages, find a secondary string.
1873 LanguageName - primary language, like "spa"
1874 StringId - string index value
1875 LanguageList - linked list of "eng", "spa+cat",...
1879 Pointer to a secondary string if found. NULL otherwise.
1883 Given: LanguageName "spa" and LanguageList "spa+cat", match the
1884 "spa" and extract the "cat" and see if there is a string defined
1890 StringDBWriteStringIdentifier (
1894 WCHAR
*IdentifierName
1897 DB_DATA_ITEM_HEADER Hdr
;
1898 memset (&Hdr
, 0, sizeof (DB_DATA_ITEM_HEADER
));
1899 Hdr
.DataType
= DB_DATA_TYPE_STRING_IDENTIFIER
;
1900 if (fwrite (&Hdr
, sizeof (DB_DATA_ITEM_HEADER
), 1, DBFptr
) != 1) {
1901 Error (NULL
, 0, 0, "failed to write string to output database file", NULL
);
1902 return STATUS_ERROR
;
1905 if (fwrite (&StringId
, sizeof (StringId
), 1, DBFptr
) != 1) {
1906 Error (NULL
, 0, 0, "failed to write StringId to output database", NULL
);
1907 return STATUS_ERROR
;
1910 if (fwrite (&Flags
, sizeof (Flags
), 1, DBFptr
) != 1) {
1911 Error (NULL
, 0, 0, "failed to write StringId flags to output database", NULL
);
1912 return STATUS_ERROR
;
1915 if (StringDBWriteGenericString (DBFptr
, IdentifierName
) != STATUS_SUCCESS
) {
1916 return STATUS_ERROR
;
1919 return STATUS_SUCCESS
;
1924 StringDBReadStringIdentifier (
1928 WCHAR
*IdentifierName
;
1933 if (fread (&StringId
, sizeof (StringId
), 1, DBFptr
) != 1) {
1934 Error (NULL
, 0, 0, "failed to read StringId from database", NULL
);
1935 return STATUS_ERROR
;
1938 if (fread (&Flags
, sizeof (Flags
), 1, DBFptr
) != 1) {
1939 Error (NULL
, 0, 0, "failed to read StringId flags from database", NULL
);
1940 return STATUS_ERROR
;
1943 if (StringDBReadGenericString (DBFptr
, &Size
, &IdentifierName
) != STATUS_SUCCESS
) {
1944 return STATUS_ERROR
;
1947 StringDBAddStringIdentifier (IdentifierName
, &StringId
, Flags
);
1949 // printf ("STRID: 0x%04X %S\n", (UINT32)StringId, IdentifierName);
1951 FREE (IdentifierName
);
1952 return STATUS_SUCCESS
;
1957 StringDBWriteString (
1966 DB_DATA_ITEM_HEADER Hdr
;
1967 memset (&Hdr
, 0, sizeof (DB_DATA_ITEM_HEADER
));
1968 Hdr
.DataType
= DB_DATA_TYPE_STRING_DEFINITION
;
1969 if (fwrite (&Hdr
, sizeof (DB_DATA_ITEM_HEADER
), 1, DBFptr
) != 1) {
1970 Error (NULL
, 0, 0, "failed to write string header to output database file", NULL
);
1971 return STATUS_ERROR
;
1974 if (fwrite (&Flags
, sizeof (Flags
), 1, DBFptr
) != 1) {
1975 Error (NULL
, 0, 0, "failed to write string flags to output database", NULL
);
1976 return STATUS_ERROR
;
1979 if (StringDBWriteGenericString (DBFptr
, Language
) != STATUS_SUCCESS
) {
1980 return STATUS_ERROR
;
1983 if (StringDBWriteGenericString (DBFptr
, StringName
) != STATUS_SUCCESS
) {
1984 return STATUS_ERROR
;
1987 if (StringDBWriteGenericString (DBFptr
, Scope
) != STATUS_SUCCESS
) {
1988 return STATUS_ERROR
;
1991 if (StringDBWriteGenericString (DBFptr
, Str
) != STATUS_SUCCESS
) {
1992 return STATUS_ERROR
;
1995 // printf ("DBWriteString: %S.%S.%S\n", Language, StringName, Scope);
1997 return STATUS_SUCCESS
;
2002 StringDBReadString (
2013 if (fread (&Flags
, sizeof (Flags
), 1, DBFptr
) != 1) {
2014 Error (NULL
, 0, 0, "failed to read string flags from database", NULL
);
2015 return STATUS_ERROR
;
2018 if (StringDBReadGenericString (DBFptr
, &Size
, &Language
) != STATUS_SUCCESS
) {
2019 return STATUS_ERROR
;
2022 if (StringDBReadGenericString (DBFptr
, &Size
, &StringName
) != STATUS_SUCCESS
) {
2023 return STATUS_ERROR
;
2026 if (StringDBReadGenericString (DBFptr
, &Size
, &Scope
) != STATUS_SUCCESS
) {
2027 return STATUS_ERROR
;
2030 if (StringDBReadGenericString (DBFptr
, &Size
, &Str
) != STATUS_SUCCESS
) {
2031 return STATUS_ERROR
;
2034 // If the first or second string (language name and printable language name),
2035 // then skip them. They're added via language definitions data items in
2038 if (StringName
[0] != L
'$') {
2039 StringDBAddString (Language
, StringName
, Scope
, Str
, FALSE
, Flags
);
2042 // printf ("DBReadString: %S.%S.%S\n", Language, StringName, Scope);
2050 if (Scope
!= NULL
) {
2054 return STATUS_SUCCESS
;
2059 StringDBWriteLanguageDefinition (
2061 WCHAR
*LanguageName
,
2062 WCHAR
*PrintableLanguageName
2065 DB_DATA_ITEM_HEADER Hdr
;
2066 memset (&Hdr
, 0, sizeof (DB_DATA_ITEM_HEADER
));
2067 Hdr
.DataType
= DB_DATA_TYPE_LANGUAGE_DEFINITION
;
2068 if (fwrite (&Hdr
, sizeof (DB_DATA_ITEM_HEADER
), 1, DBFptr
) != 1) {
2069 Error (NULL
, 0, 0, "failed to write string to output database file", NULL
);
2070 return STATUS_ERROR
;
2073 if (StringDBWriteGenericString (DBFptr
, LanguageName
) != STATUS_SUCCESS
) {
2074 return STATUS_ERROR
;
2077 if (StringDBWriteGenericString (DBFptr
, PrintableLanguageName
) != STATUS_SUCCESS
) {
2078 return STATUS_ERROR
;
2081 return STATUS_SUCCESS
;
2086 StringDBReadLanguageDefinition (
2090 WCHAR
*LanguageName
;
2091 WCHAR
*PrintableLanguageName
;
2095 if (StringDBReadGenericString (DBFptr
, &Size
, &LanguageName
) != STATUS_SUCCESS
) {
2096 return STATUS_ERROR
;
2099 if (StringDBReadGenericString (DBFptr
, &Size
, &PrintableLanguageName
) != STATUS_SUCCESS
) {
2100 return STATUS_ERROR
;
2103 // printf("LANG: %S %S\n", LanguageName, PrintableLanguageName);
2105 Status
= StringDBAddLanguage (LanguageName
, PrintableLanguageName
);
2106 FREE (LanguageName
);
2107 FREE (PrintableLanguageName
);
2111 // All unicode strings in the database consist of a UINT16 length
2112 // field, followed by the string itself. This routine reads one
2113 // of those and returns the info.
2117 StringDBReadGenericString (
2127 if (fread (&LSize
, sizeof (UINT16
), 1, DBFptr
) != 1) {
2128 Error (NULL
, 0, 0, "failed to read a string length field from the database", NULL
);
2129 return STATUS_ERROR
;
2132 if (fread (&Flags
, sizeof (UINT16
), 1, DBFptr
) != 1) {
2133 Error (NULL
, 0, 0, "failed to read a string flags field from the database", NULL
);
2134 return STATUS_ERROR
;
2137 LStr
= MALLOC (LSize
);
2139 Error (__FILE__
, __LINE__
, 0, "memory allocation failed reading the database", NULL
);
2140 return STATUS_ERROR
;
2143 if (fread (LStr
, sizeof (WCHAR
), (UINT32
) LSize
/ sizeof (WCHAR
), DBFptr
) != (UINT32
) LSize
/ sizeof (WCHAR
)) {
2144 Error (NULL
, 0, 0, "failed to read string from database", NULL
);
2145 Error (NULL
, 0, 0, "database read failure", "offset 0x%X", ftell (DBFptr
));
2147 return STATUS_ERROR
;
2150 // printf ("DBR: %S\n", LStr);
2152 // If the flags field indicated we were asked to write a NULL string, then
2153 // return them a NULL pointer.
2155 if (Flags
& STRING_FLAGS_UNDEFINED
) {
2163 return STATUS_SUCCESS
;
2168 StringDBWriteGenericString (
2175 WCHAR ZeroString
[1];
2177 // Strings in the database consist of a size UINT16 followed
2178 // by the string itself.
2183 Size
= sizeof (ZeroString
);
2184 Flags
= STRING_FLAGS_UNDEFINED
;
2187 Size
= (UINT16
) ((StrLen (Str
) + 1) * sizeof (WCHAR
));
2190 if (fwrite (&Size
, sizeof (UINT16
), 1, DBFptr
) != 1) {
2191 Error (NULL
, 0, 0, "failed to write string size to database", NULL
);
2192 return STATUS_ERROR
;
2195 if (fwrite (&Flags
, sizeof (UINT16
), 1, DBFptr
) != 1) {
2196 Error (NULL
, 0, 0, "failed to write string flags to database", NULL
);
2197 return STATUS_ERROR
;
2200 if (fwrite (Str
, sizeof (WCHAR
), Size
/ sizeof (WCHAR
), DBFptr
) != Size
/ sizeof (WCHAR
)) {
2201 Error (NULL
, 0, 0, "failed to write string to database", NULL
);
2202 return STATUS_ERROR
;
2205 return STATUS_SUCCESS
;
2210 StringDBFindString (
2211 WCHAR
*LanguageName
,
2214 WCHAR_STRING_LIST
*LanguagesOfInterest
,
2215 WCHAR_MATCHING_STRING_LIST
*IndirectionList
2218 LANGUAGE_LIST
*Lang
;
2219 STRING_LIST
*CurrString
;
2220 WCHAR_MATCHING_STRING_LIST
*IndListPtr
;
2221 WCHAR TempLangName
[LANGUAGE_IDENTIFIER_NAME_LEN
+ 1];
2225 // If we were given an indirection list, then see if one was specified for this
2226 // string identifier. That is to say, if the indirection says "STR_ID_MY_FAVORITE MyScope",
2227 // then if this string name matches one in the list, then do a lookup with the
2228 // specified scope and return that value.
2230 if (IndirectionList
!= NULL
) {
2231 for (IndListPtr
= IndirectionList
; IndListPtr
!= NULL
; IndListPtr
= IndListPtr
->Next
) {
2232 if (StrCmp (StringName
, IndListPtr
->Str1
) == 0) {
2233 CurrString
= StringDBFindString (LanguageName
, StringName
, IndListPtr
->Str2
, LanguagesOfInterest
, NULL
);
2234 if (CurrString
!= NULL
) {
2241 // First look for exact match language.stringname
2243 for (Lang
= mDBData
.LanguageList
; Lang
!= NULL
; Lang
= Lang
->Next
) {
2244 if (StrCmp (LanguageName
, Lang
->LanguageName
) == 0) {
2246 // Found language match. Try to find string name match
2248 for (CurrString
= Lang
->String
; CurrString
!= NULL
; CurrString
= CurrString
->Next
) {
2249 if (StrCmp (StringName
, CurrString
->StringName
) == 0) {
2251 // Found a string name match. See if we're supposed to find
2254 if (Scope
!= NULL
) {
2255 if (StrCmp (CurrString
->Scope
, Scope
) == 0) {
2266 // If we got here, then we didn't find a match. Look for secondary string
2267 // matches. That is to say, if we're processing "spa", and they requested
2268 // "spa+cat", then recursively call with "cat"
2270 while (LanguagesOfInterest
!= NULL
) {
2272 // If this is the language we're looking for, then process the
2273 // languages of interest list for it.
2275 if (StrnCmp (LanguageName
, LanguagesOfInterest
->Str
, LANGUAGE_IDENTIFIER_NAME_LEN
) == 0) {
2276 WCharPtr
= LanguagesOfInterest
->Str
+ LANGUAGE_IDENTIFIER_NAME_LEN
;
2279 // Double-check the length, though it should have been checked on the
2282 if (StrLen (WCharPtr
) < LANGUAGE_IDENTIFIER_NAME_LEN
) {
2283 Error (NULL
, 0, 0, "malformed alternate language list", "%S", LanguagesOfInterest
->Str
);
2287 StrnCpy (TempLangName
, WCharPtr
, LANGUAGE_IDENTIFIER_NAME_LEN
);
2288 TempLangName
[LANGUAGE_IDENTIFIER_NAME_LEN
] = 0;
2289 CurrString
= StringDBFindString (TempLangName
, StringName
, NULL
, NULL
, IndirectionList
);
2290 if (CurrString
!= NULL
) {
2294 WCharPtr
+= LANGUAGE_IDENTIFIER_NAME_LEN
;
2298 LanguagesOfInterest
= LanguagesOfInterest
->Next
;
2310 // Free up existing scope memory.
2312 if (mDBData
.CurrentScope
!= NULL
) {
2313 FREE (mDBData
.CurrentScope
);
2316 mDBData
.CurrentScope
= DuplicateString (Scope
);
2317 return STATUS_SUCCESS
;
2320 // We typically don't assign index values to string identifiers
2321 // until we're ready to write out files. To reduce the size of
2322 // the output file, re-order the string identifiers to move any
2323 // unreferenced ones to the end. Then we'll walk the list
2324 // again to assign string indexes, keeping track of the last
2329 StringDBAssignStringIndexes (
2333 STRING_IDENTIFIER
*StrId
;
2334 STRING_IDENTIFIER
*FirstUsed
;
2335 STRING_IDENTIFIER
*LastUsed
;
2336 STRING_IDENTIFIER
*FirstUnused
;
2337 STRING_IDENTIFIER
*LastUnused
;
2339 UINT32 MaxReferenced
;
2342 // Create two lists -- used and unused. Then put them together with
2343 // the unused ones on the end.
2349 StrId
= mDBData
.StringIdentifier
;
2350 while (StrId
!= NULL
) {
2351 if ((StrId
->Flags
& STRING_FLAGS_REFERENCED
) == 0) {
2353 // Put it on the unused list
2355 if (FirstUnused
== NULL
) {
2356 FirstUnused
= StrId
;
2358 LastUnused
->Next
= StrId
;
2362 StrId
= StrId
->Next
;
2363 LastUnused
->Next
= NULL
;
2366 // Put it on the used list
2368 if (FirstUsed
== NULL
) {
2371 LastUsed
->Next
= StrId
;
2375 StrId
= StrId
->Next
;
2376 LastUsed
->Next
= NULL
;
2382 if (FirstUsed
!= NULL
) {
2383 mDBData
.StringIdentifier
= FirstUsed
;
2384 LastUsed
->Next
= FirstUnused
;
2386 mDBData
.StringIdentifier
= FirstUnused
;
2391 for (StrId
= mDBData
.StringIdentifier
; StrId
!= NULL
; StrId
= StrId
->Next
) {
2392 StrId
->Index
= Index
;
2394 if (StrId
->Flags
& STRING_FLAGS_REFERENCED
) {
2395 mDBData
.NumStringIdentifiersReferenced
= Index
;
2399 mDBData
.NumStringIdentifiers
= Index
;
2413 NewStr
= MALLOC ((StrLen (Str
) + 1) * sizeof (WCHAR
));
2414 if (NewStr
== NULL
) {
2415 Error (NULL
, 0, 0, "memory allocation failure", NULL
);
2419 StrCpy (NewStr
, Str
);
2433 Len
= strlen (Str
) + 1;
2434 NewStr
= (WCHAR
*) malloc (Len
* sizeof (WCHAR
));
2435 for (Ptr
= NewStr
; *Str
!= 0; Str
++, Ptr
++) {
2436 *Ptr
= (UINT16
) (UINT8
) *Str
;
2443 /*****************************************************************************/
2447 Routine Description:
2449 Create an HII export string pack for the strings in our database.
2453 FileName - name of the output file to write
2462 StringDBCreateHiiExportPack (
2467 LANGUAGE_LIST
*Lang
;
2468 STRING_LIST
*CurrString
;
2469 STRING_LIST EmptyString
;
2473 EFI_HII_STRING_PACK StringPack
;
2475 WCHAR ZeroString
[1];
2476 WCHAR
*TempStringPtr
;
2478 STRING_IDENTIFIER
*StringIdentifier
;
2480 if ((Fptr
= fopen (FileName
, "wb")) == NULL
) {
2481 Error (NULL
, 0, 0, FileName
, "failed to open output HII export file");
2482 return STATUS_ERROR
;
2485 // Assign index values to the string identifiers
2487 StringDBAssignStringIndexes ();
2489 // If a given string is not defined, then we'll use this one.
2491 memset (&EmptyString
, 0, sizeof (EmptyString
));
2492 EmptyString
.Size
= sizeof (ZeroString
);
2493 EmptyString
.Str
= ZeroString
;
2495 // Process each language, then each string for each langage
2498 for (Lang
= mDBData
.LanguageList
; Lang
!= NULL
; Lang
= Lang
->Next
) {
2500 // Process each string for this language. We have to make 3 passes on the strings:
2501 // Pass1: computes sizes and fill in the string pack header
2502 // Pass2: write the array of offsets
2503 // Pass3: write the strings
2506 // PASS 1: Fill in and print the HII string pack header
2508 // Compute the size for this language package and write
2509 // the header out. Each string package contains:
2511 // Offset[] -- an array of offsets to strings, of type RELOFST each
2512 // String[] -- the actual strings themselves
2514 memset ((char *) &StringPack
, 0, sizeof (EFI_HII_STRING_PACK
));
2515 StringPack
.Header
.Type
= EFI_HII_STRING
;
2516 StringPack
.NumStringPointers
= (UINT16
) mDBData
.NumStringIdentifiersReferenced
;
2517 LangName
= Lang
->LanguageName
;
2519 // First string is the language name. If we're printing all languages, then
2520 // it's just the "spa". If we were given a list of languages to print, then it's
2521 // the "spacat" string. Compute its offset and fill in
2522 // the info in the header. Since we know the language name string's length,
2523 // and the printable language name follows it, use that info to fill in the
2524 // entry for the printable language name as well.
2526 StringPack
.LanguageNameString
= (STRING_OFFSET
) (sizeof (EFI_HII_STRING_PACK
) + (mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
)));
2527 StringPack
.PrintableLanguageName
= (STRING_OFFSET
) (StringPack
.LanguageNameString
+ (StrLen (LangName
) + 1) * sizeof (WCHAR
));
2529 // Add up the size of all strings so we can fill in our header.
2532 for (StringIndex
= 0; StringIndex
< mDBData
.NumStringIdentifiersReferenced
; StringIndex
++) {
2534 // For the first string (language name), we print out the "spacat" if they
2535 // requested it. We set LangName to point to the proper language name string above.
2537 if (StringIndex
== STRING_ID_LANGUAGE_NAME
) {
2538 Len
+= (StrLen (LangName
) + 1) * sizeof (WCHAR
);
2541 // Find a string with this language.stringname
2543 StringIdentifier
= StringDBFindStringIdentifierByIndex (StringIndex
);
2544 if (StringIdentifier
== NULL
) {
2545 Error (NULL
, 0, 0, "internal error", "invalid string index 0x%X", StringIndex
);
2546 return STATUS_ERROR
;
2549 // Find a matching string if this string identifier was referenced
2551 EmptyString
.Flags
= STRING_FLAGS_UNDEFINED
;
2553 if (StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) {
2554 CurrString
= StringDBFindString (
2556 StringIdentifier
->StringName
,
2558 NULL
, // LanguagesOfInterest,
2562 // IndirectionList);
2564 if (NULL
== CurrString
) {
2566 // If string for Lang->LanguageName is not found, try to get an English version
2568 CurrString
= StringDBFindString (
2570 StringIdentifier
->StringName
,
2572 NULL
, // LanguagesOfInterest,
2576 // IndirectionList);
2581 if (CurrString
== NULL
) {
2582 CurrString
= &EmptyString
;
2583 EmptyString
.Flags
|= StringIdentifier
->Flags
;
2586 Len
+= CurrString
->Size
;
2589 StringPack
.Header
.Length
= sizeof (EFI_HII_STRING_PACK
)
2590 + mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
)
2593 // Write out the string pack header
2595 fwrite ((void *) &StringPack
, sizeof (StringPack
), 1, Fptr
);
2597 // PASS2 : write the offsets
2599 // Traverse the list of strings again and write the array of offsets. The
2600 // offset to the first string is the size of the string pack header
2601 // plus the size of the offsets array. The other strings follow it.
2604 Offset
= sizeof (StringPack
) + mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
);
2605 for (StringIndex
= 0; StringIndex
< mDBData
.NumStringIdentifiersReferenced
; StringIndex
++) {
2609 fwrite (&Offset
, sizeof (STRING_OFFSET
), 1, Fptr
);
2611 // Find the string name
2613 StringIdentifier
= StringDBFindStringIdentifierByIndex (StringIndex
);
2614 if (StringIdentifier
== NULL
) {
2615 Error (NULL
, 0, 0, "internal error", "invalid string index 0x%X", StringIndex
);
2616 return STATUS_ERROR
;
2619 // For the first string (language name), we print out the "spacat" if they
2620 // requested it. We set LangName to point to the proper language name string above.
2622 if (StringIndex
== STRING_ID_LANGUAGE_NAME
) {
2623 Offset
+= (StrLen (LangName
) + 1) * sizeof (WCHAR
);
2624 CurrString
= StringDBFindString (
2626 StringIdentifier
->StringName
,
2633 // Find a matching string
2635 CurrString
= StringDBFindString (
2637 StringIdentifier
->StringName
,
2639 NULL
, // LanguagesOfInterest,
2643 // IndirectionList);
2645 if (NULL
== CurrString
) {
2646 CurrString
= StringDBFindString (
2648 StringIdentifier
->StringName
,
2650 NULL
, // LanguagesOfInterest,
2654 // IndirectionList);
2658 EmptyString
.LanguageName
= Lang
->LanguageName
;
2659 if (CurrString
== NULL
) {
2660 CurrString
= &EmptyString
;
2661 EmptyString
.Flags
= STRING_FLAGS_UNDEFINED
;
2662 } else if ((StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) == 0) {
2663 CurrString
= &EmptyString
;
2664 EmptyString
.Flags
= 0;
2667 Offset
+= CurrString
->Size
;
2672 // PASS 3: write the strings themselves.
2674 Offset
= sizeof (StringPack
) + mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
);
2675 for (StringIndex
= 0; StringIndex
< mDBData
.NumStringIdentifiersReferenced
; StringIndex
++) {
2676 StringIdentifier
= StringDBFindStringIdentifierByIndex (StringIndex
);
2677 if (StringIdentifier
== NULL
) {
2678 Error (NULL
, 0, 0, "internal error", "invalid string index 0x%X", StringIndex
);
2679 return STATUS_ERROR
;
2682 // For the first string (language name), we print out the "spacat" if they
2683 // requested it. We set LangName to point to the proper language name string above.
2685 if (StringIndex
== STRING_ID_LANGUAGE_NAME
) {
2686 TempStringPtr
= LangName
;
2689 // Find a matching string if this string identifier was referenced
2692 if (StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) {
2693 CurrString
= StringDBFindString (
2695 StringIdentifier
->StringName
,
2697 NULL
, // LanguagesOfInterest,
2701 // IndirectionList);
2703 if (NULL
== CurrString
) {
2704 CurrString
= StringDBFindString (
2706 StringIdentifier
->StringName
,
2708 NULL
, // LanguagesOfInterest,
2712 // IndirectionList);
2717 if (CurrString
== NULL
) {
2718 CurrString
= &EmptyString
;
2721 TempStringPtr
= CurrString
->Str
;
2724 for (TempIndex
= 0; TempStringPtr
[TempIndex
] != 0; TempIndex
++) {
2725 fwrite (&TempStringPtr
[TempIndex
], sizeof (CHAR16
), 1, Fptr
);
2729 // Print NULL WCHAR at the end of this string.
2732 fwrite (&TempIndex
, sizeof (CHAR16
), 1, Fptr
);
2736 // Sanity check the offset. Make sure our running offset is what we put in the
2737 // string pack header.
2739 if (StringPack
.Header
.Length
!= Offset
) {
2744 "application error",
2745 "stringpack size 0x%X does not match final size 0x%X",
2746 StringPack
.Header
.Length
,
2752 // Print terminator string pack, closing brace and close the file.
2753 // The size of 0 triggers to the consumer that this is the end.
2755 memset ((char *) &StringPack
, 0, sizeof (EFI_HII_STRING_PACK
));
2756 StringPack
.Header
.Type
= EFI_HII_STRING
;
2757 fwrite ((void *) &StringPack
, sizeof (StringPack
), 1, Fptr
);
2759 return STATUS_SUCCESS
;