mirror of
				https://github.com/acidanthera/audk.git
				synced 2025-10-29 18:23:45 +01:00 
			
		
		
		
	git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@587 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			2760 lines
		
	
	
		
			80 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2760 lines
		
	
	
		
			80 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*++
 | |
| 
 | |
| 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;
 | |
| }
 |