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