]> git.proxmox.com Git - mirror_edk2.git/blobdiff - EdkCompatibilityPkg/Sample/Tools/Source/StrGather/StrGather.c
Add in the 1st version of ECP.
[mirror_edk2.git] / EdkCompatibilityPkg / Sample / Tools / Source / StrGather / StrGather.c
diff --git a/EdkCompatibilityPkg/Sample/Tools/Source/StrGather/StrGather.c b/EdkCompatibilityPkg/Sample/Tools/Source/StrGather/StrGather.c
new file mode 100644 (file)
index 0000000..107a463
--- /dev/null
@@ -0,0 +1,2574 @@
+/*++\r
+\r
+Copyright (c) 2004 - 2007, Intel Corporation                                                         \r
+All rights reserved. This program and the accompanying materials                          \r
+are licensed and made available under the terms and conditions of the BSD License         \r
+which accompanies this distribution.  The full text of the license may be found at        \r
+http://opensource.org/licenses/bsd-license.php                                            \r
+                                                                                          \r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     \r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             \r
+\r
+Module Name:\r
+\r
+  StrGather.c  \r
+\r
+Abstract:\r
+\r
+  Parse a strings file and create or add to a string database file.\r
+\r
+--*/\r
+\r
+#include <stdio.h>\r
+#include <string.h>\r
+#include <stdlib.h>\r
+#include <ctype.h>\r
+\r
+#include "Tiano.h"\r
+#include "EfiUtilityMsgs.h"\r
+#include "StrGather.h"\r
+#include "StringDB.h"\r
+\r
+#define TOOL_VERSION  "0.31"\r
+\r
+typedef UINT16  WCHAR;\r
+\r
+#define MAX_PATH                    1024\r
+#define MAX_NEST_DEPTH              20  // just in case we get in an endless loop.\r
+#define MAX_STRING_IDENTIFIER_NAME  100 // number of wchars\r
+#define MAX_LINE_LEN                400\r
+#define STRING_TOKEN                "STRING_TOKEN"\r
+#define DEFAULT_BASE_NAME           "BaseName"\r
+//\r
+// Operational modes for this utility\r
+//\r
+#define MODE_UNKNOWN  0\r
+#define MODE_PARSE    1\r
+#define MODE_SCAN     2\r
+#define MODE_DUMP     3\r
+\r
+//\r
+// We keep a linked list of these for the source files we process\r
+//\r
+typedef struct _SOURCE_FILE {\r
+  FILE                *Fptr;\r
+  WCHAR               *FileBuffer;\r
+  WCHAR               *FileBufferPtr;\r
+  UINT32              FileSize;\r
+  INT8                FileName[MAX_PATH];\r
+  UINT32              LineNum;\r
+  BOOLEAN             EndOfFile;\r
+  BOOLEAN             SkipToHash;\r
+  struct _SOURCE_FILE *Previous;\r
+  struct _SOURCE_FILE *Next;\r
+  WCHAR               ControlCharacter;\r
+} SOURCE_FILE;\r
+\r
+#define DEFAULT_CONTROL_CHARACTER UNICODE_SLASH\r
+\r
+//\r
+// Here's all our globals. We need a linked list of include paths, a linked\r
+// list of source files, a linked list of subdirectories (appended to each\r
+// include path when searching), and a couple other fields.\r
+//\r
+static struct {\r
+  SOURCE_FILE                 SourceFiles;\r
+  TEXT_STRING_LIST            *IncludePaths;                    // all include paths to search\r
+  TEXT_STRING_LIST            *LastIncludePath;\r
+  TEXT_STRING_LIST            *ScanFileName;\r
+  TEXT_STRING_LIST            *LastScanFileName;\r
+  TEXT_STRING_LIST            *SkipExt;                         // if -skipext .uni\r
+  TEXT_STRING_LIST            *LastSkipExt;\r
+  TEXT_STRING_LIST            *IndirectionFileName;\r
+  TEXT_STRING_LIST            *LastIndirectionFileName;\r
+  TEXT_STRING_LIST            *DatabaseFileName;\r
+  TEXT_STRING_LIST            *LastDatabaseFileName;\r
+  WCHAR_STRING_LIST           *Language;\r
+  WCHAR_STRING_LIST           *LastLanguage;\r
+  WCHAR_MATCHING_STRING_LIST  *IndirectionList;                 // from indirection file(s)\r
+  WCHAR_MATCHING_STRING_LIST  *LastIndirectionList;\r
+  BOOLEAN                     Verbose;                          // for more detailed output\r
+  BOOLEAN                     VerboseDatabaseWrite;             // for more detailed output when writing database\r
+  BOOLEAN                     VerboseDatabaseRead;              // for more detailed output when reading database\r
+  BOOLEAN                     NewDatabase;                      // to start from scratch\r
+  BOOLEAN                     IgnoreNotFound;                   // when scanning\r
+  BOOLEAN                     VerboseScan;\r
+  BOOLEAN                     UnquotedStrings;                  // -uqs option\r
+  INT8                        OutputDatabaseFileName[MAX_PATH];\r
+  INT8                        StringHFileName[MAX_PATH];\r
+  INT8                        StringCFileName[MAX_PATH];        // output .C filename\r
+  INT8                        DumpUFileName[MAX_PATH];          // output unicode dump file name\r
+  INT8                        HiiExportPackFileName[MAX_PATH];  // HII export pack file name\r
+  INT8                        BaseName[MAX_PATH];               // base filename of the strings file\r
+  INT8                        OutputDependencyFileName[MAX_PATH];\r
+  FILE                        *OutputDependencyFptr;\r
+  UINT32                      Mode;\r
+} mGlobals;\r
+\r
+static\r
+BOOLEAN\r
+IsValidIdentifierChar (\r
+  INT8      Char,\r
+  BOOLEAN   FirstChar\r
+  );\r
+\r
+static\r
+void\r
+RewindFile (\r
+  SOURCE_FILE *SourceFile\r
+  );\r
+\r
+static\r
+BOOLEAN\r
+SkipTo (\r
+  SOURCE_FILE *SourceFile,\r
+  WCHAR       WChar,\r
+  BOOLEAN     StopAfterNewline\r
+  );\r
+\r
+static\r
+UINT32\r
+SkipWhiteSpace (\r
+  SOURCE_FILE *SourceFile\r
+  );\r
+\r
+static\r
+BOOLEAN\r
+IsWhiteSpace (\r
+  SOURCE_FILE *SourceFile\r
+  );\r
+\r
+static\r
+BOOLEAN\r
+EndOfFile (\r
+  SOURCE_FILE *SourceFile\r
+  );\r
+\r
+static\r
+void\r
+PreprocessFile (\r
+  SOURCE_FILE *SourceFile\r
+  );\r
+\r
+static\r
+UINT32\r
+GetStringIdentifierName (\r
+  IN SOURCE_FILE  *SourceFile,\r
+  IN OUT WCHAR    *StringIdentifierName,\r
+  IN UINT32       StringIdentifierNameLen\r
+  );\r
+\r
+static\r
+UINT32\r
+GetLanguageIdentifierName (\r
+  IN SOURCE_FILE  *SourceFile,\r
+  IN OUT WCHAR    *LanguageIdentifierName,\r
+  IN UINT32       LanguageIdentifierNameLen,\r
+  IN BOOLEAN      Optional\r
+  );\r
+\r
+static\r
+WCHAR *\r
+GetPrintableLanguageName (\r
+  IN SOURCE_FILE  *SourceFile\r
+  );\r
+\r
+static\r
+STATUS\r
+AddCommandLineLanguage (\r
+  IN INT8          *Language\r
+  );\r
+\r
+static\r
+WCHAR *\r
+GetQuotedString (\r
+  SOURCE_FILE *SourceFile,\r
+  BOOLEAN     Optional\r
+  );\r
+\r
+static\r
+STATUS\r
+ProcessIncludeFile (\r
+  SOURCE_FILE *SourceFile,\r
+  SOURCE_FILE *ParentSourceFile\r
+  );\r
+\r
+static\r
+STATUS\r
+ParseFile (\r
+  SOURCE_FILE *SourceFile\r
+  );\r
+\r
+static\r
+FILE  *\r
+FindFile (\r
+  IN INT8     *FileName,\r
+  OUT INT8    *FoundFileName,\r
+  IN UINT32   FoundFileNameLen\r
+  );\r
+\r
+static\r
+STATUS\r
+ProcessArgs (\r
+  int   Argc,\r
+  char  *Argv[]\r
+  );\r
+\r
+static\r
+STATUS\r
+ProcessFile (\r
+  SOURCE_FILE *SourceFile\r
+  );\r
+\r
+static\r
+UINT32\r
+wstrcmp (\r
+  WCHAR *Buffer,\r
+  WCHAR *Str\r
+  );\r
+\r
+static\r
+void\r
+Usage (\r
+  VOID\r
+  );\r
+\r
+static\r
+void\r
+FreeLists (\r
+  VOID\r
+  );\r
+\r
+static\r
+void\r
+ProcessTokenString (\r
+  SOURCE_FILE *SourceFile\r
+  );\r
+\r
+static\r
+void\r
+ProcessTokenInclude (\r
+  SOURCE_FILE *SourceFile\r
+  );\r
+\r
+static\r
+void\r
+ProcessTokenScope (\r
+  SOURCE_FILE *SourceFile\r
+  );\r
+\r
+static\r
+void\r
+ProcessTokenLanguage (\r
+  SOURCE_FILE *SourceFile\r
+  );\r
+\r
+static\r
+void\r
+ProcessTokenLangDef (\r
+  SOURCE_FILE *SourceFile\r
+  );\r
+\r
+static\r
+STATUS\r
+ScanFiles (\r
+  TEXT_STRING_LIST *ScanFiles\r
+  );\r
+\r
+static\r
+STATUS\r
+ParseIndirectionFiles (\r
+  TEXT_STRING_LIST    *Files\r
+  );\r
+\r
+STATUS\r
+StringDBCreateHiiExportPack (\r
+  INT8                *OutputFileName\r
+  );\r
+\r
+int\r
+main (\r
+  int   Argc,\r
+  char  *Argv[]\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+  Call the routine to parse the command-line options, then process the file.\r
+  \r
+Arguments:\r
+\r
+  Argc - Standard C main() argc and argv.\r
+  Argv - Standard C main() argc and argv.\r
+\r
+Returns:\r
+\r
+  0       if successful\r
+  nonzero otherwise\r
+  \r
+--*/\r
+{\r
+  STATUS  Status;\r
+\r
+  SetUtilityName (PROGRAM_NAME);\r
+  //\r
+  // Process the command-line arguments\r
+  //\r
+  Status = ProcessArgs (Argc, Argv);\r
+  if (Status != STATUS_SUCCESS) {\r
+    return Status;\r
+  }\r
+  //\r
+  // Initialize the database manager\r
+  //\r
+  StringDBConstructor ();\r
+  //\r
+  // We always try to read in an existing database file. It may not\r
+  // exist, which is ok usually.\r
+  //\r
+  if (mGlobals.NewDatabase == 0) {\r
+    //\r
+    // Read all databases specified.\r
+    //\r
+    for (mGlobals.LastDatabaseFileName = mGlobals.DatabaseFileName;\r
+         mGlobals.LastDatabaseFileName != NULL;\r
+         mGlobals.LastDatabaseFileName = mGlobals.LastDatabaseFileName->Next\r
+        ) {\r
+      Status = StringDBReadDatabase (mGlobals.LastDatabaseFileName->Str, TRUE, mGlobals.VerboseDatabaseRead);\r
+      if (Status != STATUS_SUCCESS) {\r
+        return Status;\r
+      }\r
+    }\r
+  }\r
+  //\r
+  // Read indirection file(s) if specified\r
+  //\r
+  if (ParseIndirectionFiles (mGlobals.IndirectionFileName) != STATUS_SUCCESS) {\r
+    goto Finish;\r
+  }\r
+  //\r
+  // If scanning source files, do that now\r
+  //\r
+  if (mGlobals.Mode == MODE_SCAN) {\r
+    ScanFiles (mGlobals.ScanFileName);\r
+  } else if (mGlobals.Mode == MODE_PARSE) {\r
+    //\r
+    // Parsing a unicode strings file\r
+    //\r
+    mGlobals.SourceFiles.ControlCharacter = DEFAULT_CONTROL_CHARACTER;\r
+    if (mGlobals.OutputDependencyFileName[0] != 0) {\r
+      if ((mGlobals.OutputDependencyFptr = fopen (mGlobals.OutputDependencyFileName, "w")) == NULL) {\r
+        Error (NULL, 0, 0, mGlobals.OutputDependencyFileName, "failed to open output dependency file");\r
+        goto Finish;\r
+      }    \r
+    }\r
+    Status = ProcessIncludeFile (&mGlobals.SourceFiles, NULL);\r
+    if (mGlobals.OutputDependencyFptr != NULL) {\r
+      fclose (mGlobals.OutputDependencyFptr);\r
+    }    \r
+    if (Status != STATUS_SUCCESS) {\r
+      goto Finish;\r
+    }\r
+  }\r
+  //\r
+  // Create the string defines header file if there have been no errors.\r
+  //\r
+  ParserSetPosition (NULL, 0);\r
+  if ((mGlobals.StringHFileName[0] != 0) && (GetUtilityStatus () < STATUS_ERROR)) {\r
+    Status = StringDBDumpStringDefines (mGlobals.StringHFileName, mGlobals.BaseName);\r
+    if (Status != EFI_SUCCESS) {\r
+      goto Finish;\r
+    }\r
+  }\r
+  //\r
+  // Dump the strings to a .c file if there have still been no errors.\r
+  //\r
+  if ((mGlobals.StringCFileName[0] != 0) && (GetUtilityStatus () < STATUS_ERROR)) {\r
+    Status = StringDBDumpCStrings (\r
+              mGlobals.StringCFileName,\r
+              mGlobals.BaseName,\r
+              mGlobals.Language,\r
+              mGlobals.IndirectionList\r
+              );\r
+    if (Status != EFI_SUCCESS) {\r
+      goto Finish;\r
+    }\r
+  }\r
+  //\r
+  // Dump the database if requested\r
+  //\r
+  if ((mGlobals.DumpUFileName[0] != 0) && (GetUtilityStatus () < STATUS_ERROR)) {\r
+    StringDBDumpDatabase (NULL, mGlobals.DumpUFileName, FALSE);\r
+  }\r
+  //\r
+  // Dump the string data as HII binary string pack if requested\r
+  //\r
+  if ((mGlobals.HiiExportPackFileName[0] != 0) && (GetUtilityStatus () < STATUS_ERROR)) {\r
+    StringDBCreateHiiExportPack (mGlobals.HiiExportPackFileName);\r
+  }\r
+  //\r
+  // Always update the database if no errors and not in dump mode. If they specified -od\r
+  // for an output database file name, then use that name. Otherwise use the name of\r
+  // the first database file specified with -db\r
+  //\r
+  if ((mGlobals.Mode != MODE_DUMP) && (GetUtilityStatus () < STATUS_ERROR)) {\r
+    if (mGlobals.OutputDatabaseFileName[0]) {\r
+      Status = StringDBWriteDatabase (mGlobals.OutputDatabaseFileName, mGlobals.VerboseDatabaseWrite);\r
+    } else {\r
+      Status = StringDBWriteDatabase (mGlobals.DatabaseFileName->Str, mGlobals.VerboseDatabaseWrite);\r
+    }\r
+\r
+    if (Status != EFI_SUCCESS) {\r
+      goto Finish;\r
+    }\r
+  }\r
+\r
+Finish:\r
+  //\r
+  // Free up memory\r
+  //\r
+  FreeLists ();\r
+  StringDBDestructor ();\r
+  return GetUtilityStatus ();\r
+}\r
+\r
+static\r
+STATUS\r
+ProcessIncludeFile (\r
+  SOURCE_FILE *SourceFile,\r
+  SOURCE_FILE *ParentSourceFile\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+  Given a source file, open the file and parse it\r
+  \r
+Arguments:\r
+\r
+  SourceFile        - name of file to parse\r
+  ParentSourceFile  - for error reporting purposes, the file that #included SourceFile.\r
+\r
+Returns:\r
+\r
+  Standard status.\r
+  \r
+--*/\r
+{\r
+  static UINT32 NestDepth = 0;\r
+  INT8          FoundFileName[MAX_PATH];\r
+  STATUS        Status;\r
+\r
+  Status = STATUS_SUCCESS;\r
+  NestDepth++;\r
+  //\r
+  // Print the file being processed. Indent so you can tell the include nesting\r
+  // depth.\r
+  //\r
+  if (mGlobals.Verbose) {\r
+    fprintf (stdout, "%*cProcessing file '%s'\n", NestDepth * 2, ' ', SourceFile->FileName);\r
+  }\r
+\r
+  //\r
+  // Make sure we didn't exceed our maximum nesting depth\r
+  //\r
+  if (NestDepth > MAX_NEST_DEPTH) {\r
+    Error (NULL, 0, 0, SourceFile->FileName, "max nesting depth (%d) exceeded", NestDepth);\r
+    Status = STATUS_ERROR;\r
+    goto Finish;\r
+  }\r
+  //\r
+  // Try to open the file locally, and if that fails try along our include paths.\r
+  //\r
+  strcpy (FoundFileName, SourceFile->FileName);\r
+  if ((SourceFile->Fptr = fopen (FoundFileName, "rb")) == NULL) {\r
+    //\r
+    // Try to find it among the paths if it has a parent (that is, it is included\r
+    // by someone else).\r
+    //\r
+    if (ParentSourceFile == NULL) {\r
+      Error (NULL, 0, 0, SourceFile->FileName, "file not found");\r
+      Status = STATUS_ERROR;\r
+      goto Finish;\r
+    }\r
+\r
+    SourceFile->Fptr = FindFile (SourceFile->FileName, FoundFileName, sizeof (FoundFileName));\r
+    if (SourceFile->Fptr == NULL) {\r
+      Error (ParentSourceFile->FileName, ParentSourceFile->LineNum, 0, SourceFile->FileName, "include file not found");\r
+      Status = STATUS_ERROR;\r
+      goto Finish;\r
+    }\r
+  }\r
+  \r
+  //\r
+  // Output the dependency \r
+  //\r
+  if (mGlobals.OutputDependencyFptr != NULL) {\r
+    fprintf (mGlobals.OutputDependencyFptr, "%s : %s\n", mGlobals.DatabaseFileName->Str, FoundFileName);    \r
+    //\r
+    // Add pseudo target to avoid incremental build failure when the file is deleted\r
+    //\r
+    fprintf (mGlobals.OutputDependencyFptr, "%s : \n", FoundFileName); \r
+  }\r
+   \r
+  //\r
+  // Process the file found\r
+  //\r
+  ProcessFile (SourceFile);\r
+\r
+Finish:\r
+  NestDepth--;\r
+  //\r
+  // Close open files and return status\r
+  //\r
+  if (SourceFile->Fptr != NULL) {\r
+    fclose (SourceFile->Fptr);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+static\r
+STATUS\r
+ProcessFile (\r
+  SOURCE_FILE *SourceFile\r
+  )\r
+{\r
+  //\r
+  // Get the file size, and then read the entire thing into memory.\r
+  // Allocate space for a terminator character.\r
+  //\r
+  fseek (SourceFile->Fptr, 0, SEEK_END);\r
+  SourceFile->FileSize = ftell (SourceFile->Fptr);\r
+  fseek (SourceFile->Fptr, 0, SEEK_SET);\r
+  SourceFile->FileBuffer = (WCHAR *) malloc (SourceFile->FileSize + sizeof (WCHAR));\r
+  if (SourceFile->FileBuffer == NULL) {\r
+    Error (NULL, 0, 0, "memory allocation failure", NULL);\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  fread ((VOID *) SourceFile->FileBuffer, SourceFile->FileSize, 1, SourceFile->Fptr);\r
+  SourceFile->FileBuffer[(SourceFile->FileSize / sizeof (WCHAR))] = UNICODE_NULL;\r
+  //\r
+  // Pre-process the file to replace comments with spaces\r
+  //\r
+  PreprocessFile (SourceFile);\r
+  //\r
+  // Parse the file\r
+  //\r
+  ParseFile (SourceFile);\r
+  free (SourceFile->FileBuffer);\r
+  return STATUS_SUCCESS;\r
+}\r
+\r
+static\r
+STATUS\r
+ParseFile (\r
+  SOURCE_FILE *SourceFile\r
+  )\r
+{\r
+  BOOLEAN InComment;\r
+  UINT32  Len;\r
+\r
+  //\r
+  // First character of a unicode file is special. Make sure\r
+  //\r
+  if (SourceFile->FileBufferPtr[0] != UNICODE_FILE_START) {\r
+    Error (SourceFile->FileName, 1, 0, SourceFile->FileName, "file does not appear to be a unicode file");\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  SourceFile->FileBufferPtr++;\r
+  InComment = FALSE;\r
+  //\r
+  // Print the first line if in verbose mode\r
+  //\r
+  if (mGlobals.Verbose) {\r
+    printf ("%d: %S\n", SourceFile->LineNum, SourceFile->FileBufferPtr);\r
+  }\r
+  //\r
+  // Since the syntax is relatively straightforward, just switch on the next char\r
+  //\r
+  while (!EndOfFile (SourceFile)) {\r
+    //\r
+    // Check for whitespace\r
+    //\r
+    if (SourceFile->FileBufferPtr[0] == UNICODE_SPACE) {\r
+      SourceFile->FileBufferPtr++;\r
+    } else if (SourceFile->FileBufferPtr[0] == UNICODE_TAB) {\r
+      SourceFile->FileBufferPtr++;\r
+    } else if (SourceFile->FileBufferPtr[0] == UNICODE_CR) {\r
+      SourceFile->FileBufferPtr++;\r
+    } else if (SourceFile->FileBufferPtr[0] == UNICODE_LF) {\r
+      SourceFile->FileBufferPtr++;\r
+      SourceFile->LineNum++;\r
+      if (mGlobals.Verbose) {\r
+        printf ("%d: %S\n", SourceFile->LineNum, SourceFile->FileBufferPtr);\r
+      }\r
+\r
+      InComment = FALSE;\r
+    } else if (SourceFile->FileBufferPtr[0] == 0) {\r
+      SourceFile->FileBufferPtr++;\r
+    } else if (InComment) {\r
+      SourceFile->FileBufferPtr++;\r
+    } else if ((SourceFile->FileBufferPtr[0] == UNICODE_SLASH) && (SourceFile->FileBufferPtr[1] == UNICODE_SLASH)) {\r
+      SourceFile->FileBufferPtr += 2;\r
+      InComment = TRUE;\r
+    } else if (SourceFile->SkipToHash && (SourceFile->FileBufferPtr[0] != SourceFile->ControlCharacter)) {\r
+      SourceFile->FileBufferPtr++;\r
+    } else {\r
+      SourceFile->SkipToHash = FALSE;\r
+      if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) &&\r
+          ((Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"include")) > 0)\r
+          ) {\r
+        SourceFile->FileBufferPtr += Len + 1;\r
+        ProcessTokenInclude (SourceFile);\r
+      } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) &&\r
+               (Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"scope")) > 0\r
+              ) {\r
+        SourceFile->FileBufferPtr += Len + 1;\r
+        ProcessTokenScope (SourceFile);\r
+      } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) &&\r
+               (Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"language")) > 0\r
+              ) {\r
+        SourceFile->FileBufferPtr += Len + 1;\r
+        ProcessTokenLanguage (SourceFile);\r
+      } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) &&\r
+               (Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"langdef")) > 0\r
+              ) {\r
+        SourceFile->FileBufferPtr += Len + 1;\r
+        ProcessTokenLangDef (SourceFile);\r
+      } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) &&\r
+               (Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"string")) > 0\r
+              ) {\r
+        SourceFile->FileBufferPtr += Len + 1;\r
+        ProcessTokenString (SourceFile);\r
+      } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) &&\r
+               (Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"EFI_BREAKPOINT()")) > 0\r
+              ) {\r
+        SourceFile->FileBufferPtr += Len;\r
+        EFI_BREAKPOINT ();\r
+      } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) &&\r
+               (SourceFile->FileBufferPtr[1] == UNICODE_EQUAL_SIGN)\r
+              ) {\r
+        SourceFile->ControlCharacter = SourceFile->FileBufferPtr[2];\r
+        SourceFile->FileBufferPtr += 3;\r
+      } else {\r
+        Error (SourceFile->FileName, SourceFile->LineNum, 0, "unrecognized token", "%S", SourceFile->FileBufferPtr);\r
+        //\r
+        // Treat rest of line as a comment.\r
+        //\r
+        InComment = TRUE;\r
+      }\r
+    }\r
+  }\r
+\r
+  return STATUS_SUCCESS;\r
+}\r
+\r
+static\r
+void\r
+PreprocessFile (\r
+  SOURCE_FILE *SourceFile\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+  Preprocess a file to replace all carriage returns with NULLs so\r
+  we can print lines from the file to the screen.\r
+  \r
+Arguments:\r
+  SourceFile - structure that we use to keep track of an input file.\r
+\r
+Returns:\r
+  Nothing.\r
+  \r
+--*/\r
+{\r
+  BOOLEAN InComment;\r
+\r
+  RewindFile (SourceFile);\r
+  InComment = FALSE;\r
+  while (!EndOfFile (SourceFile)) {\r
+    //\r
+    // If a line-feed, then no longer in a comment\r
+    //\r
+    if (SourceFile->FileBufferPtr[0] == UNICODE_LF) {\r
+      SourceFile->FileBufferPtr++;\r
+      SourceFile->LineNum++;\r
+      InComment = 0;\r
+    } else if (SourceFile->FileBufferPtr[0] == UNICODE_CR) {\r
+      //\r
+      // Replace all carriage returns with a NULL so we can print stuff\r
+      //\r
+      SourceFile->FileBufferPtr[0] = 0;\r
+      SourceFile->FileBufferPtr++;\r
+    } else if (InComment) {\r
+      SourceFile->FileBufferPtr[0] = UNICODE_SPACE;\r
+      SourceFile->FileBufferPtr++;\r
+    } else if ((SourceFile->FileBufferPtr[0] == UNICODE_SLASH) && (SourceFile->FileBufferPtr[1] == UNICODE_SLASH)) {\r
+      SourceFile->FileBufferPtr += 2;\r
+      InComment = TRUE;\r
+    } else {\r
+      SourceFile->FileBufferPtr++;\r
+    }\r
+  }\r
+  //\r
+  // Could check for end-of-file and still in a comment, but\r
+  // should not be necessary. So just restore the file pointers.\r
+  //\r
+  RewindFile (SourceFile);\r
+}\r
+\r
+static\r
+WCHAR *\r
+GetPrintableLanguageName (\r
+  IN SOURCE_FILE  *SourceFile\r
+  )\r
+{\r
+  WCHAR   *String;\r
+  WCHAR   *Start;\r
+  WCHAR   *Ptr;\r
+  UINT32  Len;\r
+\r
+  SkipWhiteSpace (SourceFile);\r
+  if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) {\r
+    Error (\r
+      SourceFile->FileName,\r
+      SourceFile->LineNum,\r
+      0,\r
+      "expected quoted printable language name",\r
+      "%S",\r
+      SourceFile->FileBufferPtr\r
+      );\r
+    SourceFile->SkipToHash = TRUE;\r
+    return NULL;\r
+  }\r
+\r
+  Len = 0;\r
+  SourceFile->FileBufferPtr++;\r
+  Start = Ptr = SourceFile->FileBufferPtr;\r
+  while (!EndOfFile (SourceFile)) {\r
+    if (SourceFile->FileBufferPtr[0] == UNICODE_CR) {\r
+      Warning (SourceFile->FileName, SourceFile->LineNum, 0, "carriage return found in quoted string", "%S", Start);\r
+      break;\r
+    } else if (SourceFile->FileBufferPtr[0] == UNICODE_DOUBLE_QUOTE) {\r
+      break;\r
+    }\r
+\r
+    SourceFile->FileBufferPtr++;\r
+    Len++;\r
+  }\r
+\r
+  if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) {\r
+    Warning (\r
+      SourceFile->FileName,\r
+      SourceFile->LineNum,\r
+      0,\r
+      "missing closing quote on printable language name string",\r
+      "%S",\r
+      Start\r
+      );\r
+  } else {\r
+    SourceFile->FileBufferPtr++;\r
+  }\r
+  //\r
+  // Now allocate memory for the string and save it off\r
+  //\r
+  String = (WCHAR *) malloc ((Len + 1) * sizeof (WCHAR));\r
+  if (String == NULL) {\r
+    Error (NULL, 0, 0, "memory allocation failed", NULL);\r
+    return NULL;\r
+  }\r
+  //\r
+  // Copy the string from the file buffer to the local copy.\r
+  // We do no reformatting of it whatsoever at this point.\r
+  //\r
+  Ptr = String;\r
+  while (Len > 0) {\r
+    *Ptr = *Start;\r
+    Start++;\r
+    Ptr++;\r
+    Len--;\r
+  }\r
+\r
+  *Ptr = 0;\r
+  //\r
+  // Now format the string to convert \wide and \narrow controls\r
+  //\r
+  StringDBFormatString (String);\r
+  return String;\r
+}\r
+\r
+static\r
+WCHAR *\r
+GetQuotedString (\r
+  SOURCE_FILE *SourceFile,\r
+  BOOLEAN     Optional\r
+  )\r
+{\r
+  WCHAR   *String;\r
+  WCHAR   *Start;\r
+  WCHAR   *Ptr;\r
+  UINT32  Len;\r
+  BOOLEAN PreviousBackslash;\r
+\r
+  if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) {\r
+    if (!Optional) {\r
+      Error (SourceFile->FileName, SourceFile->LineNum, 0, "expected quoted string", "%S", SourceFile->FileBufferPtr);\r
+    }\r
+\r
+    return NULL;\r
+  }\r
+\r
+  Len = 0;\r
+  SourceFile->FileBufferPtr++;\r
+  Start             = Ptr = SourceFile->FileBufferPtr;\r
+  PreviousBackslash = FALSE;\r
+  while (!EndOfFile (SourceFile)) {\r
+    if ((SourceFile->FileBufferPtr[0] == UNICODE_DOUBLE_QUOTE) && (!PreviousBackslash)) {\r
+      break;\r
+    } else if (SourceFile->FileBufferPtr[0] == UNICODE_CR) {\r
+      Warning (SourceFile->FileName, SourceFile->LineNum, 0, "carriage return found in quoted string", "%S", Start);\r
+      PreviousBackslash = FALSE;\r
+    } else if (SourceFile->FileBufferPtr[0] == UNICODE_BACKSLASH) {\r
+      PreviousBackslash = TRUE;\r
+    } else {\r
+      PreviousBackslash = FALSE;\r
+    }\r
+\r
+    SourceFile->FileBufferPtr++;\r
+    Len++;\r
+  }\r
+\r
+  if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) {\r
+    Warning (SourceFile->FileName, SourceFile->LineNum, 0, "missing closing quote on string", "%S", Start);\r
+  } else {\r
+    SourceFile->FileBufferPtr++;\r
+  }\r
+  //\r
+  // Now allocate memory for the string and save it off\r
+  //\r
+  String = (WCHAR *) malloc ((Len + 1) * sizeof (WCHAR));\r
+  if (String == NULL) {\r
+    Error (NULL, 0, 0, "memory allocation failed", NULL);\r
+    return NULL;\r
+  }\r
+  //\r
+  // Copy the string from the file buffer to the local copy.\r
+  // We do no reformatting of it whatsoever at this point.\r
+  //\r
+  Ptr = String;\r
+  while (Len > 0) {\r
+    *Ptr = *Start;\r
+    Start++;\r
+    Ptr++;\r
+    Len--;\r
+  }\r
+\r
+  *Ptr = 0;\r
+  return String;\r
+}\r
+//\r
+// Parse:\r
+//    #string STR_ID_NAME\r
+//\r
+// All we can do is call the string database to add the string identifier. Unfortunately\r
+// he'll have to keep track of the last identifier we added.\r
+//\r
+static\r
+void\r
+ProcessTokenString (\r
+  SOURCE_FILE *SourceFile\r
+  )\r
+{\r
+  WCHAR   StringIdentifier[MAX_STRING_IDENTIFIER_NAME];\r
+  UINT16  StringId;\r
+  //\r
+  // Extract the string identifier name and add it to the database.\r
+  //\r
+  if (GetStringIdentifierName (SourceFile, StringIdentifier, sizeof (StringIdentifier)) > 0) {\r
+    StringId = STRING_ID_INVALID;\r
+    StringDBAddStringIdentifier (StringIdentifier, &StringId, 0);\r
+  } else {\r
+    //\r
+    // Error recovery -- skip to the next #\r
+    //\r
+    SourceFile->SkipToHash = TRUE;\r
+  }\r
+}\r
+\r
+static\r
+BOOLEAN\r
+EndOfFile (\r
+  SOURCE_FILE *SourceFile\r
+  )\r
+{\r
+  //\r
+  // The file buffer pointer will typically get updated before the End-of-file flag in the\r
+  // source file structure, so check it first.\r
+  //\r
+  if (SourceFile->FileBufferPtr >= SourceFile->FileBuffer + SourceFile->FileSize / sizeof (WCHAR)) {\r
+    SourceFile->EndOfFile = TRUE;\r
+    return TRUE;\r
+  }\r
+\r
+  if (SourceFile->EndOfFile) {\r
+    return TRUE;\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+static\r
+UINT32\r
+GetStringIdentifierName (\r
+  IN SOURCE_FILE  *SourceFile,\r
+  IN OUT WCHAR    *StringIdentifierName,\r
+  IN UINT32       StringIdentifierNameLen\r
+  )\r
+{\r
+  UINT32  Len;\r
+  WCHAR   *From;\r
+  WCHAR   *Start;\r
+\r
+  //\r
+  // Skip whitespace\r
+  //\r
+  SkipWhiteSpace (SourceFile);\r
+  if (SourceFile->EndOfFile) {\r
+    Error (SourceFile->FileName, SourceFile->LineNum, 0, "end-of-file encountered", "expected string identifier");\r
+    return 0;\r
+  }\r
+  //\r
+  // Verify first character of name is [A-Za-z]\r
+  //\r
+  Len = 0;\r
+  StringIdentifierNameLen /= 2;\r
+  From  = SourceFile->FileBufferPtr;\r
+  Start = SourceFile->FileBufferPtr;\r
+  if (((SourceFile->FileBufferPtr[0] >= UNICODE_A) && (SourceFile->FileBufferPtr[0] <= UNICODE_Z)) ||\r
+      ((SourceFile->FileBufferPtr[0] >= UNICODE_z) && (SourceFile->FileBufferPtr[0] <= UNICODE_z))\r
+      ) {\r
+    //\r
+    // Do nothing\r
+    //\r
+  } else {\r
+    Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid character in string identifier name", "%S", Start);\r
+    return 0;\r
+  }\r
+\r
+  while (!EndOfFile (SourceFile)) {\r
+    if (((SourceFile->FileBufferPtr[0] >= UNICODE_A) && (SourceFile->FileBufferPtr[0] <= UNICODE_Z)) ||\r
+        ((SourceFile->FileBufferPtr[0] >= UNICODE_z) && (SourceFile->FileBufferPtr[0] <= UNICODE_z)) ||\r
+        ((SourceFile->FileBufferPtr[0] >= UNICODE_0) && (SourceFile->FileBufferPtr[0] <= UNICODE_9)) ||\r
+        (SourceFile->FileBufferPtr[0] == UNICODE_UNDERSCORE)\r
+        ) {\r
+      Len++;\r
+      if (Len >= StringIdentifierNameLen) {\r
+        Error (SourceFile->FileName, SourceFile->LineNum, 0, "string identifier name too long", "%S", Start);\r
+        return 0;\r
+      }\r
+\r
+      *StringIdentifierName = SourceFile->FileBufferPtr[0];\r
+      StringIdentifierName++;\r
+      SourceFile->FileBufferPtr++;\r
+    } else if (SkipWhiteSpace (SourceFile) == 0) {\r
+      Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid string identifier name", "%S", Start);\r
+      return 0;\r
+    } else {\r
+      break;\r
+    }\r
+  }\r
+  //\r
+  // Terminate the copy of the string.\r
+  //\r
+  *StringIdentifierName = 0;\r
+  return Len;\r
+}\r
+\r
+static\r
+UINT32\r
+GetLanguageIdentifierName (\r
+  IN SOURCE_FILE  *SourceFile,\r
+  IN OUT WCHAR    *LanguageIdentifierName,\r
+  IN UINT32       LanguageIdentifierNameLen,\r
+  IN BOOLEAN      Optional\r
+  )\r
+{\r
+  UINT32  Len;\r
+  WCHAR   *From;\r
+  WCHAR   *Start;\r
+  //\r
+  // Skip whitespace\r
+  //\r
+  SkipWhiteSpace (SourceFile);\r
+  if (SourceFile->EndOfFile) {\r
+    if (!Optional) {\r
+      Error (\r
+        SourceFile->FileName,\r
+        SourceFile->LineNum,\r
+        0,\r
+        "end-of-file encountered",\r
+        "expected language identifier"\r
+        );\r
+    }\r
+\r
+    return 0;\r
+  }\r
+  //\r
+  // This function is called to optionally get a language identifier name in:\r
+  //   #string STR_ID eng "the string"\r
+  // If it's optional, and we find a double-quote, then return now.\r
+  //\r
+  if (Optional) {\r
+    if (*SourceFile->FileBufferPtr == UNICODE_DOUBLE_QUOTE) {\r
+      return 0;\r
+    }\r
+  }\r
+\r
+  Len = 0;\r
+  LanguageIdentifierNameLen /= 2;\r
+  //\r
+  // Internal error if we weren't given at least 4 WCHAR's to work with.\r
+  //\r
+  if (LanguageIdentifierNameLen < LANGUAGE_IDENTIFIER_NAME_LEN + 1) {\r
+    Error (\r
+      SourceFile->FileName,\r
+      SourceFile->LineNum,\r
+      0,\r
+      "app error -- language identifier name length is invalid",\r
+      NULL\r
+      );\r
+  }\r
+\r
+  From  = SourceFile->FileBufferPtr;\r
+  Start = SourceFile->FileBufferPtr;\r
+  while (!EndOfFile (SourceFile)) {\r
+    if (((SourceFile->FileBufferPtr[0] >= UNICODE_a) && (SourceFile->FileBufferPtr[0] <= UNICODE_z))) {\r
+      Len++;\r
+      if (Len > LANGUAGE_IDENTIFIER_NAME_LEN) {\r
+        Error (SourceFile->FileName, SourceFile->LineNum, 0, "language identifier name too long", "%S", Start);\r
+        return 0;\r
+      }\r
+\r
+      *LanguageIdentifierName = SourceFile->FileBufferPtr[0];\r
+      SourceFile->FileBufferPtr++;\r
+      LanguageIdentifierName++;\r
+    } else if (!IsWhiteSpace (SourceFile)) {\r
+      Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid language identifier name", "%S", Start);\r
+      return 0;\r
+    } else {\r
+      break;\r
+    }\r
+  }\r
+  //\r
+  // Terminate the copy of the string.\r
+  //\r
+  *LanguageIdentifierName = 0;\r
+  return Len;\r
+}\r
+\r
+static\r
+void\r
+ProcessTokenInclude (\r
+  SOURCE_FILE *SourceFile\r
+  )\r
+{\r
+  INT8        IncludeFileName[MAX_PATH];\r
+  INT8        *To;\r
+  UINT32      Len;\r
+  BOOLEAN     ReportedError;\r
+  SOURCE_FILE IncludedSourceFile;\r
+\r
+  ReportedError = FALSE;\r
+  if (SkipWhiteSpace (SourceFile) == 0) {\r
+    Warning (SourceFile->FileName, SourceFile->LineNum, 0, "expected whitespace following #include keyword", NULL);\r
+  }\r
+  //\r
+  // Should be quoted file name\r
+  //\r
+  if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) {\r
+    Error (SourceFile->FileName, SourceFile->LineNum, 0, "expected quoted include file name", NULL);\r
+    goto FailDone;\r
+  }\r
+\r
+  SourceFile->FileBufferPtr++;\r
+  //\r
+  // Copy the filename as ascii to our local string\r
+  //\r
+  To  = IncludeFileName;\r
+  Len = 0;\r
+  while (!EndOfFile (SourceFile)) {\r
+    if ((SourceFile->FileBufferPtr[0] == UNICODE_CR) || (SourceFile->FileBufferPtr[0] == UNICODE_LF)) {\r
+      Error (SourceFile->FileName, SourceFile->LineNum, 0, "end-of-line found in quoted include file name", NULL);\r
+      goto FailDone;\r
+    }\r
+\r
+    if (SourceFile->FileBufferPtr[0] == UNICODE_DOUBLE_QUOTE) {\r
+      SourceFile->FileBufferPtr++;\r
+      break;\r
+    }\r
+    //\r
+    // If too long, then report the error once and process until the closing quote\r
+    //\r
+    Len++;\r
+    if (!ReportedError && (Len >= sizeof (IncludeFileName))) {\r
+      Error (SourceFile->FileName, SourceFile->LineNum, 0, "length of include file name exceeds limit", NULL);\r
+      ReportedError = TRUE;\r
+    }\r
+\r
+    if (!ReportedError) {\r
+      *To = UNICODE_TO_ASCII (SourceFile->FileBufferPtr[0]);\r
+      To++;\r
+    }\r
+\r
+    SourceFile->FileBufferPtr++;\r
+  }\r
+\r
+  if (!ReportedError) {\r
+    *To = 0;\r
+    memset ((char *) &IncludedSourceFile, 0, sizeof (SOURCE_FILE));\r
+    strcpy (IncludedSourceFile.FileName, IncludeFileName);\r
+    IncludedSourceFile.ControlCharacter = DEFAULT_CONTROL_CHARACTER;\r
+    ProcessIncludeFile (&IncludedSourceFile, SourceFile);\r
+    //\r
+    // printf ("including file '%s'\n", IncludeFileName);\r
+    //\r
+  }\r
+\r
+  return ;\r
+FailDone:\r
+  //\r
+  // Error recovery -- skip to next #\r
+  //\r
+  SourceFile->SkipToHash = TRUE;\r
+}\r
+\r
+static\r
+void\r
+ProcessTokenScope (\r
+  SOURCE_FILE *SourceFile\r
+  )\r
+{\r
+  WCHAR StringIdentifier[MAX_STRING_IDENTIFIER_NAME];\r
+  //\r
+  // Extract the scope name\r
+  //\r
+  if (GetStringIdentifierName (SourceFile, StringIdentifier, sizeof (StringIdentifier)) > 0) {\r
+    StringDBSetScope (StringIdentifier);\r
+  }\r
+}\r
+//\r
+// Parse:  #langdef eng "English"\r
+//         #langdef chn "\wideChinese"\r
+//\r
+static\r
+void\r
+ProcessTokenLangDef (\r
+  SOURCE_FILE *SourceFile\r
+  )\r
+{\r
+  WCHAR   LanguageIdentifier[MAX_STRING_IDENTIFIER_NAME];\r
+  UINT32  Len;\r
+  WCHAR   *PrintableName;\r
+  //\r
+  // Extract the 3-character language identifier\r
+  //\r
+  Len = GetLanguageIdentifierName (SourceFile, LanguageIdentifier, sizeof (LanguageIdentifier), FALSE);\r
+  if (Len != LANGUAGE_IDENTIFIER_NAME_LEN) {\r
+    Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid or missing language identifier", NULL);\r
+  } else {\r
+    //\r
+    // Extract the printable name\r
+    //\r
+    PrintableName = GetPrintableLanguageName (SourceFile);\r
+    if (PrintableName != NULL) {\r
+      ParserSetPosition (SourceFile->FileName, SourceFile->LineNum);\r
+      StringDBAddLanguage (LanguageIdentifier, PrintableName);\r
+      free (PrintableName);\r
+      return ;\r
+    }\r
+  }\r
+  //\r
+  // Error recovery -- skip to next #\r
+  //\r
+  SourceFile->SkipToHash = TRUE;\r
+}\r
+\r
+static\r
+BOOLEAN\r
+ApparentQuotedString (\r
+  SOURCE_FILE *SourceFile\r
+  )\r
+{\r
+  WCHAR *Ptr;\r
+  //\r
+  // See if the first and last nonblank characters on the line are double quotes\r
+  //\r
+  for (Ptr = SourceFile->FileBufferPtr; *Ptr && (*Ptr == UNICODE_SPACE); Ptr++)\r
+    ;\r
+  if (*Ptr != UNICODE_DOUBLE_QUOTE) {\r
+    return FALSE;\r
+  }\r
+\r
+  while (*Ptr) {\r
+    Ptr++;\r
+  }\r
+\r
+  Ptr--;\r
+  for (; *Ptr && (*Ptr == UNICODE_SPACE); Ptr--)\r
+    ;\r
+  if (*Ptr != UNICODE_DOUBLE_QUOTE) {\r
+    return FALSE;\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+//\r
+// Parse:\r
+//   #language eng "some string " "more string"\r
+//\r
+static\r
+void\r
+ProcessTokenLanguage (\r
+  SOURCE_FILE *SourceFile\r
+  )\r
+{\r
+  WCHAR   *String;\r
+  WCHAR   *SecondString;\r
+  WCHAR   *TempString;\r
+  WCHAR   *From;\r
+  WCHAR   *To;\r
+  WCHAR   Language[LANGUAGE_IDENTIFIER_NAME_LEN + 1];\r
+  UINT32  Len;\r
+  BOOLEAN PreviousNewline;\r
+  //\r
+  // Get the language identifier\r
+  //\r
+  Language[0] = 0;\r
+  Len         = GetLanguageIdentifierName (SourceFile, Language, sizeof (Language), TRUE);\r
+  if (Len != LANGUAGE_IDENTIFIER_NAME_LEN) {\r
+    Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid or missing language identifier", "%S", Language);\r
+    SourceFile->SkipToHash = TRUE;\r
+    return ;\r
+  }\r
+  //\r
+  // Extract the string value. It's either a quoted string that starts on the current line, or\r
+  // an unquoted string that starts on the following line and continues until the next control\r
+  // character in column 1.\r
+  // Look ahead to find a quote or a newline\r
+  //\r
+  if (SkipTo (SourceFile, UNICODE_DOUBLE_QUOTE, TRUE)) {\r
+    String = GetQuotedString (SourceFile, FALSE);\r
+    if (String != NULL) {\r
+      //\r
+      // Set the position in the file of where we are parsing for error\r
+      // reporting purposes. Then start looking ahead for additional\r
+      // quoted strings, and concatenate them until we get a failure\r
+      // back from the string parser.\r
+      //\r
+      Len = wcslen (String) + 1;\r
+      ParserSetPosition (SourceFile->FileName, SourceFile->LineNum);\r
+      do {\r
+        SkipWhiteSpace (SourceFile);\r
+        SecondString = GetQuotedString (SourceFile, TRUE);\r
+        if (SecondString != NULL) {\r
+          Len += wcslen (SecondString);\r
+          TempString = (WCHAR *) malloc (Len * sizeof (WCHAR));\r
+          if (TempString == NULL) {\r
+            Error (NULL, 0, 0, "application error", "failed to allocate memory");\r
+            return ;\r
+          }\r
+\r
+          wcscpy (TempString, String);\r
+          wcscat (TempString, SecondString);\r
+          free (String);\r
+          free (SecondString);\r
+          String = TempString;\r
+        }\r
+      } while (SecondString != NULL);\r
+      StringDBAddString (Language, NULL, NULL, String, TRUE, 0);\r
+      free (String);\r
+    } else {\r
+      //\r
+      // Error was reported at lower level. Error recovery mode.\r
+      //\r
+      SourceFile->SkipToHash = TRUE;\r
+    }\r
+  } else {\r
+    if (!mGlobals.UnquotedStrings) {\r
+      //\r
+      // They're using unquoted strings. If the next non-blank character is a double quote, and the\r
+      // last non-blank character on the line is a double quote, then more than likely they're using\r
+      // quotes, so they need to put the quoted string on the end of the previous line\r
+      //\r
+      if (ApparentQuotedString (SourceFile)) {\r
+        Warning (\r
+          SourceFile->FileName,\r
+          SourceFile->LineNum,\r
+          0,\r
+          "unexpected quoted string on line",\r
+          "specify -uqs option if necessary"\r
+          );\r
+      }\r
+    }\r
+    //\r
+    // Found end-of-line (hopefully). Skip over it and start taking in characters\r
+    // until we find a control character at the start of a line.\r
+    //\r
+    Len             = 0;\r
+    From            = SourceFile->FileBufferPtr;\r
+    PreviousNewline = FALSE;\r
+    while (!EndOfFile (SourceFile)) {\r
+      if (SourceFile->FileBufferPtr[0] == UNICODE_LF) {\r
+        PreviousNewline = TRUE;\r
+        SourceFile->LineNum++;\r
+      } else {\r
+        Len++;\r
+        if (PreviousNewline && (SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter)) {\r
+          break;\r
+        }\r
+\r
+        PreviousNewline = FALSE;\r
+      }\r
+\r
+      SourceFile->FileBufferPtr++;\r
+    }\r
+\r
+    if ((Len == 0) && EndOfFile (SourceFile)) {\r
+      Error (SourceFile->FileName, SourceFile->LineNum, 0, "unexpected end of file", NULL);\r
+      SourceFile->SkipToHash = TRUE;\r
+      return ;\r
+    }\r
+    //\r
+    // Now allocate a buffer, copy the characters, and add the string.\r
+    //\r
+    String = (WCHAR *) malloc ((Len + 1) * sizeof (WCHAR));\r
+    if (String == NULL) {\r
+      Error (NULL, 0, 0, "application error", "failed to allocate memory");\r
+      return ;\r
+    }\r
+\r
+    To = String;\r
+    while (From < SourceFile->FileBufferPtr) {\r
+      switch (*From) {\r
+      case UNICODE_LF:\r
+      case 0:\r
+        break;\r
+\r
+      default:\r
+        *To = *From;\r
+        To++;\r
+        break;\r
+      }\r
+\r
+      From++;\r
+    }\r
+\r
+    //\r
+    // String[Len] = 0;\r
+    //\r
+    *To = 0;\r
+    StringDBAddString (Language, NULL, NULL, String, TRUE, 0);\r
+  }\r
+}\r
+\r
+static\r
+BOOLEAN\r
+IsWhiteSpace (\r
+  SOURCE_FILE *SourceFile\r
+  )\r
+{\r
+  switch (SourceFile->FileBufferPtr[0]) {\r
+  case UNICODE_NULL:\r
+  case UNICODE_CR:\r
+  case UNICODE_SPACE:\r
+  case UNICODE_TAB:\r
+  case UNICODE_LF:\r
+    return TRUE;\r
+\r
+  default:\r
+    return FALSE;\r
+  }\r
+}\r
+\r
+static\r
+UINT32\r
+SkipWhiteSpace (\r
+  SOURCE_FILE *SourceFile\r
+  )\r
+{\r
+  UINT32  Count;\r
+\r
+  Count = 0;\r
+  while (!EndOfFile (SourceFile)) {\r
+    Count++;\r
+    switch (*SourceFile->FileBufferPtr) {\r
+    case UNICODE_NULL:\r
+    case UNICODE_CR:\r
+    case UNICODE_SPACE:\r
+    case UNICODE_TAB:\r
+      SourceFile->FileBufferPtr++;\r
+      break;\r
+\r
+    case UNICODE_LF:\r
+      SourceFile->FileBufferPtr++;\r
+      SourceFile->LineNum++;\r
+      if (mGlobals.Verbose) {\r
+        printf ("%d: %S\n", SourceFile->LineNum, SourceFile->FileBufferPtr);\r
+      }\r
+      break;\r
+\r
+    default:\r
+      return Count - 1;\r
+    }\r
+  }\r
+  //\r
+  // Some tokens require trailing whitespace. If we're at the end of the\r
+  // file, then we count that as well.\r
+  //\r
+  if ((Count == 0) && (EndOfFile (SourceFile))) {\r
+    Count++;\r
+  }\r
+\r
+  return Count;\r
+}\r
+\r
+static\r
+UINT32\r
+wstrcmp (\r
+  WCHAR *Buffer,\r
+  WCHAR *Str\r
+  )\r
+{\r
+  UINT32  Len;\r
+\r
+  Len = 0;\r
+  while (*Str == *Buffer) {\r
+    Buffer++;\r
+    Str++;\r
+    Len++;\r
+  }\r
+\r
+  if (*Str) {\r
+    return 0;\r
+  }\r
+\r
+  return Len;\r
+}\r
+//\r
+// Given a filename, try to find it along the include paths.\r
+//\r
+static\r
+FILE *\r
+FindFile (\r
+  IN INT8    *FileName,\r
+  OUT INT8   *FoundFileName,\r
+  IN UINT32  FoundFileNameLen\r
+  )\r
+{\r
+  FILE              *Fptr;\r
+  TEXT_STRING_LIST  *List;\r
+\r
+  //\r
+  // Traverse the list of paths and try to find the file\r
+  //\r
+  List = mGlobals.IncludePaths;\r
+  while (List != NULL) {\r
+    //\r
+    // Put the path and filename together\r
+    //\r
+    if (strlen (List->Str) + strlen (FileName) + 1 > FoundFileNameLen) {\r
+      Error (PROGRAM_NAME, 0, 0, NULL, "internal error - cannot concatenate path+filename");\r
+      return NULL;\r
+    }\r
+    //\r
+    // Append the filename to this include path and try to open the file.\r
+    //\r
+    strcpy (FoundFileName, List->Str);\r
+    strcat (FoundFileName, FileName);\r
+    if ((Fptr = fopen (FoundFileName, "rb")) != NULL) {\r
+      //\r
+      // Return the file pointer\r
+      //\r
+      return Fptr;\r
+    }\r
+\r
+    List = List->Next;\r
+  }\r
+  //\r
+  // Not found\r
+  //\r
+  FoundFileName[0] = 0;\r
+  return NULL;\r
+}\r
+//\r
+// Process the command-line arguments\r
+//\r
+static\r
+STATUS\r
+ProcessArgs (\r
+  int   Argc,\r
+  char  *Argv[]\r
+  )\r
+{\r
+  TEXT_STRING_LIST  *NewList;\r
+  //\r
+  // Clear our globals\r
+  //\r
+  memset ((char *) &mGlobals, 0, sizeof (mGlobals));\r
+  strcpy (mGlobals.BaseName, DEFAULT_BASE_NAME);\r
+  //\r
+  // Skip program name\r
+  //\r
+  Argc--;\r
+  Argv++;\r
+\r
+  if (Argc == 0) {\r
+    Usage ();\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  mGlobals.Mode = MODE_UNKNOWN;\r
+  //\r
+  // Process until no more -args.\r
+  //\r
+  while ((Argc > 0) && (Argv[0][0] == '-')) {\r
+    //\r
+    // -parse option\r
+    //\r
+    if (_stricmp (Argv[0], "-parse") == 0) {\r
+      if (mGlobals.Mode != MODE_UNKNOWN) {\r
+        Error (NULL, 0, 0, "only one of -parse/-scan/-dump allowed", NULL);\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      mGlobals.Mode = MODE_PARSE;\r
+      //\r
+      // -scan option\r
+      //\r
+    } else if (_stricmp (Argv[0], "-scan") == 0) {\r
+      if (mGlobals.Mode != MODE_UNKNOWN) {\r
+        Error (NULL, 0, 0, "only one of -parse/-scan/-dump allowed", NULL);\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      mGlobals.Mode = MODE_SCAN;\r
+      //\r
+      // -vscan verbose scanning option\r
+      //\r
+    } else if (_stricmp (Argv[0], "-vscan") == 0) {\r
+      mGlobals.VerboseScan = TRUE;\r
+      //\r
+      // -dump option\r
+      //\r
+    } else if (_stricmp (Argv[0], "-dump") == 0) {\r
+      if (mGlobals.Mode != MODE_UNKNOWN) {\r
+        Error (NULL, 0, 0, "only one of -parse/-scan/-dump allowed", NULL);\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      mGlobals.Mode = MODE_DUMP;\r
+    } else if (_stricmp (Argv[0], "-uqs") == 0) {\r
+      mGlobals.UnquotedStrings = TRUE;\r
+      //\r
+      // -i path    add include search path when parsing\r
+      //\r
+    } else if (_stricmp (Argv[0], "-i") == 0) {\r
+      //\r
+      // check for one more arg\r
+      //\r
+      if ((Argc <= 1) || (Argv[1][0] == '-')) {\r
+        Error (PROGRAM_NAME, 0, 0, Argv[0], "missing include path");\r
+        return STATUS_ERROR;\r
+      }\r
+      //\r
+      // Allocate memory for a new list element, fill it in, and\r
+      // add it to our list of include paths. Always make sure it\r
+      // has a "\" on the end of it.\r
+      //\r
+      NewList = malloc (sizeof (TEXT_STRING_LIST));\r
+      if (NewList == NULL) {\r
+        Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure");\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST));\r
+      NewList->Str = malloc (strlen (Argv[1]) + 2);\r
+      if (NewList->Str == NULL) {\r
+        free (NewList);\r
+        Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure");\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      strcpy (NewList->Str, Argv[1]);\r
+      if (NewList->Str[strlen (NewList->Str) - 1] != '\\') {\r
+        strcat (NewList->Str, "\\");\r
+      }\r
+      //\r
+      // Add it to our linked list\r
+      //\r
+      if (mGlobals.IncludePaths == NULL) {\r
+        mGlobals.IncludePaths = NewList;\r
+      } else {\r
+        mGlobals.LastIncludePath->Next = NewList;\r
+      }\r
+\r
+      mGlobals.LastIncludePath = NewList;\r
+      Argc--;\r
+      Argv++;\r
+    } else if (_stricmp (Argv[0], "-if") == 0) {\r
+      //\r
+      // Indirection file -- check for one more arg\r
+      //\r
+      if ((Argc <= 1) || (Argv[1][0] == '-')) {\r
+        Error (PROGRAM_NAME, 0, 0, Argv[0], "missing indirection file name");\r
+        return STATUS_ERROR;\r
+      }\r
+      //\r
+      // Allocate memory for a new list element, fill it in, and\r
+      // add it to our list of include paths. Always make sure it\r
+      // has a "\" on the end of it.\r
+      //\r
+      NewList = malloc (sizeof (TEXT_STRING_LIST));\r
+      if (NewList == NULL) {\r
+        Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure");\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST));\r
+      NewList->Str = malloc (strlen (Argv[1]) + 1);\r
+      if (NewList->Str == NULL) {\r
+        free (NewList);\r
+        Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure");\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      strcpy (NewList->Str, Argv[1]);\r
+      //\r
+      // Add it to our linked list\r
+      //\r
+      if (mGlobals.IndirectionFileName == NULL) {\r
+        mGlobals.IndirectionFileName = NewList;\r
+      } else {\r
+        mGlobals.LastIndirectionFileName->Next = NewList;\r
+      }\r
+\r
+      mGlobals.LastIndirectionFileName = NewList;\r
+      Argc--;\r
+      Argv++;\r
+    } else if (_stricmp (Argv[0], "-db") == 0) {\r
+      //\r
+      // -db option to specify a database file.\r
+      // Check for one more arg (the database file name)\r
+      //\r
+      if ((Argc <= 1) || (Argv[1][0] == '-')) {\r
+        Error (PROGRAM_NAME, 0, 0, Argv[0], "missing database file name");\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      NewList = malloc (sizeof (TEXT_STRING_LIST));\r
+      if (NewList == NULL) {\r
+        Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure");\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST));\r
+      NewList->Str = malloc (strlen (Argv[1]) + 1);\r
+      if (NewList->Str == NULL) {\r
+        free (NewList);\r
+        Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure");\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      strcpy (NewList->Str, Argv[1]);\r
+      //\r
+      // Add it to our linked list\r
+      //\r
+      if (mGlobals.DatabaseFileName == NULL) {\r
+        mGlobals.DatabaseFileName = NewList;\r
+      } else {\r
+        mGlobals.LastDatabaseFileName->Next = NewList;\r
+      }\r
+\r
+      mGlobals.LastDatabaseFileName = NewList;\r
+      Argc--;\r
+      Argv++;\r
+    } else if (_stricmp (Argv[0], "-ou") == 0) {\r
+      //\r
+      // -ou option to specify an output unicode file to\r
+      // which we can dump our database.\r
+      //\r
+      if ((Argc <= 1) || (Argv[1][0] == '-')) {\r
+        Error (PROGRAM_NAME, 0, 0, Argv[0], "missing database dump output file name");\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      if (mGlobals.DumpUFileName[0] == 0) {\r
+        strcpy (mGlobals.DumpUFileName, Argv[1]);\r
+      } else {\r
+        Error (PROGRAM_NAME, 0, 0, Argv[1], "-ou option already specified with '%s'", mGlobals.DumpUFileName);\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      Argc--;\r
+      Argv++;\r
+    } else if (_stricmp (Argv[0], "-hpk") == 0) {\r
+      //\r
+      // -hpk option to create an HII export pack of the input database file\r
+      //\r
+      if ((Argc <= 1) || (Argv[1][0] == '-')) {\r
+        Error (PROGRAM_NAME, 0, 0, Argv[0], "missing raw string data dump output file name");\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      if (mGlobals.HiiExportPackFileName[0] == 0) {\r
+        strcpy (mGlobals.HiiExportPackFileName, Argv[1]);\r
+      } else {\r
+        Error (PROGRAM_NAME, 0, 0, Argv[1], "-or option already specified with '%s'", mGlobals.HiiExportPackFileName);\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      Argc--;\r
+      Argv++;\r
+    } else if ((_stricmp (Argv[0], "-?") == 0) || (_stricmp (Argv[0], "-h") == 0)) {\r
+      Usage ();\r
+      return STATUS_ERROR;\r
+    } else if (_stricmp (Argv[0], "-v") == 0) {\r
+      mGlobals.Verbose = 1;\r
+    } else if (_stricmp (Argv[0], "-vdbw") == 0) {\r
+      mGlobals.VerboseDatabaseWrite = 1;\r
+    } else if (_stricmp (Argv[0], "-vdbr") == 0) {\r
+      mGlobals.VerboseDatabaseRead = 1;\r
+    } else if (_stricmp (Argv[0], "-newdb") == 0) {\r
+      mGlobals.NewDatabase = 1;\r
+    } else if (_stricmp (Argv[0], "-ignorenotfound") == 0) {\r
+      mGlobals.IgnoreNotFound = 1;\r
+    } else if (_stricmp (Argv[0], "-oc") == 0) {\r
+      //\r
+      // check for one more arg\r
+      //\r
+      if ((Argc <= 1) || (Argv[1][0] == '-')) {\r
+        Error (PROGRAM_NAME, 0, 0, Argv[0], "missing output C filename");\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      strcpy (mGlobals.StringCFileName, Argv[1]);\r
+      Argc--;\r
+      Argv++;\r
+    } else if (_stricmp (Argv[0], "-bn") == 0) {\r
+      //\r
+      // check for one more arg\r
+      //\r
+      if ((Argc <= 1) || (Argv[1][0] == '-')) {\r
+        Error (PROGRAM_NAME, 0, 0, Argv[0], "missing base name");\r
+        Usage ();\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      strcpy (mGlobals.BaseName, Argv[1]);\r
+      Argc--;\r
+      Argv++;\r
+    } else if (_stricmp (Argv[0], "-oh") == 0) {\r
+      //\r
+      // -oh to specify output .h defines file name\r
+      //\r
+      if ((Argc <= 1) || (Argv[1][0] == '-')) {\r
+        Error (PROGRAM_NAME, 0, 0, Argv[0], "missing output .h filename");\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      strcpy (mGlobals.StringHFileName, Argv[1]);\r
+      Argc--;\r
+      Argv++;\r
+    } else if (_stricmp (Argv[0], "-dep") == 0) {\r
+      //\r
+      // -dep to specify output dependency file name\r
+      //\r
+      if ((Argc <= 1) || (Argv[1][0] == '-')) {\r
+        Error (PROGRAM_NAME, 0, 0, Argv[0], "missing output dependency filename");\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      strcpy (mGlobals.OutputDependencyFileName, Argv[1]);\r
+      Argc--;\r
+      Argv++;\r
+    } else if (_stricmp (Argv[0], "-skipext") == 0) {\r
+      //\r
+      // -skipext to skip scanning of files with certain filename extensions\r
+      //\r
+      if ((Argc <= 1) || (Argv[1][0] == '-')) {\r
+        Error (PROGRAM_NAME, 0, 0, Argv[0], "missing filename extension");\r
+        return STATUS_ERROR;\r
+      }\r
+      //\r
+      // Allocate memory for a new list element, fill it in, and\r
+      // add it to our list of excluded extensions. Always make sure it\r
+      // has a "." as the first character.\r
+      //\r
+      NewList = malloc (sizeof (TEXT_STRING_LIST));\r
+      if (NewList == NULL) {\r
+        Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure");\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST));\r
+      NewList->Str = malloc (strlen (Argv[1]) + 2);\r
+      if (NewList->Str == NULL) {\r
+        free (NewList);\r
+        Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure");\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      if (Argv[1][0] == '.') {\r
+        strcpy (NewList->Str, Argv[1]);\r
+      } else {\r
+        NewList->Str[0] = '.';\r
+        strcpy (NewList->Str + 1, Argv[1]);\r
+      }\r
+      //\r
+      // Add it to our linked list\r
+      //\r
+      if (mGlobals.SkipExt == NULL) {\r
+        mGlobals.SkipExt = NewList;\r
+      } else {\r
+        mGlobals.LastSkipExt->Next = NewList;\r
+      }\r
+\r
+      mGlobals.LastSkipExt = NewList;\r
+      Argc--;\r
+      Argv++;\r
+    } else if (_stricmp (Argv[0], "-lang") == 0) {\r
+      //\r
+      // "-lang eng" or "-lang spa+cat" to only output certain languages\r
+      //\r
+      if ((Argc <= 1) || (Argv[1][0] == '-')) {\r
+        Error (PROGRAM_NAME, 0, 0, Argv[0], "missing language name");\r
+        Usage ();\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      if (AddCommandLineLanguage (Argv[1]) != STATUS_SUCCESS) {\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      Argc--;\r
+      Argv++;\r
+    } else if (_stricmp (Argv[0], "-od") == 0) {\r
+      //\r
+      // Output database file name -- check for another arg\r
+      //\r
+      if ((Argc <= 1) || (Argv[1][0] == '-')) {\r
+        Error (PROGRAM_NAME, 0, 0, Argv[0], "missing output database file name");\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      strcpy (mGlobals.OutputDatabaseFileName, Argv[1]);\r
+      Argv++;\r
+      Argc--;\r
+    } else {\r
+      //\r
+      // Unrecognized arg\r
+      //\r
+      Error (PROGRAM_NAME, 0, 0, Argv[0], "unrecognized option");\r
+      Usage ();\r
+      return STATUS_ERROR;\r
+    }\r
+\r
+    Argv++;\r
+    Argc--;\r
+  }\r
+  //\r
+  // Make sure they specified the mode parse/scan/dump\r
+  //\r
+  if (mGlobals.Mode == MODE_UNKNOWN) {\r
+    Error (NULL, 0, 0, "must specify one of -parse/-scan/-dump", NULL);\r
+    return STATUS_ERROR;\r
+  }\r
+  //\r
+  // All modes require a database filename\r
+  //\r
+  if (mGlobals.DatabaseFileName == 0) {\r
+    Error (NULL, 0, 0, "must specify a database filename using -db DbFileName", NULL);\r
+    Usage ();\r
+    return STATUS_ERROR;\r
+  }\r
+  //\r
+  // If dumping the database file, then return immediately if all\r
+  // parameters check out.\r
+  //\r
+  if (mGlobals.Mode == MODE_DUMP) {\r
+    //\r
+    // Not much use if they didn't specify -oh or -oc or -ou or -hpk\r
+    //\r
+    if ((mGlobals.DumpUFileName[0] == 0) &&\r
+        (mGlobals.StringHFileName[0] == 0) &&\r
+        (mGlobals.StringCFileName[0] == 0) &&\r
+        (mGlobals.HiiExportPackFileName[0] == 0)\r
+        ) {\r
+      Error (NULL, 0, 0, "-dump without -oc/-oh/-ou/-hpk is a NOP", NULL);\r
+      return STATUS_ERROR;\r
+    }\r
+\r
+    return STATUS_SUCCESS;\r
+  }\r
+  //\r
+  // Had to specify source string file and output string defines header filename.\r
+  //\r
+  if (mGlobals.Mode == MODE_SCAN) {\r
+    if (Argc < 1) {\r
+      Error (PROGRAM_NAME, 0, 0, NULL, "must specify at least one source file to scan with -scan");\r
+      Usage ();\r
+      return STATUS_ERROR;\r
+    }\r
+    //\r
+    // Get the list of filenames\r
+    //\r
+    while (Argc > 0) {\r
+      NewList = malloc (sizeof (TEXT_STRING_LIST));\r
+      if (NewList == NULL) {\r
+        Error (PROGRAM_NAME, 0, 0, "memory allocation failure", NULL);\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      memset (NewList, 0, sizeof (TEXT_STRING_LIST));\r
+      NewList->Str = (UINT8 *) malloc (strlen (Argv[0]) + 1);\r
+      if (NewList->Str == NULL) {\r
+        Error (PROGRAM_NAME, 0, 0, "memory allocation failure", NULL);\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      strcpy (NewList->Str, Argv[0]);\r
+      if (mGlobals.ScanFileName == NULL) {\r
+        mGlobals.ScanFileName = NewList;\r
+      } else {\r
+        mGlobals.LastScanFileName->Next = NewList;\r
+      }\r
+\r
+      mGlobals.LastScanFileName = NewList;\r
+      Argc--;\r
+      Argv++;\r
+    }\r
+  } else {\r
+    //\r
+    // Parse mode -- must specify an input unicode file name\r
+    //\r
+    if (Argc < 1) {\r
+      Error (PROGRAM_NAME, 0, 0, NULL, "must specify input unicode string file name with -parse");\r
+      Usage ();\r
+      return STATUS_ERROR;\r
+    }\r
+\r
+    strcpy (mGlobals.SourceFiles.FileName, Argv[0]);\r
+  }\r
+\r
+  return STATUS_SUCCESS;\r
+}\r
+//\r
+// Found "-lang eng,spa+cat" on the command line. Parse the\r
+// language list and save the setting for later processing.\r
+//\r
+static\r
+STATUS\r
+AddCommandLineLanguage (\r
+  IN INT8          *Language\r
+  )\r
+{\r
+  WCHAR_STRING_LIST *WNewList;\r
+  WCHAR             *From;\r
+  WCHAR             *To;\r
+  //\r
+  // Keep processing the input string until we find the end.\r
+  //\r
+  while (*Language) {\r
+    //\r
+    // Allocate memory for a new list element, fill it in, and\r
+    // add it to our list.\r
+    //\r
+    WNewList = MALLOC (sizeof (WCHAR_STRING_LIST));\r
+    if (WNewList == NULL) {\r
+      Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure");\r
+      return STATUS_ERROR;\r
+    }\r
+\r
+    memset ((char *) WNewList, 0, sizeof (WCHAR_STRING_LIST));\r
+    WNewList->Str = malloc ((strlen (Language) + 1) * sizeof (WCHAR));\r
+    if (WNewList->Str == NULL) {\r
+      free (WNewList);\r
+      Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure");\r
+      return STATUS_ERROR;\r
+    }\r
+    //\r
+    // Copy it as unicode to our new structure. Then remove the\r
+    // plus signs in it, and verify each language name is 3 characters\r
+    // long. If we find a comma, then we're done with this group, so\r
+    // break out.\r
+    //\r
+#ifdef USE_VC8\r
+    swprintf (WNewList->Str, (strlen (Language) + 1) * sizeof (WCHAR), L"%S", Language);\r
+#else\r
+    swprintf (WNewList->Str, L"%S", Language);\r
+#endif\r
+    From = To = WNewList->Str;\r
+    while (*From) {\r
+      if (*From == L',') {\r
+        break;\r
+      }\r
+\r
+      if ((wcslen (From) < LANGUAGE_IDENTIFIER_NAME_LEN) ||\r
+            (\r
+              (From[LANGUAGE_IDENTIFIER_NAME_LEN] != 0) &&\r
+              (From[LANGUAGE_IDENTIFIER_NAME_LEN] != UNICODE_PLUS_SIGN) &&\r
+              (From[LANGUAGE_IDENTIFIER_NAME_LEN] != L',')\r
+            )\r
+          ) {\r
+        Error (PROGRAM_NAME, 0, 0, Language, "invalid format for language name on command line");\r
+        FREE (WNewList->Str);\r
+        FREE (WNewList);\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      wcsncpy (To, From, LANGUAGE_IDENTIFIER_NAME_LEN);\r
+      To += LANGUAGE_IDENTIFIER_NAME_LEN;\r
+      From += LANGUAGE_IDENTIFIER_NAME_LEN;\r
+      if (*From == L'+') {\r
+        From++;\r
+      }\r
+    }\r
+\r
+    *To = 0;\r
+    //\r
+    // Add it to our linked list\r
+    //\r
+    if (mGlobals.Language == NULL) {\r
+      mGlobals.Language = WNewList;\r
+    } else {\r
+      mGlobals.LastLanguage->Next = WNewList;\r
+    }\r
+\r
+    mGlobals.LastLanguage = WNewList;\r
+    //\r
+    // Skip to next entry (comma-separated list)\r
+    //\r
+    while (*Language) {\r
+      if (*Language == L',') {\r
+        Language++;\r
+        break;\r
+      }\r
+\r
+      Language++;\r
+    }\r
+  }\r
+\r
+  return STATUS_SUCCESS;\r
+}\r
+//\r
+// The contents of the text file are expected to be (one per line)\r
+//   STRING_IDENTIFIER_NAME   ScopeName\r
+// For example:\r
+//   STR_ID_MY_FAVORITE_STRING   IBM\r
+//\r
+static\r
+STATUS\r
+ParseIndirectionFiles (\r
+  TEXT_STRING_LIST    *Files\r
+  )\r
+{\r
+  FILE                        *Fptr;\r
+  INT8                        Line[200];\r
+  INT8                        *StringName;\r
+  INT8                        *ScopeName;\r
+  INT8                        *End;\r
+  UINT32                      LineCount;\r
+  WCHAR_MATCHING_STRING_LIST  *NewList;\r
+\r
+  Line[sizeof (Line) - 1] = 0;\r
+  Fptr                    = NULL;\r
+  while (Files != NULL) {\r
+    Fptr      = fopen (Files->Str, "r");\r
+    LineCount = 0;\r
+    if (Fptr == NULL) {\r
+      Error (NULL, 0, 0, Files->Str, "failed to open input indirection file for reading");\r
+      return STATUS_ERROR;\r
+    }\r
+\r
+    while (fgets (Line, sizeof (Line), Fptr) != NULL) {\r
+      //\r
+      // remove terminating newline for error printing purposes.\r
+      //\r
+      if (Line[strlen (Line) - 1] == '\n') {\r
+        Line[strlen (Line) - 1] = 0;\r
+      }\r
+\r
+      LineCount++;\r
+      if (Line[sizeof (Line) - 1] != 0) {\r
+        Error (Files->Str, LineCount, 0, "line length exceeds maximum supported", NULL);\r
+        goto Done;\r
+      }\r
+\r
+      StringName = Line;\r
+      while (*StringName && (isspace (*StringName))) {\r
+        StringName++;\r
+      }\r
+\r
+      if (*StringName) {\r
+        if ((*StringName == '_') || isalpha (*StringName)) {\r
+          End = StringName;\r
+          while ((*End) && (*End == '_') || (isalnum (*End))) {\r
+            End++;\r
+          }\r
+\r
+          if (isspace (*End)) {\r
+            *End = 0;\r
+            End++;\r
+            while (isspace (*End)) {\r
+              End++;\r
+            }\r
+\r
+            if (*End) {\r
+              ScopeName = End;\r
+              while (*End && !isspace (*End)) {\r
+                End++;\r
+              }\r
+\r
+              *End = 0;\r
+              //\r
+              // Add the string name/scope pair\r
+              //\r
+              NewList = malloc (sizeof (WCHAR_MATCHING_STRING_LIST));\r
+              if (NewList == NULL) {\r
+                Error (NULL, 0, 0, "memory allocation error", NULL);\r
+                goto Done;\r
+              }\r
+\r
+              memset (NewList, 0, sizeof (WCHAR_MATCHING_STRING_LIST));\r
+              NewList->Str1 = (WCHAR *) malloc ((strlen (StringName) + 1) * sizeof (WCHAR));\r
+              NewList->Str2 = (WCHAR *) malloc ((strlen (ScopeName) + 1) * sizeof (WCHAR));\r
+              if ((NewList->Str1 == NULL) || (NewList->Str2 == NULL)) {\r
+                Error (NULL, 0, 0, "memory allocation error", NULL);\r
+                goto Done;\r
+              }\r
+\r
+#ifdef USE_VC8\r
+              swprintf (NewList->Str1, (strlen (StringName) + 1) * sizeof (WCHAR), L"%S", StringName);\r
+              swprintf (NewList->Str2, (strlen (ScopeName) + 1) * sizeof (WCHAR), L"%S", ScopeName);\r
+#else\r
+              swprintf (NewList->Str1, L"%S", StringName);\r
+              swprintf (NewList->Str2, L"%S", ScopeName);\r
+#endif\r
+              if (mGlobals.IndirectionList == NULL) {\r
+                mGlobals.IndirectionList = NewList;\r
+              } else {\r
+                mGlobals.LastIndirectionList->Next = NewList;\r
+              }\r
+\r
+              mGlobals.LastIndirectionList = NewList;\r
+            } else {\r
+              Error (Files->Str, LineCount, 0, StringName, "invalid line : expected 'StringIdentifier Scope'");\r
+              goto Done;\r
+            }\r
+          } else {\r
+            Error (Files->Str, LineCount, 0, StringName, "invalid line : expected 'StringIdentifier Scope'");\r
+            goto Done;\r
+          }\r
+        } else {\r
+          Error (Files->Str, LineCount, 0, StringName, "invalid string identifier");\r
+          goto Done;\r
+        }\r
+      }\r
+    }\r
+\r
+    fclose (Fptr);\r
+    Fptr  = NULL;\r
+    Files = Files->Next;\r
+  }\r
+\r
+Done:\r
+  if (Fptr != NULL) {\r
+    fclose (Fptr);\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  return STATUS_SUCCESS;\r
+}\r
+\r
+static\r
+STATUS\r
+ScanFiles (\r
+  TEXT_STRING_LIST *ScanFiles\r
+  )\r
+{\r
+  char              Line[MAX_LINE_LEN];\r
+  FILE              *Fptr;\r
+  UINT32            LineNum;\r
+  char              *Cptr;\r
+  char              *SavePtr;\r
+  char              *TermPtr;\r
+  char              *StringTokenPos;\r
+  TEXT_STRING_LIST  *SList;\r
+  BOOLEAN           SkipIt;\r
+\r
+  //\r
+  // Put a null-terminator at the end of the line. If we read in\r
+  // a line longer than we support, then we can catch it.\r
+  //\r
+  Line[MAX_LINE_LEN - 1] = 0;\r
+  //\r
+  // Process each file. If they gave us a skip extension list, then\r
+  // skip it if the extension matches.\r
+  //\r
+  while (ScanFiles != NULL) {\r
+    SkipIt = FALSE;\r
+    for (SList = mGlobals.SkipExt; SList != NULL; SList = SList->Next) {\r
+      if ((strlen (ScanFiles->Str) > strlen (SList->Str)) &&\r
+          (strcmp (ScanFiles->Str + strlen (ScanFiles->Str) - strlen (SList->Str), SList->Str) == 0)\r
+          ) {\r
+        SkipIt = TRUE;\r
+        //\r
+        // printf ("Match: %s : %s\n", ScanFiles->Str, SList->Str);\r
+        //\r
+        break;\r
+      }\r
+    }\r
+\r
+    if (!SkipIt) {\r
+      if (mGlobals.VerboseScan) {\r
+        printf ("Scanning %s\n", ScanFiles->Str);\r
+      }\r
+\r
+      Fptr = fopen (ScanFiles->Str, "r");\r
+      if (Fptr == NULL) {\r
+        Error (NULL, 0, 0, ScanFiles->Str, "failed to open input file for scanning");\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      LineNum = 0;\r
+      while (fgets (Line, sizeof (Line), Fptr) != NULL) {\r
+        LineNum++;\r
+        if (Line[MAX_LINE_LEN - 1] != 0) {\r
+          Error (ScanFiles->Str, LineNum, 0, "line length exceeds maximum supported by tool", NULL);\r
+          fclose (Fptr);\r
+          return STATUS_ERROR;\r
+        }\r
+        //\r
+        // Remove the newline from the input line so we can print a warning message\r
+        //\r
+        if (Line[strlen (Line) - 1] == '\n') {\r
+          Line[strlen (Line) - 1] = 0;\r
+        }\r
+        //\r
+        // Terminate the line at // comments\r
+        //\r
+        Cptr = strstr (Line, "//");\r
+        if (Cptr != NULL) {\r
+          *Cptr = 0;\r
+        }\r
+\r
+        Cptr = Line;\r
+        while ((Cptr = strstr (Cptr, STRING_TOKEN)) != NULL) {\r
+          //\r
+          // Found "STRING_TOKEN". Make sure we don't have NUM_STRING_TOKENS or\r
+          // something like that. Then make sure it's followed by\r
+          // an open parenthesis, a string identifier, and then a closing\r
+          // parenthesis.\r
+          //\r
+          if (mGlobals.VerboseScan) {\r
+            printf (" %d: %s", LineNum, Cptr);\r
+          }\r
+\r
+          if (((Cptr == Line) || (!IsValidIdentifierChar (*(Cptr - 1), FALSE))) &&\r
+              (!IsValidIdentifierChar (*(Cptr + sizeof (STRING_TOKEN) - 1), FALSE))\r
+              ) {\r
+            StringTokenPos  = Cptr;\r
+            SavePtr         = Cptr;\r
+            Cptr += strlen (STRING_TOKEN);\r
+            while (*Cptr && isspace (*Cptr) && (*Cptr != '(')) {\r
+              Cptr++;\r
+            }\r
+\r
+            if (*Cptr != '(') {\r
+              Warning (ScanFiles->Str, LineNum, 0, StringTokenPos, "expected "STRING_TOKEN "(identifier)");\r
+            } else {\r
+              //\r
+              // Skip over the open-parenthesis and find the next non-blank character\r
+              //\r
+              Cptr++;\r
+              while (isspace (*Cptr)) {\r
+                Cptr++;\r
+              }\r
+\r
+              SavePtr = Cptr;\r
+              if ((*Cptr == '_') || isalpha (*Cptr)) {\r
+                while ((*Cptr == '_') || (isalnum (*Cptr))) {\r
+                  Cptr++;\r
+                }\r
+\r
+                TermPtr = Cptr;\r
+                while (*Cptr && isspace (*Cptr)) {\r
+                  Cptr++;\r
+                }\r
+\r
+                if (*Cptr != ')') {\r
+                  Warning (ScanFiles->Str, LineNum, 0, StringTokenPos, "expected "STRING_TOKEN "(identifier)");\r
+                }\r
+\r
+                if (*TermPtr) {\r
+                  *TermPtr  = 0;\r
+                  Cptr      = TermPtr + 1;\r
+                } else {\r
+                  Cptr = TermPtr;\r
+                }\r
+                //\r
+                // Add the string identifier to the list of used strings\r
+                //\r
+                ParserSetPosition (ScanFiles->Str, LineNum);\r
+                StringDBSetStringReferenced (SavePtr, mGlobals.IgnoreNotFound);\r
+                if (mGlobals.VerboseScan) {\r
+                  printf ("...referenced %s", SavePtr);\r
+                }\r
+              } else {\r
+                Warning (ScanFiles->Str, LineNum, 0, StringTokenPos, "expected valid string identifier name");\r
+              }\r
+            }\r
+          } else {\r
+            //\r
+            // Found it, but it's a substring of something else. Advance our pointer.\r
+            //\r
+            Cptr++;\r
+          }\r
+\r
+          if (mGlobals.VerboseScan) {\r
+            printf ("\n");\r
+          }\r
+        }\r
+      }\r
+\r
+      fclose (Fptr);\r
+    } else {\r
+      //\r
+      // Skipping this file type\r
+      //\r
+      if (mGlobals.VerboseScan) {\r
+        printf ("Skip scanning of %s\n", ScanFiles->Str);\r
+      }\r
+    }\r
+\r
+    ScanFiles = ScanFiles->Next;\r
+  }\r
+\r
+  return STATUS_SUCCESS;\r
+}\r
+//\r
+// Free the global string lists we allocated memory for\r
+//\r
+static\r
+void\r
+FreeLists (\r
+  VOID\r
+  )\r
+{\r
+  TEXT_STRING_LIST  *Temp;\r
+  WCHAR_STRING_LIST *WTemp;\r
+\r
+  //\r
+  // Traverse the include paths, freeing each\r
+  //\r
+  while (mGlobals.IncludePaths != NULL) {\r
+    Temp = mGlobals.IncludePaths->Next;\r
+    free (mGlobals.IncludePaths->Str);\r
+    free (mGlobals.IncludePaths);\r
+    mGlobals.IncludePaths = Temp;\r
+  }\r
+  //\r
+  // If we did a scan, then free up our\r
+  // list of files to scan.\r
+  //\r
+  while (mGlobals.ScanFileName != NULL) {\r
+    Temp = mGlobals.ScanFileName->Next;\r
+    free (mGlobals.ScanFileName->Str);\r
+    free (mGlobals.ScanFileName);\r
+    mGlobals.ScanFileName = Temp;\r
+  }\r
+  //\r
+  // If they gave us a list of filename extensions to\r
+  // skip on scan, then free them up.\r
+  //\r
+  while (mGlobals.SkipExt != NULL) {\r
+    Temp = mGlobals.SkipExt->Next;\r
+    free (mGlobals.SkipExt->Str);\r
+    free (mGlobals.SkipExt);\r
+    mGlobals.SkipExt = Temp;\r
+  }\r
+  //\r
+  // Free up any languages specified\r
+  //\r
+  while (mGlobals.Language != NULL) {\r
+    WTemp = mGlobals.Language->Next;\r
+    free (mGlobals.Language->Str);\r
+    free (mGlobals.Language);\r
+    mGlobals.Language = WTemp;\r
+  }\r
+  //\r
+  // Free up our indirection list\r
+  //\r
+  while (mGlobals.IndirectionList != NULL) {\r
+    mGlobals.LastIndirectionList = mGlobals.IndirectionList->Next;\r
+    free (mGlobals.IndirectionList->Str1);\r
+    free (mGlobals.IndirectionList->Str2);\r
+    free (mGlobals.IndirectionList);\r
+    mGlobals.IndirectionList = mGlobals.LastIndirectionList;\r
+  }\r
+\r
+  while (mGlobals.IndirectionFileName != NULL) {\r
+    mGlobals.LastIndirectionFileName = mGlobals.IndirectionFileName->Next;\r
+    free (mGlobals.IndirectionFileName->Str);\r
+    free (mGlobals.IndirectionFileName);\r
+    mGlobals.IndirectionFileName = mGlobals.LastIndirectionFileName;\r
+  }\r
+}\r
+\r
+static\r
+BOOLEAN\r
+IsValidIdentifierChar (\r
+  INT8      Char,\r
+  BOOLEAN   FirstChar\r
+  )\r
+{\r
+  //\r
+  // If it's the first character of an identifier, then\r
+  // it must be one of [A-Za-z_].\r
+  //\r
+  if (FirstChar) {\r
+    if (isalpha (Char) || (Char == '_')) {\r
+      return TRUE;\r
+    }\r
+  } else {\r
+    //\r
+    // If it's not the first character, then it can\r
+    // be one of [A-Za-z_0-9]\r
+    //\r
+    if (isalnum (Char) || (Char == '_')) {\r
+      return TRUE;\r
+    }\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+static\r
+void\r
+RewindFile (\r
+  SOURCE_FILE *SourceFile\r
+  )\r
+{\r
+  SourceFile->LineNum       = 1;\r
+  SourceFile->FileBufferPtr = SourceFile->FileBuffer;\r
+  SourceFile->EndOfFile     = 0;\r
+}\r
+\r
+static\r
+BOOLEAN\r
+SkipTo (\r
+  SOURCE_FILE *SourceFile,\r
+  WCHAR       WChar,\r
+  BOOLEAN     StopAfterNewline\r
+  )\r
+{\r
+  while (!EndOfFile (SourceFile)) {\r
+    //\r
+    // Check for the character of interest\r
+    //\r
+    if (SourceFile->FileBufferPtr[0] == WChar) {\r
+      return TRUE;\r
+    } else {\r
+      if (SourceFile->FileBufferPtr[0] == UNICODE_LF) {\r
+        SourceFile->LineNum++;\r
+        if (StopAfterNewline) {\r
+          SourceFile->FileBufferPtr++;\r
+          if (SourceFile->FileBufferPtr[0] == 0) {\r
+            SourceFile->FileBufferPtr++;\r
+          }\r
+\r
+          return FALSE;\r
+        }\r
+      }\r
+\r
+      SourceFile->FileBufferPtr++;\r
+    }\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+static\r
+void\r
+Usage (\r
+  VOID\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+  Print usage information for this utility.\r
+  \r
+Arguments:\r
+\r
+  None.\r
+\r
+Returns:\r
+\r
+  Nothing.\r
+  \r
+--*/\r
+{\r
+  int               Index;\r
+  static const char *Str[] = {\r
+    "",\r
+    PROGRAM_NAME " version "TOOL_VERSION " -- process unicode strings file",\r
+    "  Usage: "PROGRAM_NAME " -parse {parse options} [FileNames]",\r
+    "         "PROGRAM_NAME " -scan {scan options} [FileName]",\r
+    "         "PROGRAM_NAME " -dump {dump options}",\r
+    "    Common options include:",\r
+    "      -h or -?         for this help information",\r
+    "      -db Database     required name of output/input database file",\r
+    "      -bn BaseName     for use in the .h and .c output files",\r
+    "                       Default = "DEFAULT_BASE_NAME,\r
+    "      -v               for verbose output",\r
+    "      -vdbw            for verbose output when writing database",\r
+    "      -vdbr            for verbose output when reading database",\r
+    "      -od FileName     to specify an output database file name",\r
+    "    Parse options include:",\r
+    "      -i IncludePath   add IncludePath to list of search paths",\r
+    "      -dep FileName    to specify an output dependency file name",\r
+    "      -newdb           to not read in existing database file",\r
+    "      -uqs             to indicate that unquoted strings are used",\r
+    "      FileNames        name of one or more unicode files to parse",\r
+    "    Scan options include:",\r
+    "      -scan            scan text file(s) for STRING_TOKEN() usage",\r
+    "      -skipext .ext    to skip scan of files with .ext filename extension",\r
+    "      -ignorenotfound  ignore if a given STRING_TOKEN(STR) is not ",\r
+    "                       found in the database",\r
+    "      FileNames        one or more files to scan",\r
+    "    Dump options include:",\r
+    "      -oc FileName     write string data to FileName",\r
+    "      -oh FileName     write string defines to FileName",\r
+    "      -ou FileName     dump database to unicode file FileName",\r
+    "      -lang Lang       only dump for the language 'Lang'",\r
+    "      -if FileName     to specify an indirection file",\r
+    "      -hpk FileName    to create an HII export pack of the strings",\r
+    "",\r
+    "  The expected process is to parse a unicode string file to create an initial",\r
+    "  database of string identifier names and string definitions. Then text files",\r
+    "  should be scanned for STRING_TOKEN() usages, and the referenced",\r
+    "  strings will be tagged as used in the database. After all files have been",\r
+    "  scanned, then the database should be dumped to create the necessary output",\r
+    "  files.",\r
+    "",\r
+    NULL\r
+  };\r
+  for (Index = 0; Str[Index] != NULL; Index++) {\r
+    fprintf (stdout, "%s\n", Str[Index]);\r
+  }\r
+}\r