/*++ 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 #include #include #include #include #include #include #include "StrGather.h" #include "StringDb.h" 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 void StringDBWriteStandardFileHeader ( FILE *OutFptr ); static WCHAR * AsciiToWchar ( INT8 *Str ); static CHAR8 * WcharToAscii ( WCHAR *Str ); static WCHAR * DuplicateString ( WCHAR *Str ); static WCHAR * WstrCatenate ( WCHAR *Dst, WCHAR *Src ); 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, WCHAR *SecondaryLanguageList ); 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->SecondaryLanguageList); 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; } } /*****************************************************************************/ 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; STRING_IDENTIFIER *StringIdentifier; // // 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 --*/ 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, WCHAR *SecondaryLanguageList ) { 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 = (WCHAR *) malloc ((wcslen (LanguageName) + 1) * 2); if (Lang->LanguageName == NULL) { Error (NULL, 0, 0, NULL, "memory allocation error"); return STATUS_ERROR; } wcscpy (Lang->LanguageName, LanguageName); Lang->PrintableLanguageName = (WCHAR *) malloc ((wcslen (PrintableLanguageName) + 1) * sizeof (WCHAR)); if (Lang->PrintableLanguageName == NULL) { Error (NULL, 0, 0, NULL, "memory allocation error"); FREE (Lang->LanguageName); return STATUS_ERROR; } wcscpy (Lang->PrintableLanguageName, PrintableLanguageName); if (SecondaryLanguageList != NULL) { Lang->SecondaryLanguageList = (WCHAR *) malloc ((wcslen (SecondaryLanguageList) + 1) * sizeof (WCHAR)); if (Lang->SecondaryLanguageList == NULL) { Error (NULL, 0, 0, NULL, "memory allocation error"); FREE (Lang->PrintableLanguageName); FREE (Lang->LanguageName); return STATUS_ERROR; } wcscpy (Lang->SecondaryLanguageList, SecondaryLanguageList); } else { Lang->SecondaryLanguageList = NULL; } 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; } STATUS StringDBAddSecondaryLanguage ( WCHAR *LanguageName, WCHAR *SecondaryLanguageList ) { LANGUAGE_LIST *Lang; Lang = StringDBFindLanguageList (LanguageName); if (Lang == NULL) { return STATUS_ERROR; } else { Lang->SecondaryLanguageList = WstrCatenate(Lang->SecondaryLanguageList, SecondaryLanguageList); 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, Lang->SecondaryLanguageList); 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, WCHAR *SecondaryLanguageList ) { 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; } if (StringDBWriteGenericString (DBFptr, SecondaryLanguageList) != STATUS_SUCCESS) { return STATUS_ERROR; } return STATUS_SUCCESS; } static STATUS StringDBReadLanguageDefinition ( FILE *DBFptr ) { WCHAR *LanguageName = NULL; WCHAR *PrintableLanguageName = NULL; WCHAR *SecondaryLanguageList = NULL; UINT16 Size; STATUS Status; if (StringDBReadGenericString (DBFptr, &Size, &LanguageName) != STATUS_SUCCESS) { return STATUS_ERROR; } if (StringDBReadGenericString (DBFptr, &Size, &PrintableLanguageName) != STATUS_SUCCESS) { return STATUS_ERROR; } if (StringDBReadGenericString (DBFptr, &Size, &SecondaryLanguageList) != STATUS_SUCCESS) { return STATUS_ERROR; } // // printf("LANG: %S %S\n", LanguageName, PrintableLanguageName); // Status = StringDBAddLanguage (LanguageName, PrintableLanguageName, SecondaryLanguageList); FREE (LanguageName); FREE (PrintableLanguageName); FREE (SecondaryLanguageList); 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 * WstrCatenate ( WCHAR *Dst, WCHAR *Src ) { UINT32 Len = 0; WCHAR *Bak = Dst; if (Src == NULL) { return Dst; } if (Dst != NULL) { Len = wcslen (Dst); } Len += wcslen (Src); Dst = (WCHAR *) malloc ((Len + 1) * 2); if (Dst == NULL) { return NULL; } Dst[0] = L'\0'; if (Bak != NULL) { wcscpy (Dst, Bak); FREE (Bak); } wcscat (Dst, Src); return Dst; } 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; } static CHAR8 * WcharToAscii ( WCHAR *Str ) { UINT32 Len; CHAR8 *NewStr; CHAR8 *Ptr; Len = wcslen (Str) + 1; NewStr = (CHAR8 *) malloc (Len * sizeof (CHAR8)); for (Ptr = NewStr; *Str != L'\0'; Str++, Ptr++) { *Ptr = (CHAR8) *Str; } *Ptr = '\0'; return NewStr; } /*****************************************************************************/ CHAR8 * unicode2ascii ( WCHAR *UnicodeStr ) { CHAR8 *RetStr = (CHAR8 *)UnicodeStr; CHAR8 *AsciiStr = (CHAR8 *)UnicodeStr; while (*UnicodeStr != '\0') { *AsciiStr = (CHAR8) *(UnicodeStr++); AsciiStr++; } *AsciiStr = '\0'; return RetStr; } STATUS BuildStringPkgHdr ( IN WCHAR *PrimaryLangName, IN WCHAR *SecondaryLangList, IN UINT32 Type, IN UINT32 PkgBlkSize, OUT EFI_HII_STRING_PACKAGE_HDR **StrPkgHdr ) { UINT32 LangNameLen; LangNameLen = wcslen (PrimaryLangName); if (SecondaryLangList != NULL) { LangNameLen += wcslen (SecondaryLangList) + 1; } *StrPkgHdr = (EFI_HII_STRING_PACKAGE_HDR *) malloc(sizeof (EFI_HII_STRING_PACKAGE_HDR) + LangNameLen); if (*StrPkgHdr == NULL) { return STATUS_ERROR; } memset (*StrPkgHdr, 0, sizeof (EFI_HII_STRING_PACKAGE_HDR) + LangNameLen); (*StrPkgHdr)->Header.Type = Type; (*StrPkgHdr)->Header.Length = PkgBlkSize + sizeof (EFI_HII_STRING_PACKAGE_HDR) + LangNameLen; (*StrPkgHdr)->HdrSize = sizeof (EFI_HII_STRING_PACKAGE_HDR) + LangNameLen; (*StrPkgHdr)->StringInfoOffset = sizeof (EFI_HII_STRING_PACKAGE_HDR) + LangNameLen; (*StrPkgHdr)->LanguageWindow[0] = L'\0'; (*StrPkgHdr)->LanguageName = (EFI_STRING_ID)1; strcpy ((*StrPkgHdr)->Language, unicode2ascii(PrimaryLangName)); if (SecondaryLangList != NULL) { strcat ((*StrPkgHdr)->Language, ";"); strcat ((*StrPkgHdr)->Language, unicode2ascii(SecondaryLangList)); } #ifdef DEBUG_STRGATHER printf ("STR HDR\t %s\n", (*StrPkgHdr)->Language); #endif return STATUS_SUCCESS; } STATUS BuildStringPkgUCS2Blk ( IN EFI_STRING_ID StringId, IN WCHAR *LangName, IN WCHAR *StrName, OUT EFI_HII_SIBT_STRING_UCS2_BLOCK **StrBlk, OUT UINT32 *BlkSize ) { UINT32 StrLen = 0; STRING_LIST *CurrString = NULL; if ((LangName == NULL) || (StrName == NULL) || (StrBlk == NULL)) { return STATUS_ERROR; } *StrBlk = NULL; *BlkSize = 0; CurrString = StringDBFindString (LangName, StrName, NULL, NULL, NULL); if (CurrString == NULL) { return STATUS_WARNING; } StrLen = wcslen (CurrString->Str); *BlkSize = sizeof (EFI_HII_SIBT_STRING_UCS2_BLOCK) + StrLen * 2; *StrBlk = (EFI_HII_SIBT_STRING_UCS2_BLOCK *) malloc (*BlkSize); if (*StrBlk == NULL) { *StrBlk = NULL; *BlkSize = 0; return STATUS_ERROR; } (*StrBlk)->Header.BlockType = EFI_HII_SIBT_STRING_UCS2; wcscpy((*StrBlk)->StringText, CurrString->Str); return STATUS_SUCCESS; } STATUS BuildStringPkgSKIP2Blk ( IN EFI_STRING_ID SkipIdCount, OUT EFI_HII_SIBT_SKIP2_BLOCK **StrBlk ) { if (StrBlk == NULL) { return STATUS_ERROR; } *StrBlk = NULL; *StrBlk = (EFI_HII_SIBT_SKIP2_BLOCK *) malloc (sizeof (EFI_HII_SIBT_SKIP2_BLOCK)); if (*StrBlk == NULL) { *StrBlk = NULL; return STATUS_ERROR; } (*StrBlk)->Header.BlockType = EFI_HII_SIBT_SKIP2; (*StrBlk)->SkipCount = SkipIdCount; return STATUS_SUCCESS; } STATUS BuildStringPkgEndBlk ( OUT EFI_HII_SIBT_END_BLOCK **End ) { *End = (EFI_HII_SIBT_END_BLOCK *) malloc (sizeof (EFI_HII_SIBT_END_BLOCK)); if (*End == NULL) { return STATUS_ERROR; } (*End)->Header.BlockType = EFI_HII_SIBT_END; return STATUS_SUCCESS; } /*++ 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 StrPkgBlkBufferListAddTail ( IN EFI_STRING_ID StringId, IN WCHAR *StrName, IN SPkgBlkBuffer **PkgBufferListHead, IN SPkgBlkBuffer **PkgBufferListTail, IN VOID *Buffer, IN UINT32 Size ) { SPkgBlkBuffer *pNew = NULL; #ifdef DEBUG_STRGATHER EFI_HII_STRING_BLOCK *SBlk = (EFI_HII_STRING_BLOCK *)Buffer; #endif if ((PkgBufferListHead == NULL) || (PkgBufferListTail == NULL)) { return STATUS_ERROR; } pNew = (SPkgBlkBuffer *) malloc (sizeof (SPkgBlkBuffer)); if (pNew == NULL) { return STATUS_ERROR; } pNew->mBlkBuffer = Buffer; pNew->mBlkSize = Size; if ((*PkgBufferListTail) == NULL) { (*PkgBufferListHead) = (*PkgBufferListTail) = pNew; } else { (*PkgBufferListTail)->mNext = pNew; (*PkgBufferListTail) = pNew; pNew->mNext = NULL; } #ifdef DEBUG_STRGATHER switch (SBlk->BlockType) { case EFI_HII_SIBT_STRING_UCS2 : printf ("\tID: [%x] TYPE: [UCS2]\t NAME: %S \t STR: %S\n", StringId, StrName, ((EFI_HII_SIBT_STRING_UCS2_BLOCK *)SBlk)->StringText); break; case EFI_HII_SIBT_SKIP2 : printf ("\tID: [NULL] TYPE: [SKIP2] SKIPCOUNT: [%x]\n", ((EFI_HII_SIBT_SKIP2_BLOCK *)SBlk)->SkipCount); break; case EFI_HII_SIBT_END : printf ("\tID: [%x] TYPE: [END]\n", StringId); break; default : printf ("!!!!UNKNOWN STRING TYPE!!!\n"); } #endif return STATUS_SUCCESS; } VOID StrPkgHdrFree ( IN EFI_HII_STRING_PACKAGE_HDR *StrPkgHdr ) { if (StrPkgHdr != NULL) { free (StrPkgHdr); } } VOID StrPkgBlkBufferListFree ( IN SPkgBlkBuffer *PkgBlkList ) { SPkgBlkBuffer *Buffer; while (PkgBlkList != NULL) { Buffer = PkgBlkList; PkgBlkList = PkgBlkList->mNext; if (Buffer->mBlkBuffer != NULL) { free (Buffer->mBlkBuffer); } free (Buffer); } } VOID WriteBlockLine ( IN FILE *pFile, IN UINT32 LineBytes, IN INT8 *LineHeader, IN INT8 *BlkBuf, IN UINT32 BlkSize ) { UINT32 Index; if ((pFile == NULL) || (LineHeader == NULL) || (BlkBuf == NULL)) { return; } for (Index = 0; Index < BlkSize; Index++) { if ((Index % LineBytes) == 0) { fprintf (pFile, "\n%s", LineHeader); } fprintf (pFile, "0x%02X, ", (UINT8)BlkBuf[Index]); } } VOID WriteBlockEnd ( IN FILE *pFile, IN UINT32 LineBytes, IN INT8 *LineHeader, IN INT8 *BlkBuf, IN UINT32 BlkSize ) { UINT32 Index; if ((BlkSize == 0) || (pFile == NULL) || (LineHeader == NULL) || (BlkBuf == NULL)) { return; } for (Index = 0; Index < BlkSize - 1; Index++) { if ((Index % LineBytes) == 0) { fprintf (pFile, "\n%s", LineHeader); } fprintf (pFile, "0x%02X, ", (UINT8)BlkBuf[Index]); } if ((Index % LineBytes) == 0) { fprintf (pFile, "\n%s", LineHeader); } fprintf (pFile, "0x%02X\n", (UINT8)BlkBuf[Index]); } #define BYTES_PRE_LINE 0x10 VOID StrPkgWriteHdrCFile ( IN FILE *File, IN EFI_HII_STRING_PACKAGE_HDR *StrPkgHdr ) { if (StrPkgHdr != NULL) { fprintf (File, "\n // PACKAGE HEADER\n"); WriteBlockLine(File, BYTES_PRE_LINE, " ", (INT8 *)StrPkgHdr, StrPkgHdr->HdrSize); } } VOID StrPkgWirteArrayLength ( IN FILE *File, IN UINT32 PkgNumber, IN EFI_HII_STRING_PACKAGE_HDR **PkgHdr ) { UINT32 Index; UINT32 ArrayLen; ArrayLen = sizeof (UINT32); for (Index = 0; Index < PkgNumber; Index++) { if (PkgHdr[Index] != NULL) { ArrayLen += PkgHdr[Index]->Header.Length; } } fprintf (File, "\n // STRING ARRAY LENGTH\n"); WriteBlockLine(File, BYTES_PRE_LINE, " ", (UINT8 *)&ArrayLen, sizeof (UINT32)); } VOID StrPkgWriteBlkListCFile ( IN FILE *File, IN SPkgBlkBuffer *BlkList, IN BOOLEAN WriteEnd ) { SPkgBlkBuffer *Buffer; fprintf (File, "\n\n // PACKAGE DATA\n"); while (BlkList != NULL) { Buffer = BlkList; BlkList = BlkList->mNext; if ((Buffer->mNext == NULL) && (WriteEnd == TRUE)) { if (Buffer->mBlkBuffer != NULL) { WriteBlockEnd (File, BYTES_PRE_LINE, " ", Buffer->mBlkBuffer, Buffer->mBlkSize); } } else { if (Buffer->mBlkBuffer != NULL) { WriteBlockLine(File, BYTES_PRE_LINE, " ", Buffer->mBlkBuffer, Buffer->mBlkSize); } } } } VOID StrPkgWriteHdrBinary ( IN FILE *File, IN EFI_HII_STRING_PACKAGE_HDR *StrPkgHdr ) { fwrite (StrPkgHdr, StrPkgHdr->HdrSize, 1, File); } VOID StrPkgWriteBlkListBinary ( IN FILE *File, IN SPkgBlkBuffer *BlkList ) { SPkgBlkBuffer *Buffer; while (BlkList != NULL) { Buffer = BlkList; BlkList = BlkList->mNext; if (Buffer->mBlkBuffer != NULL) { fwrite (Buffer->mBlkBuffer, Buffer->mBlkSize, 1, File); } } } STATUS StringDBGenStrPkgHdrAndBlkList ( IN LANGUAGE_LIST *Lang, OUT EFI_HII_STRING_PACKAGE_HDR **StrPkgHdr, OUT SPkgBlkBuffer **BlkList ) { STATUS Status; UINT32 StringIndex; EFI_STRING_ID StringIdCurrent; EFI_STRING_ID SkipIdCount; UINT32 BlkSize = 0; EFI_HII_SIBT_STRING_UCS2_BLOCK *StrUCS2Blk = NULL; EFI_HII_SIBT_SKIP2_BLOCK *StrSKIP2Blk = NULL; STRING_IDENTIFIER *StringIdentifier = NULL; EFI_HII_SIBT_END_BLOCK *EndBlk = NULL; UINT32 PkgBlkSize = 0; SPkgBlkBuffer *PkgBufferListHead = NULL; SPkgBlkBuffer *PkgBufferListTail = NULL; if ((Lang == NULL) || (StrPkgHdr == NULL) || (BlkList == NULL)) { return STATUS_ERROR; } // // Assign index values to the string identifiers // StringDBAssignStringIndexes (); StringIdCurrent = EFI_STRING_ID_BEGIN; SkipIdCount = 0; for (StringIndex = STRING_ID_PRINTABLE_LANGUAGE_NAME; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) { if ((StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex)) == NULL) { Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex); goto ExportPackOut; } if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) { Status = BuildStringPkgUCS2Blk (StringIdCurrent, Lang->LanguageName, StringIdentifier->StringName, &StrUCS2Blk, &BlkSize); switch (Status) { case STATUS_ERROR: goto ExportPackOut; break; case STATUS_WARNING : SkipIdCount++; break; case STATUS_SUCCESS : if (SkipIdCount == 0) { if (StrPkgBlkBufferListAddTail ( StringIdCurrent, StringIdentifier->StringName, &PkgBufferListHead, &PkgBufferListTail, StrUCS2Blk, BlkSize ) != STATUS_SUCCESS) { goto ExportPackOut; } PkgBlkSize += BlkSize; } else { if (BuildStringPkgSKIP2Blk (SkipIdCount, &StrSKIP2Blk) != STATUS_SUCCESS) { goto ExportPackOut; } else { if (StrPkgBlkBufferListAddTail ( StringIdCurrent, NULL, &PkgBufferListHead, &PkgBufferListTail, StrSKIP2Blk, sizeof (EFI_HII_SIBT_SKIP2_BLOCK) ) != STATUS_SUCCESS) { goto ExportPackOut; } PkgBlkSize += sizeof (EFI_HII_SIBT_SKIP2_BLOCK); SkipIdCount = 0; } if (StrPkgBlkBufferListAddTail ( StringIdCurrent, StringIdentifier->StringName, &PkgBufferListHead, &PkgBufferListTail, StrUCS2Blk, BlkSize ) != STATUS_SUCCESS) { goto ExportPackOut; } PkgBlkSize += BlkSize; } } } StringIdCurrent++; } if (SkipIdCount != 0) { if (BuildStringPkgSKIP2Blk (SkipIdCount, &StrSKIP2Blk) != STATUS_SUCCESS) { goto ExportPackOut; } else { if (StrPkgBlkBufferListAddTail ( StringIdCurrent, NULL, &PkgBufferListHead, &PkgBufferListTail, StrSKIP2Blk, sizeof (EFI_HII_SIBT_SKIP2_BLOCK) ) != STATUS_SUCCESS) { goto ExportPackOut; } PkgBlkSize += sizeof (EFI_HII_SIBT_SKIP2_BLOCK); SkipIdCount = 0; } } if (BuildStringPkgEndBlk (&EndBlk) != STATUS_SUCCESS) { goto ExportPackOut; } else if (StrPkgBlkBufferListAddTail ( StringIdCurrent, NULL, &PkgBufferListHead, &PkgBufferListTail, EndBlk, sizeof (EFI_HII_SIBT_END_BLOCK) ) != STATUS_SUCCESS) { goto ExportPackOut; } StringIdCurrent++; PkgBlkSize += sizeof (EFI_HII_SIBT_END_BLOCK); if (BuildStringPkgHdr( Lang->LanguageName, Lang->SecondaryLanguageList, EFI_HII_PACKAGE_STRINGS, PkgBlkSize, StrPkgHdr ) != STATUS_SUCCESS) { goto ExportPackOut; } *BlkList = PkgBufferListHead; return STATUS_SUCCESS; ExportPackOut: StrPkgBlkBufferListFree(PkgBufferListHead); *BlkList = NULL; *StrPkgHdr = NULL; return STATUS_ERROR; } STATUS StringDBCreateHiiExportPack ( INT8 *FileName ) { FILE *File = NULL; LANGUAGE_LIST *Lang = NULL; EFI_HII_STRING_PACKAGE_HDR *StrPkgHdr = NULL; SPkgBlkBuffer *BlkList = NULL; if (FileName == NULL) { return STATUS_ERROR; } if ((File = fopen (FileName, "wb")) == NULL) { Error (NULL, 0, 0, FileName, "failed to open output HII export file"); return STATUS_ERROR; } for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) { if (StringDBGenStrPkgHdrAndBlkList(Lang, &StrPkgHdr, &BlkList) != STATUS_SUCCESS) { fclose (File); return STATUS_SUCCESS; } StrPkgWriteHdrBinary (File, StrPkgHdr); StrPkgWriteBlkListBinary (File, BlkList); StrPkgHdrFree (StrPkgHdr); StrPkgBlkBufferListFree (BlkList); } fclose (File); return STATUS_SUCCESS; } static const char *gSourceFileHeader[] = { "//", "// DO NOT EDIT -- auto-generated file", "//", "// This file is generated by the StrGather utility", "//", NULL }; STATUS StringDBDumpCStrings ( INT8 *BaseName, INT8 *FileName ) { EFI_STATUS Status; FILE *File = NULL; LANGUAGE_LIST *Lang = NULL; EFI_HII_STRING_PACKAGE_HDR **StrPkgHdr = NULL; SPkgBlkBuffer **BlkList = NULL; UINT32 Index; UINT32 LangNumber = 0; if ((BaseName == NULL) || (FileName == NULL)) { return STATUS_ERROR; } if (mDBData.LanguageList == NULL) { return STATUS_SUCCESS; } for (LangNumber = 0, Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next, LangNumber++) ; StrPkgHdr = (EFI_HII_STRING_PACKAGE_HDR **) malloc (sizeof (EFI_HII_STRING_PACKAGE_HDR *) * LangNumber); BlkList = (SPkgBlkBuffer **) malloc (sizeof (SPkgBlkBuffer *) * LangNumber); for (Index = 0; Index < LangNumber; Index++) { StrPkgHdr[Index] = NULL; BlkList[Index] = NULL; } for (Index = 0, Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next, Index++) { Status = StringDBGenStrPkgHdrAndBlkList(Lang, &StrPkgHdr[Index], &BlkList[Index]); if (EFI_ERROR(Status)) { return Status; } } if ((File = fopen (FileName, "w")) == NULL) { Error (NULL, 0, 0, FileName, "failed to open output C file - %s", FileName); return STATUS_ERROR; } for (Index = 0; gSourceFileHeader[Index] != NULL; Index++) { fprintf (File, "%s\n", gSourceFileHeader[Index]); } fprintf (File, "\nunsigned char %s[] = {\n", BaseName); // // Save the length of the string package array. // StrPkgWirteArrayLength (File, LangNumber, StrPkgHdr); for (Index = 0, Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next, Index++) { if (StrPkgHdr[Index] != NULL) { StrPkgWriteHdrCFile (File, StrPkgHdr[Index]); StrPkgWriteBlkListCFile (File, BlkList[Index], (Lang->Next == NULL) ? TRUE : FALSE); } StrPkgHdrFree (StrPkgHdr[Index]); StrPkgBlkBufferListFree (BlkList[Index]); } fprintf (File, "\n};\n"); fclose (File); return STATUS_SUCCESS; }