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>
29 #include "EfiUtilityMsgs.h"
30 #include "StrGather.h"
32 #include "InternalFormRepresentation.h"
34 // #include EFI_PROTOCOL_DEFINITION (Hii)
38 #define STRING_OFFSET RELOFST
40 #define STRING_DB_KEY (('S' << 24) | ('D' << 16) | ('B' << 8) | 'K')
42 // Version supported by this tool
44 #define STRING_DB_VERSION 0x00010000
46 #define STRING_DB_MAJOR_VERSION_MASK 0xFFFF0000
47 #define STRING_DB_MINOR_VERSION_MASK 0x0000FFFF
49 #define DEFINE_STR L"// #define"
51 #define LANGUAGE_CODE_WIDTH 4
53 // This is the header that gets written to the top of the
54 // output binary database file.
60 UINT32 NumStringIdenfiers
;
61 UINT32 StringIdentifiersSize
;
66 // When we write out data to the database, we have a UINT16 identifier, which
67 // indicates what follows, followed by the data. Here's the structure.
72 } DB_DATA_ITEM_HEADER
;
74 #define DB_DATA_TYPE_INVALID 0x0000
75 #define DB_DATA_TYPE_STRING_IDENTIFIER 0x0001
76 #define DB_DATA_TYPE_LANGUAGE_DEFINITION 0x0002
77 #define DB_DATA_TYPE_STRING_DEFINITION 0x0003
78 #define DB_DATA_TYPE_LAST DB_DATA_TYPE_STRING_DEFINITION
81 // We have to keep track of a list of languages, each of which has its own
82 // list of strings. Define a structure to keep track of all languages and
83 // their list of strings.
85 typedef struct _STRING_LIST
{
86 struct _STRING_LIST
*Next
;
87 UINT32 Size
; // number of bytes in string, including null terminator
89 WCHAR
*StringName
; // for example STR_ID_TEXT1
91 WCHAR
*Str
; // the actual string
92 UINT16 Flags
; // properties of this string (used, undefined)
95 typedef struct _LANGUAGE_LIST
{
96 struct _LANGUAGE_LIST
*Next
;
97 WCHAR LanguageName
[4];
98 WCHAR
*PrintableLanguageName
;
100 STRING_LIST
*LastString
;
104 // We also keep track of all the string identifier names, which we assign unique
105 // values to. Create a structure to keep track of them all.
107 typedef struct _STRING_IDENTIFIER
{
108 struct _STRING_IDENTIFIER
*Next
;
109 UINT32 Index
; // only need 16 bits, but makes it easier with UINT32
111 UINT16 Flags
; // if someone referenced it via STRING_TOKEN()
114 // Keep our globals in this structure to be as modular as possible.
118 LANGUAGE_LIST
*LanguageList
;
119 LANGUAGE_LIST
*LastLanguageList
;
120 LANGUAGE_LIST
*CurrentLanguage
; // keep track of the last language they used
121 STRING_IDENTIFIER
*StringIdentifier
;
122 STRING_IDENTIFIER
*LastStringIdentifier
;
123 UINT8
*StringDBFileName
;
124 UINT32 NumStringIdentifiers
;
125 UINT32 NumStringIdentifiersReferenced
;
126 STRING_IDENTIFIER
*CurrentStringIdentifier
; // keep track of the last string identifier they added
130 static STRING_DB_DATA mDBData
;
132 static const char *mSourceFileHeader
[] = {
134 "// DO NOT EDIT -- auto-generated file",
136 "// This file is generated by the string gather utility",
147 WCHAR_STRING_LIST
*LanguagesOfInterest
,
148 WCHAR_MATCHING_STRING_LIST
*IndirectionList
153 StringDBFindStringIdentifierByName (
159 StringDBFindStringIdentifierByIndex (
165 StringDBFindLanguageList (
171 StringDBWriteStandardFileHeader (
189 StringDBWriteStringIdentifier (
193 WCHAR
*IdentifierName
198 StringDBReadStringIdentifier (
204 StringDBWriteLanguageDefinition (
207 WCHAR
*PrintableLanguageName
212 StringDBReadLanguageDefinition (
218 StringDBWriteString (
235 StringDBReadGenericString (
243 StringDBWriteGenericString (
250 StringDBAssignStringIndexes (
254 /*****************************************************************************/
259 Constructor function for the string database handler.
269 StringDBConstructor (
273 memset ((char *) &mDBData
, 0, sizeof (STRING_DB_DATA
));
274 mDBData
.CurrentScope
= DuplicateString (L
"NULL");
277 /*****************************************************************************/
282 Destructor function for the string database handler.
296 LANGUAGE_LIST
*NextLang
;
297 STRING_LIST
*NextStr
;
298 STRING_IDENTIFIER
*NextIdentifier
;
300 // Close the database file if it's open
302 if (mDBData
.StringDBFptr
!= NULL
) {
303 fclose (mDBData
.StringDBFptr
);
304 mDBData
.StringDBFptr
= NULL
;
307 // If we've allocated any strings/languages, free them up
309 while (mDBData
.LanguageList
!= NULL
) {
310 NextLang
= mDBData
.LanguageList
->Next
;
312 // Free up all strings for this language
314 while (mDBData
.LanguageList
->String
!= NULL
) {
315 NextStr
= mDBData
.LanguageList
->String
->Next
;
316 FREE (mDBData
.LanguageList
->String
->Str
);
317 FREE (mDBData
.LanguageList
->String
);
318 mDBData
.LanguageList
->String
= NextStr
;
321 FREE (mDBData
.LanguageList
->PrintableLanguageName
);
322 FREE (mDBData
.LanguageList
);
323 mDBData
.LanguageList
= NextLang
;
326 // Free up string identifiers
328 while (mDBData
.StringIdentifier
!= NULL
) {
329 NextIdentifier
= mDBData
.StringIdentifier
->Next
;
330 FREE (mDBData
.StringIdentifier
->StringName
);
331 FREE (mDBData
.StringIdentifier
);
332 mDBData
.StringIdentifier
= NextIdentifier
;
337 if (mDBData
.StringDBFileName
!= NULL
) {
338 FREE (mDBData
.StringDBFileName
);
339 mDBData
.StringDBFileName
= NULL
;
342 // We save a copy of the scope, so free it up if we
345 if (mDBData
.CurrentScope
!= NULL
) {
346 FREE (mDBData
.CurrentScope
);
347 mDBData
.CurrentScope
= NULL
;
351 /*****************************************************************************/
357 Dump the contents of a database to an output C file.
361 FileName - name of the output file to write
362 BaseName - used for the name of the C array defined
363 Languages - list of languages of interest
371 Languages is a pointer to a linked list of languages specified on
372 the command line. Format is "eng" and "spa+cat". For this, print
373 the strings for eng. Print the strings for spa too, but if one is
374 missing look for a cat string and print if it it exists.
378 StringDBDumpCStrings (
381 WCHAR_STRING_LIST
*LanguagesOfInterest
,
382 WCHAR_MATCHING_STRING_LIST
*IndirectionList
387 STRING_LIST
*CurrString
;
388 STRING_LIST EmptyString
;
392 UINT32 BytesThisLine
;
393 EFI_HII_STRING_PACK StringPack
;
397 WCHAR_STRING_LIST
*LOIPtr
;
399 WCHAR
*TempStringPtr
;
401 STRING_IDENTIFIER
*StringIdentifier
;
403 if ((Fptr
= fopen (FileName
, "w")) == NULL
) {
404 Error (NULL
, 0, 0, FileName
, "failed to open output C string file");
408 // Assign index values to the string identifiers
410 StringDBAssignStringIndexes ();
412 // Write the standard header to the output file, then the structure
413 // definition header.
415 StringDBWriteStandardFileHeader (Fptr
);
416 fprintf (Fptr
, "\nunsigned char %s[] = {\n", BaseName
);
418 // If a given string is not defined, then we'll use this one.
420 memset (&EmptyString
, 0, sizeof (EmptyString
));
421 EmptyString
.Size
= sizeof (ZeroString
);
422 EmptyString
.Str
= ZeroString
;
424 // Process each language, then each string for each langage
427 for (Lang
= mDBData
.LanguageList
; Lang
!= NULL
; Lang
= Lang
->Next
) {
429 // If we have a language list, then make sure this language is in that
433 LangName
= Lang
->LanguageName
;
434 if (LanguagesOfInterest
!= NULL
) {
436 for (LOIPtr
= LanguagesOfInterest
; LOIPtr
!= NULL
; LOIPtr
= LOIPtr
->Next
) {
437 if (wcsncmp (LOIPtr
->Str
, Lang
->LanguageName
, LANGUAGE_IDENTIFIER_NAME_LEN
) == 0) {
438 LangName
= LOIPtr
->Str
;
449 // Process each string for this language. We have to make 3 passes on the strings:
450 // Pass1: computes sizes and fill in the string pack header
451 // Pass2: write the array of offsets
452 // Pass3: write the strings
455 // PASS 1: Fill in and print the HII string pack header
457 // Compute the size for this language package and write
458 // the header out. Each string package contains:
460 // Offset[] -- an array of offsets to strings, of type RELOFST each
461 // String[] -- the actual strings themselves
465 "\n//******************************************************************************"
466 "\n// Start of string definitions for %S/%S",
468 Lang
->PrintableLanguageName
470 memset ((char *) &StringPack
, 0, sizeof (EFI_HII_STRING_PACK
));
471 StringPack
.Header
.Type
= EFI_HII_STRING
;
472 StringPack
.NumStringPointers
= (UINT16
) mDBData
.NumStringIdentifiersReferenced
;
474 // First string is the language name. If we're printing all languages, then
475 // it's just the "spa". If we were given a list of languages to print, then it's
476 // the "spacat" string. Compute its offset and fill in
477 // the info in the header. Since we know the language name string's length,
478 // and the printable language name follows it, use that info to fill in the
479 // entry for the printable language name as well.
481 StringPack
.LanguageNameString
= (STRING_OFFSET
) (sizeof (EFI_HII_STRING_PACK
) + (mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
)));
482 StringPack
.PrintableLanguageName
= (STRING_OFFSET
) (StringPack
.LanguageNameString
+ (wcslen (LangName
) + 1) * sizeof (WCHAR
));
484 // Add up the size of all strings so we can fill in our header.
487 for (StringIndex
= 0; StringIndex
< mDBData
.NumStringIdentifiersReferenced
; StringIndex
++) {
489 // For the first string (language name), we print out the "spacat" if they
490 // requested it. We set LangName to point to the proper language name string above.
492 if (StringIndex
== STRING_ID_LANGUAGE_NAME
) {
493 Len
+= (wcslen (LangName
) + 1) * sizeof (WCHAR
);
496 // Find a string with this language.stringname
498 StringIdentifier
= StringDBFindStringIdentifierByIndex (StringIndex
);
499 if (StringIdentifier
== NULL
) {
500 Error (NULL
, 0, 0, "internal error", "invalid string index 0x%X", StringIndex
);
504 // Find a matching string if this string identifier was referenced
506 EmptyString
.Flags
= STRING_FLAGS_UNDEFINED
;
508 if (StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) {
509 CurrString
= StringDBFindString (
511 StringIdentifier
->StringName
,
516 if (NULL
== CurrString
) {
518 // If string for Lang->LanguageName is not found, try to get an English version
520 CurrString
= StringDBFindString (
522 StringIdentifier
->StringName
,
530 if (CurrString
== NULL
) {
531 CurrString
= &EmptyString
;
532 EmptyString
.Flags
|= StringIdentifier
->Flags
;
535 Len
+= CurrString
->Size
;
538 StringPack
.Header
.Length
= sizeof (EFI_HII_STRING_PACK
)
539 + mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
)
542 // Write out the header one byte at a time
544 Ptr
= (UINT8
*) &StringPack
;
545 for (TempIndex
= 0; TempIndex
< sizeof (EFI_HII_STRING_PACK
); TempIndex
++, Ptr
++) {
546 if ((TempIndex
& 0x07) == 0) {
547 fprintf (Fptr
, "\n ");
550 fprintf (Fptr
, "0x%02X, ", (UINT32
) *Ptr
);
553 fprintf (Fptr
, "\n // offset 0x%X\n", sizeof (StringPack
));
555 // PASS2 : write the offsets
557 // Traverse the list of strings again and write the array of offsets. The
558 // offset to the first string is the size of the string pack header
559 // plus the size of the offsets array. The other strings follow it.
562 Offset
= sizeof (StringPack
) + mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
);
563 for (StringIndex
= 0; StringIndex
< mDBData
.NumStringIdentifiersReferenced
; StringIndex
++) {
565 // Write the offset, followed by a useful comment
568 Ptr
= (UINT8
*) &Offset
;
569 for (TempIndex
= 0; TempIndex
< sizeof (STRING_OFFSET
); TempIndex
++) {
570 fprintf (Fptr
, "0x%02X, ", (UINT32
) Ptr
[TempIndex
]);
573 // Find the string name
575 StringIdentifier
= StringDBFindStringIdentifierByIndex (StringIndex
);
576 if (StringIdentifier
== NULL
) {
577 Error (NULL
, 0, 0, "internal error", "invalid string index 0x%X", StringIndex
);
581 fprintf (Fptr
, " // offset to string %S (0x%04X)", StringIdentifier
->StringName
, StringIndex
);
583 // For the first string (language name), we print out the "spacat" if they
584 // requested it. We set LangName to point to the proper language name string above.
586 if (StringIndex
== STRING_ID_LANGUAGE_NAME
) {
587 Offset
+= (wcslen (LangName
) + 1) * sizeof (WCHAR
);
588 CurrString
= StringDBFindString (
590 StringIdentifier
->StringName
,
597 // Find a matching string
599 CurrString
= StringDBFindString (
601 StringIdentifier
->StringName
,
607 if (NULL
== CurrString
) {
608 CurrString
= StringDBFindString (
610 StringIdentifier
->StringName
,
617 EmptyString
.LanguageName
= Lang
->LanguageName
;
618 if (CurrString
== NULL
) {
619 CurrString
= &EmptyString
;
620 EmptyString
.Flags
= STRING_FLAGS_UNDEFINED
;
621 } else if ((StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) == 0) {
622 CurrString
= &EmptyString
;
623 EmptyString
.Flags
= 0;
626 Offset
+= CurrString
->Size
;
629 // Print useful info about this string
631 if ((StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) == 0) {
632 fprintf (Fptr
, " - not referenced");
635 if (CurrString
->Flags
& STRING_FLAGS_UNDEFINED
) {
636 fprintf (Fptr
, " - not defined for this language");
637 } else if (wcscmp (CurrString
->LanguageName
, Lang
->LanguageName
) != 0) {
640 " - not defined for this language -- using secondary language %S definition",
641 CurrString
->LanguageName
645 fprintf (Fptr
, "\n");
648 // For unreferenced string identifiers, print a message that they are not referenced anywhere
650 while (StringIndex
< mDBData
.NumStringIdentifiers
) {
651 StringIdentifier
= StringDBFindStringIdentifierByIndex (StringIndex
);
652 if (StringIdentifier
!= NULL
) {
653 fprintf (Fptr
, " // %S not referenced\n", StringIdentifier
->StringName
);
660 // PASS 3: write the strings themselves.
661 // Keep track of how many bytes we write per line because some editors
662 // (Visual Studio for instance) can't handle too long of lines.
664 Offset
= sizeof (StringPack
) + mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
);
665 for (StringIndex
= 0; StringIndex
< mDBData
.NumStringIdentifiersReferenced
; StringIndex
++) {
666 StringIdentifier
= StringDBFindStringIdentifierByIndex (StringIndex
);
667 if (StringIdentifier
== NULL
) {
668 Error (NULL
, 0, 0, "internal error", "invalid string index 0x%X", StringIndex
);
672 fprintf (Fptr
, " // string %S offset 0x%08X\n ", StringIdentifier
->StringName
, Offset
);
674 // For the first string (language name), we print out the "spacat" if they
675 // requested it. We set LangName to point to the proper language name string above.
677 if (StringIndex
== STRING_ID_LANGUAGE_NAME
) {
678 TempStringPtr
= LangName
;
681 // Find a matching string if this string identifier was referenced
684 if (StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) {
685 CurrString
= StringDBFindString (
687 StringIdentifier
->StringName
,
692 if (NULL
== CurrString
) {
693 CurrString
= StringDBFindString (
695 StringIdentifier
->StringName
,
703 if (CurrString
== NULL
) {
704 CurrString
= &EmptyString
;
707 TempStringPtr
= CurrString
->Str
;
711 for (TempIndex
= 0; TempStringPtr
[TempIndex
] != 0; TempIndex
++) {
715 (UINT32
) TempStringPtr
[TempIndex
] & 0xFF,
716 (UINT32
) ((TempStringPtr
[TempIndex
] >> 8) & 0xFF)
721 // Let's say we only allow 14 per line
723 if (BytesThisLine
> 14) {
724 fprintf (Fptr
, "\n ");
729 // Print NULL WCHAR at the end of this string.
731 fprintf (Fptr
, "0x00, 0x00,\n");
735 // Sanity check the offset. Make sure our running offset is what we put in the
736 // string pack header.
738 if (StringPack
.Header
.Length
!= Offset
) {
744 "stringpack size 0x%X does not match final size 0x%X",
745 StringPack
.Header
.Length
,
751 // Print terminator string pack, closing brace and close the file.
752 // The size of 0 triggers to the consumer that this is the end.
754 memset ((char *) &StringPack
, 0, sizeof (EFI_HII_STRING_PACK
));
755 StringPack
.Header
.Type
= EFI_HII_STRING
;
756 Ptr
= (UINT8
*) &StringPack
;
757 fprintf (Fptr
, "\n // strings terminator pack");
758 for (TempIndex
= 0; TempIndex
< sizeof (StringPack
); TempIndex
++, Ptr
++) {
759 if ((TempIndex
& 0x0F) == 0) {
760 fprintf (Fptr
, "\n ");
763 fprintf (Fptr
, "0x%02X, ", (UINT32
) *Ptr
);
766 fprintf (Fptr
, "\n};\n");
768 return STATUS_SUCCESS
;
771 /*****************************************************************************/
777 Dump the #define string names
781 FileName - name of the output file to write
782 BaseName - used for the protection #ifndef/#endif
790 StringDBDumpStringDefines (
796 STRING_IDENTIFIER
*Identifier
;
797 INT8 CopyBaseName
[100];
799 const INT8
*StrDefHeader
[] = {
800 "#ifndef _%s_STRINGS_DEFINE_H_\n",
801 "#define _%s_STRINGS_DEFINE_H_\n\n",
805 if ((Fptr
= fopen (FileName
, "w")) == NULL
) {
806 Error (NULL
, 0, 0, FileName
, "failed to open output string defines file");
810 // Get the base source filename and convert to uppercase.
812 if (sizeof (CopyBaseName
) <= strlen (BaseName
) + 1) {
813 Error (NULL
, 0, 0, "application error", "StringDBDumpStringDefines() string length insufficient");
817 strcpy (CopyBaseName
, BaseName
);
818 for (Index
= 0; CopyBaseName
[Index
] != 0; Index
++) {
819 if (islower (CopyBaseName
[Index
])) {
820 CopyBaseName
[Index
] = (INT8
) toupper (CopyBaseName
[Index
]);
824 // Assign index values to the string identifiers
826 StringDBAssignStringIndexes ();
828 // Write the standard header to the output file, and then the
829 // protective #ifndef.
831 StringDBWriteStandardFileHeader (Fptr
);
832 for (Index
= 0; StrDefHeader
[Index
] != NULL
; Index
++) {
833 fprintf (Fptr
, StrDefHeader
[Index
], CopyBaseName
);
836 // Print all the #defines for the string identifiers. Print identifiers
837 // whose names start with '$' as comments. Add comments for string
838 // identifiers not used as well.
840 Identifier
= mDBData
.StringIdentifier
;
841 while (Identifier
!= NULL
) {
842 if (Identifier
->StringName
[0] == L
'$') {
843 fprintf (Fptr
, "// ");
846 if (Identifier
->Flags
& STRING_FLAGS_REFERENCED
) {
847 fprintf (Fptr
, "#define %-40S 0x%04X\n", Identifier
->StringName
, Identifier
->Index
);
849 fprintf (Fptr
, "//#define %-40S 0x%04X // not referenced\n", Identifier
->StringName
, Identifier
->Index
);
852 Identifier
= Identifier
->Next
;
855 fprintf (Fptr
, "\n#endif\n");
857 return STATUS_SUCCESS
;
860 /*****************************************************************************/
866 Add a string identifier to the database.
870 StringName - name of the string identifier. For example "STR_MY_STRING"
871 NewId - if an ID has been assigned
872 Flags - characteristics for the identifier
880 StringDBAddStringIdentifier (
886 STRING_IDENTIFIER
*StringIdentifier
;
889 // If it was already used for some other language, then we don't
890 // need to add it. But set it to the current string identifier.
891 // The referenced bit is sticky.
893 Status
= STATUS_SUCCESS
;
894 StringIdentifier
= StringDBFindStringIdentifierByName (StringName
);
895 if (StringIdentifier
!= NULL
) {
896 if (Flags
& STRING_FLAGS_REFERENCED
) {
897 StringIdentifier
->Flags
|= STRING_FLAGS_REFERENCED
;
900 mDBData
.CurrentStringIdentifier
= StringIdentifier
;
901 *NewId
= (UINT16
) StringIdentifier
->Index
;
905 StringIdentifier
= (STRING_IDENTIFIER
*) MALLOC (sizeof (STRING_IDENTIFIER
));
906 if (StringIdentifier
== NULL
) {
907 Error (NULL
, 0, 0, NULL
, "memory allocation error");
911 memset ((char *) StringIdentifier
, 0, sizeof (STRING_IDENTIFIER
));
912 StringIdentifier
->StringName
= (WCHAR
*) malloc ((wcslen (StringName
) + 1) * sizeof (WCHAR
));
913 if (StringIdentifier
->StringName
== NULL
) {
914 Error (NULL
, 0, 0, NULL
, "memory allocation error");
918 wcscpy (StringIdentifier
->StringName
, StringName
);
919 if (*NewId
!= STRING_ID_INVALID
) {
920 StringIdentifier
->Index
= *NewId
;
921 StringIdentifier
->Flags
|= STRING_FLAGS_INDEX_ASSIGNED
;
922 if (mDBData
.NumStringIdentifiers
<= StringIdentifier
->Index
) {
923 mDBData
.NumStringIdentifiers
= StringIdentifier
->Index
+ 1;
926 StringIdentifier
->Index
= mDBData
.NumStringIdentifiers
++;
929 StringIdentifier
->Flags
|= Flags
;
931 // Add it to our list of string identifiers
933 if (mDBData
.StringIdentifier
== NULL
) {
934 mDBData
.StringIdentifier
= StringIdentifier
;
936 mDBData
.LastStringIdentifier
->Next
= StringIdentifier
;
939 mDBData
.LastStringIdentifier
= StringIdentifier
;
940 mDBData
.CurrentStringIdentifier
= StringIdentifier
;
941 *NewId
= (UINT16
) StringIdentifier
->Index
;
945 /*****************************************************************************/
951 Add a new string to the database.
955 LanguageName - "eng" or "spa" language name
956 StringName - "STR_MY_TEXT" string name
957 Scope - from the #scope statements in the string file
958 Format - if we should format the string
959 Flags - characteristic flags for the string
967 Several of the fields can be "inherited" from the previous calls to
968 our database functions. For example, if scope is NULL here, then
969 we'll use the previous setting.
986 WCHAR TempLangName
[4];
987 STRING_IDENTIFIER
*StringIdentifier
;
990 // Check that language name is exactly 3 characters, or emit an error.
991 // Truncate at 3 if it's longer, or make it 3 if it's shorter.
993 if (LanguageName
!= NULL
) {
994 Size
= wcslen (LanguageName
);
996 ParserError (0, "invalid length for language name", "%S", LanguageName
);
1001 // Make a local copy of the language name string, and extend to
1002 // 3 characters since we make assumptions elsewhere in this program
1005 wcscpy (TempLangName
, LanguageName
);
1006 for (; Size
< 3; Size
++) {
1007 TempLangName
[Size
] = L
'?';
1010 TempLangName
[4] = 0;
1011 LanguageName
= TempLangName
;
1016 // If they specified a language, make sure they've defined it already
1017 // via a #langdef statement. Otherwise use the current default language.
1019 if (LanguageName
!= NULL
) {
1020 Lang
= StringDBFindLanguageList (LanguageName
);
1022 ParserError (0, "language not defined", "%S", LanguageName
);
1023 return STATUS_ERROR
;
1025 StringDBSetCurrentLanguage (LanguageName
);
1028 Lang
= mDBData
.CurrentLanguage
;
1031 // Have to call SetLanguage() first
1033 ParserError (0, "no language defined", "%S", StringName
);
1034 return STATUS_ERROR
;
1038 // If they didn't define a string identifier, use the last string identifier
1041 if (StringName
== NULL
) {
1042 StringName
= mDBData
.CurrentStringIdentifier
->StringName
;
1043 if (StringName
== NULL
) {
1044 ParserError (0, "no string identifier previously specified", NULL
);
1045 return STATUS_ERROR
;
1049 // If scope was not specified, use the default setting
1051 if (Scope
!= NULL
) {
1052 Scope
= DuplicateString (Scope
);
1054 Scope
= DuplicateString (mDBData
.CurrentScope
);
1057 // printf ("Adding string: %S.%S.%S\n", Lang->LanguageName, StringName, Scope);
1059 // Check for duplicates for this Language.StringName.Scope. Allow multiple
1060 // definitions of the language name and printable language name, since the
1061 // user does not specifically define them.
1063 if (StringDBFindString (Lang
->LanguageName
, StringName
, Scope
, NULL
, NULL
) != NULL
) {
1064 if ((wcscmp (StringName
, LANGUAGE_NAME_STRING_NAME
) == 0) &&
1065 (wcscmp (StringName
, PRINTABLE_LANGUAGE_NAME_STRING_NAME
) == 0)
1069 "string multiply defined",
1070 "Language.Name.Scope = %S.%S.%S",
1075 return STATUS_ERROR
;
1079 StringIndex
= STRING_ID_INVALID
;
1080 if (StringDBAddStringIdentifier (StringName
, &StringIndex
, Flags
) != STATUS_SUCCESS
) {
1081 return STATUS_ERROR
;
1084 StringIdentifier
= StringDBFindStringIdentifierByName (StringName
);
1086 // Add this string to the end of the strings for this language.
1088 Str
= (STRING_LIST
*) malloc (sizeof (STRING_LIST
));
1090 Error (NULL
, 0, 0, NULL
, "memory allocation error");
1091 return STATUS_ERROR
;
1094 memset ((char *) Str
, 0, sizeof (STRING_LIST
));
1095 Size
= (wcslen (String
) + 1) * sizeof (WCHAR
);
1098 Str
->StringName
= StringIdentifier
->StringName
;
1099 Str
->LanguageName
= DuplicateString (LanguageName
);
1100 Str
->Str
= (WCHAR
*) MALLOC (Size
);
1101 if (Str
->Str
== NULL
) {
1102 Error (NULL
, 0, 0, NULL
, "memory allocation error");
1103 return STATUS_ERROR
;
1106 // If not formatting, just copy the string.
1108 wcscpy (Str
->Str
, String
);
1110 StringDBFormatString (Str
->Str
);
1113 // Size may change after formatting. We set the size to
1114 // the actual size of the string, including the null for
1115 // easier processing later.
1117 Str
->Size
= (wcslen (Str
->Str
) + 1) * sizeof (WCHAR
);
1118 if (Lang
->String
== NULL
) {
1121 Lang
->LastString
->Next
= Str
;
1124 Lang
->LastString
= Str
;
1125 return STATUS_SUCCESS
;
1128 /*****************************************************************************/
1132 Routine Description:
1134 Given a language name, see if a language list for it has been defined
1138 LanguageName - like "eng"
1142 A pointer to the language list
1147 StringDBFindLanguageList (
1151 LANGUAGE_LIST
*Lang
;
1153 Lang
= mDBData
.LanguageList
;
1154 while (Lang
!= NULL
) {
1155 if (wcscmp (LanguageName
, Lang
->LanguageName
) == 0) {
1165 /*****************************************************************************/
1167 StringDBSetCurrentLanguage (
1171 LANGUAGE_LIST
*Lang
;
1173 Lang
= StringDBFindLanguageList (LanguageName
);
1175 ParserError (0, "language not previously defined", "%S", LanguageName
);
1176 return STATUS_ERROR
;
1179 mDBData
.CurrentLanguage
= Lang
;
1180 return STATUS_SUCCESS
;
1183 /*****************************************************************************/
1185 StringDBAddLanguage (
1186 WCHAR
*LanguageName
,
1187 WCHAR
*PrintableLanguageName
1190 LANGUAGE_LIST
*Lang
;
1192 // Check for redefinitions
1194 Lang
= StringDBFindLanguageList (LanguageName
);
1197 // Better be the same printable name
1199 if (wcscmp (PrintableLanguageName
, Lang
->PrintableLanguageName
) != 0) {
1202 "language redefinition",
1205 Lang
->PrintableLanguageName
,
1207 PrintableLanguageName
1209 return STATUS_ERROR
;
1212 // ParserWarning (0, "benign language redefinition", "%S", PrintableLanguageName);
1213 // return STATUS_WARNING;
1218 // Allocate memory to keep track of this new language
1220 Lang
= (LANGUAGE_LIST
*) malloc (sizeof (LANGUAGE_LIST
));
1222 Error (NULL
, 0, 0, NULL
, "memory allocation error");
1223 return STATUS_ERROR
;
1226 memset ((char *) Lang
, 0, sizeof (LANGUAGE_LIST
));
1228 // Save the language name, then allocate memory to save the
1229 // printable language name
1231 wcscpy (Lang
->LanguageName
, LanguageName
);
1232 Lang
->PrintableLanguageName
= (WCHAR
*) malloc ((wcslen (PrintableLanguageName
) + 1) * sizeof (WCHAR
));
1233 if (Lang
->PrintableLanguageName
== NULL
) {
1234 Error (NULL
, 0, 0, NULL
, "memory allocation error");
1235 return STATUS_ERROR
;
1238 wcscpy (Lang
->PrintableLanguageName
, PrintableLanguageName
);
1240 if (mDBData
.LanguageList
== NULL
) {
1241 mDBData
.LanguageList
= Lang
;
1243 mDBData
.LastLanguageList
->Next
= Lang
;
1246 mDBData
.LastLanguageList
= Lang
;
1249 // Default is to make our active language this new one
1251 StringDBSetCurrentLanguage (LanguageName
);
1253 // The first two strings for any language are the language name,
1254 // followed by the printable language name. Add them and set them
1255 // to referenced so they never get stripped out.
1259 LANGUAGE_NAME_STRING_NAME
,
1263 STRING_FLAGS_REFERENCED
1267 PRINTABLE_LANGUAGE_NAME_STRING_NAME
,
1269 PrintableLanguageName
,
1271 STRING_FLAGS_REFERENCED
1273 return STATUS_SUCCESS
;
1276 /*****************************************************************************/
1279 StringDBFindStringIdentifierByName (
1283 STRING_IDENTIFIER
*Identifier
;
1285 Identifier
= mDBData
.StringIdentifier
;
1286 while (Identifier
!= NULL
) {
1287 if (wcscmp (StringName
, Identifier
->StringName
) == 0) {
1291 Identifier
= Identifier
->Next
;
1299 StringDBFindStringIdentifierByIndex (
1303 STRING_IDENTIFIER
*Identifier
;
1305 Identifier
= mDBData
.StringIdentifier
;
1306 while (Identifier
!= NULL
) {
1307 if (Identifier
->Index
== StringIndex
) {
1311 Identifier
= Identifier
->Next
;
1317 /*****************************************************************************/
1320 StringDBWriteStandardFileHeader (
1325 for (TempIndex
= 0; mSourceFileHeader
[TempIndex
] != NULL
; TempIndex
++) {
1326 fprintf (OutFptr
, "%s\n", mSourceFileHeader
[TempIndex
]);
1330 /*****************************************************************************/
1334 Routine Description:
1336 Given a Unicode string from an input file, reformat the string to replace
1337 backslash control sequences with the appropriate encoding.
1341 String - pointer to string to reformat
1349 StringDBFormatString (
1358 // Go through the string and process any formatting characters
1363 if (*From
== UNICODE_BACKSLASH
) {
1365 // First look for \wide and replace with the appropriate control character. Note that
1366 // when you have "define STR L"ABC"", then sizeof(ABC) is 8 because the null char is
1367 // counted. Make adjustments for this. We advance From below, so subtract 2 each time.
1369 if (wcsncmp (From
, UNICODE_WIDE_STRING
, sizeof (UNICODE_WIDE_STRING
) / sizeof (WCHAR
) - 1) == 0) {
1371 From
+= sizeof (UNICODE_WIDE_STRING
) / sizeof (WCHAR
) - 2;
1372 } else if (wcsncmp (From
, UNICODE_NARROW_STRING
, sizeof (UNICODE_NARROW_STRING
) / sizeof (WCHAR
) - 1) == 0) {
1377 From
+= sizeof (UNICODE_NARROW_STRING
) / sizeof (WCHAR
) - 2;
1378 } else if (wcsncmp (From
, UNICODE_NBR_STRING
, sizeof (UNICODE_NBR_STRING
) / sizeof (WCHAR
) - 1) == 0) {
1382 *To
= NON_BREAKING_CHAR
;
1383 From
+= sizeof (UNICODE_NBR_STRING
) / sizeof (WCHAR
) - 2;
1384 } else if (wcsncmp (From
, UNICODE_BR_STRING
, sizeof (UNICODE_BR_STRING
) / sizeof (WCHAR
) - 1) == 0) {
1386 // Found: \br -- pass through untouched
1391 // Standard one-character control sequences such as \n, \r, \\, or \x
1395 case ASCII_TO_UNICODE ('n'):
1404 case ASCII_TO_UNICODE ('r'):
1411 case UNICODE_BACKSLASH
:
1412 *To
= UNICODE_BACKSLASH
;
1418 case ASCII_TO_UNICODE ('t'):
1423 // embedded double-quote
1425 case UNICODE_DOUBLE_QUOTE
:
1426 *To
= UNICODE_DOUBLE_QUOTE
;
1430 // Hex Unicode character \x1234. We'll process up to 4 hex characters
1432 case ASCII_TO_UNICODE ('x'):
1434 for (HexNibbles
= 0; HexNibbles
< 4; HexNibbles
++) {
1435 if ((From
[1] >= UNICODE_0
) && (From
[1] <= UNICODE_9
)) {
1436 HexValue
= (HexValue
<< 4) | (From
[1] - UNICODE_0
);
1437 } else if ((From
[1] >= UNICODE_a
) && (From
[1] <= UNICODE_f
)) {
1438 HexValue
= (HexValue
<< 4) | (10 + From
[1] - UNICODE_a
);
1439 } else if ((From
[1] >= UNICODE_A
) && (From
[1] <= UNICODE_F
)) {
1440 HexValue
= (HexValue
<< 4) | (10 + From
[1] - UNICODE_A
);
1448 if (HexNibbles
== 0) {
1451 "expected at least one valid hex digit with \\x escaped character in string",
1461 *To
= UNICODE_SPACE
;
1462 ParserWarning (0, "invalid escaped character in string", "\\%C", *From
);
1477 /*****************************************************************************/
1479 StringDBReadDatabase (
1481 BOOLEAN IgnoreIfNotExist
,
1485 STRING_DB_HEADER DbHeader
;
1488 DB_DATA_ITEM_HEADER DataItemHeader
;
1490 Status
= STATUS_SUCCESS
;
1494 // fprintf (stdout, "Reading database file %s\n", DBFileName);
1497 // Try to open the input file
1499 if ((DBFptr
= fopen (DBFileName
, "rb")) == NULL
) {
1500 if (IgnoreIfNotExist
) {
1501 return STATUS_SUCCESS
;
1504 Error (NULL
, 0, 0, DBFileName
, "failed to open input database file for reading");
1505 return STATUS_ERROR
;
1508 // Read and verify the database header
1510 if (fread ((void *) &DbHeader
, sizeof (STRING_DB_HEADER
), 1, DBFptr
) != 1) {
1511 Error (NULL
, 0, 0, DBFileName
, "failed to read header from database file");
1512 Status
= STATUS_ERROR
;
1516 if (DbHeader
.Key
!= STRING_DB_KEY
) {
1517 Error (NULL
, 0, 0, DBFileName
, "invalid header in database file");
1518 Status
= STATUS_ERROR
;
1522 if ((DbHeader
.Version
& STRING_DB_MAJOR_VERSION_MASK
) != (STRING_DB_VERSION
& STRING_DB_MAJOR_VERSION_MASK
)) {
1523 Error (NULL
, 0, 0, DBFileName
, "incompatible database file version -- rebuild clean");
1524 Status
= STATUS_ERROR
;
1528 // Read remaining items
1530 while (fread (&DataItemHeader
, sizeof (DataItemHeader
), 1, DBFptr
) == 1) {
1531 switch (DataItemHeader
.DataType
) {
1532 case DB_DATA_TYPE_STRING_IDENTIFIER
:
1533 StringDBReadStringIdentifier (DBFptr
);
1536 case DB_DATA_TYPE_LANGUAGE_DEFINITION
:
1537 StringDBReadLanguageDefinition (DBFptr
);
1540 case DB_DATA_TYPE_STRING_DEFINITION
:
1541 StringDBReadString (DBFptr
);
1549 "database corrupted",
1550 "invalid data item type 0x%X at offset 0x%X",
1551 (UINT32
) DataItemHeader
.DataType
,
1552 ftell (DBFptr
) - sizeof (DataItemHeader
)
1554 Status
= STATUS_ERROR
;
1560 if (DBFptr
!= NULL
) {
1567 /*****************************************************************************/
1571 Routine Description:
1573 Write everything we know to the output database file. Write:
1576 String identifiers[]
1581 DBFileName - name of the file to write to
1582 Verbose - for debug purposes, print info messages along the way.
1590 StringDBWriteDatabase (
1595 STRING_DB_HEADER DbHeader
;
1598 LANGUAGE_LIST
*Lang
;
1599 STRING_IDENTIFIER
*StringIdentifier
;
1600 STRING_LIST
*StrList
;
1604 fprintf (stdout
, "Writing database %s\n", DBFileName
);
1607 if ((DBFptr
= fopen (DBFileName
, "wb")) == NULL
) {
1608 Error (NULL
, 0, 0, DBFileName
, "failed to open output database file for writing");
1609 return STATUS_ERROR
;
1612 // Fill in and write the database header
1614 memset (&DbHeader
, 0, sizeof (STRING_DB_HEADER
));
1615 DbHeader
.HeaderSize
= sizeof (STRING_DB_HEADER
);
1616 DbHeader
.Key
= STRING_DB_KEY
;
1617 DbHeader
.Version
= STRING_DB_VERSION
;
1619 // Count the number of languages we have
1621 for (Lang
= mDBData
.LanguageList
; Lang
!= NULL
; Lang
= Lang
->Next
) {
1622 DbHeader
.NumLanguages
++;
1625 // Count up how many string identifiers we have, and total up the
1626 // size of the names plus the size of the flags field we will
1629 DbHeader
.NumStringIdenfiers
= mDBData
.NumStringIdentifiers
;
1630 StringIdentifier
= mDBData
.StringIdentifier
;
1631 for (Counter
= 0; Counter
< mDBData
.NumStringIdentifiers
; Counter
++) {
1632 StrLen
= wcslen (StringIdentifier
->StringName
) + 1;
1633 DbHeader
.StringIdentifiersSize
+= StrLen
* sizeof (WCHAR
) + sizeof (StringIdentifier
->Flags
);
1634 StringIdentifier
= StringIdentifier
->Next
;
1640 fwrite (&DbHeader
, sizeof (STRING_DB_HEADER
), 1, DBFptr
);
1642 fprintf (stdout
, " Number of string identifiers 0x%04X\n", DbHeader
.NumStringIdenfiers
);
1643 fprintf (stdout
, " Number of languages %d\n", DbHeader
.NumLanguages
);
1646 // Write the string identifiers
1648 for (StringIdentifier
= mDBData
.StringIdentifier
; StringIdentifier
!= NULL
; StringIdentifier
= StringIdentifier
->Next
) {
1649 StringDBWriteStringIdentifier (
1651 (UINT16
) StringIdentifier
->Index
,
1652 StringIdentifier
->Flags
,
1653 StringIdentifier
->StringName
1657 // Now write all the strings for each language
1659 for (Lang
= mDBData
.LanguageList
; Lang
!= NULL
; Lang
= Lang
->Next
) {
1660 StringDBWriteLanguageDefinition (DBFptr
, Lang
->LanguageName
, Lang
->PrintableLanguageName
);
1661 for (StrList
= Lang
->String
; StrList
!= NULL
; StrList
= StrList
->Next
) {
1662 StringDBWriteString (
1666 StrList
->StringName
,
1674 return STATUS_SUCCESS
;
1678 StringDBSetStringReferenced (
1679 INT8
*StringIdentifierName
,
1680 BOOLEAN IgnoreNotFound
1683 STRING_IDENTIFIER
*Id
;
1687 // See if it's already been defined.
1689 Status
= STATUS_SUCCESS
;
1690 WName
= (WCHAR
*) malloc ((strlen (StringIdentifierName
) + 1) * sizeof (WCHAR
));
1691 swprintf (WName
, L
"%S", StringIdentifierName
);
1692 Id
= StringDBFindStringIdentifierByName (WName
);
1694 Id
->Flags
|= STRING_FLAGS_REFERENCED
;
1696 if (IgnoreNotFound
== 0) {
1697 ParserWarning (0, StringIdentifierName
, "string identifier not found in database");
1698 Status
= STATUS_WARNING
;
1706 /*****************************************************************************/
1710 Routine Description:
1712 Dump the contents of a database to an output unicode file.
1716 DBFileName - name of the pre-existing database file to read
1717 OutputFileName - name of the file to dump the database contents to
1718 Verbose - for printing of additional info useful for debugging
1726 There's some issue with the unicode printing routines. Therefore to
1727 write to the output file properly, open it as binary and use fwrite.
1728 Ideally we could open it with just L"w" and use fwprintf().
1732 StringDBDumpDatabase (
1734 INT8
*OutputFileName
,
1738 LANGUAGE_LIST
*Lang
;
1739 STRING_IDENTIFIER
*StringIdentifier
;
1740 STRING_LIST
*StrList
;
1747 // This function assumes the database has already been read, and
1748 // we're just dumping our internal data structures to a unicode file.
1751 fprintf (stdout
, "Dumping database file %s\n", DBFileName
);
1754 OutFptr
= fopen (OutputFileName
, "wb");
1755 if (OutFptr
== NULL
) {
1756 Error (NULL
, 0, 0, OutputFileName
, "failed to open output file for writing");
1757 return STATUS_ERROR
;
1760 WChar
= UNICODE_FILE_START
;
1761 fwrite (&WChar
, sizeof (WCHAR
), 1, OutFptr
);
1762 CrLf
[1] = UNICODE_LF
;
1763 CrLf
[0] = UNICODE_CR
;
1765 // The default control character is '/'. Make it '#' by writing
1766 // "/=#" to the output file.
1768 swprintf (Line
, L
"/=#");
1769 fwrite (Line
, wcslen (Line
) * sizeof (WCHAR
), 1, OutFptr
);
1770 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1771 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1773 // Dump all the string identifiers and their values
1775 StringDBAssignStringIndexes ();
1776 for (StringIdentifier
= mDBData
.StringIdentifier
; StringIdentifier
!= NULL
; StringIdentifier
= StringIdentifier
->Next
) {
1778 // Write the "#define " string
1780 if (StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) {
1783 L
"%s %-60.60s 0x%04X",
1785 StringIdentifier
->StringName
,
1786 StringIdentifier
->Index
1791 L
"%s %-60.60s 0x%04X // NOT REFERENCED",
1793 StringIdentifier
->StringName
,
1794 StringIdentifier
->Index
1798 fwrite (Line
, wcslen (Line
) * sizeof (WCHAR
), 1, OutFptr
);
1799 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1802 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1804 // Now write all the strings for each language.
1806 WChar
= UNICODE_DOUBLE_QUOTE
;
1808 for (Lang
= mDBData
.LanguageList
; Lang
!= NULL
; Lang
= Lang
->Next
) {
1809 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1810 swprintf (Line
, L
"#langdef %s \"%s\"", Lang
->LanguageName
, Lang
->PrintableLanguageName
);
1811 fwrite (Line
, wcslen (Line
) * sizeof (WCHAR
), 1, OutFptr
);
1812 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1813 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1815 // Now the strings (in double-quotes) for this language. Write
1816 // #string STR_NAME #language eng "string"
1818 for (StrList
= Lang
->String
; StrList
!= NULL
; StrList
= StrList
->Next
) {
1820 // Print the internal flags for debug
1822 swprintf (Line
, L
"// flags=0x%02X", (UINT32
) StrList
->Flags
);
1823 fwrite (Line
, wcslen (Line
) * sizeof (WCHAR
), 1, OutFptr
);
1824 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1826 // Print the scope if changed
1828 if ((Scope
== NULL
) || (wcscmp (Scope
, StrList
->Scope
) != 0)) {
1829 swprintf (Line
, L
"#scope %s", StrList
->Scope
);
1830 fwrite (Line
, wcslen (Line
) * sizeof (WCHAR
), 1, OutFptr
);
1831 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1832 Scope
= StrList
->Scope
;
1837 L
"#string %-50.50s #language %s \"",
1838 StrList
->StringName
,
1841 fwrite (Line
, wcslen (Line
) * sizeof (WCHAR
), 1, OutFptr
);
1842 fwrite (StrList
->Str
, StrList
->Size
- sizeof (WCHAR
), 1, OutFptr
);
1843 swprintf (Line
, L
"\"");
1844 fwrite (Line
, wcslen (Line
) * sizeof (WCHAR
), 1, OutFptr
);
1845 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1850 return STATUS_SUCCESS
;
1853 /*****************************************************************************/
1857 Routine Description:
1859 Given a primary language, a string identifier number, and a list of
1860 languages, find a secondary string.
1864 LanguageName - primary language, like "spa"
1865 StringId - string index value
1866 LanguageList - linked list of "eng", "spa+cat",...
1870 Pointer to a secondary string if found. NULL otherwise.
1874 Given: LanguageName "spa" and LanguageList "spa+cat", match the
1875 "spa" and extract the "cat" and see if there is a string defined
1881 StringDBWriteStringIdentifier (
1885 WCHAR
*IdentifierName
1888 DB_DATA_ITEM_HEADER Hdr
;
1889 memset (&Hdr
, 0, sizeof (DB_DATA_ITEM_HEADER
));
1890 Hdr
.DataType
= DB_DATA_TYPE_STRING_IDENTIFIER
;
1891 if (fwrite (&Hdr
, sizeof (DB_DATA_ITEM_HEADER
), 1, DBFptr
) != 1) {
1892 Error (NULL
, 0, 0, "failed to write string to output database file", NULL
);
1893 return STATUS_ERROR
;
1896 if (fwrite (&StringId
, sizeof (StringId
), 1, DBFptr
) != 1) {
1897 Error (NULL
, 0, 0, "failed to write StringId to output database", NULL
);
1898 return STATUS_ERROR
;
1901 if (fwrite (&Flags
, sizeof (Flags
), 1, DBFptr
) != 1) {
1902 Error (NULL
, 0, 0, "failed to write StringId flags to output database", NULL
);
1903 return STATUS_ERROR
;
1906 if (StringDBWriteGenericString (DBFptr
, IdentifierName
) != STATUS_SUCCESS
) {
1907 return STATUS_ERROR
;
1910 return STATUS_SUCCESS
;
1915 StringDBReadStringIdentifier (
1919 WCHAR
*IdentifierName
;
1924 if (fread (&StringId
, sizeof (StringId
), 1, DBFptr
) != 1) {
1925 Error (NULL
, 0, 0, "failed to read StringId from database", NULL
);
1926 return STATUS_ERROR
;
1929 if (fread (&Flags
, sizeof (Flags
), 1, DBFptr
) != 1) {
1930 Error (NULL
, 0, 0, "failed to read StringId flags from database", NULL
);
1931 return STATUS_ERROR
;
1934 if (StringDBReadGenericString (DBFptr
, &Size
, &IdentifierName
) != STATUS_SUCCESS
) {
1935 return STATUS_ERROR
;
1938 StringDBAddStringIdentifier (IdentifierName
, &StringId
, Flags
);
1940 // printf ("STRID: 0x%04X %S\n", (UINT32)StringId, IdentifierName);
1942 FREE (IdentifierName
);
1943 return STATUS_SUCCESS
;
1948 StringDBWriteString (
1957 DB_DATA_ITEM_HEADER Hdr
;
1958 memset (&Hdr
, 0, sizeof (DB_DATA_ITEM_HEADER
));
1959 Hdr
.DataType
= DB_DATA_TYPE_STRING_DEFINITION
;
1960 if (fwrite (&Hdr
, sizeof (DB_DATA_ITEM_HEADER
), 1, DBFptr
) != 1) {
1961 Error (NULL
, 0, 0, "failed to write string header to output database file", NULL
);
1962 return STATUS_ERROR
;
1965 if (fwrite (&Flags
, sizeof (Flags
), 1, DBFptr
) != 1) {
1966 Error (NULL
, 0, 0, "failed to write string flags to output database", NULL
);
1967 return STATUS_ERROR
;
1970 if (StringDBWriteGenericString (DBFptr
, Language
) != STATUS_SUCCESS
) {
1971 return STATUS_ERROR
;
1974 if (StringDBWriteGenericString (DBFptr
, StringName
) != STATUS_SUCCESS
) {
1975 return STATUS_ERROR
;
1978 if (StringDBWriteGenericString (DBFptr
, Scope
) != STATUS_SUCCESS
) {
1979 return STATUS_ERROR
;
1982 if (StringDBWriteGenericString (DBFptr
, Str
) != STATUS_SUCCESS
) {
1983 return STATUS_ERROR
;
1986 // printf ("DBWriteString: %S.%S.%S\n", Language, StringName, Scope);
1988 return STATUS_SUCCESS
;
1993 StringDBReadString (
2004 if (fread (&Flags
, sizeof (Flags
), 1, DBFptr
) != 1) {
2005 Error (NULL
, 0, 0, "failed to read string flags from database", NULL
);
2006 return STATUS_ERROR
;
2009 if (StringDBReadGenericString (DBFptr
, &Size
, &Language
) != STATUS_SUCCESS
) {
2010 return STATUS_ERROR
;
2013 if (StringDBReadGenericString (DBFptr
, &Size
, &StringName
) != STATUS_SUCCESS
) {
2014 return STATUS_ERROR
;
2017 if (StringDBReadGenericString (DBFptr
, &Size
, &Scope
) != STATUS_SUCCESS
) {
2018 return STATUS_ERROR
;
2021 if (StringDBReadGenericString (DBFptr
, &Size
, &Str
) != STATUS_SUCCESS
) {
2022 return STATUS_ERROR
;
2025 // If the first or second string (language name and printable language name),
2026 // then skip them. They're added via language definitions data items in
2029 if (StringName
[0] != L
'$') {
2030 StringDBAddString (Language
, StringName
, Scope
, Str
, FALSE
, Flags
);
2033 // printf ("DBReadString: %S.%S.%S\n", Language, StringName, Scope);
2041 if (Scope
!= NULL
) {
2045 return STATUS_SUCCESS
;
2050 StringDBWriteLanguageDefinition (
2052 WCHAR
*LanguageName
,
2053 WCHAR
*PrintableLanguageName
2056 DB_DATA_ITEM_HEADER Hdr
;
2057 memset (&Hdr
, 0, sizeof (DB_DATA_ITEM_HEADER
));
2058 Hdr
.DataType
= DB_DATA_TYPE_LANGUAGE_DEFINITION
;
2059 if (fwrite (&Hdr
, sizeof (DB_DATA_ITEM_HEADER
), 1, DBFptr
) != 1) {
2060 Error (NULL
, 0, 0, "failed to write string to output database file", NULL
);
2061 return STATUS_ERROR
;
2064 if (StringDBWriteGenericString (DBFptr
, LanguageName
) != STATUS_SUCCESS
) {
2065 return STATUS_ERROR
;
2068 if (StringDBWriteGenericString (DBFptr
, PrintableLanguageName
) != STATUS_SUCCESS
) {
2069 return STATUS_ERROR
;
2072 return STATUS_SUCCESS
;
2077 StringDBReadLanguageDefinition (
2081 WCHAR
*LanguageName
;
2082 WCHAR
*PrintableLanguageName
;
2086 if (StringDBReadGenericString (DBFptr
, &Size
, &LanguageName
) != STATUS_SUCCESS
) {
2087 return STATUS_ERROR
;
2090 if (StringDBReadGenericString (DBFptr
, &Size
, &PrintableLanguageName
) != STATUS_SUCCESS
) {
2091 return STATUS_ERROR
;
2094 // printf("LANG: %S %S\n", LanguageName, PrintableLanguageName);
2096 Status
= StringDBAddLanguage (LanguageName
, PrintableLanguageName
);
2097 FREE (LanguageName
);
2098 FREE (PrintableLanguageName
);
2102 // All unicode strings in the database consist of a UINT16 length
2103 // field, followed by the string itself. This routine reads one
2104 // of those and returns the info.
2108 StringDBReadGenericString (
2118 if (fread (&LSize
, sizeof (UINT16
), 1, DBFptr
) != 1) {
2119 Error (NULL
, 0, 0, "failed to read a string length field from the database", NULL
);
2120 return STATUS_ERROR
;
2123 if (fread (&Flags
, sizeof (UINT16
), 1, DBFptr
) != 1) {
2124 Error (NULL
, 0, 0, "failed to read a string flags field from the database", NULL
);
2125 return STATUS_ERROR
;
2128 LStr
= MALLOC (LSize
);
2130 Error (__FILE__
, __LINE__
, 0, "memory allocation failed reading the database", NULL
);
2131 return STATUS_ERROR
;
2134 if (fread (LStr
, sizeof (WCHAR
), (UINT32
) LSize
/ sizeof (WCHAR
), DBFptr
) != (UINT32
) LSize
/ sizeof (WCHAR
)) {
2135 Error (NULL
, 0, 0, "failed to read string from database", NULL
);
2136 Error (NULL
, 0, 0, "database read failure", "offset 0x%X", ftell (DBFptr
));
2138 return STATUS_ERROR
;
2141 // printf ("DBR: %S\n", LStr);
2143 // If the flags field indicated we were asked to write a NULL string, then
2144 // return them a NULL pointer.
2146 if (Flags
& STRING_FLAGS_UNDEFINED
) {
2154 return STATUS_SUCCESS
;
2159 StringDBWriteGenericString (
2166 WCHAR ZeroString
[1];
2168 // Strings in the database consist of a size UINT16 followed
2169 // by the string itself.
2174 Size
= sizeof (ZeroString
);
2175 Flags
= STRING_FLAGS_UNDEFINED
;
2178 Size
= (UINT16
) ((wcslen (Str
) + 1) * sizeof (WCHAR
));
2181 if (fwrite (&Size
, sizeof (UINT16
), 1, DBFptr
) != 1) {
2182 Error (NULL
, 0, 0, "failed to write string size to database", NULL
);
2183 return STATUS_ERROR
;
2186 if (fwrite (&Flags
, sizeof (UINT16
), 1, DBFptr
) != 1) {
2187 Error (NULL
, 0, 0, "failed to write string flags to database", NULL
);
2188 return STATUS_ERROR
;
2191 if (fwrite (Str
, sizeof (WCHAR
), Size
/ sizeof (WCHAR
), DBFptr
) != Size
/ sizeof (WCHAR
)) {
2192 Error (NULL
, 0, 0, "failed to write string to database", NULL
);
2193 return STATUS_ERROR
;
2196 return STATUS_SUCCESS
;
2201 StringDBFindString (
2202 WCHAR
*LanguageName
,
2205 WCHAR_STRING_LIST
*LanguagesOfInterest
,
2206 WCHAR_MATCHING_STRING_LIST
*IndirectionList
2209 LANGUAGE_LIST
*Lang
;
2210 STRING_LIST
*CurrString
;
2211 WCHAR_MATCHING_STRING_LIST
*IndListPtr
;
2212 WCHAR TempLangName
[LANGUAGE_IDENTIFIER_NAME_LEN
+ 1];
2216 // If we were given an indirection list, then see if one was specified for this
2217 // string identifier. That is to say, if the indirection says "STR_ID_MY_FAVORITE MyScope",
2218 // then if this string name matches one in the list, then do a lookup with the
2219 // specified scope and return that value.
2221 if (IndirectionList
!= NULL
) {
2222 for (IndListPtr
= IndirectionList
; IndListPtr
!= NULL
; IndListPtr
= IndListPtr
->Next
) {
2223 if (wcscmp (StringName
, IndListPtr
->Str1
) == 0) {
2224 CurrString
= StringDBFindString (LanguageName
, StringName
, IndListPtr
->Str2
, LanguagesOfInterest
, NULL
);
2225 if (CurrString
!= NULL
) {
2232 // First look for exact match language.stringname
2234 for (Lang
= mDBData
.LanguageList
; Lang
!= NULL
; Lang
= Lang
->Next
) {
2235 if (wcscmp (LanguageName
, Lang
->LanguageName
) == 0) {
2237 // Found language match. Try to find string name match
2239 for (CurrString
= Lang
->String
; CurrString
!= NULL
; CurrString
= CurrString
->Next
) {
2240 if (wcscmp (StringName
, CurrString
->StringName
) == 0) {
2242 // Found a string name match. See if we're supposed to find
2245 if (Scope
!= NULL
) {
2246 if (wcscmp (CurrString
->Scope
, Scope
) == 0) {
2257 // If we got here, then we didn't find a match. Look for secondary string
2258 // matches. That is to say, if we're processing "spa", and they requested
2259 // "spa+cat", then recursively call with "cat"
2261 while (LanguagesOfInterest
!= NULL
) {
2263 // If this is the language we're looking for, then process the
2264 // languages of interest list for it.
2266 if (wcsncmp (LanguageName
, LanguagesOfInterest
->Str
, LANGUAGE_IDENTIFIER_NAME_LEN
) == 0) {
2267 WCharPtr
= LanguagesOfInterest
->Str
+ LANGUAGE_IDENTIFIER_NAME_LEN
;
2270 // Double-check the length, though it should have been checked on the
2273 if (wcslen (WCharPtr
) < LANGUAGE_IDENTIFIER_NAME_LEN
) {
2274 Error (NULL
, 0, 0, "malformed alternate language list", "%S", LanguagesOfInterest
->Str
);
2278 wcsncpy (TempLangName
, WCharPtr
, LANGUAGE_IDENTIFIER_NAME_LEN
);
2279 TempLangName
[LANGUAGE_IDENTIFIER_NAME_LEN
] = 0;
2280 CurrString
= StringDBFindString (TempLangName
, StringName
, NULL
, NULL
, IndirectionList
);
2281 if (CurrString
!= NULL
) {
2285 WCharPtr
+= LANGUAGE_IDENTIFIER_NAME_LEN
;
2289 LanguagesOfInterest
= LanguagesOfInterest
->Next
;
2301 // Free up existing scope memory.
2303 if (mDBData
.CurrentScope
!= NULL
) {
2304 FREE (mDBData
.CurrentScope
);
2307 mDBData
.CurrentScope
= DuplicateString (Scope
);
2308 return STATUS_SUCCESS
;
2311 // We typically don't assign index values to string identifiers
2312 // until we're ready to write out files. To reduce the size of
2313 // the output file, re-order the string identifiers to move any
2314 // unreferenced ones to the end. Then we'll walk the list
2315 // again to assign string indexes, keeping track of the last
2320 StringDBAssignStringIndexes (
2324 STRING_IDENTIFIER
*StrId
;
2325 STRING_IDENTIFIER
*FirstUsed
;
2326 STRING_IDENTIFIER
*LastUsed
;
2327 STRING_IDENTIFIER
*FirstUnused
;
2328 STRING_IDENTIFIER
*LastUnused
;
2330 UINT32 MaxReferenced
;
2333 // Create two lists -- used and unused. Then put them together with
2334 // the unused ones on the end.
2340 StrId
= mDBData
.StringIdentifier
;
2341 while (StrId
!= NULL
) {
2342 if ((StrId
->Flags
& STRING_FLAGS_REFERENCED
) == 0) {
2344 // Put it on the unused list
2346 if (FirstUnused
== NULL
) {
2347 FirstUnused
= StrId
;
2349 LastUnused
->Next
= StrId
;
2353 StrId
= StrId
->Next
;
2354 LastUnused
->Next
= NULL
;
2357 // Put it on the used list
2359 if (FirstUsed
== NULL
) {
2362 LastUsed
->Next
= StrId
;
2366 StrId
= StrId
->Next
;
2367 LastUsed
->Next
= NULL
;
2373 if (FirstUsed
!= NULL
) {
2374 mDBData
.StringIdentifier
= FirstUsed
;
2375 LastUsed
->Next
= FirstUnused
;
2377 mDBData
.StringIdentifier
= FirstUnused
;
2382 for (StrId
= mDBData
.StringIdentifier
; StrId
!= NULL
; StrId
= StrId
->Next
) {
2383 StrId
->Index
= Index
;
2385 if (StrId
->Flags
& STRING_FLAGS_REFERENCED
) {
2386 mDBData
.NumStringIdentifiersReferenced
= Index
;
2390 mDBData
.NumStringIdentifiers
= Index
;
2404 NewStr
= MALLOC ((wcslen (Str
) + 1) * sizeof (WCHAR
));
2405 if (NewStr
== NULL
) {
2406 Error (NULL
, 0, 0, "memory allocation failure", NULL
);
2410 wcscpy (NewStr
, Str
);
2424 Len
= strlen (Str
) + 1;
2425 NewStr
= (WCHAR
*) malloc (Len
* sizeof (WCHAR
));
2426 for (Ptr
= NewStr
; *Str
!= 0; Str
++, Ptr
++) {
2427 *Ptr
= (UINT16
) (UINT8
) *Str
;
2434 /*****************************************************************************/
2438 Routine Description:
2440 Create an HII export string pack for the strings in our database.
2444 FileName - name of the output file to write
2453 StringDBCreateHiiExportPack (
2458 LANGUAGE_LIST
*Lang
;
2459 STRING_LIST
*CurrString
;
2460 STRING_LIST EmptyString
;
2464 EFI_HII_STRING_PACK StringPack
;
2466 WCHAR ZeroString
[1];
2467 WCHAR
*TempStringPtr
;
2469 STRING_IDENTIFIER
*StringIdentifier
;
2471 if ((Fptr
= fopen (FileName
, "wb")) == NULL
) {
2472 Error (NULL
, 0, 0, FileName
, "failed to open output HII export file");
2473 return STATUS_ERROR
;
2476 // Assign index values to the string identifiers
2478 StringDBAssignStringIndexes ();
2480 // If a given string is not defined, then we'll use this one.
2482 memset (&EmptyString
, 0, sizeof (EmptyString
));
2483 EmptyString
.Size
= sizeof (ZeroString
);
2484 EmptyString
.Str
= ZeroString
;
2486 // Process each language, then each string for each langage
2489 for (Lang
= mDBData
.LanguageList
; Lang
!= NULL
; Lang
= Lang
->Next
) {
2491 // Process each string for this language. We have to make 3 passes on the strings:
2492 // Pass1: computes sizes and fill in the string pack header
2493 // Pass2: write the array of offsets
2494 // Pass3: write the strings
2497 // PASS 1: Fill in and print the HII string pack header
2499 // Compute the size for this language package and write
2500 // the header out. Each string package contains:
2502 // Offset[] -- an array of offsets to strings, of type RELOFST each
2503 // String[] -- the actual strings themselves
2505 memset ((char *) &StringPack
, 0, sizeof (EFI_HII_STRING_PACK
));
2506 StringPack
.Header
.Type
= EFI_HII_STRING
;
2507 StringPack
.NumStringPointers
= (UINT16
) mDBData
.NumStringIdentifiersReferenced
;
2508 LangName
= Lang
->LanguageName
;
2510 // First string is the language name. If we're printing all languages, then
2511 // it's just the "spa". If we were given a list of languages to print, then it's
2512 // the "spacat" string. Compute its offset and fill in
2513 // the info in the header. Since we know the language name string's length,
2514 // and the printable language name follows it, use that info to fill in the
2515 // entry for the printable language name as well.
2517 StringPack
.LanguageNameString
= (STRING_OFFSET
) (sizeof (EFI_HII_STRING_PACK
) + (mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
)));
2518 StringPack
.PrintableLanguageName
= (STRING_OFFSET
) (StringPack
.LanguageNameString
+ (wcslen (LangName
) + 1) * sizeof (WCHAR
));
2520 // Add up the size of all strings so we can fill in our header.
2523 for (StringIndex
= 0; StringIndex
< mDBData
.NumStringIdentifiersReferenced
; StringIndex
++) {
2525 // For the first string (language name), we print out the "spacat" if they
2526 // requested it. We set LangName to point to the proper language name string above.
2528 if (StringIndex
== STRING_ID_LANGUAGE_NAME
) {
2529 Len
+= (wcslen (LangName
) + 1) * sizeof (WCHAR
);
2532 // Find a string with this language.stringname
2534 StringIdentifier
= StringDBFindStringIdentifierByIndex (StringIndex
);
2535 if (StringIdentifier
== NULL
) {
2536 Error (NULL
, 0, 0, "internal error", "invalid string index 0x%X", StringIndex
);
2537 return STATUS_ERROR
;
2540 // Find a matching string if this string identifier was referenced
2542 EmptyString
.Flags
= STRING_FLAGS_UNDEFINED
;
2544 if (StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) {
2545 CurrString
= StringDBFindString (
2547 StringIdentifier
->StringName
,
2549 NULL
, // LanguagesOfInterest,
2553 // IndirectionList);
2555 if (NULL
== CurrString
) {
2557 // If string for Lang->LanguageName is not found, try to get an English version
2559 CurrString
= StringDBFindString (
2561 StringIdentifier
->StringName
,
2563 NULL
, // LanguagesOfInterest,
2567 // IndirectionList);
2572 if (CurrString
== NULL
) {
2573 CurrString
= &EmptyString
;
2574 EmptyString
.Flags
|= StringIdentifier
->Flags
;
2577 Len
+= CurrString
->Size
;
2580 StringPack
.Header
.Length
= sizeof (EFI_HII_STRING_PACK
)
2581 + mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
)
2584 // Write out the string pack header
2586 fwrite ((void *) &StringPack
, sizeof (StringPack
), 1, Fptr
);
2588 // PASS2 : write the offsets
2590 // Traverse the list of strings again and write the array of offsets. The
2591 // offset to the first string is the size of the string pack header
2592 // plus the size of the offsets array. The other strings follow it.
2595 Offset
= sizeof (StringPack
) + mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
);
2596 for (StringIndex
= 0; StringIndex
< mDBData
.NumStringIdentifiersReferenced
; StringIndex
++) {
2600 fwrite (&Offset
, sizeof (STRING_OFFSET
), 1, Fptr
);
2602 // Find the string name
2604 StringIdentifier
= StringDBFindStringIdentifierByIndex (StringIndex
);
2605 if (StringIdentifier
== NULL
) {
2606 Error (NULL
, 0, 0, "internal error", "invalid string index 0x%X", StringIndex
);
2607 return STATUS_ERROR
;
2610 // For the first string (language name), we print out the "spacat" if they
2611 // requested it. We set LangName to point to the proper language name string above.
2613 if (StringIndex
== STRING_ID_LANGUAGE_NAME
) {
2614 Offset
+= (wcslen (LangName
) + 1) * sizeof (WCHAR
);
2615 CurrString
= StringDBFindString (
2617 StringIdentifier
->StringName
,
2624 // Find a matching string
2626 CurrString
= StringDBFindString (
2628 StringIdentifier
->StringName
,
2630 NULL
, // LanguagesOfInterest,
2634 // IndirectionList);
2636 if (NULL
== CurrString
) {
2637 CurrString
= StringDBFindString (
2639 StringIdentifier
->StringName
,
2641 NULL
, // LanguagesOfInterest,
2645 // IndirectionList);
2649 EmptyString
.LanguageName
= Lang
->LanguageName
;
2650 if (CurrString
== NULL
) {
2651 CurrString
= &EmptyString
;
2652 EmptyString
.Flags
= STRING_FLAGS_UNDEFINED
;
2653 } else if ((StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) == 0) {
2654 CurrString
= &EmptyString
;
2655 EmptyString
.Flags
= 0;
2658 Offset
+= CurrString
->Size
;
2663 // PASS 3: write the strings themselves.
2665 Offset
= sizeof (StringPack
) + mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
);
2666 for (StringIndex
= 0; StringIndex
< mDBData
.NumStringIdentifiersReferenced
; StringIndex
++) {
2667 StringIdentifier
= StringDBFindStringIdentifierByIndex (StringIndex
);
2668 if (StringIdentifier
== NULL
) {
2669 Error (NULL
, 0, 0, "internal error", "invalid string index 0x%X", StringIndex
);
2670 return STATUS_ERROR
;
2673 // For the first string (language name), we print out the "spacat" if they
2674 // requested it. We set LangName to point to the proper language name string above.
2676 if (StringIndex
== STRING_ID_LANGUAGE_NAME
) {
2677 TempStringPtr
= LangName
;
2680 // Find a matching string if this string identifier was referenced
2683 if (StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) {
2684 CurrString
= StringDBFindString (
2686 StringIdentifier
->StringName
,
2688 NULL
, // LanguagesOfInterest,
2692 // IndirectionList);
2694 if (NULL
== CurrString
) {
2695 CurrString
= StringDBFindString (
2697 StringIdentifier
->StringName
,
2699 NULL
, // LanguagesOfInterest,
2703 // IndirectionList);
2708 if (CurrString
== NULL
) {
2709 CurrString
= &EmptyString
;
2712 TempStringPtr
= CurrString
->Str
;
2715 for (TempIndex
= 0; TempStringPtr
[TempIndex
] != 0; TempIndex
++) {
2716 fwrite (&TempStringPtr
[TempIndex
], sizeof (CHAR16
), 1, Fptr
);
2720 // Print NULL WCHAR at the end of this string.
2723 fwrite (&TempIndex
, sizeof (CHAR16
), 1, Fptr
);
2727 // Sanity check the offset. Make sure our running offset is what we put in the
2728 // string pack header.
2730 if (StringPack
.Header
.Length
!= Offset
) {
2735 "application error",
2736 "stringpack size 0x%X does not match final size 0x%X",
2737 StringPack
.Header
.Length
,
2743 // Print terminator string pack, closing brace and close the file.
2744 // The size of 0 triggers to the consumer that this is the end.
2746 memset ((char *) &StringPack
, 0, sizeof (EFI_HII_STRING_PACK
));
2747 StringPack
.Header
.Type
= EFI_HII_STRING
;
2748 fwrite ((void *) &StringPack
, sizeof (StringPack
), 1, Fptr
);
2750 return STATUS_SUCCESS
;