/*++ | |
Copyright (c) 2004, 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 <Common/UefiBaseTypes.h> | |
#include <Common/MultiPhase.h> | |
#include <Common/InternalFormRepresentation.h> | |
#include <Protocol/UgaDraw.h> // for EFI_UGA_PIXEL definition | |
#include <Protocol/Hii.h> | |
#include "EfiUtilityMsgs.h" | |
#include "StrGather.h" | |
#include "StringDB.h" | |
#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 ( | |
CHAR8 *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 ( | |
CHAR8 *FileName, | |
CHAR8 *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 StringPack; | |
UINT8 *Ptr; | |
UINT32 Len; | |
WCHAR ZeroString[1]; | |
WCHAR_STRING_LIST *LOIPtr; | |
BOOLEAN LanguageOk; | |
WCHAR *TempStringPtr; | |
WCHAR *LangName; | |
STRING_IDENTIFIER *StringIdentifier; | |
WCHAR Line[200]; | |
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 (StrnCmp (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 | |
// | |
AsciiSPrint ( Line, sizeof(Line), | |
"\n//******************************************************************************" | |
"\n// Start of string definitions for %s/%s", | |
Lang->LanguageName, | |
Lang->PrintableLanguageName | |
); | |
fprintf (Fptr, "%s", Line); | |
memset ((char *) &StringPack, 0, sizeof (EFI_HII_STRING_PACK)); | |
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) + (mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET))); | |
StringPack.PrintableLanguageName = (STRING_OFFSET) (StringPack.LanguageNameString + (StrLen (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 += (StrLen (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) | |
+ 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); 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; | |
} | |
AsciiSPrint (Line, sizeof(Line) , " // offset to string %s (0x%04X)", StringIdentifier->StringName, StringIndex); | |
fprintf (Fptr, "%s", Line); | |
// | |
// 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 += (StrLen (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 (StrCmp (CurrString->LanguageName, Lang->LanguageName) != 0) { | |
AsciiSPrint ( | |
Line, sizeof(Line), | |
" - not defined for this language -- using secondary language %s definition", | |
CurrString->LanguageName | |
); | |
fprintf ( Fptr, "%s", Line); | |
} | |
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) { | |
AsciiSPrint (Line, sizeof(Line), " // %s not referenced\n", StringIdentifier->StringName); | |
fprintf (Fptr, "%s", Line); | |
} | |
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; | |
} | |
AsciiSPrint (Line, sizeof(Line), " // string %s offset 0x%08X\n ", StringIdentifier->StringName, Offset); | |
fprintf (Fptr, "%s", Line); | |
// | |
// 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)); | |
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 ( | |
CHAR8 *FileName, | |
CHAR8 *BaseName | |
) | |
{ | |
FILE *Fptr; | |
STRING_IDENTIFIER *Identifier; | |
CHAR8 CopyBaseName[100]; | |
WCHAR Line[200]; | |
UINT32 Index; | |
const CHAR8 *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) { | |
AsciiSPrint (Line, sizeof(Line), "#define %-40s 0x%04X\n", Identifier->StringName, Identifier->Index); | |
fprintf (Fptr, "%s", Line); | |
} else { | |
AsciiSPrint (Line, sizeof(Line), "//#define %-40s 0x%04X // not referenced\n", Identifier->StringName, Identifier->Index); | |
fprintf (Fptr, "%s", Line); | |
} | |
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 ((StrLen (StringName) + 1) * sizeof (WCHAR)); | |
if (StringIdentifier->StringName == NULL) { | |
Error (NULL, 0, 0, NULL, "memory allocation error"); | |
return STATUS_ERROR; | |
} | |
StrCpy (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 = StrLen (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. | |
// | |
StrCpy (TempLangName, LanguageName); | |
for (; Size < 3; Size++) { | |
TempLangName[Size] = L'?'; | |
} | |
TempLangName[4] = 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 ((StrCmp (StringName, LANGUAGE_NAME_STRING_NAME) == 0) && | |
(StrCmp (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 = (StrLen (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. | |
// | |
StrCpy (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 = (StrLen (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 (StrCmp (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 (StrCmp (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 | |
// | |
StrCpy (Lang->LanguageName, LanguageName); | |
Lang->PrintableLanguageName = (WCHAR *) malloc ((StrLen (PrintableLanguageName) + 1) * sizeof (WCHAR)); | |
if (Lang->PrintableLanguageName == NULL) { | |
Error (NULL, 0, 0, NULL, "memory allocation error"); | |
return STATUS_ERROR; | |
} | |
StrCpy (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 (StrCmp (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 (StrnCmp (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 (StrnCmp (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 (StrnCmp (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 (StrnCmp (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 ( | |
CHAR8 *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 ( | |
CHAR8 *DBFileName, | |
BOOLEAN Verbose | |
) | |
{ | |
STRING_DB_HEADER DbHeader; | |
UINT32 Counter; | |
UINT32 StrLength; | |
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++) { | |
StrLength = StrLen (StringIdentifier->StringName) + 1; | |
DbHeader.StringIdentifiersSize += StrLength * 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 ( | |
CHAR8 *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)); | |
UnicodeSPrint (WName, (strlen (StringIdentifierName) + 1) * sizeof (WCHAR), L"%a", StringIdentifierName); | |
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 ( | |
CHAR8 *DBFileName, | |
CHAR8 *OutputFileName, | |
BOOLEAN Verbose | |
) | |
{ | |
LANGUAGE_LIST *Lang; | |
STRING_IDENTIFIER *StringIdentifier; | |
STRING_LIST *StrList; | |
FILE *OutFptr; | |
WCHAR WChar; | |
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); | |
} | |
OutFptr = fopen (OutputFileName, "wb"); | |
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. | |
// | |
UnicodeSPrint (Line, sizeof(Line), L"/=#"); | |
fwrite (Line, StrLen (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) { | |
UnicodeSPrint ( | |
Line, | |
sizeof(Line), L"%s %-60.60s 0x%04X", | |
DEFINE_STR, | |
StringIdentifier->StringName, | |
StringIdentifier->Index | |
); | |
} else { | |
UnicodeSPrint ( | |
Line, | |
sizeof(Line), L"%s %-60.60s 0x%04X // NOT REFERENCED", | |
DEFINE_STR, | |
StringIdentifier->StringName, | |
StringIdentifier->Index | |
); | |
} | |
fwrite (Line, StrLen (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); | |
UnicodeSPrint (Line, sizeof(Line), L"#langdef %s \"%s\"", Lang->LanguageName, Lang->PrintableLanguageName); | |
fwrite (Line, StrLen (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 | |
// | |
UnicodeSPrint (Line, sizeof(Line), L"// flags=0x%02X", (UINT32) StrList->Flags); | |
fwrite (Line, StrLen (Line) * sizeof (WCHAR), 1, OutFptr); | |
fwrite (&CrLf, sizeof (CrLf), 1, OutFptr); | |
// | |
// Print the scope if changed | |
// | |
if ((Scope == NULL) || (StrCmp (Scope, StrList->Scope) != 0)) { | |
UnicodeSPrint (Line, sizeof(Line), L"#scope %s", StrList->Scope); | |
fwrite (Line, StrLen (Line) * sizeof (WCHAR), 1, OutFptr); | |
fwrite (&CrLf, sizeof (CrLf), 1, OutFptr); | |
Scope = StrList->Scope; | |
} | |
UnicodeSPrint ( | |
Line, | |
sizeof(Line), L"#string %-50.50s #language %s \"", | |
StrList->StringName, | |
Lang->LanguageName | |
); | |
fwrite (Line, StrLen (Line) * sizeof (WCHAR), 1, OutFptr); | |
fwrite (StrList->Str, StrList->Size - sizeof (WCHAR), 1, OutFptr); | |
UnicodeSPrint (Line, sizeof(Line), L"\""); | |
fwrite (Line, StrLen (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) ((StrLen (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 (StrCmp (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 (StrCmp (LanguageName, Lang->LanguageName) == 0) { | |
// | |
// Found language match. Try to find string name match | |
// | |
for (CurrString = Lang->String; CurrString != NULL; CurrString = CurrString->Next) { | |
if (StrCmp (StringName, CurrString->StringName) == 0) { | |
// | |
// Found a string name match. See if we're supposed to find | |
// a scope match. | |
// | |
if (Scope != NULL) { | |
if (StrCmp (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 (StrnCmp (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 (StrLen (WCharPtr) < LANGUAGE_IDENTIFIER_NAME_LEN) { | |
Error (NULL, 0, 0, "malformed alternate language list", "%S", LanguagesOfInterest->Str); | |
return NULL; | |
} | |
StrnCpy (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 ((StrLen (Str) + 1) * sizeof (WCHAR)); | |
if (NewStr == NULL) { | |
Error (NULL, 0, 0, "memory allocation failure", NULL); | |
return NULL; | |
} | |
StrCpy (NewStr, Str); | |
return NewStr; | |
} | |
static | |
WCHAR * | |
AsciiToWchar ( | |
CHAR8 *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 ( | |
CHAR8 *FileName | |
) | |
{ | |
FILE *Fptr; | |
LANGUAGE_LIST *Lang; | |
STRING_LIST *CurrString; | |
STRING_LIST EmptyString; | |
UINT32 Offset; | |
UINT32 StringIndex; | |
UINT32 TempIndex; | |
EFI_HII_STRING_PACK 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)); | |
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) + (mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET))); | |
StringPack.PrintableLanguageName = (STRING_OFFSET) (StringPack.LanguageNameString + (StrLen (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 += (StrLen (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) | |
+ 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 += (StrLen (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)); | |
StringPack.Header.Type = EFI_HII_STRING; | |
fwrite ((void *) &StringPack, sizeof (StringPack), 1, Fptr); | |
fclose (Fptr); | |
return STATUS_SUCCESS; | |
} |