summaryrefslogtreecommitdiff
path: root/EdkCompatibilityPkg/Sample/Tools/Source/StrGather/StringDB.c
diff options
context:
space:
mode:
Diffstat (limited to 'EdkCompatibilityPkg/Sample/Tools/Source/StrGather/StringDB.c')
-rw-r--r--EdkCompatibilityPkg/Sample/Tools/Source/StrGather/StringDB.c2808
1 files changed, 2808 insertions, 0 deletions
diff --git a/EdkCompatibilityPkg/Sample/Tools/Source/StrGather/StringDB.c b/EdkCompatibilityPkg/Sample/Tools/Source/StrGather/StringDB.c
new file mode 100644
index 0000000000..8fa01e80ab
--- /dev/null
+++ b/EdkCompatibilityPkg/Sample/Tools/Source/StrGather/StringDB.c
@@ -0,0 +1,2808 @@
+/*++
+
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ StringDB.c
+
+Abstract:
+
+ String database implementation
+
+--*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h> // for tolower()
+#include "Tiano.h"
+#include "EfiUtilityMsgs.h"
+#include "StrGather.h"
+#include "StringDb.h"
+#include "EfiInternalFormRepresentation.h"
+
+#include EFI_PROTOCOL_DEFINITION (Hii)
+
+typedef CHAR16 WCHAR;
+#define STRING_OFFSET RELOFST
+
+#define STRING_DB_KEY (('S' << 24) | ('D' << 16) | ('B' << 8) | 'K')
+//
+// Version supported by this tool
+//
+#define STRING_DB_VERSION 0x00010000
+
+#define STRING_DB_MAJOR_VERSION_MASK 0xFFFF0000
+#define STRING_DB_MINOR_VERSION_MASK 0x0000FFFF
+
+#define DEFINE_STR L"// #define"
+
+#define LANGUAGE_CODE_WIDTH 4
+//
+// This is the header that gets written to the top of the
+// output binary database file.
+//
+typedef struct {
+ UINT32 Key;
+ UINT32 HeaderSize;
+ UINT32 Version;
+ UINT32 NumStringIdenfiers;
+ UINT32 StringIdentifiersSize;
+ UINT32 NumLanguages;
+} STRING_DB_HEADER;
+
+//
+// When we write out data to the database, we have a UINT16 identifier, which
+// indicates what follows, followed by the data. Here's the structure.
+//
+typedef struct {
+ UINT16 DataType;
+ UINT16 Reserved;
+} DB_DATA_ITEM_HEADER;
+
+#define DB_DATA_TYPE_INVALID 0x0000
+#define DB_DATA_TYPE_STRING_IDENTIFIER 0x0001
+#define DB_DATA_TYPE_LANGUAGE_DEFINITION 0x0002
+#define DB_DATA_TYPE_STRING_DEFINITION 0x0003
+#define DB_DATA_TYPE_LAST DB_DATA_TYPE_STRING_DEFINITION
+
+//
+// We have to keep track of a list of languages, each of which has its own
+// list of strings. Define a structure to keep track of all languages and
+// their list of strings.
+//
+typedef struct _STRING_LIST {
+ struct _STRING_LIST *Next;
+ UINT32 Size; // number of bytes in string, including null terminator
+ WCHAR *LanguageName;
+ WCHAR *StringName; // for example STR_ID_TEXT1
+ WCHAR *Scope; //
+ WCHAR *Str; // the actual string
+ UINT16 Flags; // properties of this string (used, undefined)
+} STRING_LIST;
+
+typedef struct _LANGUAGE_LIST {
+ struct _LANGUAGE_LIST *Next;
+ WCHAR LanguageName[4];
+ WCHAR *PrintableLanguageName;
+ STRING_LIST *String;
+ STRING_LIST *LastString;
+} LANGUAGE_LIST;
+
+//
+// We also keep track of all the string identifier names, which we assign unique
+// values to. Create a structure to keep track of them all.
+//
+typedef struct _STRING_IDENTIFIER {
+ struct _STRING_IDENTIFIER *Next;
+ UINT32 Index; // only need 16 bits, but makes it easier with UINT32
+ WCHAR *StringName;
+ UINT16 Flags; // if someone referenced it via STRING_TOKEN()
+} STRING_IDENTIFIER;
+//
+// Keep our globals in this structure to be as modular as possible.
+//
+typedef struct {
+ FILE *StringDBFptr;
+ LANGUAGE_LIST *LanguageList;
+ LANGUAGE_LIST *LastLanguageList;
+ LANGUAGE_LIST *CurrentLanguage; // keep track of the last language they used
+ STRING_IDENTIFIER *StringIdentifier;
+ STRING_IDENTIFIER *LastStringIdentifier;
+ UINT8 *StringDBFileName;
+ UINT32 NumStringIdentifiers;
+ UINT32 NumStringIdentifiersReferenced;
+ STRING_IDENTIFIER *CurrentStringIdentifier; // keep track of the last string identifier they added
+ WCHAR *CurrentScope;
+} STRING_DB_DATA;
+
+static STRING_DB_DATA mDBData;
+
+static const char *mSourceFileHeader[] = {
+ "//",
+ "// DO NOT EDIT -- auto-generated file",
+ "//",
+ "// This file is generated by the string gather utility",
+ "//",
+ NULL
+};
+
+static
+STRING_LIST *
+StringDBFindString (
+ WCHAR *LanguageName,
+ WCHAR *StringName,
+ WCHAR *Scope,
+ WCHAR_STRING_LIST *LanguagesOfInterest,
+ WCHAR_MATCHING_STRING_LIST *IndirectionList
+ );
+
+static
+STRING_IDENTIFIER *
+StringDBFindStringIdentifierByName (
+ WCHAR *Name
+ );
+
+static
+STRING_IDENTIFIER *
+StringDBFindStringIdentifierByIndex (
+ UINT32 Index
+ );
+
+static
+LANGUAGE_LIST *
+StringDBFindLanguageList (
+ WCHAR *LanguageName
+ );
+
+static
+void
+StringDBWriteStandardFileHeader (
+ FILE *OutFptr
+ );
+
+static
+WCHAR *
+AsciiToWchar (
+ INT8 *Str
+ );
+
+static
+WCHAR *
+DuplicateString (
+ WCHAR *Str
+ );
+
+static
+STATUS
+StringDBWriteStringIdentifier (
+ FILE *DBFptr,
+ UINT16 StringId,
+ UINT16 Flags,
+ WCHAR *IdentifierName
+ );
+
+static
+STATUS
+StringDBReadStringIdentifier (
+ FILE *DBFptr
+ );
+
+static
+STATUS
+StringDBWriteLanguageDefinition (
+ FILE *DBFptr,
+ WCHAR *LanguageName,
+ WCHAR *PrintableLanguageName
+ );
+
+static
+STATUS
+StringDBReadLanguageDefinition (
+ FILE *DBFptr
+ );
+
+static
+STATUS
+StringDBWriteString (
+ FILE *DBFptr,
+ UINT16 Flags,
+ WCHAR *Language,
+ WCHAR *StringName,
+ WCHAR *Scope,
+ WCHAR *Str
+ );
+
+static
+STATUS
+StringDBReadString (
+ FILE *DBFptr
+ );
+
+static
+STATUS
+StringDBReadGenericString (
+ FILE *DBFptr,
+ UINT16 *Size,
+ WCHAR **Str
+ );
+
+static
+STATUS
+StringDBWriteGenericString (
+ FILE *DBFptr,
+ WCHAR *Str
+ );
+
+static
+void
+StringDBAssignStringIndexes (
+ VOID
+ );
+
+/*****************************************************************************/
+
+/*++
+
+Routine Description:
+ Constructor function for the string database handler.
+
+Arguments:
+ None.
+
+Returns:
+ None.
+
+--*/
+void
+StringDBConstructor (
+ VOID
+ )
+{
+ memset ((char *) &mDBData, 0, sizeof (STRING_DB_DATA));
+ mDBData.CurrentScope = DuplicateString (L"NULL");
+}
+
+/*****************************************************************************/
+
+/*++
+
+Routine Description:
+ Destructor function for the string database handler.
+
+Arguments:
+ None.
+
+Returns:
+ None.
+
+--*/
+void
+StringDBDestructor (
+ VOID
+ )
+{
+ LANGUAGE_LIST *NextLang;
+ STRING_LIST *NextStr;
+ STRING_IDENTIFIER *NextIdentifier;
+ //
+ // Close the database file if it's open
+ //
+ if (mDBData.StringDBFptr != NULL) {
+ fclose (mDBData.StringDBFptr);
+ mDBData.StringDBFptr = NULL;
+ }
+ //
+ // If we've allocated any strings/languages, free them up
+ //
+ while (mDBData.LanguageList != NULL) {
+ NextLang = mDBData.LanguageList->Next;
+ //
+ // Free up all strings for this language
+ //
+ while (mDBData.LanguageList->String != NULL) {
+ NextStr = mDBData.LanguageList->String->Next;
+ FREE (mDBData.LanguageList->String->Str);
+ FREE (mDBData.LanguageList->String);
+ mDBData.LanguageList->String = NextStr;
+ }
+
+ FREE (mDBData.LanguageList->PrintableLanguageName);
+ FREE (mDBData.LanguageList);
+ mDBData.LanguageList = NextLang;
+ }
+ //
+ // Free up string identifiers
+ //
+ while (mDBData.StringIdentifier != NULL) {
+ NextIdentifier = mDBData.StringIdentifier->Next;
+ FREE (mDBData.StringIdentifier->StringName);
+ FREE (mDBData.StringIdentifier);
+ mDBData.StringIdentifier = NextIdentifier;
+ }
+ //
+ // Free the filename
+ //
+ if (mDBData.StringDBFileName != NULL) {
+ FREE (mDBData.StringDBFileName);
+ mDBData.StringDBFileName = NULL;
+ }
+ //
+ // We save a copy of the scope, so free it up if we
+ // have one.
+ //
+ if (mDBData.CurrentScope != NULL) {
+ FREE (mDBData.CurrentScope);
+ mDBData.CurrentScope = NULL;
+ }
+}
+
+/*****************************************************************************/
+
+/*++
+
+Routine Description:
+
+ Dump the contents of a database to an output C file.
+
+Arguments:
+
+ FileName - name of the output file to write
+ BaseName - used for the name of the C array defined
+ Languages - list of languages of interest
+
+Returns:
+
+ STATUS
+
+Notes:
+
+ Languages is a pointer to a linked list of languages specified on
+ the command line. Format is "eng" and "spa+cat". For this, print
+ the strings for eng. Print the strings for spa too, but if one is
+ missing look for a cat string and print if it it exists.
+
+--*/
+STATUS
+StringDBDumpCStrings (
+ INT8 *FileName,
+ INT8 *BaseName,
+ WCHAR_STRING_LIST *LanguagesOfInterest,
+ WCHAR_MATCHING_STRING_LIST *IndirectionList
+ )
+{
+ FILE *Fptr;
+ LANGUAGE_LIST *Lang;
+ STRING_LIST *CurrString;
+ STRING_LIST EmptyString;
+ UINT32 Offset;
+ UINT32 StringIndex;
+ UINT32 TempIndex;
+ UINT32 BytesThisLine;
+ EFI_HII_STRING_PACK_HEADER StringPack;
+ UINT8 *Ptr;
+ UINT32 Len;
+ WCHAR ZeroString[1];
+ WCHAR_STRING_LIST *LOIPtr;
+ BOOLEAN LanguageOk;
+ WCHAR *TempStringPtr;
+ WCHAR *LangName;
+ STRING_IDENTIFIER *StringIdentifier;
+
+ if ((Fptr = fopen (FileName, "w")) == NULL) {
+ Error (NULL, 0, 0, FileName, "failed to open output C string file");
+ return STATUS_ERROR;
+ }
+ //
+ // Assign index values to the string identifiers
+ //
+ StringDBAssignStringIndexes ();
+ //
+ // Write the standard header to the output file, then the structure
+ // definition header.
+ //
+ StringDBWriteStandardFileHeader (Fptr);
+ fprintf (Fptr, "\nunsigned char %s[] = {\n", BaseName);
+ //
+ // If a given string is not defined, then we'll use this one.
+ //
+ memset (&EmptyString, 0, sizeof (EmptyString));
+ EmptyString.Size = sizeof (ZeroString);
+ EmptyString.Str = ZeroString;
+ //
+ // Process each language, then each string for each langage
+ //
+ ZeroString[0] = 0;
+ for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {
+ //
+ // If we have a language list, then make sure this language is in that
+ // list.
+ //
+ LanguageOk = TRUE;
+ LangName = Lang->LanguageName;
+ if (LanguagesOfInterest != NULL) {
+ LanguageOk = FALSE;
+ for (LOIPtr = LanguagesOfInterest; LOIPtr != NULL; LOIPtr = LOIPtr->Next) {
+ if (wcsncmp (LOIPtr->Str, Lang->LanguageName, LANGUAGE_IDENTIFIER_NAME_LEN) == 0) {
+ LangName = LOIPtr->Str;
+ LanguageOk = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!LanguageOk) {
+ continue;
+ }
+ //
+ // Process each string for this language. We have to make 3 passes on the strings:
+ // Pass1: computes sizes and fill in the string pack header
+ // Pass2: write the array of offsets
+ // Pass3: write the strings
+ //
+ //
+ // PASS 1: Fill in and print the HII string pack header
+ //
+ // Compute the size for this language package and write
+ // the header out. Each string package contains:
+ // Header
+ // Offset[] -- an array of offsets to strings, of type RELOFST each
+ // String[] -- the actual strings themselves
+ //
+ fprintf (
+ Fptr,
+ "\n//******************************************************************************"
+ "\n// Start of string definitions for %S/%S",
+ Lang->LanguageName,
+ Lang->PrintableLanguageName
+ );
+ memset ((char *) &StringPack, 0, sizeof (EFI_HII_STRING_PACK_HEADER));
+ StringPack.Header.Type = EFI_HII_STRING;
+ StringPack.NumStringPointers = (UINT16) mDBData.NumStringIdentifiersReferenced;
+ //
+ // First string is the language name. If we're printing all languages, then
+ // it's just the "spa". If we were given a list of languages to print, then it's
+ // the "spacat" string. Compute its offset and fill in
+ // the info in the header. Since we know the language name string's length,
+ // and the printable language name follows it, use that info to fill in the
+ // entry for the printable language name as well.
+ //
+ StringPack.LanguageNameString = (STRING_OFFSET) (sizeof (EFI_HII_STRING_PACK_HEADER) + (mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET)));
+ StringPack.PrintableLanguageName = (STRING_OFFSET) (StringPack.LanguageNameString + (wcslen (LangName) + 1) * sizeof (WCHAR));
+ //
+ // Add up the size of all strings so we can fill in our header.
+ //
+ Len = 0;
+ for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {
+ //
+ // For the first string (language name), we print out the "spacat" if they
+ // requested it. We set LangName to point to the proper language name string above.
+ //
+ if (StringIndex == STRING_ID_LANGUAGE_NAME) {
+ Len += (wcslen (LangName) + 1) * sizeof (WCHAR);
+ } else {
+ //
+ // Find a string with this language.stringname
+ //
+ StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);
+ if (StringIdentifier == NULL) {
+ Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);
+ return STATUS_ERROR;
+ }
+ //
+ // Find a matching string if this string identifier was referenced
+ //
+ EmptyString.Flags = STRING_FLAGS_UNDEFINED;
+ CurrString = NULL;
+ if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) {
+ CurrString = StringDBFindString (
+ Lang->LanguageName,
+ StringIdentifier->StringName,
+ NULL,
+ LanguagesOfInterest,
+ IndirectionList
+ );
+ if (NULL == CurrString) {
+ //
+ // If string for Lang->LanguageName is not found, try to get an English version
+ //
+ CurrString = StringDBFindString (
+ L"eng",
+ StringIdentifier->StringName,
+ NULL,
+ LanguagesOfInterest,
+ IndirectionList
+ );
+ }
+ }
+
+ if (CurrString == NULL) {
+ CurrString = &EmptyString;
+ EmptyString.Flags |= StringIdentifier->Flags;
+ }
+
+ Len += CurrString->Size;
+ }
+ }
+ StringPack.Header.Length = sizeof (EFI_HII_STRING_PACK_HEADER)
+ + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET)
+ + Len;
+ //
+ // Write out the header one byte at a time
+ //
+ Ptr = (UINT8 *) &StringPack;
+ for (TempIndex = 0; TempIndex < sizeof (EFI_HII_STRING_PACK_HEADER); TempIndex++, Ptr++) {
+ if ((TempIndex & 0x07) == 0) {
+ fprintf (Fptr, "\n ");
+ }
+
+ fprintf (Fptr, "0x%02X, ", (UINT32) *Ptr);
+ }
+
+ fprintf (Fptr, "\n // offset 0x%X\n", sizeof (StringPack));
+ //
+ // PASS2 : write the offsets
+ //
+ // Traverse the list of strings again and write the array of offsets. The
+ // offset to the first string is the size of the string pack header
+ // plus the size of the offsets array. The other strings follow it.
+ //
+ StringIndex = 0;
+ Offset = sizeof (StringPack) + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET);
+ for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {
+ //
+ // Write the offset, followed by a useful comment
+ //
+ fprintf (Fptr, " ");
+ Ptr = (UINT8 *) &Offset;
+ for (TempIndex = 0; TempIndex < sizeof (STRING_OFFSET); TempIndex++) {
+ fprintf (Fptr, "0x%02X, ", (UINT32) Ptr[TempIndex]);
+ }
+ //
+ // Find the string name
+ //
+ StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);
+ if (StringIdentifier == NULL) {
+ Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);
+ return STATUS_ERROR;
+ }
+
+ fprintf (Fptr, " // offset to string %S (0x%04X)", StringIdentifier->StringName, StringIndex);
+ //
+ // For the first string (language name), we print out the "spacat" if they
+ // requested it. We set LangName to point to the proper language name string above.
+ //
+ if (StringIndex == STRING_ID_LANGUAGE_NAME) {
+ Offset += (wcslen (LangName) + 1) * sizeof (WCHAR);
+ CurrString = StringDBFindString (
+ Lang->LanguageName,
+ StringIdentifier->StringName,
+ NULL, // scope
+ NULL,
+ NULL
+ );
+ } else {
+ //
+ // Find a matching string
+ //
+ CurrString = StringDBFindString (
+ Lang->LanguageName,
+ StringIdentifier->StringName,
+ NULL, // scope
+ LanguagesOfInterest,
+ IndirectionList
+ );
+
+ if (NULL == CurrString) {
+ CurrString = StringDBFindString (
+ L"eng",
+ StringIdentifier->StringName,
+ NULL, // scope
+ LanguagesOfInterest,
+ IndirectionList
+ );
+ }
+
+ EmptyString.LanguageName = Lang->LanguageName;
+ if (CurrString == NULL) {
+ CurrString = &EmptyString;
+ EmptyString.Flags = STRING_FLAGS_UNDEFINED;
+ } else if ((StringIdentifier->Flags & STRING_FLAGS_REFERENCED) == 0) {
+ CurrString = &EmptyString;
+ EmptyString.Flags = 0;
+ }
+
+ Offset += CurrString->Size;
+ }
+ //
+ // Print useful info about this string
+ //
+ if ((StringIdentifier->Flags & STRING_FLAGS_REFERENCED) == 0) {
+ fprintf (Fptr, " - not referenced");
+ }
+
+ if (CurrString->Flags & STRING_FLAGS_UNDEFINED) {
+ fprintf (Fptr, " - not defined for this language");
+ } else if (wcscmp (CurrString->LanguageName, Lang->LanguageName) != 0) {
+ fprintf (
+ Fptr,
+ " - not defined for this language -- using secondary language %S definition",
+ CurrString->LanguageName
+ );
+ }
+
+ fprintf (Fptr, "\n");
+ }
+ //
+ // For unreferenced string identifiers, print a message that they are not referenced anywhere
+ //
+ while (StringIndex < mDBData.NumStringIdentifiers) {
+ StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);
+ if (StringIdentifier != NULL) {
+ fprintf (Fptr, " // %S not referenced\n", StringIdentifier->StringName);
+ }
+
+ StringIndex++;
+ }
+
+ //
+ // PASS 3: write the strings themselves.
+ // Keep track of how many bytes we write per line because some editors
+ // (Visual Studio for instance) can't handle too long of lines.
+ //
+ Offset = sizeof (StringPack) + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET);
+ for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {
+ StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);
+ if (StringIdentifier == NULL) {
+ Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);
+ return STATUS_ERROR;
+ }
+
+ fprintf (Fptr, " // string %S offset 0x%08X\n ", StringIdentifier->StringName, Offset);
+ //
+ // For the first string (language name), we print out the "spacat" if they
+ // requested it. We set LangName to point to the proper language name string above.
+ //
+ if (StringIndex == STRING_ID_LANGUAGE_NAME) {
+ TempStringPtr = LangName;
+ } else {
+ //
+ // Find a matching string if this string identifier was referenced
+ //
+ CurrString = NULL;
+ if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) {
+ CurrString = StringDBFindString (
+ Lang->LanguageName,
+ StringIdentifier->StringName,
+ NULL, // scope
+ LanguagesOfInterest,
+ IndirectionList
+ );
+ if (NULL == CurrString) {
+ CurrString = StringDBFindString (
+ L"eng",
+ StringIdentifier->StringName,
+ NULL, // scope
+ LanguagesOfInterest,
+ IndirectionList
+ );
+ }
+ }
+
+ if (CurrString == NULL) {
+ CurrString = &EmptyString;
+ }
+
+ TempStringPtr = CurrString->Str;
+ }
+
+ BytesThisLine = 0;
+ for (TempIndex = 0; TempStringPtr[TempIndex] != 0; TempIndex++) {
+ fprintf (
+ Fptr,
+ "0x%02X, 0x%02X, ",
+ (UINT32) TempStringPtr[TempIndex] & 0xFF,
+ (UINT32) ((TempStringPtr[TempIndex] >> 8) & 0xFF)
+ );
+ BytesThisLine += 2;
+ Offset += 2;
+ //
+ // Let's say we only allow 14 per line
+ //
+ if (BytesThisLine > 14) {
+ fprintf (Fptr, "\n ");
+ BytesThisLine = 0;
+ }
+ }
+ //
+ // Print NULL WCHAR at the end of this string.
+ //
+ fprintf (Fptr, "0x00, 0x00,\n");
+ Offset += 2;
+ }
+ //
+ // Sanity check the offset. Make sure our running offset is what we put in the
+ // string pack header.
+ //
+ if (StringPack.Header.Length != Offset) {
+ Error (
+ __FILE__,
+ __LINE__,
+ 0,
+ "application error",
+ "stringpack size 0x%X does not match final size 0x%X",
+ StringPack.Header.Length,
+ Offset
+ );
+ }
+ }
+ //
+ // Print terminator string pack, closing brace and close the file.
+ // The size of 0 triggers to the consumer that this is the end.
+ //
+ memset ((char *) &StringPack, 0, sizeof (EFI_HII_STRING_PACK_HEADER));
+ StringPack.Header.Type = EFI_HII_STRING;
+ Ptr = (UINT8 *) &StringPack;
+ fprintf (Fptr, "\n // strings terminator pack");
+ for (TempIndex = 0; TempIndex < sizeof (StringPack); TempIndex++, Ptr++) {
+ if ((TempIndex & 0x0F) == 0) {
+ fprintf (Fptr, "\n ");
+ }
+
+ fprintf (Fptr, "0x%02X, ", (UINT32) *Ptr);
+ }
+
+ fprintf (Fptr, "\n};\n");
+ fclose (Fptr);
+ return STATUS_SUCCESS;
+}
+
+/*****************************************************************************/
+
+/*++
+
+Routine Description:
+
+ Dump the #define string names
+
+Arguments:
+
+ FileName - name of the output file to write
+ BaseName - used for the protection #ifndef/#endif
+
+Returns:
+
+ STATUS
+
+--*/
+STATUS
+StringDBDumpStringDefines (
+ INT8 *FileName,
+ INT8 *BaseName
+ )
+{
+ FILE *Fptr;
+ STRING_IDENTIFIER *Identifier;
+ INT8 CopyBaseName[100];
+ UINT32 Index;
+ const INT8 *StrDefHeader[] = {
+ "#ifndef _%s_STRINGS_DEFINE_H_\n",
+ "#define _%s_STRINGS_DEFINE_H_\n\n",
+ NULL
+ };
+
+ if ((Fptr = fopen (FileName, "w")) == NULL) {
+ Error (NULL, 0, 0, FileName, "failed to open output string defines file");
+ return STATUS_ERROR;
+ }
+ //
+ // Get the base source filename and convert to uppercase.
+ //
+ if (sizeof (CopyBaseName) <= strlen (BaseName) + 1) {
+ Error (NULL, 0, 0, "application error", "StringDBDumpStringDefines() string length insufficient");
+ return STATUS_ERROR;
+ }
+
+ strcpy (CopyBaseName, BaseName);
+ for (Index = 0; CopyBaseName[Index] != 0; Index++) {
+ if (islower (CopyBaseName[Index])) {
+ CopyBaseName[Index] = (INT8) toupper (CopyBaseName[Index]);
+ }
+ }
+ //
+ // Assign index values to the string identifiers
+ //
+ StringDBAssignStringIndexes ();
+ //
+ // Write the standard header to the output file, and then the
+ // protective #ifndef.
+ //
+ StringDBWriteStandardFileHeader (Fptr);
+ for (Index = 0; StrDefHeader[Index] != NULL; Index++) {
+ fprintf (Fptr, StrDefHeader[Index], CopyBaseName);
+ }
+ //
+ // Print all the #defines for the string identifiers. Print identifiers
+ // whose names start with '$' as comments. Add comments for string
+ // identifiers not used as well.
+ //
+ Identifier = mDBData.StringIdentifier;
+ while (Identifier != NULL) {
+ if (Identifier->StringName[0] == L'$') {
+ fprintf (Fptr, "// ");
+ }
+
+ if (Identifier->Flags & STRING_FLAGS_REFERENCED) {
+ fprintf (Fptr, "#define %-40S 0x%04X\n", Identifier->StringName, Identifier->Index);
+ } else {
+ fprintf (Fptr, "//#define %-40S 0x%04X // not referenced\n", Identifier->StringName, Identifier->Index);
+ }
+
+ Identifier = Identifier->Next;
+ }
+
+ fprintf (Fptr, "\n#endif\n");
+ fclose (Fptr);
+ return STATUS_SUCCESS;
+}
+
+/*****************************************************************************/
+
+/*++
+
+Routine Description:
+
+ Add a string identifier to the database.
+
+Arguments:
+
+ StringName - name of the string identifier. For example "STR_MY_STRING"
+ NewId - if an ID has been assigned
+ Flags - characteristics for the identifier
+
+Returns:
+
+ STATUS
+
+--*/
+STATUS
+StringDBAddStringIdentifier (
+ WCHAR *StringName,
+ UINT16 *NewId,
+ UINT16 Flags
+ )
+{
+ STRING_IDENTIFIER *StringIdentifier;
+ STATUS Status;
+ //
+ // If it was already used for some other language, then we don't
+ // need to add it. But set it to the current string identifier.
+ // The referenced bit is sticky.
+ //
+ Status = STATUS_SUCCESS;
+ StringIdentifier = StringDBFindStringIdentifierByName (StringName);
+ if (StringIdentifier != NULL) {
+ if (Flags & STRING_FLAGS_REFERENCED) {
+ StringIdentifier->Flags |= STRING_FLAGS_REFERENCED;
+ }
+
+ mDBData.CurrentStringIdentifier = StringIdentifier;
+ *NewId = (UINT16) StringIdentifier->Index;
+ return Status;
+ }
+
+ StringIdentifier = (STRING_IDENTIFIER *) MALLOC (sizeof (STRING_IDENTIFIER));
+ if (StringIdentifier == NULL) {
+ Error (NULL, 0, 0, NULL, "memory allocation error");
+ return STATUS_ERROR;
+ }
+
+ memset ((char *) StringIdentifier, 0, sizeof (STRING_IDENTIFIER));
+ StringIdentifier->StringName = (WCHAR *) malloc ((wcslen (StringName) + 1) * sizeof (WCHAR));
+ if (StringIdentifier->StringName == NULL) {
+ Error (NULL, 0, 0, NULL, "memory allocation error");
+ return STATUS_ERROR;
+ }
+
+ wcscpy (StringIdentifier->StringName, StringName);
+ if (*NewId != STRING_ID_INVALID) {
+ StringIdentifier->Index = *NewId;
+ StringIdentifier->Flags |= STRING_FLAGS_INDEX_ASSIGNED;
+ if (mDBData.NumStringIdentifiers <= StringIdentifier->Index) {
+ mDBData.NumStringIdentifiers = StringIdentifier->Index + 1;
+ }
+ } else {
+ StringIdentifier->Index = mDBData.NumStringIdentifiers++;
+ }
+
+ StringIdentifier->Flags |= Flags;
+ //
+ // Add it to our list of string identifiers
+ //
+ if (mDBData.StringIdentifier == NULL) {
+ mDBData.StringIdentifier = StringIdentifier;
+ } else {
+ mDBData.LastStringIdentifier->Next = StringIdentifier;
+ }
+
+ mDBData.LastStringIdentifier = StringIdentifier;
+ mDBData.CurrentStringIdentifier = StringIdentifier;
+ *NewId = (UINT16) StringIdentifier->Index;
+ return Status;
+}
+
+/*****************************************************************************/
+
+/*++
+
+Routine Description:
+
+ Add a new string to the database.
+
+Arguments:
+
+ LanguageName - "eng" or "spa" language name
+ StringName - "STR_MY_TEXT" string name
+ Scope - from the #scope statements in the string file
+ Format - if we should format the string
+ Flags - characteristic flags for the string
+
+Returns:
+
+ STATUS
+
+Notes:
+
+ Several of the fields can be "inherited" from the previous calls to
+ our database functions. For example, if scope is NULL here, then
+ we'll use the previous setting.
+
+--*/
+STATUS
+StringDBAddString (
+ WCHAR *LanguageName,
+ WCHAR *StringName,
+ WCHAR *Scope,
+ WCHAR *String,
+ BOOLEAN Format,
+ UINT16 Flags
+ )
+{
+ LANGUAGE_LIST *Lang;
+ UINT32 Size;
+ STRING_LIST *Str;
+ UINT16 StringIndex;
+ WCHAR TempLangName[4];
+ STRING_IDENTIFIER *StringIdentifier;
+
+ //
+ // Check that language name is exactly 3 characters, or emit an error.
+ // Truncate at 3 if it's longer, or make it 3 if it's shorter.
+ //
+ if (LanguageName != NULL) {
+ Size = wcslen (LanguageName);
+ if (Size != 3) {
+ ParserError (0, "invalid length for language name", "%S", LanguageName);
+ if (Size > 3) {
+ LanguageName[3] = 0;
+ } else {
+ //
+ // Make a local copy of the language name string, and extend to
+ // 3 characters since we make assumptions elsewhere in this program
+ // on the length.
+ //
+ wcscpy (TempLangName, LanguageName);
+ for (; Size < 3; Size++) {
+ TempLangName[Size] = L'?';
+ }
+
+ TempLangName[3] = 0;
+ LanguageName = TempLangName;
+ }
+ }
+ }
+ //
+ // If they specified a language, make sure they've defined it already
+ // via a #langdef statement. Otherwise use the current default language.
+ //
+ if (LanguageName != NULL) {
+ Lang = StringDBFindLanguageList (LanguageName);
+ if (Lang == NULL) {
+ ParserError (0, "language not defined", "%S", LanguageName);
+ return STATUS_ERROR;
+ } else {
+ StringDBSetCurrentLanguage (LanguageName);
+ }
+ } else {
+ Lang = mDBData.CurrentLanguage;
+ if (Lang == NULL) {
+ //
+ // Have to call SetLanguage() first
+ //
+ ParserError (0, "no language defined", "%S", StringName);
+ return STATUS_ERROR;
+ }
+ }
+ //
+ // If they didn't define a string identifier, use the last string identifier
+ // added.
+ //
+ if (StringName == NULL) {
+ StringName = mDBData.CurrentStringIdentifier->StringName;
+ if (StringName == NULL) {
+ ParserError (0, "no string identifier previously specified", NULL);
+ return STATUS_ERROR;
+ }
+ }
+ //
+ // If scope was not specified, use the default setting
+ //
+ if (Scope != NULL) {
+ Scope = DuplicateString (Scope);
+ } else {
+ Scope = DuplicateString (mDBData.CurrentScope);
+ }
+ //
+ // printf ("Adding string: %S.%S.%S\n", Lang->LanguageName, StringName, Scope);
+ //
+ // Check for duplicates for this Language.StringName.Scope. Allow multiple
+ // definitions of the language name and printable language name, since the
+ // user does not specifically define them.
+ //
+ if (StringDBFindString (Lang->LanguageName, StringName, Scope, NULL, NULL) != NULL) {
+ if ((wcscmp (StringName, LANGUAGE_NAME_STRING_NAME) == 0) &&
+ (wcscmp (StringName, PRINTABLE_LANGUAGE_NAME_STRING_NAME) == 0)
+ ) {
+ ParserError (
+ 0,
+ "string multiply defined",
+ "Language.Name.Scope = %S.%S.%S",
+ Lang->LanguageName,
+ StringName,
+ Scope
+ );
+ return STATUS_ERROR;
+ }
+ }
+
+ StringIndex = STRING_ID_INVALID;
+ if (StringDBAddStringIdentifier (StringName, &StringIndex, Flags) != STATUS_SUCCESS) {
+ return STATUS_ERROR;
+ }
+
+ StringIdentifier = StringDBFindStringIdentifierByName (StringName);
+ //
+ // Add this string to the end of the strings for this language.
+ //
+ Str = (STRING_LIST *) malloc (sizeof (STRING_LIST));
+ if (Str == NULL) {
+ Error (NULL, 0, 0, NULL, "memory allocation error");
+ return STATUS_ERROR;
+ }
+
+ memset ((char *) Str, 0, sizeof (STRING_LIST));
+ Size = (wcslen (String) + 1) * sizeof (WCHAR);
+ Str->Flags = Flags;
+ Str->Scope = Scope;
+ Str->StringName = StringIdentifier->StringName;
+ Str->LanguageName = DuplicateString (LanguageName);
+ Str->Str = (WCHAR *) MALLOC (Size);
+ if (Str->Str == NULL) {
+ Error (NULL, 0, 0, NULL, "memory allocation error");
+ return STATUS_ERROR;
+ }
+ //
+ // If not formatting, just copy the string.
+ //
+ wcscpy (Str->Str, String);
+ if (Format) {
+ StringDBFormatString (Str->Str);
+ }
+ //
+ // Size may change after formatting. We set the size to
+ // the actual size of the string, including the null for
+ // easier processing later.
+ //
+ Str->Size = (wcslen (Str->Str) + 1) * sizeof (WCHAR);
+ if (Lang->String == NULL) {
+ Lang->String = Str;
+ } else {
+ Lang->LastString->Next = Str;
+ }
+
+ Lang->LastString = Str;
+ return STATUS_SUCCESS;
+}
+
+/*****************************************************************************/
+
+/*++
+
+Routine Description:
+
+ Given a language name, see if a language list for it has been defined
+
+Arguments:
+
+ LanguageName - like "eng"
+
+Returns:
+
+ A pointer to the language list
+
+--*/
+static
+LANGUAGE_LIST *
+StringDBFindLanguageList (
+ WCHAR *LanguageName
+ )
+{
+ LANGUAGE_LIST *Lang;
+
+ Lang = mDBData.LanguageList;
+ while (Lang != NULL) {
+ if (wcscmp (LanguageName, Lang->LanguageName) == 0) {
+ break;
+ }
+
+ Lang = Lang->Next;
+ }
+
+ return Lang;
+}
+
+/*****************************************************************************/
+STATUS
+StringDBSetCurrentLanguage (
+ WCHAR *LanguageName
+ )
+{
+ LANGUAGE_LIST *Lang;
+
+ Lang = StringDBFindLanguageList (LanguageName);
+ if (Lang == NULL) {
+ ParserError (0, "language not previously defined", "%S", LanguageName);
+ return STATUS_ERROR;
+ }
+
+ mDBData.CurrentLanguage = Lang;
+ return STATUS_SUCCESS;
+}
+
+/*****************************************************************************/
+STATUS
+StringDBAddLanguage (
+ WCHAR *LanguageName,
+ WCHAR *PrintableLanguageName
+ )
+{
+ LANGUAGE_LIST *Lang;
+ //
+ // Check for redefinitions
+ //
+ Lang = StringDBFindLanguageList (LanguageName);
+ if (Lang != NULL) {
+ //
+ // Better be the same printable name
+ //
+ if (wcscmp (PrintableLanguageName, Lang->PrintableLanguageName) != 0) {
+ ParserError (
+ 0,
+ "language redefinition",
+ "%S:%S != %S:%S",
+ Lang->LanguageName,
+ Lang->PrintableLanguageName,
+ LanguageName,
+ PrintableLanguageName
+ );
+ return STATUS_ERROR;
+ //
+ // } else {
+ // ParserWarning (0, "benign language redefinition", "%S", PrintableLanguageName);
+ // return STATUS_WARNING;
+ //
+ }
+ } else {
+ //
+ // Allocate memory to keep track of this new language
+ //
+ Lang = (LANGUAGE_LIST *) malloc (sizeof (LANGUAGE_LIST));
+ if (Lang == NULL) {
+ Error (NULL, 0, 0, NULL, "memory allocation error");
+ return STATUS_ERROR;
+ }
+
+ memset ((char *) Lang, 0, sizeof (LANGUAGE_LIST));
+ //
+ // Save the language name, then allocate memory to save the
+ // printable language name
+ //
+ Lang->LanguageName[3] = 0;
+ wcsncpy (Lang->LanguageName, LanguageName, 3);
+ Lang->PrintableLanguageName = (WCHAR *) malloc ((wcslen (PrintableLanguageName) + 1) * sizeof (WCHAR));
+ if (Lang->PrintableLanguageName == NULL) {
+ Error (NULL, 0, 0, NULL, "memory allocation error");
+ return STATUS_ERROR;
+ }
+
+ wcscpy (Lang->PrintableLanguageName, PrintableLanguageName);
+
+ if (mDBData.LanguageList == NULL) {
+ mDBData.LanguageList = Lang;
+ } else {
+ mDBData.LastLanguageList->Next = Lang;
+ }
+
+ mDBData.LastLanguageList = Lang;
+ }
+ //
+ // Default is to make our active language this new one
+ //
+ StringDBSetCurrentLanguage (LanguageName);
+ //
+ // The first two strings for any language are the language name,
+ // followed by the printable language name. Add them and set them
+ // to referenced so they never get stripped out.
+ //
+ StringDBAddString (
+ LanguageName,
+ LANGUAGE_NAME_STRING_NAME,
+ NULL,
+ LanguageName,
+ FALSE,
+ STRING_FLAGS_REFERENCED
+ );
+ StringDBAddString (
+ LanguageName,
+ PRINTABLE_LANGUAGE_NAME_STRING_NAME,
+ NULL,
+ PrintableLanguageName,
+ FALSE,
+ STRING_FLAGS_REFERENCED
+ );
+ return STATUS_SUCCESS;
+}
+
+/*****************************************************************************/
+static
+STRING_IDENTIFIER *
+StringDBFindStringIdentifierByName (
+ WCHAR *StringName
+ )
+{
+ STRING_IDENTIFIER *Identifier;
+
+ Identifier = mDBData.StringIdentifier;
+ while (Identifier != NULL) {
+ if (wcscmp (StringName, Identifier->StringName) == 0) {
+ return Identifier;
+ }
+
+ Identifier = Identifier->Next;
+ }
+
+ return NULL;
+}
+
+static
+STRING_IDENTIFIER *
+StringDBFindStringIdentifierByIndex (
+ UINT32 StringIndex
+ )
+{
+ STRING_IDENTIFIER *Identifier;
+
+ Identifier = mDBData.StringIdentifier;
+ while (Identifier != NULL) {
+ if (Identifier->Index == StringIndex) {
+ return Identifier;
+ }
+
+ Identifier = Identifier->Next;
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************/
+static
+void
+StringDBWriteStandardFileHeader (
+ FILE *OutFptr
+ )
+{
+ UINT32 TempIndex;
+ for (TempIndex = 0; mSourceFileHeader[TempIndex] != NULL; TempIndex++) {
+ fprintf (OutFptr, "%s\n", mSourceFileHeader[TempIndex]);
+ }
+}
+
+/*****************************************************************************/
+
+/*++
+
+Routine Description:
+
+ Given a Unicode string from an input file, reformat the string to replace
+ backslash control sequences with the appropriate encoding.
+
+Arguments:
+
+ String - pointer to string to reformat
+
+Returns:
+
+ Nothing
+
+--*/
+void
+StringDBFormatString (
+ WCHAR *String
+ )
+{
+ WCHAR *From;
+ WCHAR *To;
+ int HexNibbles;
+ WCHAR HexValue;
+ //
+ // Go through the string and process any formatting characters
+ //
+ From = String;
+ To = String;
+ while (*From) {
+ if (*From == UNICODE_BACKSLASH) {
+ //
+ // First look for \wide and replace with the appropriate control character. Note that
+ // when you have "define STR L"ABC"", then sizeof(ABC) is 8 because the null char is
+ // counted. Make adjustments for this. We advance From below, so subtract 2 each time.
+ //
+ if (wcsncmp (From, UNICODE_WIDE_STRING, sizeof (UNICODE_WIDE_STRING) / sizeof (WCHAR) - 1) == 0) {
+ *To = WIDE_CHAR;
+ From += sizeof (UNICODE_WIDE_STRING) / sizeof (WCHAR) - 2;
+ } else if (wcsncmp (From, UNICODE_NARROW_STRING, sizeof (UNICODE_NARROW_STRING) / sizeof (WCHAR) - 1) == 0) {
+ //
+ // Found: \narrow
+ //
+ *To = NARROW_CHAR;
+ From += sizeof (UNICODE_NARROW_STRING) / sizeof (WCHAR) - 2;
+ } else if (wcsncmp (From, UNICODE_NBR_STRING, sizeof (UNICODE_NBR_STRING) / sizeof (WCHAR) - 1) == 0) {
+ //
+ // Found: \nbr
+ //
+ *To = NON_BREAKING_CHAR;
+ From += sizeof (UNICODE_NBR_STRING) / sizeof (WCHAR) - 2;
+ } else if (wcsncmp (From, UNICODE_BR_STRING, sizeof (UNICODE_BR_STRING) / sizeof (WCHAR) - 1) == 0) {
+ //
+ // Found: \br -- pass through untouched
+ //
+ *To = *From;
+ } else {
+ //
+ // Standard one-character control sequences such as \n, \r, \\, or \x
+ //
+ From++;
+ switch (*From) {
+ case ASCII_TO_UNICODE ('n'):
+ *To = UNICODE_CR;
+ To++;
+ *To = UNICODE_LF;
+ break;
+
+ //
+ // carriage return
+ //
+ case ASCII_TO_UNICODE ('r'):
+ *To = UNICODE_CR;
+ break;
+
+ //
+ // backslash
+ //
+ case UNICODE_BACKSLASH:
+ *To = UNICODE_BACKSLASH;
+ break;
+
+ //
+ // Tab
+ //
+ case ASCII_TO_UNICODE ('t'):
+ *To = UNICODE_TAB;
+ break;
+
+ //
+ // embedded double-quote
+ //
+ case UNICODE_DOUBLE_QUOTE:
+ *To = UNICODE_DOUBLE_QUOTE;
+ break;
+
+ //
+ // Hex Unicode character \x1234. We'll process up to 4 hex characters
+ //
+ case ASCII_TO_UNICODE ('x'):
+ HexValue = 0;
+ for (HexNibbles = 0; HexNibbles < 4; HexNibbles++) {
+ if ((From[1] >= UNICODE_0) && (From[1] <= UNICODE_9)) {
+ HexValue = (HexValue << 4) | (From[1] - UNICODE_0);
+ } else if ((From[1] >= UNICODE_a) && (From[1] <= UNICODE_f)) {
+ HexValue = (HexValue << 4) | (10 + From[1] - UNICODE_a);
+ } else if ((From[1] >= UNICODE_A) && (From[1] <= UNICODE_F)) {
+ HexValue = (HexValue << 4) | (10 + From[1] - UNICODE_A);
+ } else {
+ break;
+ }
+
+ From++;
+ }
+
+ if (HexNibbles == 0) {
+ ParserWarning (
+ 0,
+ "expected at least one valid hex digit with \\x escaped character in string",
+ "\\%C",
+ *From
+ );
+ } else {
+ *To = HexValue;
+ }
+ break;
+
+ default:
+ *To = UNICODE_SPACE;
+ ParserWarning (0, "invalid escaped character in string", "\\%C", *From);
+ break;
+ }
+ }
+ } else {
+ *To = *From;
+ }
+
+ From++;
+ To++;
+ }
+
+ *To = 0;
+}
+
+/*****************************************************************************/
+STATUS
+StringDBReadDatabase (
+ INT8 *DBFileName,
+ BOOLEAN IgnoreIfNotExist,
+ BOOLEAN Verbose
+ )
+{
+ STRING_DB_HEADER DbHeader;
+ STATUS Status;
+ FILE *DBFptr;
+ DB_DATA_ITEM_HEADER DataItemHeader;
+
+ Status = STATUS_SUCCESS;
+ DBFptr = NULL;
+ //
+ // if (Verbose) {
+ // fprintf (stdout, "Reading database file %s\n", DBFileName);
+ // }
+ //
+ // Try to open the input file
+ //
+ if ((DBFptr = fopen (DBFileName, "rb")) == NULL) {
+ if (IgnoreIfNotExist) {
+ return STATUS_SUCCESS;
+ }
+
+ Error (NULL, 0, 0, DBFileName, "failed to open input database file for reading");
+ return STATUS_ERROR;
+ }
+ //
+ // Read and verify the database header
+ //
+ if (fread ((void *) &DbHeader, sizeof (STRING_DB_HEADER), 1, DBFptr) != 1) {
+ Error (NULL, 0, 0, DBFileName, "failed to read header from database file");
+ Status = STATUS_ERROR;
+ goto Finish;
+ }
+
+ if (DbHeader.Key != STRING_DB_KEY) {
+ Error (NULL, 0, 0, DBFileName, "invalid header in database file");
+ Status = STATUS_ERROR;
+ goto Finish;
+ }
+
+ if ((DbHeader.Version & STRING_DB_MAJOR_VERSION_MASK) != (STRING_DB_VERSION & STRING_DB_MAJOR_VERSION_MASK)) {
+ Error (NULL, 0, 0, DBFileName, "incompatible database file version -- rebuild clean");
+ Status = STATUS_ERROR;
+ goto Finish;
+ }
+ //
+ // Read remaining items
+ //
+ while (fread (&DataItemHeader, sizeof (DataItemHeader), 1, DBFptr) == 1) {
+ switch (DataItemHeader.DataType) {
+ case DB_DATA_TYPE_STRING_IDENTIFIER:
+ StringDBReadStringIdentifier (DBFptr);
+ break;
+
+ case DB_DATA_TYPE_LANGUAGE_DEFINITION:
+ StringDBReadLanguageDefinition (DBFptr);
+ break;
+
+ case DB_DATA_TYPE_STRING_DEFINITION:
+ StringDBReadString (DBFptr);
+ break;
+
+ default:
+ Error (
+ NULL,
+ 0,
+ 0,
+ "database corrupted",
+ "invalid data item type 0x%X at offset 0x%X",
+ (UINT32) DataItemHeader.DataType,
+ ftell (DBFptr) - sizeof (DataItemHeader)
+ );
+ Status = STATUS_ERROR;
+ goto Finish;
+ }
+ }
+
+Finish:
+ if (DBFptr != NULL) {
+ fclose (DBFptr);
+ }
+
+ return Status;
+}
+
+/*****************************************************************************/
+
+/*++
+
+Routine Description:
+
+ Write everything we know to the output database file. Write:
+
+ Database header
+ String identifiers[]
+ StringPacks[]
+
+Arguments:
+
+ DBFileName - name of the file to write to
+ Verbose - for debug purposes, print info messages along the way.
+
+Returns:
+
+ STATUS
+
+--*/
+STATUS
+StringDBWriteDatabase (
+ INT8 *DBFileName,
+ BOOLEAN Verbose
+ )
+{
+ STRING_DB_HEADER DbHeader;
+ UINT32 Counter;
+ UINT32 StrLen;
+ LANGUAGE_LIST *Lang;
+ STRING_IDENTIFIER *StringIdentifier;
+ STRING_LIST *StrList;
+ FILE *DBFptr;
+
+ if (Verbose) {
+ fprintf (stdout, "Writing database %s\n", DBFileName);
+ }
+
+ if ((DBFptr = fopen (DBFileName, "wb")) == NULL) {
+ Error (NULL, 0, 0, DBFileName, "failed to open output database file for writing");
+ return STATUS_ERROR;
+ }
+ //
+ // Fill in and write the database header
+ //
+ memset (&DbHeader, 0, sizeof (STRING_DB_HEADER));
+ DbHeader.HeaderSize = sizeof (STRING_DB_HEADER);
+ DbHeader.Key = STRING_DB_KEY;
+ DbHeader.Version = STRING_DB_VERSION;
+ //
+ // Count the number of languages we have
+ //
+ for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {
+ DbHeader.NumLanguages++;
+ }
+ //
+ // Count up how many string identifiers we have, and total up the
+ // size of the names plus the size of the flags field we will
+ // write out too.
+ //
+ DbHeader.NumStringIdenfiers = mDBData.NumStringIdentifiers;
+ StringIdentifier = mDBData.StringIdentifier;
+ for (Counter = 0; Counter < mDBData.NumStringIdentifiers; Counter++) {
+ StrLen = wcslen (StringIdentifier->StringName) + 1;
+ DbHeader.StringIdentifiersSize += StrLen * sizeof (WCHAR) + sizeof (StringIdentifier->Flags);
+ StringIdentifier = StringIdentifier->Next;
+ }
+
+ //
+ // Write the header
+ //
+ fwrite (&DbHeader, sizeof (STRING_DB_HEADER), 1, DBFptr);
+ if (Verbose) {
+ fprintf (stdout, " Number of string identifiers 0x%04X\n", DbHeader.NumStringIdenfiers);
+ fprintf (stdout, " Number of languages %d\n", DbHeader.NumLanguages);
+ }
+ //
+ // Write the string identifiers
+ //
+ for (StringIdentifier = mDBData.StringIdentifier; StringIdentifier != NULL; StringIdentifier = StringIdentifier->Next) {
+ StringDBWriteStringIdentifier (
+ DBFptr,
+ (UINT16) StringIdentifier->Index,
+ StringIdentifier->Flags,
+ StringIdentifier->StringName
+ );
+ }
+ //
+ // Now write all the strings for each language
+ //
+ for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {
+ StringDBWriteLanguageDefinition (DBFptr, Lang->LanguageName, Lang->PrintableLanguageName);
+ for (StrList = Lang->String; StrList != NULL; StrList = StrList->Next) {
+ StringDBWriteString (
+ DBFptr,
+ StrList->Flags,
+ Lang->LanguageName,
+ StrList->StringName,
+ StrList->Scope,
+ StrList->Str
+ );
+ }
+ }
+
+ fclose (DBFptr);
+ return STATUS_SUCCESS;
+}
+
+STATUS
+StringDBSetStringReferenced (
+ INT8 *StringIdentifierName,
+ BOOLEAN IgnoreNotFound
+ )
+{
+ STRING_IDENTIFIER *Id;
+ WCHAR *WName;
+ STATUS Status;
+ //
+ // See if it's already been defined.
+ //
+ Status = STATUS_SUCCESS;
+ WName = (WCHAR *) malloc ((strlen (StringIdentifierName) + 1) * sizeof (WCHAR));
+#ifdef USE_VC8
+ swprintf (WName, (strlen (StringIdentifierName) + 1) * sizeof (WCHAR), L"%S", StringIdentifierName);
+#else
+ swprintf (WName, L"%S", StringIdentifierName);
+#endif
+ Id = StringDBFindStringIdentifierByName (WName);
+ if (Id != NULL) {
+ Id->Flags |= STRING_FLAGS_REFERENCED;
+ } else {
+ if (IgnoreNotFound == 0) {
+ ParserWarning (0, StringIdentifierName, "string identifier not found in database");
+ Status = STATUS_WARNING;
+ }
+ }
+
+ free (WName);
+ return Status;
+}
+
+/*****************************************************************************/
+
+/*++
+
+Routine Description:
+
+ Dump the contents of a database to an output unicode file.
+
+Arguments:
+
+ DBFileName - name of the pre-existing database file to read
+ OutputFileName - name of the file to dump the database contents to
+ Verbose - for printing of additional info useful for debugging
+
+Returns:
+
+ STATUS
+
+Notes:
+
+ There's some issue with the unicode printing routines. Therefore to
+ write to the output file properly, open it as binary and use fwrite.
+ Ideally we could open it with just L"w" and use fwprintf().
+
+--*/
+STATUS
+StringDBDumpDatabase (
+ INT8 *DBFileName,
+ INT8 *OutputFileName,
+ BOOLEAN Verbose
+ )
+{
+ LANGUAGE_LIST *Lang;
+ STRING_IDENTIFIER *StringIdentifier;
+ STRING_LIST *StrList;
+ FILE *OutFptr;
+ WCHAR WChar;
+ WCHAR *WOutputFileName;
+ WCHAR CrLf[2];
+ WCHAR Line[200];
+ WCHAR *Scope;
+ //
+ // This function assumes the database has already been read, and
+ // we're just dumping our internal data structures to a unicode file.
+ //
+ if (Verbose) {
+ fprintf (stdout, "Dumping database file %s\n", DBFileName);
+ }
+
+ WOutputFileName = AsciiToWchar (OutputFileName);
+ OutFptr = _wfopen (WOutputFileName, L"wb");
+ free (WOutputFileName);
+ if (OutFptr == NULL) {
+ Error (NULL, 0, 0, OutputFileName, "failed to open output file for writing");
+ return STATUS_ERROR;
+ }
+
+ WChar = UNICODE_FILE_START;
+ fwrite (&WChar, sizeof (WCHAR), 1, OutFptr);
+ CrLf[1] = UNICODE_LF;
+ CrLf[0] = UNICODE_CR;
+ //
+ // The default control character is '/'. Make it '#' by writing
+ // "/=#" to the output file.
+ //
+#ifdef USE_VC8
+ swprintf (Line, wcslen(Line) * sizeof (WCHAR), L"/=#");
+#else
+ swprintf (Line, L"/=#");
+#endif
+ fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);
+ fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
+ fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
+ //
+ // Dump all the string identifiers and their values
+ //
+ StringDBAssignStringIndexes ();
+ for (StringIdentifier = mDBData.StringIdentifier; StringIdentifier != NULL; StringIdentifier = StringIdentifier->Next) {
+ //
+ // Write the "#define " string
+ //
+ if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) {
+#ifdef USE_VC8
+ swprintf (
+ Line,
+ wcslen(Line) * sizeof (WCHAR),
+ L"%s %-60.60s 0x%04X",
+ DEFINE_STR,
+ StringIdentifier->StringName,
+ StringIdentifier->Index
+ );
+#else
+ swprintf (
+ Line,
+ L"%s %-60.60s 0x%04X",
+ DEFINE_STR,
+ StringIdentifier->StringName,
+ StringIdentifier->Index
+ );
+#endif
+ } else {
+#ifdef USE_VC8
+ swprintf (
+ Line,
+ wcslen(Line) * sizeof (WCHAR),
+ L"%s %-60.60s 0x%04X // NOT REFERENCED",
+ DEFINE_STR,
+ StringIdentifier->StringName,
+ StringIdentifier->Index
+ );
+#else
+ swprintf (
+ Line,
+ L"%s %-60.60s 0x%04X // NOT REFERENCED",
+ DEFINE_STR,
+ StringIdentifier->StringName,
+ StringIdentifier->Index
+ );
+#endif
+ }
+
+ fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);
+ fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
+ }
+
+ fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
+ //
+ // Now write all the strings for each language.
+ //
+ WChar = UNICODE_DOUBLE_QUOTE;
+ Scope = NULL;
+ for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {
+ fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
+#ifdef USE_VC8
+ swprintf (Line, wcslen(Line) * sizeof (WCHAR), L"#langdef %s \"%s\"", Lang->LanguageName, Lang->PrintableLanguageName);
+#else
+ swprintf (Line, L"#langdef %s \"%s\"", Lang->LanguageName, Lang->PrintableLanguageName);
+#endif
+ fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);
+ fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
+ fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
+ //
+ // Now the strings (in double-quotes) for this language. Write
+ // #string STR_NAME #language eng "string"
+ //
+ for (StrList = Lang->String; StrList != NULL; StrList = StrList->Next) {
+ //
+ // Print the internal flags for debug
+ //
+#ifdef USE_VC8
+ swprintf (Line, wcslen(Line) * sizeof (WCHAR), L"// flags=0x%02X", (UINT32) StrList->Flags);
+#else
+ swprintf (Line, L"// flags=0x%02X", (UINT32) StrList->Flags);
+#endif
+ fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);
+ fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
+ //
+ // Print the scope if changed
+ //
+ if ((Scope == NULL) || (wcscmp (Scope, StrList->Scope) != 0)) {
+#ifdef USE_VC8
+ swprintf (Line, wcslen(Line) * sizeof (WCHAR), L"#scope %s", StrList->Scope);
+#else
+ swprintf (Line, L"#scope %s", StrList->Scope);
+#endif
+ fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);
+ fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
+ Scope = StrList->Scope;
+ }
+
+#ifdef USE_VC8
+ swprintf (
+ Line,
+ wcslen(Line) * sizeof (WCHAR),
+ L"#string %-50.50s #language %s \"",
+ StrList->StringName,
+ Lang->LanguageName
+ );
+#else
+ swprintf (
+ Line,
+ L"#string %-50.50s #language %s \"",
+ StrList->StringName,
+ Lang->LanguageName
+ );
+#endif
+ fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);
+ fwrite (StrList->Str, StrList->Size - sizeof (WCHAR), 1, OutFptr);
+#ifdef USE_VC8
+ swprintf (Line, wcslen(Line) * sizeof (WCHAR), L"\"");
+#else
+ swprintf (Line, L"\"");
+#endif
+ fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);
+ fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
+ }
+ }
+
+ fclose (OutFptr);
+ return STATUS_SUCCESS;
+}
+
+/*****************************************************************************/
+
+/*++
+
+Routine Description:
+
+ Given a primary language, a string identifier number, and a list of
+ languages, find a secondary string.
+
+Arguments:
+
+ LanguageName - primary language, like "spa"
+ StringId - string index value
+ LanguageList - linked list of "eng", "spa+cat",...
+
+Returns:
+
+ Pointer to a secondary string if found. NULL otherwise.
+
+Notes:
+
+ Given: LanguageName "spa" and LanguageList "spa+cat", match the
+ "spa" and extract the "cat" and see if there is a string defined
+ for "cat".StringId.
+
+--*/
+static
+STATUS
+StringDBWriteStringIdentifier (
+ FILE *DBFptr,
+ UINT16 StringId,
+ UINT16 Flags,
+ WCHAR *IdentifierName
+ )
+{
+ DB_DATA_ITEM_HEADER Hdr;
+ memset (&Hdr, 0, sizeof (DB_DATA_ITEM_HEADER));
+ Hdr.DataType = DB_DATA_TYPE_STRING_IDENTIFIER;
+ if (fwrite (&Hdr, sizeof (DB_DATA_ITEM_HEADER), 1, DBFptr) != 1) {
+ Error (NULL, 0, 0, "failed to write string to output database file", NULL);
+ return STATUS_ERROR;
+ }
+
+ if (fwrite (&StringId, sizeof (StringId), 1, DBFptr) != 1) {
+ Error (NULL, 0, 0, "failed to write StringId to output database", NULL);
+ return STATUS_ERROR;
+ }
+
+ if (fwrite (&Flags, sizeof (Flags), 1, DBFptr) != 1) {
+ Error (NULL, 0, 0, "failed to write StringId flags to output database", NULL);
+ return STATUS_ERROR;
+ }
+
+ if (StringDBWriteGenericString (DBFptr, IdentifierName) != STATUS_SUCCESS) {
+ return STATUS_ERROR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static
+STATUS
+StringDBReadStringIdentifier (
+ FILE *DBFptr
+ )
+{
+ WCHAR *IdentifierName;
+ UINT16 Flags;
+ UINT16 StringId;
+ UINT16 Size;
+
+ if (fread (&StringId, sizeof (StringId), 1, DBFptr) != 1) {
+ Error (NULL, 0, 0, "failed to read StringId from database", NULL);
+ return STATUS_ERROR;
+ }
+
+ if (fread (&Flags, sizeof (Flags), 1, DBFptr) != 1) {
+ Error (NULL, 0, 0, "failed to read StringId flags from database", NULL);
+ return STATUS_ERROR;
+ }
+
+ if (StringDBReadGenericString (DBFptr, &Size, &IdentifierName) != STATUS_SUCCESS) {
+ return STATUS_ERROR;
+ }
+
+ StringDBAddStringIdentifier (IdentifierName, &StringId, Flags);
+ //
+ // printf ("STRID: 0x%04X %S\n", (UINT32)StringId, IdentifierName);
+ //
+ FREE (IdentifierName);
+ return STATUS_SUCCESS;
+}
+
+static
+STATUS
+StringDBWriteString (
+ FILE *DBFptr,
+ UINT16 Flags,
+ WCHAR *Language,
+ WCHAR *StringName,
+ WCHAR *Scope,
+ WCHAR *Str
+ )
+{
+ DB_DATA_ITEM_HEADER Hdr;
+ memset (&Hdr, 0, sizeof (DB_DATA_ITEM_HEADER));
+ Hdr.DataType = DB_DATA_TYPE_STRING_DEFINITION;
+ if (fwrite (&Hdr, sizeof (DB_DATA_ITEM_HEADER), 1, DBFptr) != 1) {
+ Error (NULL, 0, 0, "failed to write string header to output database file", NULL);
+ return STATUS_ERROR;
+ }
+
+ if (fwrite (&Flags, sizeof (Flags), 1, DBFptr) != 1) {
+ Error (NULL, 0, 0, "failed to write string flags to output database", NULL);
+ return STATUS_ERROR;
+ }
+
+ if (StringDBWriteGenericString (DBFptr, Language) != STATUS_SUCCESS) {
+ return STATUS_ERROR;
+ }
+
+ if (StringDBWriteGenericString (DBFptr, StringName) != STATUS_SUCCESS) {
+ return STATUS_ERROR;
+ }
+
+ if (StringDBWriteGenericString (DBFptr, Scope) != STATUS_SUCCESS) {
+ return STATUS_ERROR;
+ }
+
+ if (StringDBWriteGenericString (DBFptr, Str) != STATUS_SUCCESS) {
+ return STATUS_ERROR;
+ }
+ //
+ // printf ("DBWriteString: %S.%S.%S\n", Language, StringName, Scope);
+ //
+ return STATUS_SUCCESS;
+}
+
+static
+STATUS
+StringDBReadString (
+ FILE *DBFptr
+ )
+{
+ UINT16 Flags;
+ UINT16 Size;
+ WCHAR *Language;
+ WCHAR *StringName;
+ WCHAR *Scope;
+ WCHAR *Str;
+
+ if (fread (&Flags, sizeof (Flags), 1, DBFptr) != 1) {
+ Error (NULL, 0, 0, "failed to read string flags from database", NULL);
+ return STATUS_ERROR;
+ }
+
+ if (StringDBReadGenericString (DBFptr, &Size, &Language) != STATUS_SUCCESS) {
+ return STATUS_ERROR;
+ }
+
+ if (StringDBReadGenericString (DBFptr, &Size, &StringName) != STATUS_SUCCESS) {
+ return STATUS_ERROR;
+ }
+
+ if (StringDBReadGenericString (DBFptr, &Size, &Scope) != STATUS_SUCCESS) {
+ return STATUS_ERROR;
+ }
+
+ if (StringDBReadGenericString (DBFptr, &Size, &Str) != STATUS_SUCCESS) {
+ return STATUS_ERROR;
+ }
+ //
+ // If the first or second string (language name and printable language name),
+ // then skip them. They're added via language definitions data items in
+ // the database.
+ //
+ if (StringName[0] != L'$') {
+ StringDBAddString (Language, StringName, Scope, Str, FALSE, Flags);
+ }
+ //
+ // printf ("DBReadString: %S.%S.%S\n", Language, StringName, Scope);
+ //
+ FREE (Language);
+ FREE (StringName);
+ if (Str != NULL) {
+ FREE (Str);
+ }
+
+ if (Scope != NULL) {
+ FREE (Scope);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static
+STATUS
+StringDBWriteLanguageDefinition (
+ FILE *DBFptr,
+ WCHAR *LanguageName,
+ WCHAR *PrintableLanguageName
+ )
+{
+ DB_DATA_ITEM_HEADER Hdr;
+ memset (&Hdr, 0, sizeof (DB_DATA_ITEM_HEADER));
+ Hdr.DataType = DB_DATA_TYPE_LANGUAGE_DEFINITION;
+ if (fwrite (&Hdr, sizeof (DB_DATA_ITEM_HEADER), 1, DBFptr) != 1) {
+ Error (NULL, 0, 0, "failed to write string to output database file", NULL);
+ return STATUS_ERROR;
+ }
+
+ if (StringDBWriteGenericString (DBFptr, LanguageName) != STATUS_SUCCESS) {
+ return STATUS_ERROR;
+ }
+
+ if (StringDBWriteGenericString (DBFptr, PrintableLanguageName) != STATUS_SUCCESS) {
+ return STATUS_ERROR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static
+STATUS
+StringDBReadLanguageDefinition (
+ FILE *DBFptr
+ )
+{
+ WCHAR *LanguageName;
+ WCHAR *PrintableLanguageName;
+ UINT16 Size;
+ STATUS Status;
+
+ if (StringDBReadGenericString (DBFptr, &Size, &LanguageName) != STATUS_SUCCESS) {
+ return STATUS_ERROR;
+ }
+
+ if (StringDBReadGenericString (DBFptr, &Size, &PrintableLanguageName) != STATUS_SUCCESS) {
+ return STATUS_ERROR;
+ }
+ //
+ // printf("LANG: %S %S\n", LanguageName, PrintableLanguageName);
+ //
+ Status = StringDBAddLanguage (LanguageName, PrintableLanguageName);
+ FREE (LanguageName);
+ FREE (PrintableLanguageName);
+ return Status;
+}
+//
+// All unicode strings in the database consist of a UINT16 length
+// field, followed by the string itself. This routine reads one
+// of those and returns the info.
+//
+static
+STATUS
+StringDBReadGenericString (
+ FILE *DBFptr,
+ UINT16 *Size,
+ WCHAR **Str
+ )
+{
+ UINT16 LSize;
+ UINT16 Flags;
+ WCHAR *LStr;
+
+ if (fread (&LSize, sizeof (UINT16), 1, DBFptr) != 1) {
+ Error (NULL, 0, 0, "failed to read a string length field from the database", NULL);
+ return STATUS_ERROR;
+ }
+
+ if (fread (&Flags, sizeof (UINT16), 1, DBFptr) != 1) {
+ Error (NULL, 0, 0, "failed to read a string flags field from the database", NULL);
+ return STATUS_ERROR;
+ }
+
+ LStr = MALLOC (LSize);
+ if (LStr == NULL) {
+ Error (__FILE__, __LINE__, 0, "memory allocation failed reading the database", NULL);
+ return STATUS_ERROR;
+ }
+
+ if (fread (LStr, sizeof (WCHAR), (UINT32) LSize / sizeof (WCHAR), DBFptr) != (UINT32) LSize / sizeof (WCHAR)) {
+ Error (NULL, 0, 0, "failed to read string from database", NULL);
+ Error (NULL, 0, 0, "database read failure", "offset 0x%X", ftell (DBFptr));
+ free (LStr);
+ return STATUS_ERROR;
+ }
+ //
+ // printf ("DBR: %S\n", LStr);
+ //
+ // If the flags field indicated we were asked to write a NULL string, then
+ // return them a NULL pointer.
+ //
+ if (Flags & STRING_FLAGS_UNDEFINED) {
+ *Size = 0;
+ *Str = NULL;
+ } else {
+ *Size = LSize;
+ *Str = LStr;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static
+STATUS
+StringDBWriteGenericString (
+ FILE *DBFptr,
+ WCHAR *Str
+ )
+{
+ UINT16 Size;
+ UINT16 Flags;
+ WCHAR ZeroString[1];
+ //
+ // Strings in the database consist of a size UINT16 followed
+ // by the string itself.
+ //
+ if (Str == NULL) {
+ ZeroString[0] = 0;
+ Str = ZeroString;
+ Size = sizeof (ZeroString);
+ Flags = STRING_FLAGS_UNDEFINED;
+ } else {
+ Flags = 0;
+ Size = (UINT16) ((wcslen (Str) + 1) * sizeof (WCHAR));
+ }
+
+ if (fwrite (&Size, sizeof (UINT16), 1, DBFptr) != 1) {
+ Error (NULL, 0, 0, "failed to write string size to database", NULL);
+ return STATUS_ERROR;
+ }
+
+ if (fwrite (&Flags, sizeof (UINT16), 1, DBFptr) != 1) {
+ Error (NULL, 0, 0, "failed to write string flags to database", NULL);
+ return STATUS_ERROR;
+ }
+
+ if (fwrite (Str, sizeof (WCHAR), Size / sizeof (WCHAR), DBFptr) != Size / sizeof (WCHAR)) {
+ Error (NULL, 0, 0, "failed to write string to database", NULL);
+ return STATUS_ERROR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static
+STRING_LIST *
+StringDBFindString (
+ WCHAR *LanguageName,
+ WCHAR *StringName,
+ WCHAR *Scope,
+ WCHAR_STRING_LIST *LanguagesOfInterest,
+ WCHAR_MATCHING_STRING_LIST *IndirectionList
+ )
+{
+ LANGUAGE_LIST *Lang;
+ STRING_LIST *CurrString;
+ WCHAR_MATCHING_STRING_LIST *IndListPtr;
+ WCHAR TempLangName[LANGUAGE_IDENTIFIER_NAME_LEN + 1];
+ WCHAR *WCharPtr;
+
+ //
+ // If we were given an indirection list, then see if one was specified for this
+ // string identifier. That is to say, if the indirection says "STR_ID_MY_FAVORITE MyScope",
+ // then if this string name matches one in the list, then do a lookup with the
+ // specified scope and return that value.
+ //
+ if (IndirectionList != NULL) {
+ for (IndListPtr = IndirectionList; IndListPtr != NULL; IndListPtr = IndListPtr->Next) {
+ if (wcscmp (StringName, IndListPtr->Str1) == 0) {
+ CurrString = StringDBFindString (LanguageName, StringName, IndListPtr->Str2, LanguagesOfInterest, NULL);
+ if (CurrString != NULL) {
+ return CurrString;
+ }
+ }
+ }
+ }
+ //
+ // First look for exact match language.stringname
+ //
+ for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {
+ if (wcscmp (LanguageName, Lang->LanguageName) == 0) {
+ //
+ // Found language match. Try to find string name match
+ //
+ for (CurrString = Lang->String; CurrString != NULL; CurrString = CurrString->Next) {
+ if (wcscmp (StringName, CurrString->StringName) == 0) {
+ //
+ // Found a string name match. See if we're supposed to find
+ // a scope match.
+ //
+ if (Scope != NULL) {
+ if (wcscmp (CurrString->Scope, Scope) == 0) {
+ return CurrString;
+ }
+ } else {
+ return CurrString;
+ }
+ }
+ }
+ }
+ }
+ //
+ // If we got here, then we didn't find a match. Look for secondary string
+ // matches. That is to say, if we're processing "spa", and they requested
+ // "spa+cat", then recursively call with "cat"
+ //
+ while (LanguagesOfInterest != NULL) {
+ //
+ // If this is the language we're looking for, then process the
+ // languages of interest list for it.
+ //
+ if (wcsncmp (LanguageName, LanguagesOfInterest->Str, LANGUAGE_IDENTIFIER_NAME_LEN) == 0) {
+ WCharPtr = LanguagesOfInterest->Str + LANGUAGE_IDENTIFIER_NAME_LEN;
+ while (*WCharPtr) {
+ //
+ // Double-check the length, though it should have been checked on the
+ // command line.
+ //
+ if (wcslen (WCharPtr) < LANGUAGE_IDENTIFIER_NAME_LEN) {
+ Error (NULL, 0, 0, "malformed alternate language list", "%S", LanguagesOfInterest->Str);
+ return NULL;
+ }
+
+ wcsncpy (TempLangName, WCharPtr, LANGUAGE_IDENTIFIER_NAME_LEN);
+ TempLangName[LANGUAGE_IDENTIFIER_NAME_LEN] = 0;
+ CurrString = StringDBFindString (TempLangName, StringName, NULL, NULL, IndirectionList);
+ if (CurrString != NULL) {
+ return CurrString;
+ }
+
+ WCharPtr += LANGUAGE_IDENTIFIER_NAME_LEN;
+ }
+ }
+
+ LanguagesOfInterest = LanguagesOfInterest->Next;
+ }
+
+ return NULL;
+}
+
+STATUS
+StringDBSetScope (
+ WCHAR *Scope
+ )
+{
+ //
+ // Free up existing scope memory.
+ //
+ if (mDBData.CurrentScope != NULL) {
+ FREE (mDBData.CurrentScope);
+ }
+
+ mDBData.CurrentScope = DuplicateString (Scope);
+ return STATUS_SUCCESS;
+}
+//
+// We typically don't assign index values to string identifiers
+// until we're ready to write out files. To reduce the size of
+// the output file, re-order the string identifiers to move any
+// unreferenced ones to the end. Then we'll walk the list
+// again to assign string indexes, keeping track of the last
+// one referenced.
+//
+static
+void
+StringDBAssignStringIndexes (
+ VOID
+ )
+{
+ STRING_IDENTIFIER *StrId;
+ STRING_IDENTIFIER *FirstUsed;
+ STRING_IDENTIFIER *LastUsed;
+ STRING_IDENTIFIER *FirstUnused;
+ STRING_IDENTIFIER *LastUnused;
+ UINT32 Index;
+ UINT32 MaxReferenced;
+
+ //
+ // Create two lists -- used and unused. Then put them together with
+ // the unused ones on the end.
+ //
+ FirstUsed = NULL;
+ LastUsed = NULL;
+ FirstUnused = NULL;
+ LastUnused = NULL;
+ StrId = mDBData.StringIdentifier;
+ while (StrId != NULL) {
+ if ((StrId->Flags & STRING_FLAGS_REFERENCED) == 0) {
+ //
+ // Put it on the unused list
+ //
+ if (FirstUnused == NULL) {
+ FirstUnused = StrId;
+ } else {
+ LastUnused->Next = StrId;
+ }
+
+ LastUnused = StrId;
+ StrId = StrId->Next;
+ LastUnused->Next = NULL;
+ } else {
+ //
+ // Put it on the used list
+ //
+ if (FirstUsed == NULL) {
+ FirstUsed = StrId;
+ } else {
+ LastUsed->Next = StrId;
+ }
+
+ LastUsed = StrId;
+ StrId = StrId->Next;
+ LastUsed->Next = NULL;
+ }
+ }
+ //
+ // Join the lists
+ //
+ if (FirstUsed != NULL) {
+ mDBData.StringIdentifier = FirstUsed;
+ LastUsed->Next = FirstUnused;
+ } else {
+ mDBData.StringIdentifier = FirstUnused;
+ }
+
+ MaxReferenced = 0;
+ Index = 0;
+ for (StrId = mDBData.StringIdentifier; StrId != NULL; StrId = StrId->Next) {
+ StrId->Index = Index;
+ Index++;
+ if (StrId->Flags & STRING_FLAGS_REFERENCED) {
+ mDBData.NumStringIdentifiersReferenced = Index;
+ }
+ }
+
+ mDBData.NumStringIdentifiers = Index;
+}
+
+static
+WCHAR *
+DuplicateString (
+ WCHAR *Str
+ )
+{
+ WCHAR *NewStr;
+ if (Str == NULL) {
+ return NULL;
+ }
+
+ NewStr = MALLOC ((wcslen (Str) + 1) * sizeof (WCHAR));
+ if (NewStr == NULL) {
+ Error (NULL, 0, 0, "memory allocation failure", NULL);
+ return NULL;
+ }
+
+ wcscpy (NewStr, Str);
+ return NewStr;
+}
+
+static
+WCHAR *
+AsciiToWchar (
+ INT8 *Str
+ )
+{
+ UINT32 Len;
+ WCHAR *NewStr;
+ WCHAR *Ptr;
+
+ Len = strlen (Str) + 1;
+ NewStr = (WCHAR *) malloc (Len * sizeof (WCHAR));
+ for (Ptr = NewStr; *Str != 0; Str++, Ptr++) {
+ *Ptr = (UINT16) (UINT8) *Str;
+ }
+
+ *Ptr = 0;
+ return NewStr;
+}
+
+/*****************************************************************************/
+
+/*++
+
+Routine Description:
+
+ Create an HII export string pack for the strings in our database.
+
+Arguments:
+
+ FileName - name of the output file to write
+
+Returns:
+
+ STATUS
+
+
+--*/
+STATUS
+StringDBCreateHiiExportPack (
+ INT8 *FileName
+ )
+{
+ FILE *Fptr;
+ LANGUAGE_LIST *Lang;
+ STRING_LIST *CurrString;
+ STRING_LIST EmptyString;
+ UINT32 Offset;
+ UINT32 StringIndex;
+ UINT32 TempIndex;
+ EFI_HII_STRING_PACK_HEADER StringPack;
+ UINT32 Len;
+ WCHAR ZeroString[1];
+ WCHAR *TempStringPtr;
+ WCHAR *LangName;
+ STRING_IDENTIFIER *StringIdentifier;
+
+ if ((Fptr = fopen (FileName, "wb")) == NULL) {
+ Error (NULL, 0, 0, FileName, "failed to open output HII export file");
+ return STATUS_ERROR;
+ }
+ //
+ // Assign index values to the string identifiers
+ //
+ StringDBAssignStringIndexes ();
+ //
+ // If a given string is not defined, then we'll use this one.
+ //
+ memset (&EmptyString, 0, sizeof (EmptyString));
+ EmptyString.Size = sizeof (ZeroString);
+ EmptyString.Str = ZeroString;
+ //
+ // Process each language, then each string for each langage
+ //
+ ZeroString[0] = 0;
+ for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {
+ //
+ // Process each string for this language. We have to make 3 passes on the strings:
+ // Pass1: computes sizes and fill in the string pack header
+ // Pass2: write the array of offsets
+ // Pass3: write the strings
+ //
+ //
+ // PASS 1: Fill in and print the HII string pack header
+ //
+ // Compute the size for this language package and write
+ // the header out. Each string package contains:
+ // Header
+ // Offset[] -- an array of offsets to strings, of type RELOFST each
+ // String[] -- the actual strings themselves
+ //
+ memset ((char *) &StringPack, 0, sizeof (EFI_HII_STRING_PACK_HEADER));
+ StringPack.Header.Type = EFI_HII_STRING;
+ StringPack.NumStringPointers = (UINT16) mDBData.NumStringIdentifiersReferenced;
+ LangName = Lang->LanguageName;
+ //
+ // First string is the language name. If we're printing all languages, then
+ // it's just the "spa". If we were given a list of languages to print, then it's
+ // the "spacat" string. Compute its offset and fill in
+ // the info in the header. Since we know the language name string's length,
+ // and the printable language name follows it, use that info to fill in the
+ // entry for the printable language name as well.
+ //
+ StringPack.LanguageNameString = (STRING_OFFSET) (sizeof (EFI_HII_STRING_PACK_HEADER) + (mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET)));
+ StringPack.PrintableLanguageName = (STRING_OFFSET) (StringPack.LanguageNameString + (wcslen (LangName) + 1) * sizeof (WCHAR));
+ //
+ // Add up the size of all strings so we can fill in our header.
+ //
+ Len = 0;
+ for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {
+ //
+ // For the first string (language name), we print out the "spacat" if they
+ // requested it. We set LangName to point to the proper language name string above.
+ //
+ if (StringIndex == STRING_ID_LANGUAGE_NAME) {
+ Len += (wcslen (LangName) + 1) * sizeof (WCHAR);
+ } else {
+ //
+ // Find a string with this language.stringname
+ //
+ StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);
+ if (StringIdentifier == NULL) {
+ Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);
+ return STATUS_ERROR;
+ }
+ //
+ // Find a matching string if this string identifier was referenced
+ //
+ EmptyString.Flags = STRING_FLAGS_UNDEFINED;
+ CurrString = NULL;
+ if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) {
+ CurrString = StringDBFindString (
+ Lang->LanguageName,
+ StringIdentifier->StringName,
+ NULL,
+ NULL, // LanguagesOfInterest,
+ NULL
+ );
+ //
+ // IndirectionList);
+ //
+ if (NULL == CurrString) {
+ //
+ // If string for Lang->LanguageName is not found, try to get an English version
+ //
+ CurrString = StringDBFindString (
+ L"eng",
+ StringIdentifier->StringName,
+ NULL,
+ NULL, // LanguagesOfInterest,
+ NULL
+ );
+ //
+ // IndirectionList);
+ //
+ }
+ }
+
+ if (CurrString == NULL) {
+ CurrString = &EmptyString;
+ EmptyString.Flags |= StringIdentifier->Flags;
+ }
+
+ Len += CurrString->Size;
+ }
+ }
+ StringPack.Header.Length = sizeof (EFI_HII_STRING_PACK_HEADER)
+ + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET)
+ + Len;
+ //
+ // Write out the string pack header
+ //
+ fwrite ((void *) &StringPack, sizeof (StringPack), 1, Fptr);
+ //
+ // PASS2 : write the offsets
+ //
+ // Traverse the list of strings again and write the array of offsets. The
+ // offset to the first string is the size of the string pack header
+ // plus the size of the offsets array. The other strings follow it.
+ //
+ StringIndex = 0;
+ Offset = sizeof (StringPack) + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET);
+ for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {
+ //
+ // Write the offset
+ //
+ fwrite (&Offset, sizeof (STRING_OFFSET), 1, Fptr);
+ //
+ // Find the string name
+ //
+ StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);
+ if (StringIdentifier == NULL) {
+ Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);
+ return STATUS_ERROR;
+ }
+ //
+ // For the first string (language name), we print out the "spacat" if they
+ // requested it. We set LangName to point to the proper language name string above.
+ //
+ if (StringIndex == STRING_ID_LANGUAGE_NAME) {
+ Offset += (wcslen (LangName) + 1) * sizeof (WCHAR);
+ CurrString = StringDBFindString (
+ Lang->LanguageName,
+ StringIdentifier->StringName,
+ NULL, // scope
+ NULL,
+ NULL
+ );
+ } else {
+ //
+ // Find a matching string
+ //
+ CurrString = StringDBFindString (
+ Lang->LanguageName,
+ StringIdentifier->StringName,
+ NULL, // scope
+ NULL, // LanguagesOfInterest,
+ NULL
+ );
+ //
+ // IndirectionList);
+ //
+ if (NULL == CurrString) {
+ CurrString = StringDBFindString (
+ L"eng",
+ StringIdentifier->StringName,
+ NULL, // scope
+ NULL, // LanguagesOfInterest,
+ NULL
+ );
+ //
+ // IndirectionList);
+ //
+ }
+
+ EmptyString.LanguageName = Lang->LanguageName;
+ if (CurrString == NULL) {
+ CurrString = &EmptyString;
+ EmptyString.Flags = STRING_FLAGS_UNDEFINED;
+ } else if ((StringIdentifier->Flags & STRING_FLAGS_REFERENCED) == 0) {
+ CurrString = &EmptyString;
+ EmptyString.Flags = 0;
+ }
+
+ Offset += CurrString->Size;
+ }
+ }
+
+ //
+ // PASS 3: write the strings themselves.
+ //
+ Offset = sizeof (StringPack) + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET);
+ for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {
+ StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);
+ if (StringIdentifier == NULL) {
+ Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);
+ return STATUS_ERROR;
+ }
+ //
+ // For the first string (language name), we print out the "spacat" if they
+ // requested it. We set LangName to point to the proper language name string above.
+ //
+ if (StringIndex == STRING_ID_LANGUAGE_NAME) {
+ TempStringPtr = LangName;
+ } else {
+ //
+ // Find a matching string if this string identifier was referenced
+ //
+ CurrString = NULL;
+ if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) {
+ CurrString = StringDBFindString (
+ Lang->LanguageName,
+ StringIdentifier->StringName,
+ NULL, // scope
+ NULL, // LanguagesOfInterest,
+ NULL
+ );
+ //
+ // IndirectionList);
+ //
+ if (NULL == CurrString) {
+ CurrString = StringDBFindString (
+ L"eng",
+ StringIdentifier->StringName,
+ NULL, // scope
+ NULL, // LanguagesOfInterest,
+ NULL
+ );
+ //
+ // IndirectionList);
+ //
+ }
+ }
+
+ if (CurrString == NULL) {
+ CurrString = &EmptyString;
+ }
+
+ TempStringPtr = CurrString->Str;
+ }
+
+ for (TempIndex = 0; TempStringPtr[TempIndex] != 0; TempIndex++) {
+ fwrite (&TempStringPtr[TempIndex], sizeof (CHAR16), 1, Fptr);
+ Offset += 2;
+ }
+ //
+ // Print NULL WCHAR at the end of this string.
+ //
+ TempIndex = 0;
+ fwrite (&TempIndex, sizeof (CHAR16), 1, Fptr);
+ Offset += 2;
+ }
+ //
+ // Sanity check the offset. Make sure our running offset is what we put in the
+ // string pack header.
+ //
+ if (StringPack.Header.Length != Offset) {
+ Error (
+ __FILE__,
+ __LINE__,
+ 0,
+ "application error",
+ "stringpack size 0x%X does not match final size 0x%X",
+ StringPack.Header.Length,
+ Offset
+ );
+ }
+ }
+ //
+ // Print terminator string pack, closing brace and close the file.
+ // The size of 0 triggers to the consumer that this is the end.
+ //
+ memset ((char *) &StringPack, 0, sizeof (EFI_HII_STRING_PACK_HEADER));
+ StringPack.Header.Type = EFI_HII_STRING;
+ fwrite ((void *) &StringPack, sizeof (StringPack), 1, Fptr);
+ fclose (Fptr);
+ return STATUS_SUCCESS;
+}