From 604371b98d3ad2472e96be8d126df92b2fcf68df Mon Sep 17 00:00:00 2001 From: lhauch Date: Thu, 5 Oct 2006 23:24:16 +0000 Subject: More moves for Tool Packages git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@1676 6f19259b-4bc3-4df7-8a09-765794883524 --- Tools/CCode/Source/StrGather/StrGather.c | 2531 +++++++++++++++++++++++++++ Tools/CCode/Source/StrGather/StrGather.h | 84 + Tools/CCode/Source/StrGather/StringDB.c | 2759 ++++++++++++++++++++++++++++++ Tools/CCode/Source/StrGather/StringDB.h | 136 ++ Tools/CCode/Source/StrGather/build.xml | 72 + 5 files changed, 5582 insertions(+) create mode 100644 Tools/CCode/Source/StrGather/StrGather.c create mode 100644 Tools/CCode/Source/StrGather/StrGather.h create mode 100644 Tools/CCode/Source/StrGather/StringDB.c create mode 100644 Tools/CCode/Source/StrGather/StringDB.h create mode 100644 Tools/CCode/Source/StrGather/build.xml (limited to 'Tools/CCode/Source/StrGather') diff --git a/Tools/CCode/Source/StrGather/StrGather.c b/Tools/CCode/Source/StrGather/StrGather.c new file mode 100644 index 0000000000..9eb5c5a19e --- /dev/null +++ b/Tools/CCode/Source/StrGather/StrGather.c @@ -0,0 +1,2531 @@ +/*++ + +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: + + StrGather.c + +Abstract: + + Parse a strings file and create or add to a string database file. + +--*/ + +#include +#include +#include +#include + +#include + +#include "CommonLib.h" +#include "EfiUtilityMsgs.h" +#include "StrGather.h" +#include "StringDB.h" + +#define TOOL_VERSION "0.31" + +#ifndef MAX_PATH +#define MAX_PATH 255 +#endif +#define MAX_NEST_DEPTH 20 // just in case we get in an endless loop. +#define MAX_STRING_IDENTIFIER_NAME 100 // number of wchars +#define MAX_LINE_LEN 200 +#define STRING_TOKEN "STRING_TOKEN" +#define DEFAULT_BASE_NAME "BaseName" +// +// Operational modes for this utility +// +#define MODE_UNKNOWN 0 +#define MODE_PARSE 1 +#define MODE_SCAN 2 +#define MODE_DUMP 3 + +// +// We keep a linked list of these for the source files we process +// +typedef struct _SOURCE_FILE { + FILE *Fptr; + WCHAR *FileBuffer; + WCHAR *FileBufferPtr; + UINT32 FileSize; + CHAR8 FileName[MAX_PATH]; + UINT32 LineNum; + BOOLEAN EndOfFile; + BOOLEAN SkipToHash; + struct _SOURCE_FILE *Previous; + struct _SOURCE_FILE *Next; + WCHAR ControlCharacter; +} SOURCE_FILE; + +#define DEFAULT_CONTROL_CHARACTER UNICODE_SLASH + +// +// Here's all our globals. We need a linked list of include paths, a linked +// list of source files, a linked list of subdirectories (appended to each +// include path when searching), and a couple other fields. +// +static struct { + SOURCE_FILE SourceFiles; + TEXT_STRING_LIST *IncludePaths; // all include paths to search + TEXT_STRING_LIST *LastIncludePath; + TEXT_STRING_LIST *ScanFileName; + TEXT_STRING_LIST *LastScanFileName; + TEXT_STRING_LIST *SkipExt; // if -skipext .uni + TEXT_STRING_LIST *LastSkipExt; + TEXT_STRING_LIST *IndirectionFileName; + TEXT_STRING_LIST *LastIndirectionFileName; + TEXT_STRING_LIST *DatabaseFileName; + TEXT_STRING_LIST *LastDatabaseFileName; + WCHAR_STRING_LIST *Language; + WCHAR_STRING_LIST *LastLanguage; + WCHAR_MATCHING_STRING_LIST *IndirectionList; // from indirection file(s) + WCHAR_MATCHING_STRING_LIST *LastIndirectionList; + BOOLEAN Verbose; // for more detailed output + BOOLEAN VerboseDatabaseWrite; // for more detailed output when writing database + BOOLEAN VerboseDatabaseRead; // for more detailed output when reading database + BOOLEAN NewDatabase; // to start from scratch + BOOLEAN IgnoreNotFound; // when scanning + BOOLEAN VerboseScan; + BOOLEAN UnquotedStrings; // -uqs option + CHAR8 OutputDatabaseFileName[MAX_PATH]; + CHAR8 StringHFileName[MAX_PATH]; + CHAR8 StringCFileName[MAX_PATH]; // output .C filename + CHAR8 DumpUFileName[MAX_PATH]; // output unicode dump file name + CHAR8 HiiExportPackFileName[MAX_PATH]; // HII export pack file name + CHAR8 BaseName[MAX_PATH]; // base filename of the strings file + UINT32 Mode; +} mGlobals; + +static +BOOLEAN +IsValidIdentifierChar ( + CHAR8 Char, + BOOLEAN FirstChar + ); + +static +void +RewindFile ( + SOURCE_FILE *SourceFile + ); + +static +BOOLEAN +SkipTo ( + SOURCE_FILE *SourceFile, + WCHAR WChar, + BOOLEAN StopAfterNewline + ); + +static +UINT32 +SkipWhiteSpace ( + SOURCE_FILE *SourceFile + ); + +static +BOOLEAN +IsWhiteSpace ( + SOURCE_FILE *SourceFile + ); + +static +BOOLEAN +EndOfFile ( + SOURCE_FILE *SourceFile + ); + +static +void +PreprocessFile ( + SOURCE_FILE *SourceFile + ); + +static +UINT32 +GetStringIdentifierName ( + IN SOURCE_FILE *SourceFile, + IN OUT WCHAR *StringIdentifierName, + IN UINT32 StringIdentifierNameLen + ); + +static +UINT32 +GetLanguageIdentifierName ( + IN SOURCE_FILE *SourceFile, + IN OUT WCHAR *LanguageIdentifierName, + IN UINT32 LanguageIdentifierNameLen, + IN BOOLEAN Optional + ); + +static +WCHAR * +GetPrintableLanguageName ( + IN SOURCE_FILE *SourceFile + ); + +static +STATUS +AddCommandLineLanguage ( + IN CHAR8 *Language + ); + +static +WCHAR * +GetQuotedString ( + SOURCE_FILE *SourceFile, + BOOLEAN Optional + ); + +static +STATUS +ProcessIncludeFile ( + SOURCE_FILE *SourceFile, + SOURCE_FILE *ParentSourceFile + ); + +static +STATUS +ParseFile ( + SOURCE_FILE *SourceFile + ); + +static +FILE * +FindFile ( + IN CHAR8 *FileName, + OUT CHAR8 *FoundFileName, + IN UINT32 FoundFileNameLen + ); + +static +STATUS +ProcessArgs ( + int Argc, + char *Argv[] + ); + +static +STATUS +ProcessFile ( + SOURCE_FILE *SourceFile + ); + +static +UINT32 +wstrcmp ( + WCHAR *Buffer, + WCHAR *Str + ); + +static +void +Usage ( + VOID + ); + +static +void +FreeLists ( + VOID + ); + +static +void +ProcessTokenString ( + SOURCE_FILE *SourceFile + ); + +static +void +ProcessTokenInclude ( + SOURCE_FILE *SourceFile + ); + +static +void +ProcessTokenScope ( + SOURCE_FILE *SourceFile + ); + +static +void +ProcessTokenLanguage ( + SOURCE_FILE *SourceFile + ); + +static +void +ProcessTokenLangDef ( + SOURCE_FILE *SourceFile + ); + +static +STATUS +ScanFiles ( + TEXT_STRING_LIST *ScanFiles + ); + +static +STATUS +ParseIndirectionFiles ( + TEXT_STRING_LIST *Files + ); + +STATUS +StringDBCreateHiiExportPack ( + CHAR8 *OutputFileName + ); + +int +main ( + int Argc, + char *Argv[] + ) +/*++ + +Routine Description: + + Call the routine to parse the command-line options, then process the file. + +Arguments: + + Argc - Standard C main() argc and argv. + Argv - Standard C main() argc and argv. + +Returns: + + 0 if successful + nonzero otherwise + +--*/ +{ + STATUS Status; + + SetUtilityName (PROGRAM_NAME); + // + // Process the command-line arguments + // + Status = ProcessArgs (Argc, Argv); + if (Status != STATUS_SUCCESS) { + return Status; + } + // + // Initialize the database manager + // + StringDBConstructor (); + // + // We always try to read in an existing database file. It may not + // exist, which is ok usually. + // + if (mGlobals.NewDatabase == 0) { + // + // Read all databases specified. + // + for (mGlobals.LastDatabaseFileName = mGlobals.DatabaseFileName; + mGlobals.LastDatabaseFileName != NULL; + mGlobals.LastDatabaseFileName = mGlobals.LastDatabaseFileName->Next + ) { + Status = StringDBReadDatabase (mGlobals.LastDatabaseFileName->Str, TRUE, mGlobals.VerboseDatabaseRead); + if (Status != STATUS_SUCCESS) { + return Status; + } + } + } + // + // Read indirection file(s) if specified + // + if (ParseIndirectionFiles (mGlobals.IndirectionFileName) != STATUS_SUCCESS) { + goto Finish; + } + // + // If scanning source files, do that now + // + if (mGlobals.Mode == MODE_SCAN) { + ScanFiles (mGlobals.ScanFileName); + } else if (mGlobals.Mode == MODE_PARSE) { + // + // Parsing a unicode strings file + // + mGlobals.SourceFiles.ControlCharacter = DEFAULT_CONTROL_CHARACTER; + Status = ProcessIncludeFile (&mGlobals.SourceFiles, NULL); + if (Status != STATUS_SUCCESS) { + goto Finish; + } + } + // + // Create the string defines header file if there have been no errors. + // + ParserSetPosition (NULL, 0); + if ((mGlobals.StringHFileName[0] != 0) && (GetUtilityStatus () < STATUS_ERROR)) { + Status = StringDBDumpStringDefines (mGlobals.StringHFileName, mGlobals.BaseName); + if (Status != EFI_SUCCESS) { + goto Finish; + } + } + // + // Dump the strings to a .c file if there have still been no errors. + // + if ((mGlobals.StringCFileName[0] != 0) && (GetUtilityStatus () < STATUS_ERROR)) { + Status = StringDBDumpCStrings ( + mGlobals.StringCFileName, + mGlobals.BaseName, + mGlobals.Language, + mGlobals.IndirectionList + ); + if (Status != EFI_SUCCESS) { + goto Finish; + } + } + // + // Dump the database if requested + // + if ((mGlobals.DumpUFileName[0] != 0) && (GetUtilityStatus () < STATUS_ERROR)) { + StringDBDumpDatabase (NULL, mGlobals.DumpUFileName, FALSE); + } + // + // Dump the string data as HII binary string pack if requested + // + if ((mGlobals.HiiExportPackFileName[0] != 0) && (GetUtilityStatus () < STATUS_ERROR)) { + StringDBCreateHiiExportPack (mGlobals.HiiExportPackFileName); + } + // + // Always update the database if no errors and not in dump mode. If they specified -od + // for an output database file name, then use that name. Otherwise use the name of + // the first database file specified with -db + // + if ((mGlobals.Mode != MODE_DUMP) && (GetUtilityStatus () < STATUS_ERROR)) { + if (mGlobals.OutputDatabaseFileName[0]) { + Status = StringDBWriteDatabase (mGlobals.OutputDatabaseFileName, mGlobals.VerboseDatabaseWrite); + } else { + Status = StringDBWriteDatabase (mGlobals.DatabaseFileName->Str, mGlobals.VerboseDatabaseWrite); + } + + if (Status != EFI_SUCCESS) { + goto Finish; + } + } + +Finish: + // + // Free up memory + // + FreeLists (); + StringDBDestructor (); + return GetUtilityStatus (); +} + +static +STATUS +ProcessIncludeFile ( + SOURCE_FILE *SourceFile, + SOURCE_FILE *ParentSourceFile + ) +/*++ + +Routine Description: + + Given a source file, open the file and parse it + +Arguments: + + SourceFile - name of file to parse + ParentSourceFile - for error reporting purposes, the file that #included SourceFile. + +Returns: + + Standard status. + +--*/ +{ + static UINT32 NestDepth = 0; + CHAR8 FoundFileName[MAX_PATH]; + STATUS Status; + + Status = STATUS_SUCCESS; + NestDepth++; + // + // Print the file being processed. Indent so you can tell the include nesting + // depth. + // + if (mGlobals.Verbose) { + fprintf (stdout, "%*cProcessing file '%s'\n", NestDepth * 2, ' ', SourceFile->FileName); + } + + // + // Make sure we didn't exceed our maximum nesting depth + // + if (NestDepth > MAX_NEST_DEPTH) { + Error (NULL, 0, 0, SourceFile->FileName, "max nesting depth (%d) exceeded", NestDepth); + Status = STATUS_ERROR; + goto Finish; + } + // + // Try to open the file locally, and if that fails try along our include paths. + // + strcpy (FoundFileName, SourceFile->FileName); + if ((SourceFile->Fptr = fopen (FoundFileName, "rb")) == NULL) { + // + // Try to find it among the paths if it has a parent (that is, it is included + // by someone else). + // + if (ParentSourceFile == NULL) { + Error (NULL, 0, 0, SourceFile->FileName, "file not found"); + return STATUS_ERROR; + } + + SourceFile->Fptr = FindFile (SourceFile->FileName, FoundFileName, sizeof (FoundFileName)); + if (SourceFile->Fptr == NULL) { + Error (ParentSourceFile->FileName, ParentSourceFile->LineNum, 0, SourceFile->FileName, "include file not found"); + return STATUS_ERROR; + } + } + // + // Process the file found + // + ProcessFile (SourceFile); +Finish: + // + // Close open files and return status + // + if (SourceFile->Fptr != NULL) { + fclose (SourceFile->Fptr); + } + + return Status; +} + +static +STATUS +ProcessFile ( + SOURCE_FILE *SourceFile + ) +{ + // + // Get the file size, and then read the entire thing into memory. + // Allocate space for a terminator character. + // + fseek (SourceFile->Fptr, 0, SEEK_END); + SourceFile->FileSize = ftell (SourceFile->Fptr); + fseek (SourceFile->Fptr, 0, SEEK_SET); + SourceFile->FileBuffer = (WCHAR *) malloc (SourceFile->FileSize + sizeof (WCHAR)); + if (SourceFile->FileBuffer == NULL) { + Error (NULL, 0, 0, "memory allocation failure", NULL); + return STATUS_ERROR; + } + + fread ((VOID *) SourceFile->FileBuffer, SourceFile->FileSize, 1, SourceFile->Fptr); + SourceFile->FileBuffer[(SourceFile->FileSize / sizeof (WCHAR))] = UNICODE_NULL; + // + // Pre-process the file to replace comments with spaces + // + PreprocessFile (SourceFile); + // + // Parse the file + // + ParseFile (SourceFile); + free (SourceFile->FileBuffer); + return STATUS_SUCCESS; +} + +static +STATUS +ParseFile ( + SOURCE_FILE *SourceFile + ) +{ + BOOLEAN InComment; + UINT32 Len; + + // + // First character of a unicode file is special. Make sure + // + if (SourceFile->FileBufferPtr[0] != UNICODE_FILE_START) { + Error (SourceFile->FileName, 1, 0, SourceFile->FileName, "file does not appear to be a unicode file"); + return STATUS_ERROR; + } + + SourceFile->FileBufferPtr++; + InComment = FALSE; + // + // Print the first line if in verbose mode + // + if (mGlobals.Verbose) { + printf ("%d: %S\n", SourceFile->LineNum, SourceFile->FileBufferPtr); + } + // + // Since the syntax is relatively straightforward, just switch on the next char + // + while (!EndOfFile (SourceFile)) { + // + // Check for whitespace + // + if (SourceFile->FileBufferPtr[0] == UNICODE_SPACE) { + SourceFile->FileBufferPtr++; + } else if (SourceFile->FileBufferPtr[0] == UNICODE_TAB) { + SourceFile->FileBufferPtr++; + } else if (SourceFile->FileBufferPtr[0] == UNICODE_CR) { + SourceFile->FileBufferPtr++; + } else if (SourceFile->FileBufferPtr[0] == UNICODE_LF) { + SourceFile->FileBufferPtr++; + SourceFile->LineNum++; + if (mGlobals.Verbose) { + printf ("%d: %S\n", SourceFile->LineNum, SourceFile->FileBufferPtr); + } + + InComment = FALSE; + } else if (SourceFile->FileBufferPtr[0] == 0) { + SourceFile->FileBufferPtr++; + } else if (InComment) { + SourceFile->FileBufferPtr++; + } else if ((SourceFile->FileBufferPtr[0] == UNICODE_SLASH) && (SourceFile->FileBufferPtr[1] == UNICODE_SLASH)) { + SourceFile->FileBufferPtr += 2; + InComment = TRUE; + } else if (SourceFile->SkipToHash && (SourceFile->FileBufferPtr[0] != SourceFile->ControlCharacter)) { + SourceFile->FileBufferPtr++; + } else { + SourceFile->SkipToHash = FALSE; + if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && + ((Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"include")) > 0) + ) { + SourceFile->FileBufferPtr += Len + 1; + ProcessTokenInclude (SourceFile); + } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && + (Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"scope")) > 0 + ) { + SourceFile->FileBufferPtr += Len + 1; + ProcessTokenScope (SourceFile); + } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && + (Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"language")) > 0 + ) { + SourceFile->FileBufferPtr += Len + 1; + ProcessTokenLanguage (SourceFile); + } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && + (Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"langdef")) > 0 + ) { + SourceFile->FileBufferPtr += Len + 1; + ProcessTokenLangDef (SourceFile); + } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && + (Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"string")) > 0 + ) { + SourceFile->FileBufferPtr += Len + 1; + ProcessTokenString (SourceFile); + } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && + (Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"EFI_BREAKPOINT()")) > 0 + ) { + SourceFile->FileBufferPtr += Len; + // + // BUGBUG: Caling EFI_BREAKOINT() is breaking the link. What is the proper action for this tool + // in this condition? + // +// EFI_BREAKPOINT (); + } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && + (SourceFile->FileBufferPtr[1] == UNICODE_EQUAL_SIGN) + ) { + SourceFile->ControlCharacter = SourceFile->FileBufferPtr[2]; + SourceFile->FileBufferPtr += 3; + } else { + Error (SourceFile->FileName, SourceFile->LineNum, 0, "unrecognized token", "%S", SourceFile->FileBufferPtr); + // + // Treat rest of line as a comment. + // + InComment = TRUE; + } + } + } + + return STATUS_SUCCESS; +} + +static +void +PreprocessFile ( + SOURCE_FILE *SourceFile + ) +/*++ + +Routine Description: + Preprocess a file to replace all carriage returns with NULLs so + we can print lines from the file to the screen. + +Arguments: + SourceFile - structure that we use to keep track of an input file. + +Returns: + Nothing. + +--*/ +{ + BOOLEAN InComment; + + RewindFile (SourceFile); + InComment = FALSE; + while (!EndOfFile (SourceFile)) { + // + // If a line-feed, then no longer in a comment + // + if (SourceFile->FileBufferPtr[0] == UNICODE_LF) { + SourceFile->FileBufferPtr++; + SourceFile->LineNum++; + InComment = 0; + } else if (SourceFile->FileBufferPtr[0] == UNICODE_CR) { + // + // Replace all carriage returns with a NULL so we can print stuff + // + SourceFile->FileBufferPtr[0] = 0; + SourceFile->FileBufferPtr++; + } else if (InComment) { + SourceFile->FileBufferPtr[0] = UNICODE_SPACE; + SourceFile->FileBufferPtr++; + } else if ((SourceFile->FileBufferPtr[0] == UNICODE_SLASH) && (SourceFile->FileBufferPtr[1] == UNICODE_SLASH)) { + SourceFile->FileBufferPtr += 2; + InComment = TRUE; + } else { + SourceFile->FileBufferPtr++; + } + } + // + // Could check for end-of-file and still in a comment, but + // should not be necessary. So just restore the file pointers. + // + RewindFile (SourceFile); +} + +static +WCHAR * +GetPrintableLanguageName ( + IN SOURCE_FILE *SourceFile + ) +{ + WCHAR *String; + WCHAR *Start; + WCHAR *Ptr; + UINT32 Len; + + SkipWhiteSpace (SourceFile); + if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) { + Error ( + SourceFile->FileName, + SourceFile->LineNum, + 0, + "expected quoted printable language name", + "%S", + SourceFile->FileBufferPtr + ); + SourceFile->SkipToHash = TRUE; + return NULL; + } + + Len = 0; + SourceFile->FileBufferPtr++; + Start = Ptr = SourceFile->FileBufferPtr; + while (!EndOfFile (SourceFile)) { + if (SourceFile->FileBufferPtr[0] == UNICODE_CR) { + Warning (SourceFile->FileName, SourceFile->LineNum, 0, "carriage return found in quoted string", "%S", Start); + break; + } else if (SourceFile->FileBufferPtr[0] == UNICODE_DOUBLE_QUOTE) { + break; + } + + SourceFile->FileBufferPtr++; + Len++; + } + + if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) { + Warning ( + SourceFile->FileName, + SourceFile->LineNum, + 0, + "missing closing quote on printable language name string", + "%S", + Start + ); + } else { + SourceFile->FileBufferPtr++; + } + // + // Now allocate memory for the string and save it off + // + String = (WCHAR *) malloc ((Len + 1) * sizeof (WCHAR)); + if (String == NULL) { + Error (NULL, 0, 0, "memory allocation failed", NULL); + return NULL; + } + // + // Copy the string from the file buffer to the local copy. + // We do no reformatting of it whatsoever at this point. + // + Ptr = String; + while (Len > 0) { + *Ptr = *Start; + Start++; + Ptr++; + Len--; + } + + *Ptr = 0; + // + // Now format the string to convert \wide and \narrow controls + // + StringDBFormatString (String); + return String; +} + +static +WCHAR * +GetQuotedString ( + SOURCE_FILE *SourceFile, + BOOLEAN Optional + ) +{ + WCHAR *String; + WCHAR *Start; + WCHAR *Ptr; + UINT32 Len; + BOOLEAN PreviousBackslash; + + if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) { + if (!Optional) { + Error (SourceFile->FileName, SourceFile->LineNum, 0, "expected quoted string", "%S", SourceFile->FileBufferPtr); + } + + return NULL; + } + + Len = 0; + SourceFile->FileBufferPtr++; + Start = Ptr = SourceFile->FileBufferPtr; + PreviousBackslash = FALSE; + while (!EndOfFile (SourceFile)) { + if ((SourceFile->FileBufferPtr[0] == UNICODE_DOUBLE_QUOTE) && (!PreviousBackslash)) { + break; + } else if (SourceFile->FileBufferPtr[0] == UNICODE_CR) { + Warning (SourceFile->FileName, SourceFile->LineNum, 0, "carriage return found in quoted string", "%S", Start); + PreviousBackslash = FALSE; + } else if (SourceFile->FileBufferPtr[0] == UNICODE_BACKSLASH) { + PreviousBackslash = TRUE; + } else { + PreviousBackslash = FALSE; + } + + SourceFile->FileBufferPtr++; + Len++; + } + + if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) { + Warning (SourceFile->FileName, SourceFile->LineNum, 0, "missing closing quote on string", "%S", Start); + } else { + SourceFile->FileBufferPtr++; + } + // + // Now allocate memory for the string and save it off + // + String = (WCHAR *) malloc ((Len + 1) * sizeof (WCHAR)); + if (String == NULL) { + Error (NULL, 0, 0, "memory allocation failed", NULL); + return NULL; + } + // + // Copy the string from the file buffer to the local copy. + // We do no reformatting of it whatsoever at this point. + // + Ptr = String; + while (Len > 0) { + *Ptr = *Start; + Start++; + Ptr++; + Len--; + } + + *Ptr = 0; + return String; +} +// +// Parse: +// #string STR_ID_NAME +// +// All we can do is call the string database to add the string identifier. Unfortunately +// he'll have to keep track of the last identifier we added. +// +static +void +ProcessTokenString ( + SOURCE_FILE *SourceFile + ) +{ + WCHAR StringIdentifier[MAX_STRING_IDENTIFIER_NAME]; + UINT16 StringId; + // + // Extract the string identifier name and add it to the database. + // + if (GetStringIdentifierName (SourceFile, StringIdentifier, sizeof (StringIdentifier)) > 0) { + StringId = STRING_ID_INVALID; + StringDBAddStringIdentifier (StringIdentifier, &StringId, 0); + } else { + // + // Error recovery -- skip to the next # + // + SourceFile->SkipToHash = TRUE; + } +} + +static +BOOLEAN +EndOfFile ( + SOURCE_FILE *SourceFile + ) +{ + // + // The file buffer pointer will typically get updated before the End-of-file flag in the + // source file structure, so check it first. + // + if (SourceFile->FileBufferPtr >= SourceFile->FileBuffer + SourceFile->FileSize / sizeof (WCHAR)) { + SourceFile->EndOfFile = TRUE; + return TRUE; + } + + if (SourceFile->EndOfFile) { + return TRUE; + } + + return FALSE; +} + +static +UINT32 +GetStringIdentifierName ( + IN SOURCE_FILE *SourceFile, + IN OUT WCHAR *StringIdentifierName, + IN UINT32 StringIdentifierNameLen + ) +{ + UINT32 Len; + WCHAR *From; + WCHAR *Start; + + // + // Skip whitespace + // + SkipWhiteSpace (SourceFile); + if (SourceFile->EndOfFile) { + Error (SourceFile->FileName, SourceFile->LineNum, 0, "end-of-file encountered", "expected string identifier"); + return 0; + } + // + // Verify first character of name is [A-Za-z] + // + Len = 0; + StringIdentifierNameLen /= 2; + From = SourceFile->FileBufferPtr; + Start = SourceFile->FileBufferPtr; + if (((SourceFile->FileBufferPtr[0] >= UNICODE_A) && (SourceFile->FileBufferPtr[0] <= UNICODE_Z)) || + ((SourceFile->FileBufferPtr[0] >= UNICODE_z) && (SourceFile->FileBufferPtr[0] <= UNICODE_z)) + ) { + // + // Do nothing + // + } else { + Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid character in string identifier name", "%S", Start); + return 0; + } + + while (!EndOfFile (SourceFile)) { + if (((SourceFile->FileBufferPtr[0] >= UNICODE_A) && (SourceFile->FileBufferPtr[0] <= UNICODE_Z)) || + ((SourceFile->FileBufferPtr[0] >= UNICODE_z) && (SourceFile->FileBufferPtr[0] <= UNICODE_z)) || + ((SourceFile->FileBufferPtr[0] >= UNICODE_0) && (SourceFile->FileBufferPtr[0] <= UNICODE_9)) || + (SourceFile->FileBufferPtr[0] == UNICODE_UNDERSCORE) + ) { + Len++; + if (Len >= StringIdentifierNameLen) { + Error (SourceFile->FileName, SourceFile->LineNum, 0, "string identifier name too long", "%S", Start); + return 0; + } + + *StringIdentifierName = SourceFile->FileBufferPtr[0]; + StringIdentifierName++; + SourceFile->FileBufferPtr++; + } else if (SkipWhiteSpace (SourceFile) == 0) { + Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid string identifier name", "%S", Start); + return 0; + } else { + break; + } + } + // + // Terminate the copy of the string. + // + *StringIdentifierName = 0; + return Len; +} + +static +UINT32 +GetLanguageIdentifierName ( + IN SOURCE_FILE *SourceFile, + IN OUT WCHAR *LanguageIdentifierName, + IN UINT32 LanguageIdentifierNameLen, + IN BOOLEAN Optional + ) +{ + UINT32 Len; + WCHAR *From; + WCHAR *Start; + // + // Skip whitespace + // + SkipWhiteSpace (SourceFile); + if (SourceFile->EndOfFile) { + if (!Optional) { + Error ( + SourceFile->FileName, + SourceFile->LineNum, + 0, + "end-of-file encountered", + "expected language identifier" + ); + } + + return 0; + } + // + // This function is called to optionally get a language identifier name in: + // #string STR_ID eng "the string" + // If it's optional, and we find a double-quote, then return now. + // + if (Optional) { + if (*SourceFile->FileBufferPtr == UNICODE_DOUBLE_QUOTE) { + return 0; + } + } + + Len = 0; + LanguageIdentifierNameLen /= 2; + // + // Internal error if we weren't given at least 4 WCHAR's to work with. + // + if (LanguageIdentifierNameLen < LANGUAGE_IDENTIFIER_NAME_LEN + 1) { + Error ( + SourceFile->FileName, + SourceFile->LineNum, + 0, + "app error -- language identifier name length is invalid", + NULL + ); + } + + From = SourceFile->FileBufferPtr; + Start = SourceFile->FileBufferPtr; + while (!EndOfFile (SourceFile)) { + if (((SourceFile->FileBufferPtr[0] >= UNICODE_a) && (SourceFile->FileBufferPtr[0] <= UNICODE_z))) { + Len++; + if (Len > LANGUAGE_IDENTIFIER_NAME_LEN) { + Error (SourceFile->FileName, SourceFile->LineNum, 0, "language identifier name too long", "%S", Start); + return 0; + } + + *LanguageIdentifierName = SourceFile->FileBufferPtr[0]; + SourceFile->FileBufferPtr++; + LanguageIdentifierName++; + } else if (!IsWhiteSpace (SourceFile)) { + Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid language identifier name", "%S", Start); + return 0; + } else { + break; + } + } + // + // Terminate the copy of the string. + // + *LanguageIdentifierName = 0; + return Len; +} + +static +void +ProcessTokenInclude ( + SOURCE_FILE *SourceFile + ) +{ + CHAR8 IncludeFileName[MAX_PATH]; + CHAR8 *To; + UINT32 Len; + BOOLEAN ReportedError; + SOURCE_FILE IncludedSourceFile; + + ReportedError = FALSE; + if (SkipWhiteSpace (SourceFile) == 0) { + Warning (SourceFile->FileName, SourceFile->LineNum, 0, "expected whitespace following #include keyword", NULL); + } + // + // Should be quoted file name + // + if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) { + Error (SourceFile->FileName, SourceFile->LineNum, 0, "expected quoted include file name", NULL); + goto FailDone; + } + + SourceFile->FileBufferPtr++; + // + // Copy the filename as ascii to our local string + // + To = IncludeFileName; + Len = 0; + while (!EndOfFile (SourceFile)) { + if ((SourceFile->FileBufferPtr[0] == UNICODE_CR) || (SourceFile->FileBufferPtr[0] == UNICODE_LF)) { + Error (SourceFile->FileName, SourceFile->LineNum, 0, "end-of-line found in quoted include file name", NULL); + goto FailDone; + } + + if (SourceFile->FileBufferPtr[0] == UNICODE_DOUBLE_QUOTE) { + SourceFile->FileBufferPtr++; + break; + } + // + // If too long, then report the error once and process until the closing quote + // + Len++; + if (!ReportedError && (Len >= sizeof (IncludeFileName))) { + Error (SourceFile->FileName, SourceFile->LineNum, 0, "length of include file name exceeds limit", NULL); + ReportedError = TRUE; + } + + if (!ReportedError) { + *To = UNICODE_TO_ASCII (SourceFile->FileBufferPtr[0]); + To++; + } + + SourceFile->FileBufferPtr++; + } + + if (!ReportedError) { + *To = 0; + memset ((char *) &IncludedSourceFile, 0, sizeof (SOURCE_FILE)); + strcpy (IncludedSourceFile.FileName, IncludeFileName); + IncludedSourceFile.ControlCharacter = DEFAULT_CONTROL_CHARACTER; + ProcessIncludeFile (&IncludedSourceFile, SourceFile); + // + // printf ("including file '%s'\n", IncludeFileName); + // + } + + return ; +FailDone: + // + // Error recovery -- skip to next # + // + SourceFile->SkipToHash = TRUE; +} + +static +void +ProcessTokenScope ( + SOURCE_FILE *SourceFile + ) +{ + WCHAR StringIdentifier[MAX_STRING_IDENTIFIER_NAME]; + // + // Extract the scope name + // + if (GetStringIdentifierName (SourceFile, StringIdentifier, sizeof (StringIdentifier)) > 0) { + StringDBSetScope (StringIdentifier); + } +} +// +// Parse: #langdef eng "English" +// #langdef chn "\wideChinese" +// +static +void +ProcessTokenLangDef ( + SOURCE_FILE *SourceFile + ) +{ + WCHAR LanguageIdentifier[MAX_STRING_IDENTIFIER_NAME]; + UINT32 Len; + WCHAR *PrintableName; + // + // Extract the 3-character language identifier + // + Len = GetLanguageIdentifierName (SourceFile, LanguageIdentifier, sizeof (LanguageIdentifier), FALSE); + if (Len != LANGUAGE_IDENTIFIER_NAME_LEN) { + Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid or missing language identifier", NULL); + } else { + // + // Extract the printable name + // + PrintableName = GetPrintableLanguageName (SourceFile); + if (PrintableName != NULL) { + ParserSetPosition (SourceFile->FileName, SourceFile->LineNum); + StringDBAddLanguage (LanguageIdentifier, PrintableName); + free (PrintableName); + return ; + } + } + // + // Error recovery -- skip to next # + // + SourceFile->SkipToHash = TRUE; +} + +static +BOOLEAN +ApparentQuotedString ( + SOURCE_FILE *SourceFile + ) +{ + WCHAR *Ptr; + // + // See if the first and last nonblank characters on the line are double quotes + // + for (Ptr = SourceFile->FileBufferPtr; *Ptr && (*Ptr == UNICODE_SPACE); Ptr++) + ; + if (*Ptr != UNICODE_DOUBLE_QUOTE) { + return FALSE; + } + + while (*Ptr) { + Ptr++; + } + + Ptr--; + for (; *Ptr && (*Ptr == UNICODE_SPACE); Ptr--) + ; + if (*Ptr != UNICODE_DOUBLE_QUOTE) { + return FALSE; + } + + return TRUE; +} +// +// Parse: +// #language eng "some string " "more string" +// +static +void +ProcessTokenLanguage ( + SOURCE_FILE *SourceFile + ) +{ + WCHAR *String; + WCHAR *SecondString; + WCHAR *TempString; + WCHAR *From; + WCHAR *To; + WCHAR Language[LANGUAGE_IDENTIFIER_NAME_LEN + 1]; + UINT32 Len; + BOOLEAN PreviousNewline; + // + // Get the language identifier + // + Language[0] = 0; + Len = GetLanguageIdentifierName (SourceFile, Language, sizeof (Language), TRUE); + if (Len != LANGUAGE_IDENTIFIER_NAME_LEN) { + Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid or missing language identifier", "%S", Language); + SourceFile->SkipToHash = TRUE; + return ; + } + // + // Extract the string value. It's either a quoted string that starts on the current line, or + // an unquoted string that starts on the following line and continues until the next control + // character in column 1. + // Look ahead to find a quote or a newline + // + if (SkipTo (SourceFile, UNICODE_DOUBLE_QUOTE, TRUE)) { + String = GetQuotedString (SourceFile, FALSE); + if (String != NULL) { + // + // Set the position in the file of where we are parsing for error + // reporting purposes. Then start looking ahead for additional + // quoted strings, and concatenate them until we get a failure + // back from the string parser. + // + Len = StrLen (String) + 1; + ParserSetPosition (SourceFile->FileName, SourceFile->LineNum); + do { + SkipWhiteSpace (SourceFile); + SecondString = GetQuotedString (SourceFile, TRUE); + if (SecondString != NULL) { + Len += StrLen (SecondString); + TempString = (WCHAR *) malloc (Len * sizeof (WCHAR)); + if (TempString == NULL) { + Error (NULL, 0, 0, "application error", "failed to allocate memory"); + return ; + } + + StrCpy (TempString, String); + StrCat (TempString, SecondString); + free (String); + free (SecondString); + String = TempString; + } + } while (SecondString != NULL); + StringDBAddString (Language, NULL, NULL, String, TRUE, 0); + free (String); + } else { + // + // Error was reported at lower level. Error recovery mode. + // + SourceFile->SkipToHash = TRUE; + } + } else { + if (!mGlobals.UnquotedStrings) { + // + // They're using unquoted strings. If the next non-blank character is a double quote, and the + // last non-blank character on the line is a double quote, then more than likely they're using + // quotes, so they need to put the quoted string on the end of the previous line + // + if (ApparentQuotedString (SourceFile)) { + Warning ( + SourceFile->FileName, + SourceFile->LineNum, + 0, + "unexpected quoted string on line", + "specify -uqs option if necessary" + ); + } + } + // + // Found end-of-line (hopefully). Skip over it and start taking in characters + // until we find a control character at the start of a line. + // + Len = 0; + From = SourceFile->FileBufferPtr; + PreviousNewline = FALSE; + while (!EndOfFile (SourceFile)) { + if (SourceFile->FileBufferPtr[0] == UNICODE_LF) { + PreviousNewline = TRUE; + SourceFile->LineNum++; + } else { + Len++; + if (PreviousNewline && (SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter)) { + break; + } + + PreviousNewline = FALSE; + } + + SourceFile->FileBufferPtr++; + } + + if ((Len == 0) && EndOfFile (SourceFile)) { + Error (SourceFile->FileName, SourceFile->LineNum, 0, "unexpected end of file", NULL); + SourceFile->SkipToHash = TRUE; + return ; + } + // + // Now allocate a buffer, copy the characters, and add the string. + // + String = (WCHAR *) malloc ((Len + 1) * sizeof (WCHAR)); + if (String == NULL) { + Error (NULL, 0, 0, "application error", "failed to allocate memory"); + return ; + } + + To = String; + while (From < SourceFile->FileBufferPtr) { + switch (*From) { + case UNICODE_LF: + case 0: + break; + + default: + *To = *From; + To++; + break; + } + + From++; + } + + // + // String[Len] = 0; + // + *To = 0; + StringDBAddString (Language, NULL, NULL, String, TRUE, 0); + } +} + +static +BOOLEAN +IsWhiteSpace ( + SOURCE_FILE *SourceFile + ) +{ + switch (SourceFile->FileBufferPtr[0]) { + case UNICODE_NULL: + case UNICODE_CR: + case UNICODE_SPACE: + case UNICODE_TAB: + case UNICODE_LF: + return TRUE; + + default: + return FALSE; + } +} + +static +UINT32 +SkipWhiteSpace ( + SOURCE_FILE *SourceFile + ) +{ + UINT32 Count; + + Count = 0; + while (!EndOfFile (SourceFile)) { + Count++; + switch (*SourceFile->FileBufferPtr) { + case UNICODE_NULL: + case UNICODE_CR: + case UNICODE_SPACE: + case UNICODE_TAB: + SourceFile->FileBufferPtr++; + break; + + case UNICODE_LF: + SourceFile->FileBufferPtr++; + SourceFile->LineNum++; + if (mGlobals.Verbose) { + printf ("%d: %S\n", SourceFile->LineNum, SourceFile->FileBufferPtr); + } + break; + + default: + return Count - 1; + } + } + // + // Some tokens require trailing whitespace. If we're at the end of the + // file, then we count that as well. + // + if ((Count == 0) && (EndOfFile (SourceFile))) { + Count++; + } + + return Count; +} + +static +UINT32 +wstrcmp ( + WCHAR *Buffer, + WCHAR *Str + ) +{ + UINT32 Len; + + Len = 0; + while (*Str == *Buffer) { + Buffer++; + Str++; + Len++; + } + + if (*Str) { + return 0; + } + + return Len; +} +// +// Given a filename, try to find it along the include paths. +// +static +FILE * +FindFile ( + IN CHAR8 *FileName, + OUT CHAR8 *FoundFileName, + IN UINT32 FoundFileNameLen + ) +{ + FILE *Fptr; + TEXT_STRING_LIST *List; + + // + // Traverse the list of paths and try to find the file + // + List = mGlobals.IncludePaths; + while (List != NULL) { + // + // Put the path and filename together + // + if (strlen (List->Str) + strlen (FileName) + 1 > FoundFileNameLen) { + Error (PROGRAM_NAME, 0, 0, NULL, "internal error - cannot concatenate path+filename"); + return NULL; + } + // + // Append the filename to this include path and try to open the file. + // + strcpy (FoundFileName, List->Str); + strcat (FoundFileName, FileName); + if ((Fptr = fopen (FoundFileName, "rb")) != NULL) { + // + // Return the file pointer + // + return Fptr; + } + + List = List->Next; + } + // + // Not found + // + FoundFileName[0] = 0; + return NULL; +} +// +// Process the command-line arguments +// +static +STATUS +ProcessArgs ( + int Argc, + char *Argv[] + ) +{ + TEXT_STRING_LIST *NewList; + // + // Clear our globals + // + memset ((char *) &mGlobals, 0, sizeof (mGlobals)); + strcpy (mGlobals.BaseName, DEFAULT_BASE_NAME); + // + // Skip program name + // + Argc--; + Argv++; + + if (Argc == 0) { + Usage (); + return STATUS_ERROR; + } + + mGlobals.Mode = MODE_UNKNOWN; + // + // Process until no more -args. + // + while ((Argc > 0) && (Argv[0][0] == '-')) { + // + // -parse option + // + if (stricmp (Argv[0], "-parse") == 0) { + if (mGlobals.Mode != MODE_UNKNOWN) { + Error (NULL, 0, 0, "only one of -parse/-scan/-dump allowed", NULL); + return STATUS_ERROR; + } + + mGlobals.Mode = MODE_PARSE; + // + // -scan option + // + } else if (stricmp (Argv[0], "-scan") == 0) { + if (mGlobals.Mode != MODE_UNKNOWN) { + Error (NULL, 0, 0, "only one of -parse/-scan/-dump allowed", NULL); + return STATUS_ERROR; + } + + mGlobals.Mode = MODE_SCAN; + // + // -vscan verbose scanning option + // + } else if (stricmp (Argv[0], "-vscan") == 0) { + mGlobals.VerboseScan = TRUE; + // + // -dump option + // + } else if (stricmp (Argv[0], "-dump") == 0) { + if (mGlobals.Mode != MODE_UNKNOWN) { + Error (NULL, 0, 0, "only one of -parse/-scan/-dump allowed", NULL); + return STATUS_ERROR; + } + + mGlobals.Mode = MODE_DUMP; + } else if (stricmp (Argv[0], "-uqs") == 0) { + mGlobals.UnquotedStrings = TRUE; + // + // -i path add include search path when parsing + // + } else if (stricmp (Argv[0], "-i") == 0) { + // + // check for one more arg + // + if ((Argc <= 1) || (Argv[1][0] == '-')) { + Error (PROGRAM_NAME, 0, 0, Argv[0], "missing include path"); + return STATUS_ERROR; + } + // + // Allocate memory for a new list element, fill it in, and + // add it to our list of include paths. Always make sure it + // has a "\" on the end of it. + // + NewList = malloc (sizeof (TEXT_STRING_LIST)); + if (NewList == NULL) { + Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure"); + return STATUS_ERROR; + } + + memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST)); + NewList->Str = malloc (strlen (Argv[1]) + 2); + if (NewList->Str == NULL) { + free (NewList); + Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure"); + return STATUS_ERROR; + } + + strcpy (NewList->Str, Argv[1]); + if (NewList->Str[strlen (NewList->Str) - 1] != '\\') { + strcat (NewList->Str, "\\"); + } + // + // Add it to our linked list + // + if (mGlobals.IncludePaths == NULL) { + mGlobals.IncludePaths = NewList; + } else { + mGlobals.LastIncludePath->Next = NewList; + } + + mGlobals.LastIncludePath = NewList; + Argc--; + Argv++; + } else if (stricmp (Argv[0], "-if") == 0) { + // + // Indirection file -- check for one more arg + // + if ((Argc <= 1) || (Argv[1][0] == '-')) { + Error (PROGRAM_NAME, 0, 0, Argv[0], "missing indirection file name"); + return STATUS_ERROR; + } + // + // Allocate memory for a new list element, fill it in, and + // add it to our list of include paths. Always make sure it + // has a "\" on the end of it. + // + NewList = malloc (sizeof (TEXT_STRING_LIST)); + if (NewList == NULL) { + Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure"); + return STATUS_ERROR; + } + + memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST)); + NewList->Str = malloc (strlen (Argv[1]) + 1); + if (NewList->Str == NULL) { + free (NewList); + Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure"); + return STATUS_ERROR; + } + + strcpy (NewList->Str, Argv[1]); + // + // Add it to our linked list + // + if (mGlobals.IndirectionFileName == NULL) { + mGlobals.IndirectionFileName = NewList; + } else { + mGlobals.LastIndirectionFileName->Next = NewList; + } + + mGlobals.LastIndirectionFileName = NewList; + Argc--; + Argv++; + } else if (stricmp (Argv[0], "-db") == 0) { + // + // -db option to specify a database file. + // Check for one more arg (the database file name) + // + if ((Argc <= 1) || (Argv[1][0] == '-')) { + Error (PROGRAM_NAME, 0, 0, Argv[0], "missing database file name"); + return STATUS_ERROR; + } + + NewList = malloc (sizeof (TEXT_STRING_LIST)); + if (NewList == NULL) { + Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure"); + return STATUS_ERROR; + } + + memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST)); + NewList->Str = malloc (strlen (Argv[1]) + 1); + if (NewList->Str == NULL) { + free (NewList); + Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure"); + return STATUS_ERROR; + } + + strcpy (NewList->Str, Argv[1]); + // + // Add it to our linked list + // + if (mGlobals.DatabaseFileName == NULL) { + mGlobals.DatabaseFileName = NewList; + } else { + mGlobals.LastDatabaseFileName->Next = NewList; + } + + mGlobals.LastDatabaseFileName = NewList; + Argc--; + Argv++; + } else if (stricmp (Argv[0], "-ou") == 0) { + // + // -ou option to specify an output unicode file to + // which we can dump our database. + // + if ((Argc <= 1) || (Argv[1][0] == '-')) { + Error (PROGRAM_NAME, 0, 0, Argv[0], "missing database dump output file name"); + return STATUS_ERROR; + } + + if (mGlobals.DumpUFileName[0] == 0) { + strcpy (mGlobals.DumpUFileName, Argv[1]); + } else { + Error (PROGRAM_NAME, 0, 0, Argv[1], "-ou option already specified with '%s'", mGlobals.DumpUFileName); + return STATUS_ERROR; + } + + Argc--; + Argv++; + } else if (stricmp (Argv[0], "-hpk") == 0) { + // + // -hpk option to create an HII export pack of the input database file + // + if ((Argc <= 1) || (Argv[1][0] == '-')) { + Error (PROGRAM_NAME, 0, 0, Argv[0], "missing raw string data dump output file name"); + return STATUS_ERROR; + } + + if (mGlobals.HiiExportPackFileName[0] == 0) { + strcpy (mGlobals.HiiExportPackFileName, Argv[1]); + } else { + Error (PROGRAM_NAME, 0, 0, Argv[1], "-or option already specified with '%s'", mGlobals.HiiExportPackFileName); + return STATUS_ERROR; + } + + Argc--; + Argv++; + } else if ((stricmp (Argv[0], "-?") == 0) || (stricmp (Argv[0], "-h") == 0)) { + Usage (); + return STATUS_ERROR; + } else if (stricmp (Argv[0], "-v") == 0) { + mGlobals.Verbose = 1; + } else if (stricmp (Argv[0], "-vdbw") == 0) { + mGlobals.VerboseDatabaseWrite = 1; + } else if (stricmp (Argv[0], "-vdbr") == 0) { + mGlobals.VerboseDatabaseRead = 1; + } else if (stricmp (Argv[0], "-newdb") == 0) { + mGlobals.NewDatabase = 1; + } else if (stricmp (Argv[0], "-ignorenotfound") == 0) { + mGlobals.IgnoreNotFound = 1; + } else if (stricmp (Argv[0], "-oc") == 0) { + // + // check for one more arg + // + if ((Argc <= 1) || (Argv[1][0] == '-')) { + Error (PROGRAM_NAME, 0, 0, Argv[0], "missing output C filename"); + return STATUS_ERROR; + } + + strcpy (mGlobals.StringCFileName, Argv[1]); + Argc--; + Argv++; + } else if (stricmp (Argv[0], "-bn") == 0) { + // + // check for one more arg + // + if ((Argc <= 1) || (Argv[1][0] == '-')) { + Error (PROGRAM_NAME, 0, 0, Argv[0], "missing base name"); + Usage (); + return STATUS_ERROR; + } + + strcpy (mGlobals.BaseName, Argv[1]); + Argc--; + Argv++; + } else if (stricmp (Argv[0], "-oh") == 0) { + // + // -oh to specify output .h defines file name + // + if ((Argc <= 1) || (Argv[1][0] == '-')) { + Error (PROGRAM_NAME, 0, 0, Argv[0], "missing output .h filename"); + return STATUS_ERROR; + } + + strcpy (mGlobals.StringHFileName, Argv[1]); + Argc--; + Argv++; + } else if (stricmp (Argv[0], "-skipext") == 0) { + // + // -skipext to skip scanning of files with certain filename extensions + // + if ((Argc <= 1) || (Argv[1][0] == '-')) { + Error (PROGRAM_NAME, 0, 0, Argv[0], "missing filename extension"); + return STATUS_ERROR; + } + // + // Allocate memory for a new list element, fill it in, and + // add it to our list of excluded extensions. Always make sure it + // has a "." as the first character. + // + NewList = malloc (sizeof (TEXT_STRING_LIST)); + if (NewList == NULL) { + Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure"); + return STATUS_ERROR; + } + + memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST)); + NewList->Str = malloc (strlen (Argv[1]) + 2); + if (NewList->Str == NULL) { + free (NewList); + Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure"); + return STATUS_ERROR; + } + + if (Argv[1][0] == '.') { + strcpy (NewList->Str, Argv[1]); + } else { + NewList->Str[0] = '.'; + strcpy (NewList->Str + 1, Argv[1]); + } + // + // Add it to our linked list + // + if (mGlobals.SkipExt == NULL) { + mGlobals.SkipExt = NewList; + } else { + mGlobals.LastSkipExt->Next = NewList; + } + + mGlobals.LastSkipExt = NewList; + Argc--; + Argv++; + } else if (stricmp (Argv[0], "-lang") == 0) { + // + // "-lang eng" or "-lang spa+cat" to only output certain languages + // + if ((Argc <= 1) || (Argv[1][0] == '-')) { + Error (PROGRAM_NAME, 0, 0, Argv[0], "missing language name"); + Usage (); + return STATUS_ERROR; + } + + if (AddCommandLineLanguage (Argv[1]) != STATUS_SUCCESS) { + return STATUS_ERROR; + } + + Argc--; + Argv++; + } else if (stricmp (Argv[0], "-od") == 0) { + // + // Output database file name -- check for another arg + // + if ((Argc <= 1) || (Argv[1][0] == '-')) { + Error (PROGRAM_NAME, 0, 0, Argv[0], "missing output database file name"); + return STATUS_ERROR; + } + + strcpy (mGlobals.OutputDatabaseFileName, Argv[1]); + Argv++; + Argc--; + } else { + // + // Unrecognized arg + // + Error (PROGRAM_NAME, 0, 0, Argv[0], "unrecognized option"); + Usage (); + return STATUS_ERROR; + } + + Argv++; + Argc--; + } + // + // Make sure they specified the mode parse/scan/dump + // + if (mGlobals.Mode == MODE_UNKNOWN) { + Error (NULL, 0, 0, "must specify one of -parse/-scan/-dump", NULL); + return STATUS_ERROR; + } + // + // All modes require a database filename + // + if (mGlobals.DatabaseFileName == 0) { + Error (NULL, 0, 0, "must specify a database filename using -db DbFileName", NULL); + Usage (); + return STATUS_ERROR; + } + // + // If dumping the database file, then return immediately if all + // parameters check out. + // + if (mGlobals.Mode == MODE_DUMP) { + // + // Not much use if they didn't specify -oh or -oc or -ou or -hpk + // + if ((mGlobals.DumpUFileName[0] == 0) && + (mGlobals.StringHFileName[0] == 0) && + (mGlobals.StringCFileName[0] == 0) && + (mGlobals.HiiExportPackFileName[0] == 0) + ) { + Error (NULL, 0, 0, "-dump without -oc/-oh/-ou/-hpk is a NOP", NULL); + return STATUS_ERROR; + } + + return STATUS_SUCCESS; + } + // + // Had to specify source string file and output string defines header filename. + // + if (mGlobals.Mode == MODE_SCAN) { + if (Argc < 1) { + Error (PROGRAM_NAME, 0, 0, NULL, "must specify at least one source file to scan with -scan"); + Usage (); + return STATUS_ERROR; + } + // + // Get the list of filenames + // + while (Argc > 0) { + NewList = malloc (sizeof (TEXT_STRING_LIST)); + if (NewList == NULL) { + Error (PROGRAM_NAME, 0, 0, "memory allocation failure", NULL); + return STATUS_ERROR; + } + + memset (NewList, 0, sizeof (TEXT_STRING_LIST)); + NewList->Str = (CHAR8 *) malloc (strlen (Argv[0]) + 1); + if (NewList->Str == NULL) { + Error (PROGRAM_NAME, 0, 0, "memory allocation failure", NULL); + return STATUS_ERROR; + } + + strcpy (NewList->Str, Argv[0]); + if (mGlobals.ScanFileName == NULL) { + mGlobals.ScanFileName = NewList; + } else { + mGlobals.LastScanFileName->Next = NewList; + } + + mGlobals.LastScanFileName = NewList; + Argc--; + Argv++; + } + } else { + // + // Parse mode -- must specify an input unicode file name + // + if (Argc < 1) { + Error (PROGRAM_NAME, 0, 0, NULL, "must specify input unicode string file name with -parse"); + Usage (); + return STATUS_ERROR; + } + + strcpy (mGlobals.SourceFiles.FileName, Argv[0]); + } + + return STATUS_SUCCESS; +} +// +// Found "-lang eng,spa+cat" on the command line. Parse the +// language list and save the setting for later processing. +// +static +STATUS +AddCommandLineLanguage ( + IN CHAR8 *Language + ) +{ + WCHAR_STRING_LIST *WNewList; + WCHAR *From; + WCHAR *To; + // + // Keep processing the input string until we find the end. + // + while (*Language) { + // + // Allocate memory for a new list element, fill it in, and + // add it to our list. + // + WNewList = MALLOC (sizeof (WCHAR_STRING_LIST)); + if (WNewList == NULL) { + Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure"); + return STATUS_ERROR; + } + + memset ((char *) WNewList, 0, sizeof (WCHAR_STRING_LIST)); + WNewList->Str = malloc ((strlen (Language) + 1) * sizeof (WCHAR)); + if (WNewList->Str == NULL) { + free (WNewList); + Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure"); + return STATUS_ERROR; + } + // + // Copy it as unicode to our new structure. Then remove the + // plus signs in it, and verify each language name is 3 characters + // long. If we find a comma, then we're done with this group, so + // break out. + // + UnicodeSPrint (WNewList->Str, (strlen (Language) + 1) * sizeof (WCHAR), L"%a", Language); + From = To = WNewList->Str; + while (*From) { + if (*From == L',') { + break; + } + + if ((StrLen (From) < LANGUAGE_IDENTIFIER_NAME_LEN) || + ( + (From[LANGUAGE_IDENTIFIER_NAME_LEN] != 0) && + (From[LANGUAGE_IDENTIFIER_NAME_LEN] != UNICODE_PLUS_SIGN) && + (From[LANGUAGE_IDENTIFIER_NAME_LEN] != L',') + ) + ) { + Error (PROGRAM_NAME, 0, 0, Language, "invalid format for language name on command line"); + FREE (WNewList->Str); + FREE (WNewList); + return STATUS_ERROR; + } + + StrnCpy (To, From, LANGUAGE_IDENTIFIER_NAME_LEN); + To += LANGUAGE_IDENTIFIER_NAME_LEN; + From += LANGUAGE_IDENTIFIER_NAME_LEN; + if (*From == L'+') { + From++; + } + } + + *To = 0; + // + // Add it to our linked list + // + if (mGlobals.Language == NULL) { + mGlobals.Language = WNewList; + } else { + mGlobals.LastLanguage->Next = WNewList; + } + + mGlobals.LastLanguage = WNewList; + // + // Skip to next entry (comma-separated list) + // + while (*Language) { + if (*Language == L',') { + Language++; + break; + } + + Language++; + } + } + + return STATUS_SUCCESS; +} +// +// The contents of the text file are expected to be (one per line) +// STRING_IDENTIFIER_NAME ScopeName +// For example: +// STR_ID_MY_FAVORITE_STRING IBM +// +static +STATUS +ParseIndirectionFiles ( + TEXT_STRING_LIST *Files + ) +{ + FILE *Fptr; + CHAR8 Line[200]; + CHAR8 *StringName; + CHAR8 *ScopeName; + CHAR8 *End; + UINT32 LineCount; + WCHAR_MATCHING_STRING_LIST *NewList; + + Line[sizeof (Line) - 1] = 0; + Fptr = NULL; + while (Files != NULL) { + Fptr = fopen (Files->Str, "r"); + LineCount = 0; + if (Fptr == NULL) { + Error (NULL, 0, 0, Files->Str, "failed to open input indirection file for reading"); + return STATUS_ERROR; + } + + while (fgets (Line, sizeof (Line), Fptr) != NULL) { + // + // remove terminating newline for error printing purposes. + // + if (Line[strlen (Line) - 1] == '\n') { + Line[strlen (Line) - 1] = 0; + } + + LineCount++; + if (Line[sizeof (Line) - 1] != 0) { + Error (Files->Str, LineCount, 0, "line length exceeds maximum supported", NULL); + goto Done; + } + + StringName = Line; + while (*StringName && (isspace (*StringName))) { + StringName++; + } + + if (*StringName) { + if ((*StringName == '_') || isalpha (*StringName)) { + End = StringName; + while ((*End) && (*End == '_') || (isalnum (*End))) { + End++; + } + + if (isspace (*End)) { + *End = 0; + End++; + while (isspace (*End)) { + End++; + } + + if (*End) { + ScopeName = End; + while (*End && !isspace (*End)) { + End++; + } + + *End = 0; + // + // Add the string name/scope pair + // + NewList = malloc (sizeof (WCHAR_MATCHING_STRING_LIST)); + if (NewList == NULL) { + Error (NULL, 0, 0, "memory allocation error", NULL); + goto Done; + } + + memset (NewList, 0, sizeof (WCHAR_MATCHING_STRING_LIST)); + NewList->Str1 = (WCHAR *) malloc ((strlen (StringName) + 1) * sizeof (WCHAR)); + NewList->Str2 = (WCHAR *) malloc ((strlen (ScopeName) + 1) * sizeof (WCHAR)); + if ((NewList->Str1 == NULL) || (NewList->Str2 == NULL)) { + Error (NULL, 0, 0, "memory allocation error", NULL); + goto Done; + } + + UnicodeSPrint (NewList->Str1, strlen (StringName) + 1, L"%a", StringName); + UnicodeSPrint (NewList->Str2, strlen (ScopeName) + 1, L"%a", ScopeName); + if (mGlobals.IndirectionList == NULL) { + mGlobals.IndirectionList = NewList; + } else { + mGlobals.LastIndirectionList->Next = NewList; + } + + mGlobals.LastIndirectionList = NewList; + } else { + Error (Files->Str, LineCount, 0, StringName, "invalid line : expected 'StringIdentifier Scope'"); + goto Done; + } + } else { + Error (Files->Str, LineCount, 0, StringName, "invalid line : expected 'StringIdentifier Scope'"); + goto Done; + } + } else { + Error (Files->Str, LineCount, 0, StringName, "invalid string identifier"); + goto Done; + } + } + } + + fclose (Fptr); + Fptr = NULL; + Files = Files->Next; + } + +Done: + if (Fptr != NULL) { + fclose (Fptr); + return STATUS_ERROR; + } + + return STATUS_SUCCESS; +} + +static +STATUS +ScanFiles ( + TEXT_STRING_LIST *ScanFiles + ) +{ + char Line[MAX_LINE_LEN]; + FILE *Fptr; + UINT32 LineNum; + char *Cptr; + char *SavePtr; + char *TermPtr; + char *StringTokenPos; + TEXT_STRING_LIST *SList; + BOOLEAN SkipIt; + + // + // Put a null-terminator at the end of the line. If we read in + // a line longer than we support, then we can catch it. + // + Line[MAX_LINE_LEN - 1] = 0; + // + // Process each file. If they gave us a skip extension list, then + // skip it if the extension matches. + // + while (ScanFiles != NULL) { + SkipIt = FALSE; + for (SList = mGlobals.SkipExt; SList != NULL; SList = SList->Next) { + if ((strlen (ScanFiles->Str) > strlen (SList->Str)) && + (strcmp (ScanFiles->Str + strlen (ScanFiles->Str) - strlen (SList->Str), SList->Str) == 0) + ) { + SkipIt = TRUE; + // + // printf ("Match: %s : %s\n", ScanFiles->Str, SList->Str); + // + break; + } + } + + if (!SkipIt) { + if (mGlobals.VerboseScan) { + printf ("Scanning %s\n", ScanFiles->Str); + } + + Fptr = fopen (ScanFiles->Str, "r"); + if (Fptr == NULL) { + Error (NULL, 0, 0, ScanFiles->Str, "failed to open input file for scanning"); + return STATUS_ERROR; + } + + LineNum = 0; + while (fgets (Line, sizeof (Line), Fptr) != NULL) { + LineNum++; + if (Line[MAX_LINE_LEN - 1] != 0) { + Error (ScanFiles->Str, LineNum, 0, "line length exceeds maximum supported by tool", NULL); + fclose (Fptr); + return STATUS_ERROR; + } + // + // Remove the newline from the input line so we can print a warning message + // + if (Line[strlen (Line) - 1] == '\n') { + Line[strlen (Line) - 1] = 0; + } + // + // Terminate the line at // comments + // + Cptr = strstr (Line, "//"); + if (Cptr != NULL) { + *Cptr = 0; + } + + Cptr = Line; + while ((Cptr = strstr (Cptr, STRING_TOKEN)) != NULL) { + // + // Found "STRING_TOKEN". Make sure we don't have NUM_STRING_TOKENS or + // something like that. Then make sure it's followed by + // an open parenthesis, a string identifier, and then a closing + // parenthesis. + // + if (mGlobals.VerboseScan) { + printf (" %d: %s", LineNum, Cptr); + } + + if (((Cptr == Line) || (!IsValidIdentifierChar (*(Cptr - 1), FALSE))) && + (!IsValidIdentifierChar (*(Cptr + sizeof (STRING_TOKEN) - 1), FALSE)) + ) { + StringTokenPos = Cptr; + SavePtr = Cptr; + Cptr += strlen (STRING_TOKEN); + while (*Cptr && isspace (*Cptr) && (*Cptr != '(')) { + Cptr++; + } + + if (*Cptr != '(') { + Warning (ScanFiles->Str, LineNum, 0, StringTokenPos, "expected "STRING_TOKEN "(identifier)"); + } else { + // + // Skip over the open-parenthesis and find the next non-blank character + // + Cptr++; + while (isspace (*Cptr)) { + Cptr++; + } + + SavePtr = Cptr; + if ((*Cptr == '_') || isalpha (*Cptr)) { + while ((*Cptr == '_') || (isalnum (*Cptr))) { + Cptr++; + } + + TermPtr = Cptr; + while (*Cptr && isspace (*Cptr)) { + Cptr++; + } + + if (*Cptr != ')') { + Warning (ScanFiles->Str, LineNum, 0, StringTokenPos, "expected "STRING_TOKEN "(identifier)"); + } + + if (*TermPtr) { + *TermPtr = 0; + Cptr = TermPtr + 1; + } else { + Cptr = TermPtr; + } + // + // Add the string identifier to the list of used strings + // + ParserSetPosition (ScanFiles->Str, LineNum); + StringDBSetStringReferenced (SavePtr, mGlobals.IgnoreNotFound); + if (mGlobals.VerboseScan) { + printf ("...referenced %s", SavePtr); + } + } else { + Warning (ScanFiles->Str, LineNum, 0, StringTokenPos, "expected valid string identifier name"); + } + } + } else { + // + // Found it, but it's a substring of something else. Advance our pointer. + // + Cptr++; + } + + if (mGlobals.VerboseScan) { + printf ("\n"); + } + } + } + + fclose (Fptr); + } else { + // + // Skipping this file type + // + if (mGlobals.VerboseScan) { + printf ("Skip scanning of %s\n", ScanFiles->Str); + } + } + + ScanFiles = ScanFiles->Next; + } + + return STATUS_SUCCESS; +} +// +// Free the global string lists we allocated memory for +// +static +void +FreeLists ( + VOID + ) +{ + TEXT_STRING_LIST *Temp; + WCHAR_STRING_LIST *WTemp; + + // + // Traverse the include paths, freeing each + // + while (mGlobals.IncludePaths != NULL) { + Temp = mGlobals.IncludePaths->Next; + free (mGlobals.IncludePaths->Str); + free (mGlobals.IncludePaths); + mGlobals.IncludePaths = Temp; + } + // + // If we did a scan, then free up our + // list of files to scan. + // + while (mGlobals.ScanFileName != NULL) { + Temp = mGlobals.ScanFileName->Next; + free (mGlobals.ScanFileName->Str); + free (mGlobals.ScanFileName); + mGlobals.ScanFileName = Temp; + } + // + // If they gave us a list of filename extensions to + // skip on scan, then free them up. + // + while (mGlobals.SkipExt != NULL) { + Temp = mGlobals.SkipExt->Next; + free (mGlobals.SkipExt->Str); + free (mGlobals.SkipExt); + mGlobals.SkipExt = Temp; + } + // + // Free up any languages specified + // + while (mGlobals.Language != NULL) { + WTemp = mGlobals.Language->Next; + free (mGlobals.Language->Str); + free (mGlobals.Language); + mGlobals.Language = WTemp; + } + // + // Free up our indirection list + // + while (mGlobals.IndirectionList != NULL) { + mGlobals.LastIndirectionList = mGlobals.IndirectionList->Next; + free (mGlobals.IndirectionList->Str1); + free (mGlobals.IndirectionList->Str2); + free (mGlobals.IndirectionList); + mGlobals.IndirectionList = mGlobals.LastIndirectionList; + } + + while (mGlobals.IndirectionFileName != NULL) { + mGlobals.LastIndirectionFileName = mGlobals.IndirectionFileName->Next; + free (mGlobals.IndirectionFileName->Str); + free (mGlobals.IndirectionFileName); + mGlobals.IndirectionFileName = mGlobals.LastIndirectionFileName; + } +} + +static +BOOLEAN +IsValidIdentifierChar ( + CHAR8 Char, + BOOLEAN FirstChar + ) +{ + // + // If it's the first character of an identifier, then + // it must be one of [A-Za-z_]. + // + if (FirstChar) { + if (isalpha (Char) || (Char == '_')) { + return TRUE; + } + } else { + // + // If it's not the first character, then it can + // be one of [A-Za-z_0-9] + // + if (isalnum (Char) || (Char == '_')) { + return TRUE; + } + } + + return FALSE; +} + +static +void +RewindFile ( + SOURCE_FILE *SourceFile + ) +{ + SourceFile->LineNum = 1; + SourceFile->FileBufferPtr = SourceFile->FileBuffer; + SourceFile->EndOfFile = 0; +} + +static +BOOLEAN +SkipTo ( + SOURCE_FILE *SourceFile, + WCHAR WChar, + BOOLEAN StopAfterNewline + ) +{ + while (!EndOfFile (SourceFile)) { + // + // Check for the character of interest + // + if (SourceFile->FileBufferPtr[0] == WChar) { + return TRUE; + } else { + if (SourceFile->FileBufferPtr[0] == UNICODE_LF) { + SourceFile->LineNum++; + if (StopAfterNewline) { + SourceFile->FileBufferPtr++; + if (SourceFile->FileBufferPtr[0] == 0) { + SourceFile->FileBufferPtr++; + } + + return FALSE; + } + } + + SourceFile->FileBufferPtr++; + } + } + + return FALSE; +} + +static +void +Usage ( + VOID + ) +/*++ + +Routine Description: + + Print usage information for this utility. + +Arguments: + + None. + +Returns: + + Nothing. + +--*/ +{ + int Index; + static const char *Str[] = { + "", + PROGRAM_NAME " version "TOOL_VERSION " -- process unicode strings file", + " Usage: "PROGRAM_NAME " -parse {parse options} [FileNames]", + " "PROGRAM_NAME " -scan {scan options} [FileName]", + " "PROGRAM_NAME " -dump {dump options}", + " Common options include:", + " -h or -? for this help information", + " -db Database required name of output/input database file", + " -bn BaseName for use in the .h and .c output files", + " Default = "DEFAULT_BASE_NAME, + " -v for verbose output", + " -vdbw for verbose output when writing database", + " -vdbr for verbose output when reading database", + " -od FileName to specify an output database file name", + " Parse options include:", + " -i IncludePath add IncludePath to list of search paths", + " -newdb to not read in existing database file", + " -uqs to indicate that unquoted strings are used", + " FileNames name of one or more unicode files to parse", + " Scan options include:", + " -scan scan text file(s) for STRING_TOKEN() usage", + " -skipext .ext to skip scan of files with .ext filename extension", + " -ignorenotfound ignore if a given STRING_TOKEN(STR) is not ", + " found in the database", + " FileNames one or more files to scan", + " Dump options include:", + " -oc FileName write string data to FileName", + " -oh FileName write string defines to FileName", + " -ou FileName dump database to unicode file FileName", + " -lang Lang only dump for the language 'Lang'", + " -if FileName to specify an indirection file", + " -hpk FileName to create an HII export pack of the strings", + "", + " The expected process is to parse a unicode string file to create an initial", + " database of string identifier names and string definitions. Then text files", + " should be scanned for STRING_TOKEN() usages, and the referenced", + " strings will be tagged as used in the database. After all files have been", + " scanned, then the database should be dumped to create the necessary output", + " files.", + "", + NULL + }; + for (Index = 0; Str[Index] != NULL; Index++) { + fprintf (stdout, "%s\n", Str[Index]); + } +} diff --git a/Tools/CCode/Source/StrGather/StrGather.h b/Tools/CCode/Source/StrGather/StrGather.h new file mode 100644 index 0000000000..65dc15cbe8 --- /dev/null +++ b/Tools/CCode/Source/StrGather/StrGather.h @@ -0,0 +1,84 @@ +/*++ + +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: + + StrGather.h + +Abstract: + + Common defines and prototypes for StrGather. + +--*/ + +#ifndef _STR_GATHER_H_ +#define _STR_GATHER_H_ + +#define MALLOC(size) malloc (size) +#define FREE(ptr) free (ptr) + +#define PROGRAM_NAME "StrGather" + +typedef CHAR16 WCHAR; + +#define UNICODE_TO_ASCII(w) (INT8) ((w) & 0xFF) +#define ASCII_TO_UNICODE(a) (WCHAR) ((UINT8) (a)) + +#define UNICODE_HASH L'#' +#define UNICODE_BACKSLASH L'\\' +#define UNICODE_SLASH L'/' +#define UNICODE_EQUAL_SIGN L'=' +#define UNICODE_PLUS_SIGN L'+' + +#define UNICODE_FILE_START 0xFEFF +#define UNICODE_CR 0x000D +#define UNICODE_LF 0x000A +#define UNICODE_NULL 0x0000 +#define UNICODE_SPACE L' ' +#define UNICODE_SLASH L'/' +#define UNICODE_DOUBLE_QUOTE L'"' +#define UNICODE_Z L'Z' +#define UNICODE_z L'z' +#define UNICODE_A L'A' +#define UNICODE_a L'a' +#define UNICODE_F L'F' +#define UNICODE_f L'f' +#define UNICODE_UNDERSCORE L'_' +#define UNICODE_0 L'0' +#define UNICODE_9 L'9' +#define UNICODE_TAB L'\t' +#define UNICODE_NBR_STRING L"\\nbr" +#define UNICODE_BR_STRING L"\\br" +#define UNICODE_WIDE_STRING L"\\wide" +#define UNICODE_NARROW_STRING L"\\narrow" + +// +// This is the length of a valid string identifier +// +#define LANGUAGE_IDENTIFIER_NAME_LEN 3 + +typedef struct _TEXT_STRING_LIST { + struct _TEXT_STRING_LIST *Next; + CHAR8 *Str; +} TEXT_STRING_LIST; + +typedef struct _WCHAR_STRING_LIST { + struct _WCHAR_STRING_LIST *Next; + WCHAR *Str; +} WCHAR_STRING_LIST; + +typedef struct _WCHAR_MATCHING_STRING_LIST { + struct _WCHAR_MATCHING_STRING_LIST *Next; + WCHAR *Str1; + WCHAR *Str2; +} WCHAR_MATCHING_STRING_LIST; + +#endif // #ifndef _STR_GATHER_H_ diff --git a/Tools/CCode/Source/StrGather/StringDB.c b/Tools/CCode/Source/StrGather/StringDB.c new file mode 100644 index 0000000000..16ef0526d7 --- /dev/null +++ b/Tools/CCode/Source/StrGather/StringDB.c @@ -0,0 +1,2759 @@ +/*++ + +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 +#include +#include +#include // for tolower() + +#include +#include +#include +#include // for EFI_UGA_PIXEL definition +#include + +#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; +} diff --git a/Tools/CCode/Source/StrGather/StringDB.h b/Tools/CCode/Source/StrGather/StringDB.h new file mode 100644 index 0000000000..c52573151f --- /dev/null +++ b/Tools/CCode/Source/StrGather/StringDB.h @@ -0,0 +1,136 @@ +/*++ + +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.h + +Abstract: + + Common defines and prototypes for string database management + +--*/ + +#ifndef _STRING_DB_H_ +#define _STRING_DB_H_ + +#define LANGUAGE_NAME_STRING_NAME L"$LANGUAGE_NAME" +#define PRINTABLE_LANGUAGE_NAME_STRING_NAME L"$PRINTABLE_LANGUAGE_NAME" + +void +StringDBConstructor ( + void + ) +; +void +StringDBDestructor ( + void + ) +; + +STATUS +StringDBAddString ( + WCHAR *LanguageName, + WCHAR *StringIdentifier, + WCHAR *Scope, + WCHAR *String, + BOOLEAN Format, + UINT16 Flags + ) +; + +STATUS +StringDBSetScope ( + WCHAR *Scope + ) +; + +#define STRING_FLAGS_REFERENCED 0x0001 // if referenced somewhere +#define STRING_FLAGS_UNDEFINED 0x0002 // if we added it for padding purposes +#define STRING_FLAGS_INDEX_ASSIGNED 0x0004 // so don't change the index value +#define STRING_ID_INVALID 0xFFFF +#define STRING_ID_LANGUAGE_NAME 0x0000 +#define STRING_ID_PRINTABLE_LANGUAGE_NAME 0x0001 + +STATUS +StringDBAddStringIdentifier ( + WCHAR *StringIdentifier, + UINT16 *NewId, + UINT16 Flags + ) +; + +STATUS +StringDBReadDatabase ( + CHAR8 *DBFileName, + BOOLEAN IgnoreIfNotExist, + BOOLEAN Verbose + ) +; + +STATUS +StringDBWriteDatabase ( + CHAR8 *DBFileName, + BOOLEAN Verbose + ) +; + +STATUS +StringDBDumpDatabase ( + CHAR8 *DBFileName, + CHAR8 *OutputFileName, + BOOLEAN Verbose + ) +; + +STATUS +StringDBAddLanguage ( + WCHAR *LanguageName, + WCHAR *PrintableLanguageName + ) +; + +STATUS +StringDBDumpCStrings ( + CHAR8 *FileName, + CHAR8 *BaseName, + WCHAR_STRING_LIST *LanguagesOfInterest, + WCHAR_MATCHING_STRING_LIST *IndirectionList + ) +; + +STATUS +StringDBDumpStringDefines ( + CHAR8 *FileName, + CHAR8 *BaseName + ) +; + +STATUS +StringDBSetCurrentLanguage ( + WCHAR *LanguageName + ) +; + +STATUS +StringDBSetStringReferenced ( + CHAR8 *StringIdentifierName, + BOOLEAN IgnoreNotFound + ) +; + +void +StringDBFormatString ( + WCHAR *String + ) +; + +#endif // #ifndef _STRING_DB_H_ diff --git a/Tools/CCode/Source/StrGather/build.xml b/Tools/CCode/Source/StrGather/build.xml new file mode 100644 index 0000000000..f01117777a --- /dev/null +++ b/Tools/CCode/Source/StrGather/build.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.3