Home | History | Annotate | Download | only in UefiStrGather
      1 /*++
      2 
      3 Copyright (c) 2004 - 2010, Intel Corporation. All rights reserved.<BR>
      4 This program and the accompanying materials
      5 are licensed and made available under the terms and conditions of the BSD License
      6 which accompanies this distribution.  The full text of the license may be found at
      7 http://opensource.org/licenses/bsd-license.php
      8 
      9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     11 
     12 Module Name:
     13 
     14   StrGather.c
     15 
     16 Abstract:
     17 
     18   Parse a strings file and create or add to a string database file.
     19 
     20 --*/
     21 
     22 #include <stdio.h>
     23 #include <string.h>
     24 #include <stdlib.h>
     25 #include <ctype.h>
     26 #include <Tiano.h>
     27 #include <EfiUtilityMsgs.h>
     28 #include <EfiHii.h>
     29 #include "StrGather.h"
     30 #include "StringDB.h"
     31 
     32 #define UTILITY_NAME     "StrGather"
     33 #define UTILITY_VERSION  "v1.2"
     34 
     35 typedef UINT16  WCHAR;
     36 
     37 #define MAX_PATH                    1024
     38 #define MAX_NEST_DEPTH              20  // just in case we get in an endless loop.
     39 #define MAX_STRING_IDENTIFIER_NAME  128 // number of wchars
     40 #define MAX_LINE_LEN                400
     41 #define STRING_TOKEN                "STRING_TOKEN"
     42 #define DEFAULT_BASE_NAME           "BaseName"
     43 //
     44 // Operational modes for this utility
     45 //
     46 #define MODE_UNKNOWN  0
     47 #define MODE_PARSE    1
     48 #define MODE_SCAN     2
     49 #define MODE_DUMP     3
     50 
     51 //
     52 // This is how we invoke the C preprocessor on the source file
     53 // to resolve #if, #else, etc.
     54 //
     55 #define PREPROCESSOR_COMMAND                "cl"
     56 #define PREPROCESSOR_OPTIONS                "/nologo /EP /TC /DSTRGATHER"
     57 #define PREPROCESS_TEMP_FILE_EXTENSION      ".ii"
     58 #define PREPROCESS_OUTPUT_FILE_EXTENSION    ".iii"
     59 
     60 //
     61 // We keep a linked list of these for the source files we process
     62 //
     63 typedef struct _SOURCE_FILE {
     64   FILE                *Fptr;
     65   WCHAR               *FileBuffer;
     66   WCHAR               *FileBufferPtr;
     67   UINT32              FileSize;
     68   INT8                FileName[MAX_PATH];
     69   UINT32              LineNum;
     70   BOOLEAN             EndOfFile;
     71   BOOLEAN             SkipToHash;
     72   struct _SOURCE_FILE *Previous;
     73   struct _SOURCE_FILE *Next;
     74   WCHAR               ControlCharacter;
     75 } SOURCE_FILE;
     76 
     77 #define DEFAULT_CONTROL_CHARACTER UNICODE_SLASH
     78 
     79 //
     80 // Here's all our globals. We need a linked list of include paths, a linked
     81 // list of source files, a linked list of subdirectories (appended to each
     82 // include path when searching), and a couple other fields.
     83 //
     84 static struct {
     85   SOURCE_FILE                 SourceFiles;
     86   TEXT_STRING_LIST            *IncludePaths;                    // all include paths to search
     87   TEXT_STRING_LIST            *LastIncludePath;
     88   TEXT_STRING_LIST            *ScanFileName;
     89   TEXT_STRING_LIST            *LastScanFileName;
     90   TEXT_STRING_LIST            *SkipExt;                         // if -skipext .uni
     91   TEXT_STRING_LIST            *LastSkipExt;
     92   TEXT_STRING_LIST            *IndirectionFileName;
     93   TEXT_STRING_LIST            *LastIndirectionFileName;
     94   TEXT_STRING_LIST            *DatabaseFileName;
     95   TEXT_STRING_LIST            *LastDatabaseFileName;
     96   TEXT_STRING_LIST            *PreprocessFlags;
     97   TEXT_STRING_LIST            *LastPreprocessFlags;
     98   WCHAR_STRING_LIST           *Language;
     99   WCHAR_STRING_LIST           *LastLanguage;
    100   WCHAR_MATCHING_STRING_LIST  *IndirectionList;                 // from indirection file(s)
    101   WCHAR_MATCHING_STRING_LIST  *LastIndirectionList;
    102   BOOLEAN                     Verbose;                          // for more detailed output
    103   BOOLEAN                     VerboseDatabaseWrite;             // for more detailed output when writing database
    104   BOOLEAN                     VerboseDatabaseRead;              // for more detailed output when reading database
    105   BOOLEAN                     NewDatabase;                      // to start from scratch
    106   BOOLEAN                     IgnoreNotFound;                   // when scanning
    107   BOOLEAN                     VerboseScan;
    108   BOOLEAN                     UnquotedStrings;                  // -uqs option
    109   BOOLEAN                     Preprocess;                       // -ppflag option
    110   INT8                        PreprocessFileName[MAX_PATH];
    111   INT8                        OutputDatabaseFileName[MAX_PATH];
    112   INT8                        StringHFileName[MAX_PATH];
    113   INT8                        StringCFileName[MAX_PATH];        // output .C filename
    114   INT8                        DumpUFileName[MAX_PATH];          // output unicode dump file name
    115   INT8                        HiiExportPackFileName[MAX_PATH];  // HII export pack file name
    116   INT8                        BaseName[MAX_PATH];               // base filename of the strings file
    117   INT8                        OutputDependencyFileName[MAX_PATH];
    118   FILE                        *OutputDependencyFptr;
    119   UINT32                      Mode;
    120 } mGlobals;
    121 
    122 static
    123 BOOLEAN
    124 IsValidIdentifierChar (
    125   INT8      Char,
    126   BOOLEAN   FirstChar
    127   );
    128 
    129 static
    130 void
    131 RewindFile (
    132   SOURCE_FILE *SourceFile
    133   );
    134 
    135 static
    136 BOOLEAN
    137 SkipTo (
    138   SOURCE_FILE *SourceFile,
    139   WCHAR       WChar,
    140   BOOLEAN     StopAfterNewline
    141   );
    142 
    143 static
    144 UINT32
    145 SkipWhiteSpace (
    146   SOURCE_FILE *SourceFile
    147   );
    148 
    149 static
    150 BOOLEAN
    151 IsWhiteSpace (
    152   SOURCE_FILE *SourceFile
    153   );
    154 
    155 static
    156 BOOLEAN
    157 EndOfFile (
    158   SOURCE_FILE *SourceFile
    159   );
    160 
    161 static
    162 void
    163 PreprocessFile (
    164   SOURCE_FILE *SourceFile
    165   );
    166 
    167 static
    168 UINT32
    169 GetStringIdentifierName (
    170   IN SOURCE_FILE  *SourceFile,
    171   IN OUT WCHAR    *StringIdentifierName,
    172   IN UINT32       StringIdentifierNameLen
    173   );
    174 
    175 static
    176 STATUS
    177 GetLanguageIdentifierName (
    178   IN SOURCE_FILE  *SourceFile,
    179   IN OUT WCHAR    *LanguageIdentifierName,
    180   IN UINT32       LanguageIdentifierNameLen,
    181   IN BOOLEAN      Optional
    182   );
    183 
    184 static
    185 WCHAR *
    186 GetPrintableLanguageName (
    187   IN SOURCE_FILE  *SourceFile
    188   );
    189 
    190 static
    191 STATUS
    192 AddCommandLineLanguage (
    193   IN INT8          *Language
    194   );
    195 
    196 static
    197 WCHAR *
    198 GetQuotedString (
    199   SOURCE_FILE *SourceFile,
    200   BOOLEAN     Optional
    201   );
    202 
    203 static
    204 STATUS
    205 ProcessIncludeFile (
    206   SOURCE_FILE *SourceFile,
    207   SOURCE_FILE *ParentSourceFile
    208   );
    209 
    210 static
    211 STATUS
    212 ParseFile (
    213   SOURCE_FILE *SourceFile
    214   );
    215 
    216 static
    217 FILE  *
    218 FindFile (
    219   IN INT8     *FileName,
    220   OUT INT8    *FoundFileName,
    221   IN UINT32   FoundFileNameLen
    222   );
    223 
    224 static
    225 STATUS
    226 ProcessArgs (
    227   int   Argc,
    228   char  *Argv[]
    229   );
    230 
    231 static
    232 STATUS
    233 ProcessFile (
    234   SOURCE_FILE *SourceFile
    235   );
    236 
    237 static
    238 UINT32
    239 wstrcmp (
    240   WCHAR *Buffer,
    241   WCHAR *Str
    242   );
    243 
    244 static
    245 WCHAR *
    246 wstrcatenate (
    247   WCHAR *Dst,
    248   WCHAR *Src
    249   );
    250 
    251 static
    252 void
    253 Usage (
    254   VOID
    255   );
    256 
    257 static
    258 void
    259 FreeLists (
    260   VOID
    261   );
    262 
    263 static
    264 void
    265 ProcessTokenString (
    266   SOURCE_FILE *SourceFile
    267   );
    268 
    269 static
    270 void
    271 ProcessTokenInclude (
    272   SOURCE_FILE *SourceFile
    273   );
    274 
    275 static
    276 void
    277 ProcessTokenScope (
    278   SOURCE_FILE *SourceFile
    279   );
    280 
    281 static
    282 void
    283 ProcessTokenLanguage (
    284   SOURCE_FILE *SourceFile
    285   );
    286 
    287 static
    288 void
    289 ProcessTokenLangDef (
    290   SOURCE_FILE *SourceFile
    291   );
    292 
    293 static
    294 VOID
    295 ProcessTokenSecondaryLangDef (
    296   SOURCE_FILE *SourceFile
    297   );
    298 
    299 static
    300 STATUS
    301 ScanFiles (
    302   TEXT_STRING_LIST *ScanFiles
    303   );
    304 
    305 static
    306 STATUS
    307 ParseIndirectionFiles (
    308   TEXT_STRING_LIST    *Files
    309   );
    310 
    311 int
    312 main (
    313   int   Argc,
    314   char  *Argv[]
    315   )
    316 /*++
    317 
    318 Routine Description:
    319 
    320   Call the routine to parse the command-line options, then process the file.
    321 
    322 Arguments:
    323 
    324   Argc - Standard C main() argc and argv.
    325   Argv - Standard C main() argc and argv.
    326 
    327 Returns:
    328 
    329   0       if successful
    330   nonzero otherwise
    331 
    332 --*/
    333 {
    334   STATUS  Status;
    335 
    336   SetUtilityName (UTILITY_NAME);
    337   //
    338   // Process the command-line arguments
    339   //
    340   Status = ProcessArgs (Argc, Argv);
    341   if (Status != STATUS_SUCCESS) {
    342     return Status;
    343   }
    344   //
    345   // Initialize the database manager
    346   //
    347   StringDBConstructor ();
    348   //
    349   // We always try to read in an existing database file. It may not
    350   // exist, which is ok usually.
    351   //
    352   if (mGlobals.NewDatabase == 0) {
    353     //
    354     // Read all databases specified.
    355     //
    356     for (mGlobals.LastDatabaseFileName = mGlobals.DatabaseFileName;
    357          mGlobals.LastDatabaseFileName != NULL;
    358          mGlobals.LastDatabaseFileName = mGlobals.LastDatabaseFileName->Next
    359         ) {
    360       Status = StringDBReadDatabase (mGlobals.LastDatabaseFileName->Str, TRUE, mGlobals.VerboseDatabaseRead);
    361       if (Status != STATUS_SUCCESS) {
    362         return Status;
    363       }
    364     }
    365   }
    366   //
    367   // Read indirection file(s) if specified
    368   //
    369   if (ParseIndirectionFiles (mGlobals.IndirectionFileName) != STATUS_SUCCESS) {
    370     goto Finish;
    371   }
    372   //
    373   // If scanning source files, do that now
    374   //
    375   if (mGlobals.Mode == MODE_SCAN) {
    376     ScanFiles (mGlobals.ScanFileName);
    377   } else if (mGlobals.Mode == MODE_PARSE) {
    378     //
    379     // Parsing a unicode strings file
    380     //
    381     mGlobals.SourceFiles.ControlCharacter = DEFAULT_CONTROL_CHARACTER;
    382     if (mGlobals.OutputDependencyFileName[0] != 0) {
    383       if ((mGlobals.OutputDependencyFptr = fopen (mGlobals.OutputDependencyFileName, "w")) == NULL) {
    384         Error (NULL, 0, 0, mGlobals.OutputDependencyFileName, "failed to open output dependency file");
    385         goto Finish;
    386       }
    387     }
    388     Status = ProcessIncludeFile (&mGlobals.SourceFiles, NULL);
    389     if (mGlobals.OutputDependencyFptr != NULL) {
    390       fclose (mGlobals.OutputDependencyFptr);
    391     }
    392     if (Status != STATUS_SUCCESS) {
    393       goto Finish;
    394     }
    395   }
    396   //
    397   // Create the string defines header file if there have been no errors.
    398   //
    399   ParserSetPosition (NULL, 0);
    400   if ((mGlobals.StringHFileName[0] != 0) && (GetUtilityStatus () < STATUS_ERROR)) {
    401     Status = StringDBDumpStringDefines (mGlobals.StringHFileName, mGlobals.BaseName);
    402     if (Status != EFI_SUCCESS) {
    403       goto Finish;
    404     }
    405   }
    406 
    407   //
    408   // Dump the strings to a .c file if there have still been no errors.
    409   //
    410   if ((mGlobals.StringCFileName[0] != 0) && (GetUtilityStatus () < STATUS_ERROR)) {
    411     Status = StringDBDumpCStrings (
    412               mGlobals.BaseName,
    413               mGlobals.StringCFileName,
    414               mGlobals.Language
    415               );
    416     if (Status != EFI_SUCCESS) {
    417       goto Finish;
    418     }
    419   }
    420 
    421   //
    422   // Dump the database if requested
    423   //
    424   if ((mGlobals.DumpUFileName[0] != 0) && (GetUtilityStatus () < STATUS_ERROR)) {
    425     StringDBDumpDatabase (NULL, mGlobals.DumpUFileName, FALSE);
    426   }
    427   //
    428   // Dump the string data as HII binary string pack if requested
    429   //
    430   if ((mGlobals.HiiExportPackFileName[0] != 0) && (GetUtilityStatus () < STATUS_ERROR)) {
    431     StringDBCreateHiiExportPack (mGlobals.HiiExportPackFileName, mGlobals.Language);
    432   }
    433   //
    434   // Always update the database if no errors and not in dump mode. If they specified -od
    435   // for an output database file name, then use that name. Otherwise use the name of
    436   // the first database file specified with -db
    437   //
    438   if ((mGlobals.Mode != MODE_DUMP) && (GetUtilityStatus () < STATUS_ERROR)) {
    439     if (mGlobals.OutputDatabaseFileName[0]) {
    440       Status = StringDBWriteDatabase (mGlobals.OutputDatabaseFileName, mGlobals.VerboseDatabaseWrite);
    441     } else {
    442       Status = StringDBWriteDatabase (mGlobals.DatabaseFileName->Str, mGlobals.VerboseDatabaseWrite);
    443     }
    444 
    445     if (Status != EFI_SUCCESS) {
    446       goto Finish;
    447     }
    448   }
    449 
    450 Finish:
    451   //
    452   // Free up memory
    453   //
    454   FreeLists ();
    455   StringDBDestructor ();
    456   return GetUtilityStatus ();
    457 }
    458 
    459 static
    460 STATUS
    461 ProcessIncludeFile (
    462   SOURCE_FILE *SourceFile,
    463   SOURCE_FILE *ParentSourceFile
    464   )
    465 /*++
    466 
    467 Routine Description:
    468 
    469   Given a source file, open the file and parse it
    470 
    471 Arguments:
    472 
    473   SourceFile        - name of file to parse
    474   ParentSourceFile  - for error reporting purposes, the file that #included SourceFile.
    475 
    476 Returns:
    477 
    478   Standard status.
    479 
    480 --*/
    481 {
    482   static UINT32 NestDepth = 0;
    483   INT8          FoundFileName[MAX_PATH];
    484   STATUS        Status;
    485 
    486   Status = STATUS_SUCCESS;
    487   NestDepth++;
    488   //
    489   // Print the file being processed. Indent so you can tell the include nesting
    490   // depth.
    491   //
    492   if (mGlobals.Verbose) {
    493     fprintf (stdout, "%*cProcessing file '%s'\n", NestDepth * 2, ' ', SourceFile->FileName);
    494   }
    495 
    496   //
    497   // Make sure we didn't exceed our maximum nesting depth
    498   //
    499   if (NestDepth > MAX_NEST_DEPTH) {
    500     Error (NULL, 0, 0, SourceFile->FileName, "max nesting depth (%d) exceeded", NestDepth);
    501     Status = STATUS_ERROR;
    502     goto Finish;
    503   }
    504   //
    505   // Try to open the file locally, and if that fails try along our include paths.
    506   //
    507   strcpy (FoundFileName, SourceFile->FileName);
    508   if ((SourceFile->Fptr = fopen (FoundFileName, "rb")) == NULL) {
    509     //
    510     // Try to find it among the paths if it has a parent (that is, it is included
    511     // by someone else).
    512     //
    513     if (ParentSourceFile == NULL) {
    514       Error (NULL, 0, 0, SourceFile->FileName, "file not found");
    515       Status = STATUS_ERROR;
    516       goto Finish;
    517     }
    518 
    519     SourceFile->Fptr = FindFile (SourceFile->FileName, FoundFileName, sizeof (FoundFileName));
    520     if (SourceFile->Fptr == NULL) {
    521       Error (ParentSourceFile->FileName, ParentSourceFile->LineNum, 0, SourceFile->FileName, "include file not found");
    522       Status = STATUS_ERROR;
    523       goto Finish;
    524     }
    525   }
    526 
    527   //
    528   // Output the dependency
    529   //
    530   if (mGlobals.OutputDependencyFptr != NULL) {
    531     fprintf (mGlobals.OutputDependencyFptr, "%s : %s\n", mGlobals.DatabaseFileName->Str, FoundFileName);
    532     //
    533     // Add pseudo target to avoid incremental build failure when the file is deleted
    534     //
    535     fprintf (mGlobals.OutputDependencyFptr, "%s : \n", FoundFileName);
    536   }
    537 
    538   //
    539   // Process the file found
    540   //
    541   ProcessFile (SourceFile);
    542 
    543 Finish:
    544   NestDepth--;
    545   //
    546   // Close open files and return status
    547   //
    548   if (SourceFile->Fptr != NULL) {
    549     fclose (SourceFile->Fptr);
    550   }
    551 
    552   return Status;
    553 }
    554 
    555 static
    556 STATUS
    557 ProcessFile (
    558   SOURCE_FILE *SourceFile
    559   )
    560 {
    561   //
    562   // Get the file size, and then read the entire thing into memory.
    563   // Allocate space for a terminator character.
    564   //
    565   fseek (SourceFile->Fptr, 0, SEEK_END);
    566   SourceFile->FileSize = ftell (SourceFile->Fptr);
    567   fseek (SourceFile->Fptr, 0, SEEK_SET);
    568   SourceFile->FileBuffer = (WCHAR *) malloc (SourceFile->FileSize + sizeof (WCHAR));
    569   if (SourceFile->FileBuffer == NULL) {
    570     Error (NULL, 0, 0, "memory allocation failure", NULL);
    571     return STATUS_ERROR;
    572   }
    573 
    574   fread ((VOID *) SourceFile->FileBuffer, SourceFile->FileSize, 1, SourceFile->Fptr);
    575   SourceFile->FileBuffer[(SourceFile->FileSize / sizeof (WCHAR))] = UNICODE_NULL;
    576   //
    577   // Pre-process the file to replace comments with spaces
    578   //
    579   PreprocessFile (SourceFile);
    580   //
    581   // Parse the file
    582   //
    583   ParseFile (SourceFile);
    584   free (SourceFile->FileBuffer);
    585   return STATUS_SUCCESS;
    586 }
    587 
    588 static
    589 STATUS
    590 ParseFile (
    591   SOURCE_FILE *SourceFile
    592   )
    593 {
    594   BOOLEAN InComment;
    595   UINT32  Len;
    596 
    597   //
    598   // First character of a unicode file is special. Make sure
    599   //
    600   if (SourceFile->FileBufferPtr[0] != UNICODE_FILE_START) {
    601     Error (SourceFile->FileName, 1, 0, SourceFile->FileName, "file does not appear to be a unicode file");
    602     return STATUS_ERROR;
    603   }
    604 
    605   SourceFile->FileBufferPtr++;
    606   InComment = FALSE;
    607   //
    608   // Print the first line if in verbose mode
    609   //
    610   if (mGlobals.Verbose) {
    611     printf ("%d: %S\n", SourceFile->LineNum, SourceFile->FileBufferPtr);
    612   }
    613   //
    614   // Since the syntax is relatively straightforward, just switch on the next char
    615   //
    616   while (!EndOfFile (SourceFile)) {
    617     //
    618     // Check for whitespace
    619     //
    620     if (SourceFile->FileBufferPtr[0] == UNICODE_SPACE) {
    621       SourceFile->FileBufferPtr++;
    622     } else if (SourceFile->FileBufferPtr[0] == UNICODE_TAB) {
    623       SourceFile->FileBufferPtr++;
    624     } else if (SourceFile->FileBufferPtr[0] == UNICODE_CR) {
    625       SourceFile->FileBufferPtr++;
    626     } else if (SourceFile->FileBufferPtr[0] == UNICODE_LF) {
    627       SourceFile->FileBufferPtr++;
    628       SourceFile->LineNum++;
    629       if (mGlobals.Verbose) {
    630         printf ("%d: %S\n", SourceFile->LineNum, SourceFile->FileBufferPtr);
    631       }
    632 
    633       InComment = FALSE;
    634     } else if (SourceFile->FileBufferPtr[0] == 0) {
    635       SourceFile->FileBufferPtr++;
    636     } else if (InComment) {
    637       SourceFile->FileBufferPtr++;
    638     } else if ((SourceFile->FileBufferPtr[0] == UNICODE_SLASH) && (SourceFile->FileBufferPtr[1] == UNICODE_SLASH)) {
    639       SourceFile->FileBufferPtr += 2;
    640       InComment = TRUE;
    641     } else if (SourceFile->SkipToHash && (SourceFile->FileBufferPtr[0] != SourceFile->ControlCharacter)) {
    642       SourceFile->FileBufferPtr++;
    643     } else {
    644       SourceFile->SkipToHash = FALSE;
    645       if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) &&
    646           ((Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"include")) > 0)
    647           ) {
    648         SourceFile->FileBufferPtr += Len + 1;
    649         ProcessTokenInclude (SourceFile);
    650       } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) &&
    651                (Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"scope")) > 0
    652               ) {
    653         SourceFile->FileBufferPtr += Len + 1;
    654         ProcessTokenScope (SourceFile);
    655       } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) &&
    656                (Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"language")) > 0
    657               ) {
    658         SourceFile->FileBufferPtr += Len + 1;
    659         ProcessTokenLanguage (SourceFile);
    660       } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) &&
    661                (Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"langdef")) > 0
    662               ) {
    663         SourceFile->FileBufferPtr += Len + 1;
    664         ProcessTokenLangDef (SourceFile);
    665       } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) &&
    666                  (Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"secondarylang")) > 0
    667                 ) {
    668         SourceFile->FileBufferPtr += Len + 1;
    669         ProcessTokenSecondaryLangDef (SourceFile);
    670       } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) &&
    671                (Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"string")) > 0
    672               ) {
    673         SourceFile->FileBufferPtr += Len + 1;
    674         ProcessTokenString (SourceFile);
    675       } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) &&
    676                (Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"EFI_BREAKPOINT()")) > 0
    677               ) {
    678         SourceFile->FileBufferPtr += Len;
    679         EFI_BREAKPOINT ();
    680       } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) &&
    681                (SourceFile->FileBufferPtr[1] == UNICODE_EQUAL_SIGN)
    682               ) {
    683         SourceFile->ControlCharacter = SourceFile->FileBufferPtr[2];
    684         SourceFile->FileBufferPtr += 3;
    685       } else {
    686         Error (SourceFile->FileName, SourceFile->LineNum, 0, "unrecognized token", "%S", SourceFile->FileBufferPtr);
    687         //
    688         // Treat rest of line as a comment.
    689         //
    690         InComment = TRUE;
    691       }
    692     }
    693   }
    694 
    695   return STATUS_SUCCESS;
    696 }
    697 
    698 static
    699 void
    700 PreprocessFile (
    701   SOURCE_FILE *SourceFile
    702   )
    703 /*++
    704 
    705 Routine Description:
    706   Preprocess a file to replace all carriage returns with NULLs so
    707   we can print lines from the file to the screen.
    708 
    709 Arguments:
    710   SourceFile - structure that we use to keep track of an input file.
    711 
    712 Returns:
    713   Nothing.
    714 
    715 --*/
    716 {
    717   BOOLEAN InComment;
    718 
    719   RewindFile (SourceFile);
    720   InComment = FALSE;
    721   while (!EndOfFile (SourceFile)) {
    722     //
    723     // If a line-feed, then no longer in a comment
    724     //
    725     if (SourceFile->FileBufferPtr[0] == UNICODE_LF) {
    726       SourceFile->FileBufferPtr++;
    727       SourceFile->LineNum++;
    728       InComment = 0;
    729     } else if (SourceFile->FileBufferPtr[0] == UNICODE_CR) {
    730       //
    731       // Replace all carriage returns with a NULL so we can print stuff
    732       //
    733       SourceFile->FileBufferPtr[0] = 0;
    734       SourceFile->FileBufferPtr++;
    735     } else if (InComment) {
    736       SourceFile->FileBufferPtr[0] = UNICODE_SPACE;
    737       SourceFile->FileBufferPtr++;
    738     } else if ((SourceFile->FileBufferPtr[0] == UNICODE_SLASH) && (SourceFile->FileBufferPtr[1] == UNICODE_SLASH)) {
    739       SourceFile->FileBufferPtr += 2;
    740       InComment = TRUE;
    741     } else {
    742       SourceFile->FileBufferPtr++;
    743     }
    744   }
    745   //
    746   // Could check for end-of-file and still in a comment, but
    747   // should not be necessary. So just restore the file pointers.
    748   //
    749   RewindFile (SourceFile);
    750 }
    751 
    752 static
    753 WCHAR *
    754 GetPrintableLanguageName (
    755   IN SOURCE_FILE  *SourceFile
    756   )
    757 {
    758   WCHAR   *String;
    759   WCHAR   *Start;
    760   WCHAR   *Ptr;
    761   UINT32  Len;
    762 
    763   SkipWhiteSpace (SourceFile);
    764   if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) {
    765     Error (SourceFile->FileName, SourceFile->LineNum, 0, "expected quoted printable language name", "%S", SourceFile->FileBufferPtr);
    766     SourceFile->SkipToHash = TRUE;
    767     return NULL;
    768   }
    769 
    770   Len = 0;
    771   SourceFile->FileBufferPtr++;
    772   Start = Ptr = SourceFile->FileBufferPtr;
    773   while (!EndOfFile (SourceFile)) {
    774     if (SourceFile->FileBufferPtr[0] == UNICODE_CR) {
    775       Warning (SourceFile->FileName, SourceFile->LineNum, 0, "carriage return found in quoted string", "%S", Start);
    776       break;
    777     } else if (SourceFile->FileBufferPtr[0] == UNICODE_DOUBLE_QUOTE) {
    778       break;
    779     }
    780 
    781     SourceFile->FileBufferPtr++;
    782     Len++;
    783   }
    784 
    785   if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) {
    786     Warning (
    787       SourceFile->FileName,
    788       SourceFile->LineNum,
    789       0,
    790       "missing closing quote on printable language name string",
    791       "%S",
    792       Start
    793       );
    794   } else {
    795     SourceFile->FileBufferPtr++;
    796   }
    797   //
    798   // Now allocate memory for the string and save it off
    799   //
    800   String = (WCHAR *) malloc ((Len + 1) * sizeof (WCHAR));
    801   if (String == NULL) {
    802     Error (NULL, 0, 0, "memory allocation failed", NULL);
    803     return NULL;
    804   }
    805   //
    806   // Copy the string from the file buffer to the local copy.
    807   // We do no reformatting of it whatsoever at this point.
    808   //
    809   Ptr = String;
    810   while (Len > 0) {
    811     *Ptr = *Start;
    812     Start++;
    813     Ptr++;
    814     Len--;
    815   }
    816 
    817   *Ptr = 0;
    818   //
    819   // Now format the string to convert \wide and \narrow controls
    820   //
    821   StringDBFormatString (String);
    822   return String;
    823 }
    824 
    825 static struct {
    826   WCHAR *ISO639;
    827   WCHAR *RFC3066;
    828 } LanguageConvertTable[] = {
    829   { L"eng", L"en-US" },
    830   { L"fra", L"fr-FR" },
    831   { L"spa", L"es-ES" },
    832   { NULL, NULL }
    833 };
    834 
    835 WCHAR *
    836 GetLangCode (
    837   IN WCHAR                        *Lang
    838   )
    839 {
    840   UINT32  Index;
    841   WCHAR   *LangCode;
    842 
    843   LangCode = NULL;
    844 
    845   //
    846   // The Lang is xx-XX format and return.
    847   //
    848   if (wcschr (Lang, L'-') != NULL) {
    849     LangCode = (WCHAR *) malloc ((wcslen (Lang) + 1) * sizeof(WCHAR));
    850     if (LangCode != NULL) {
    851       wcscpy (LangCode, Lang);
    852     }
    853     return LangCode;
    854   }
    855 
    856   //
    857   // Convert the language accoring to the table.
    858   //
    859   for (Index = 0; LanguageConvertTable[Index].ISO639 != NULL; Index++) {
    860     if (wcscmp(LanguageConvertTable[Index].ISO639, Lang) == 0) {
    861       LangCode = (WCHAR *) malloc ((wcslen (LanguageConvertTable[Index].RFC3066) + 1) * sizeof (WCHAR));
    862       if (LangCode != NULL) {
    863         wcscpy (LangCode, LanguageConvertTable[Index].RFC3066);
    864       }
    865       return LangCode;
    866     }
    867   }
    868 
    869   return NULL;
    870 }
    871 
    872 WCHAR *
    873 GetLangCodeList (
    874   IN WCHAR                        *SecondaryLangList
    875   )
    876 {
    877   WCHAR *CodeBeg, *CodeEnd;
    878   WCHAR *CodeRet;
    879   WCHAR *LangCodeList = NULL;
    880   WCHAR *TempLangCodeList = NULL;
    881 
    882   TempLangCodeList = (WCHAR *) malloc ((wcslen(SecondaryLangList) + 1) * sizeof(WCHAR));
    883   if (TempLangCodeList == NULL) {
    884     return NULL;
    885   }
    886   wcscpy (TempLangCodeList, SecondaryLangList);
    887   CodeBeg = TempLangCodeList;
    888 
    889   while (CodeBeg != NULL) {
    890     CodeEnd = wcschr (CodeBeg, L';');
    891     if (CodeEnd != NULL) {
    892       *CodeEnd = L'\0';
    893       CodeEnd++;
    894     }
    895 
    896     CodeRet = GetLangCode (CodeBeg);
    897     if (CodeRet != NULL) {
    898       if (LangCodeList != NULL) {
    899         LangCodeList = wstrcatenate (LangCodeList, L";");
    900       }
    901       LangCodeList = wstrcatenate (LangCodeList, CodeRet);
    902     }
    903 
    904     CodeBeg = CodeEnd;
    905     FREE (CodeRet);
    906   }
    907 
    908   free (TempLangCodeList);
    909 
    910   return LangCodeList;
    911 }
    912 
    913 static
    914 WCHAR *
    915 GetSecondaryLanguageList (
    916   IN SOURCE_FILE  *SourceFile
    917   )
    918 {
    919   WCHAR   *SecondaryLangList = NULL;
    920   WCHAR   SecondaryLang[MAX_STRING_IDENTIFIER_NAME + 1];
    921   WCHAR   *LangCodeList;
    922   WCHAR   *Start;
    923   WCHAR   *Ptr;
    924   UINT32  Index;
    925 
    926   SkipWhiteSpace (SourceFile);
    927 
    928   if (SourceFile->FileBufferPtr[0] != UNICODE_OPEN_PAREN) {
    929     Error (SourceFile->FileName, SourceFile->LineNum, 0, "expected open bracket", "%S", SourceFile->FileBufferPtr);
    930     SourceFile->SkipToHash = TRUE;
    931     return NULL;
    932   }
    933 
    934   Index             = 0;
    935   SecondaryLang [0] = L'\0';
    936   SourceFile->FileBufferPtr++;
    937   Start = Ptr = SourceFile->FileBufferPtr;
    938   while (!EndOfFile (SourceFile)) {
    939     if (((SourceFile->FileBufferPtr[0] >= UNICODE_a) && (SourceFile->FileBufferPtr[0] <= UNICODE_z)) ||
    940 		((SourceFile->FileBufferPtr[0] >= UNICODE_A) && (SourceFile->FileBufferPtr[0] <= UNICODE_Z)) ||
    941 		(SourceFile->FileBufferPtr[0] == UNICODE_MINUS)) {
    942       if (Index > MAX_STRING_IDENTIFIER_NAME) {
    943         Error (SourceFile->FileName, SourceFile->LineNum, 0, "secondary language length is too lang", "%S", SourceFile->FileBufferPtr);
    944         goto Err;
    945       }
    946       SecondaryLang[Index] = SourceFile->FileBufferPtr[0];
    947       Index++;
    948     } else if (SourceFile->FileBufferPtr[0] == UNICODE_SPACE) {
    949       SecondaryLang[Index] = L'\0';
    950 
    951       if (SecondaryLang[0] != L'\0') {
    952         if (SecondaryLangList != NULL) {
    953           SecondaryLangList = wstrcatenate (SecondaryLangList, L";");
    954         }
    955         SecondaryLangList = wstrcatenate (SecondaryLangList, SecondaryLang);
    956         Index             = 0;
    957         SecondaryLang [0] = L'\0';
    958         SourceFile->FileBufferPtr++;
    959         continue;
    960       }
    961     } else if (SourceFile->FileBufferPtr[0] == UNICODE_CLOSE_PAREN) {
    962       if (SecondaryLangList != NULL) {
    963         SecondaryLangList = wstrcatenate (SecondaryLangList, L";");
    964       }
    965       SecondaryLangList = wstrcatenate (SecondaryLangList, SecondaryLang);
    966       break;
    967     } else {
    968       Error (SourceFile->FileName, SourceFile->LineNum, 0, "can not recognize the secondary language", "%S", SourceFile->FileBufferPtr);
    969       goto Err;
    970     }
    971 
    972     SourceFile->FileBufferPtr++;
    973   }
    974 
    975   if (SourceFile->FileBufferPtr[0] != UNICODE_CLOSE_PAREN) {
    976     Error (SourceFile->FileName, SourceFile->LineNum, 0, "missing closing bracket", "%S", Start);
    977   } else {
    978     SourceFile->FileBufferPtr++;
    979   }
    980 
    981   LangCodeList = GetLangCodeList (SecondaryLangList);
    982   FREE (SecondaryLangList);
    983   return LangCodeList;
    984 
    985 Err:
    986   FREE(SecondaryLangList);
    987   return NULL;
    988 }
    989 
    990 static
    991 WCHAR *
    992 GetQuotedString (
    993   SOURCE_FILE *SourceFile,
    994   BOOLEAN     Optional
    995   )
    996 {
    997   WCHAR   *String;
    998   WCHAR   *Start;
    999   WCHAR   *Ptr;
   1000   UINT32  Len;
   1001   BOOLEAN PreviousBackslash;
   1002 
   1003   if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) {
   1004     if (!Optional) {
   1005       Error (SourceFile->FileName, SourceFile->LineNum, 0, "expected quoted string", "%S", SourceFile->FileBufferPtr);
   1006     }
   1007 
   1008     return NULL;
   1009   }
   1010 
   1011   Len = 0;
   1012   SourceFile->FileBufferPtr++;
   1013   Start             = Ptr = SourceFile->FileBufferPtr;
   1014   PreviousBackslash = FALSE;
   1015   while (!EndOfFile (SourceFile)) {
   1016     if ((SourceFile->FileBufferPtr[0] == UNICODE_DOUBLE_QUOTE) && (!PreviousBackslash)) {
   1017       break;
   1018     } else if (SourceFile->FileBufferPtr[0] == UNICODE_CR) {
   1019       Warning (SourceFile->FileName, SourceFile->LineNum, 0, "carriage return found in quoted string", "%S", Start);
   1020       PreviousBackslash = FALSE;
   1021     } else if (SourceFile->FileBufferPtr[0] == UNICODE_BACKSLASH) {
   1022       PreviousBackslash = TRUE;
   1023     } else {
   1024       PreviousBackslash = FALSE;
   1025     }
   1026 
   1027     SourceFile->FileBufferPtr++;
   1028     Len++;
   1029   }
   1030 
   1031   if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) {
   1032     Warning (SourceFile->FileName, SourceFile->LineNum, 0, "missing closing quote on string", "%S", Start);
   1033   } else {
   1034     SourceFile->FileBufferPtr++;
   1035   }
   1036   //
   1037   // Now allocate memory for the string and save it off
   1038   //
   1039   String = (WCHAR *) malloc ((Len + 1) * sizeof (WCHAR));
   1040   if (String == NULL) {
   1041     Error (NULL, 0, 0, "memory allocation failed", NULL);
   1042     return NULL;
   1043   }
   1044   //
   1045   // Copy the string from the file buffer to the local copy.
   1046   // We do no reformatting of it whatsoever at this point.
   1047   //
   1048   Ptr = String;
   1049   while (Len > 0) {
   1050     *Ptr = *Start;
   1051     Start++;
   1052     Ptr++;
   1053     Len--;
   1054   }
   1055 
   1056   *Ptr = 0;
   1057   return String;
   1058 }
   1059 //
   1060 // Parse:
   1061 //    #string STR_ID_NAME
   1062 //
   1063 // All we can do is call the string database to add the string identifier. Unfortunately
   1064 // he'll have to keep track of the last identifier we added.
   1065 //
   1066 static
   1067 void
   1068 ProcessTokenString (
   1069   SOURCE_FILE *SourceFile
   1070   )
   1071 {
   1072   WCHAR   StringIdentifier[MAX_STRING_IDENTIFIER_NAME + 1];
   1073   UINT16  StringId;
   1074   //
   1075   // Extract the string identifier name and add it to the database.
   1076   //
   1077   if (GetStringIdentifierName (SourceFile, StringIdentifier, sizeof (StringIdentifier)) > 0) {
   1078     StringId = STRING_ID_INVALID;
   1079     StringDBAddStringIdentifier (StringIdentifier, &StringId, 0);
   1080   } else {
   1081     //
   1082     // Error recovery -- skip to the next #
   1083     //
   1084     SourceFile->SkipToHash = TRUE;
   1085   }
   1086 }
   1087 
   1088 static
   1089 BOOLEAN
   1090 EndOfFile (
   1091   SOURCE_FILE *SourceFile
   1092   )
   1093 {
   1094   //
   1095   // The file buffer pointer will typically get updated before the End-of-file flag in the
   1096   // source file structure, so check it first.
   1097   //
   1098   if (SourceFile->FileBufferPtr >= SourceFile->FileBuffer + SourceFile->FileSize / sizeof (WCHAR)) {
   1099     SourceFile->EndOfFile = TRUE;
   1100     return TRUE;
   1101   }
   1102 
   1103   if (SourceFile->EndOfFile) {
   1104     return TRUE;
   1105   }
   1106 
   1107   return FALSE;
   1108 }
   1109 
   1110 static
   1111 UINT32
   1112 GetStringIdentifierName (
   1113   IN SOURCE_FILE  *SourceFile,
   1114   IN OUT WCHAR    *StringIdentifierName,
   1115   IN UINT32       StringIdentifierNameLen
   1116   )
   1117 {
   1118   UINT32  Len;
   1119   WCHAR   *From;
   1120   WCHAR   *Start;
   1121 
   1122   //
   1123   // Skip whitespace
   1124   //
   1125   SkipWhiteSpace (SourceFile);
   1126   if (SourceFile->EndOfFile) {
   1127     Error (SourceFile->FileName, SourceFile->LineNum, 0, "end-of-file encountered", "expected string identifier");
   1128     return 0;
   1129   }
   1130   //
   1131   // Verify first character of name is [A-Za-z]
   1132   //
   1133   Len = 0;
   1134   StringIdentifierNameLen /= 2;
   1135   From  = SourceFile->FileBufferPtr;
   1136   Start = SourceFile->FileBufferPtr;
   1137   if (((SourceFile->FileBufferPtr[0] >= UNICODE_A) && (SourceFile->FileBufferPtr[0] <= UNICODE_Z)) ||
   1138       ((SourceFile->FileBufferPtr[0] >= UNICODE_z) && (SourceFile->FileBufferPtr[0] <= UNICODE_z))
   1139       ) {
   1140     //
   1141     // Do nothing
   1142     //
   1143   } else {
   1144     Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid character in string identifier name", "%S", Start);
   1145     return 0;
   1146   }
   1147 
   1148   while (!EndOfFile (SourceFile)) {
   1149     if (((SourceFile->FileBufferPtr[0] >= UNICODE_A) && (SourceFile->FileBufferPtr[0] <= UNICODE_Z)) ||
   1150         ((SourceFile->FileBufferPtr[0] >= UNICODE_z) && (SourceFile->FileBufferPtr[0] <= UNICODE_z)) ||
   1151         ((SourceFile->FileBufferPtr[0] >= UNICODE_0) && (SourceFile->FileBufferPtr[0] <= UNICODE_9)) ||
   1152         (SourceFile->FileBufferPtr[0] == UNICODE_UNDERSCORE)
   1153         ) {
   1154       Len++;
   1155       if (Len >= StringIdentifierNameLen) {
   1156         Error (SourceFile->FileName, SourceFile->LineNum, 0, "string identifier name too long", "%S", Start);
   1157         return 0;
   1158       }
   1159 
   1160       *StringIdentifierName = SourceFile->FileBufferPtr[0];
   1161       StringIdentifierName++;
   1162       SourceFile->FileBufferPtr++;
   1163     } else if (SkipWhiteSpace (SourceFile) == 0) {
   1164       Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid string identifier name", "%S", Start);
   1165       return 0;
   1166     } else {
   1167       break;
   1168     }
   1169   }
   1170   //
   1171   // Terminate the copy of the string.
   1172   //
   1173   *StringIdentifierName = 0;
   1174   return Len;
   1175 }
   1176 
   1177 static
   1178 STATUS
   1179 GetLanguageIdentifierName (
   1180   IN SOURCE_FILE  *SourceFile,
   1181   IN OUT WCHAR    *LanguageIdentifierName,
   1182   IN UINT32       LanguageIdentifierNameLen,
   1183   IN BOOLEAN      Optional
   1184   )
   1185 {
   1186   UINT32  Len;
   1187   WCHAR   *Start;
   1188   WCHAR   *LangCode;
   1189   WCHAR   *LanguageIdentifier;
   1190 
   1191   LanguageIdentifier = LanguageIdentifierName;
   1192 
   1193   //
   1194   // Skip whitespace
   1195   //
   1196   SkipWhiteSpace (SourceFile);
   1197   if (SourceFile->EndOfFile) {
   1198     if (!Optional) {
   1199       Error (SourceFile->FileName, SourceFile->LineNum, 0, "end-of-file encountered", "expected language identifier");
   1200       return STATUS_ERROR;
   1201     }
   1202 
   1203     return STATUS_SUCCESS;
   1204   }
   1205   //
   1206   // This function is called to optionally get a language identifier name in:
   1207   //   #string STR_ID eng "the string"
   1208   // If it's optional, and we find a double-quote, then return now.
   1209   //
   1210   if (Optional) {
   1211     if (*SourceFile->FileBufferPtr == UNICODE_DOUBLE_QUOTE) {
   1212       return STATUS_SUCCESS;
   1213     }
   1214   }
   1215 
   1216   LanguageIdentifierNameLen /= 2;
   1217   //
   1218   // Internal error if we weren't given at least 4 WCHAR's to work with.
   1219   //
   1220   if (LanguageIdentifierNameLen < LANGUAGE_IDENTIFIER_NAME_LEN + 1) {
   1221     Error (
   1222       SourceFile->FileName,
   1223       SourceFile->LineNum,
   1224       0,
   1225       "app error -- language identifier name length is invalid",
   1226       NULL
   1227       );
   1228   }
   1229 
   1230   Len   = 0;
   1231   Start = SourceFile->FileBufferPtr;
   1232   while (!EndOfFile (SourceFile)) {
   1233     if (((SourceFile->FileBufferPtr[0] >= UNICODE_a) && (SourceFile->FileBufferPtr[0] <= UNICODE_z)) ||
   1234         ((SourceFile->FileBufferPtr[0] >= UNICODE_A) && (SourceFile->FileBufferPtr[0] <= UNICODE_Z)) ||
   1235         (SourceFile->FileBufferPtr[0] == UNICODE_MINUS)) {
   1236       Len++;
   1237       if (Len > LANGUAGE_IDENTIFIER_NAME_LEN) {
   1238         Error (SourceFile->FileName, SourceFile->LineNum, 0, "language identifier name too long", "%S", Start);
   1239         return STATUS_ERROR;
   1240       }
   1241       *LanguageIdentifierName = SourceFile->FileBufferPtr[0];
   1242       SourceFile->FileBufferPtr++;
   1243       LanguageIdentifierName++;
   1244     } else if (!IsWhiteSpace (SourceFile)) {
   1245       Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid language identifier name", "%S", Start);
   1246       return STATUS_ERROR;
   1247     } else {
   1248       break;
   1249     }
   1250   }
   1251   //
   1252   // Terminate the copy of the string.
   1253   //
   1254   *LanguageIdentifierName = 0;
   1255   LangCode = GetLangCode (LanguageIdentifier);
   1256   if (LangCode != NULL) {
   1257     wcscpy (LanguageIdentifier, LangCode);
   1258     FREE (LangCode);
   1259   }
   1260   return STATUS_SUCCESS;
   1261 }
   1262 
   1263 static
   1264 void
   1265 ProcessTokenInclude (
   1266   SOURCE_FILE *SourceFile
   1267   )
   1268 {
   1269   INT8        IncludeFileName[MAX_PATH];
   1270   INT8        *To;
   1271   UINT32      Len;
   1272   BOOLEAN     ReportedError;
   1273   SOURCE_FILE IncludedSourceFile;
   1274 
   1275   ReportedError = FALSE;
   1276   if (SkipWhiteSpace (SourceFile) == 0) {
   1277     Warning (SourceFile->FileName, SourceFile->LineNum, 0, "expected whitespace following #include keyword", NULL);
   1278   }
   1279   //
   1280   // Should be quoted file name
   1281   //
   1282   if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) {
   1283     Error (SourceFile->FileName, SourceFile->LineNum, 0, "expected quoted include file name", NULL);
   1284     goto FailDone;
   1285   }
   1286 
   1287   SourceFile->FileBufferPtr++;
   1288   //
   1289   // Copy the filename as ascii to our local string
   1290   //
   1291   To  = IncludeFileName;
   1292   Len = 0;
   1293   while (!EndOfFile (SourceFile)) {
   1294     if ((SourceFile->FileBufferPtr[0] == UNICODE_CR) || (SourceFile->FileBufferPtr[0] == UNICODE_LF)) {
   1295       Error (SourceFile->FileName, SourceFile->LineNum, 0, "end-of-line found in quoted include file name", NULL);
   1296       goto FailDone;
   1297     }
   1298 
   1299     if (SourceFile->FileBufferPtr[0] == UNICODE_DOUBLE_QUOTE) {
   1300       SourceFile->FileBufferPtr++;
   1301       break;
   1302     }
   1303     //
   1304     // If too long, then report the error once and process until the closing quote
   1305     //
   1306     Len++;
   1307     if (!ReportedError && (Len >= sizeof (IncludeFileName))) {
   1308       Error (SourceFile->FileName, SourceFile->LineNum, 0, "length of include file name exceeds limit", NULL);
   1309       ReportedError = TRUE;
   1310     }
   1311 
   1312     if (!ReportedError) {
   1313       *To = UNICODE_TO_ASCII (SourceFile->FileBufferPtr[0]);
   1314       To++;
   1315     }
   1316 
   1317     SourceFile->FileBufferPtr++;
   1318   }
   1319 
   1320   if (!ReportedError) {
   1321     *To = 0;
   1322     memset ((char *) &IncludedSourceFile, 0, sizeof (SOURCE_FILE));
   1323     strcpy (IncludedSourceFile.FileName, IncludeFileName);
   1324     IncludedSourceFile.ControlCharacter = DEFAULT_CONTROL_CHARACTER;
   1325     ProcessIncludeFile (&IncludedSourceFile, SourceFile);
   1326     //
   1327     // printf ("including file '%s'\n", IncludeFileName);
   1328     //
   1329   }
   1330 
   1331   return ;
   1332 FailDone:
   1333   //
   1334   // Error recovery -- skip to next #
   1335   //
   1336   SourceFile->SkipToHash = TRUE;
   1337 }
   1338 
   1339 static
   1340 void
   1341 ProcessTokenScope (
   1342   SOURCE_FILE *SourceFile
   1343   )
   1344 {
   1345   WCHAR StringIdentifier[MAX_STRING_IDENTIFIER_NAME + 1];
   1346   //
   1347   // Extract the scope name
   1348   //
   1349   if (GetStringIdentifierName (SourceFile, StringIdentifier, sizeof (StringIdentifier)) > 0) {
   1350     StringDBSetScope (StringIdentifier);
   1351   }
   1352 }
   1353 
   1354 //
   1355 // Parse:  #langdef eng "English"
   1356 //         #langdef chn "\wideChinese"
   1357 //
   1358 static
   1359 void
   1360 ProcessTokenLangDef (
   1361   SOURCE_FILE *SourceFile
   1362   )
   1363 {
   1364   STATUS  Status;
   1365   WCHAR   LanguageIdentifier[MAX_STRING_IDENTIFIER_NAME + 1];
   1366   WCHAR   *PrintableName;
   1367 
   1368   Status = GetLanguageIdentifierName (SourceFile, LanguageIdentifier, sizeof (LanguageIdentifier), FALSE);
   1369   if (Status != STATUS_SUCCESS) {
   1370     return;
   1371   }
   1372 
   1373   //
   1374   // Extract the printable name
   1375   //
   1376   PrintableName = GetPrintableLanguageName (SourceFile);
   1377   if (PrintableName != NULL) {
   1378     ParserSetPosition (SourceFile->FileName, SourceFile->LineNum);
   1379     StringDBAddLanguage (LanguageIdentifier, PrintableName, NULL);
   1380     FREE (PrintableName);
   1381     return ;
   1382   }
   1383   //
   1384   // Error recovery -- skip to next #
   1385   //
   1386   SourceFile->SkipToHash = TRUE;
   1387 }
   1388 
   1389 static
   1390 VOID
   1391 ProcessTokenSecondaryLangDef (
   1392   SOURCE_FILE *SourceFile
   1393   )
   1394 {
   1395   STATUS        Status;
   1396   LANGUAGE_LIST *Lang;
   1397   WCHAR         LanguageIdentifier[MAX_STRING_IDENTIFIER_NAME + 1];
   1398   WCHAR         *LangCode;
   1399   WCHAR         *SecondaryLangList = NULL;
   1400 
   1401   Status = GetLanguageIdentifierName (SourceFile, LanguageIdentifier, sizeof (LanguageIdentifier), FALSE);
   1402   if (Status != STATUS_SUCCESS) {
   1403     return;
   1404   }
   1405   LangCode = GetLangCode(LanguageIdentifier);
   1406   if (LangCode == NULL) {
   1407     return ;
   1408   }
   1409 
   1410   Lang = StringDBFindLanguageList (LanguageIdentifier);
   1411   if (Lang == NULL) {
   1412     return;
   1413   }
   1414 
   1415   SecondaryLangList = GetSecondaryLanguageList (SourceFile);
   1416   if (SecondaryLangList != NULL) {
   1417     ParserSetPosition (SourceFile->FileName, SourceFile->LineNum);
   1418     Status = StringDBAddSecondaryLanguage (LangCode, GetLangCodeList(SecondaryLangList));
   1419     if (Status != STATUS_SUCCESS) {
   1420       SourceFile->SkipToHash = TRUE;
   1421     }
   1422     FREE (LangCode);
   1423     FREE (SecondaryLangList);
   1424     return ;
   1425   }
   1426   FREE (LangCode);
   1427 
   1428 
   1429   SourceFile->SkipToHash = TRUE;
   1430 }
   1431 
   1432 static
   1433 BOOLEAN
   1434 ApparentQuotedString (
   1435   SOURCE_FILE *SourceFile
   1436   )
   1437 {
   1438   WCHAR *Ptr;
   1439   //
   1440   // See if the first and last nonblank characters on the line are double quotes
   1441   //
   1442   for (Ptr = SourceFile->FileBufferPtr; *Ptr && (*Ptr == UNICODE_SPACE); Ptr++)
   1443     ;
   1444   if (*Ptr != UNICODE_DOUBLE_QUOTE) {
   1445     return FALSE;
   1446   }
   1447 
   1448   while (*Ptr) {
   1449     Ptr++;
   1450   }
   1451 
   1452   Ptr--;
   1453   for (; *Ptr && (*Ptr == UNICODE_SPACE); Ptr--)
   1454     ;
   1455   if (*Ptr != UNICODE_DOUBLE_QUOTE) {
   1456     return FALSE;
   1457   }
   1458 
   1459   return TRUE;
   1460 }
   1461 //
   1462 // Parse:
   1463 //   #language eng "some string " "more string"
   1464 //
   1465 static
   1466 void
   1467 ProcessTokenLanguage (
   1468   SOURCE_FILE *SourceFile
   1469   )
   1470 {
   1471   STATUS  Status;
   1472   WCHAR   *String;
   1473   WCHAR   *SecondString;
   1474   WCHAR   *TempString;
   1475   WCHAR   *From;
   1476   WCHAR   *To;
   1477   WCHAR   Language[LANGUAGE_IDENTIFIER_NAME_LEN + 1];
   1478   UINT32  Len;
   1479   BOOLEAN PreviousNewline;
   1480   //
   1481   // Get the language identifier
   1482   //
   1483   Language[0] = 0;
   1484   Status = GetLanguageIdentifierName (SourceFile, Language, sizeof (Language), TRUE);
   1485   if (Status != STATUS_SUCCESS) {
   1486     return;
   1487   }
   1488 
   1489   //
   1490   // Extract the string value. It's either a quoted string that starts on the current line, or
   1491   // an unquoted string that starts on the following line and continues until the next control
   1492   // character in column 1.
   1493   // Look ahead to find a quote or a newline
   1494   //
   1495   if (SkipTo (SourceFile, UNICODE_DOUBLE_QUOTE, TRUE)) {
   1496     String = GetQuotedString (SourceFile, FALSE);
   1497     if (String != NULL) {
   1498       //
   1499       // Set the position in the file of where we are parsing for error
   1500       // reporting purposes. Then start looking ahead for additional
   1501       // quoted strings, and concatenate them until we get a failure
   1502       // back from the string parser.
   1503       //
   1504       Len = wcslen (String) + 1;
   1505       ParserSetPosition (SourceFile->FileName, SourceFile->LineNum);
   1506       do {
   1507         SkipWhiteSpace (SourceFile);
   1508         SecondString = GetQuotedString (SourceFile, TRUE);
   1509         if (SecondString != NULL) {
   1510           Len += wcslen (SecondString);
   1511           TempString = (WCHAR *) malloc (Len * sizeof (WCHAR));
   1512           if (TempString == NULL) {
   1513             Error (NULL, 0, 0, "application error", "failed to allocate memory");
   1514             return ;
   1515           }
   1516 
   1517           wcscpy (TempString, String);
   1518           wcscat (TempString, SecondString);
   1519           free (String);
   1520           free (SecondString);
   1521           String = TempString;
   1522         }
   1523       } while (SecondString != NULL);
   1524       StringDBAddString (Language, NULL, NULL, String, TRUE, 0);
   1525       free (String);
   1526     } else {
   1527       //
   1528       // Error was reported at lower level. Error recovery mode.
   1529       //
   1530       SourceFile->SkipToHash = TRUE;
   1531     }
   1532   } else {
   1533     if (!mGlobals.UnquotedStrings) {
   1534       //
   1535       // They're using unquoted strings. If the next non-blank character is a double quote, and the
   1536       // last non-blank character on the line is a double quote, then more than likely they're using
   1537       // quotes, so they need to put the quoted string on the end of the previous line
   1538       //
   1539       if (ApparentQuotedString (SourceFile)) {
   1540         Warning (
   1541           SourceFile->FileName,
   1542           SourceFile->LineNum,
   1543           0,
   1544           "unexpected quoted string on line",
   1545           "specify -uqs option if necessary"
   1546           );
   1547       }
   1548     }
   1549     //
   1550     // Found end-of-line (hopefully). Skip over it and start taking in characters
   1551     // until we find a control character at the start of a line.
   1552     //
   1553     Len             = 0;
   1554     From            = SourceFile->FileBufferPtr;
   1555     PreviousNewline = FALSE;
   1556     while (!EndOfFile (SourceFile)) {
   1557       if (SourceFile->FileBufferPtr[0] == UNICODE_LF) {
   1558         PreviousNewline = TRUE;
   1559         SourceFile->LineNum++;
   1560       } else {
   1561         Len++;
   1562         if (PreviousNewline && (SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter)) {
   1563           break;
   1564         }
   1565 
   1566         PreviousNewline = FALSE;
   1567       }
   1568 
   1569       SourceFile->FileBufferPtr++;
   1570     }
   1571 
   1572     if ((Len == 0) && EndOfFile (SourceFile)) {
   1573       Error (SourceFile->FileName, SourceFile->LineNum, 0, "unexpected end of file", NULL);
   1574       SourceFile->SkipToHash = TRUE;
   1575       return ;
   1576     }
   1577     //
   1578     // Now allocate a buffer, copy the characters, and add the string.
   1579     //
   1580     String = (WCHAR *) malloc ((Len + 1) * sizeof (WCHAR));
   1581     if (String == NULL) {
   1582       Error (NULL, 0, 0, "application error", "failed to allocate memory");
   1583       return ;
   1584     }
   1585 
   1586     To = String;
   1587     while (From < SourceFile->FileBufferPtr) {
   1588       switch (*From) {
   1589       case UNICODE_LF:
   1590       case 0:
   1591         break;
   1592 
   1593       default:
   1594         *To = *From;
   1595         To++;
   1596         break;
   1597       }
   1598 
   1599       From++;
   1600     }
   1601 
   1602     //
   1603     // String[Len] = 0;
   1604     //
   1605     *To = 0;
   1606     StringDBAddString (Language, NULL, NULL, String, TRUE, 0);
   1607   }
   1608 }
   1609 
   1610 static
   1611 BOOLEAN
   1612 IsWhiteSpace (
   1613   SOURCE_FILE *SourceFile
   1614   )
   1615 {
   1616   switch (SourceFile->FileBufferPtr[0]) {
   1617   case UNICODE_NULL:
   1618   case UNICODE_CR:
   1619   case UNICODE_SPACE:
   1620   case UNICODE_TAB:
   1621   case UNICODE_LF:
   1622     return TRUE;
   1623 
   1624   default:
   1625     return FALSE;
   1626   }
   1627 }
   1628 
   1629 static
   1630 UINT32
   1631 SkipWhiteSpace (
   1632   SOURCE_FILE *SourceFile
   1633   )
   1634 {
   1635   UINT32  Count;
   1636 
   1637   Count = 0;
   1638   while (!EndOfFile (SourceFile)) {
   1639     Count++;
   1640     switch (*SourceFile->FileBufferPtr) {
   1641     case UNICODE_NULL:
   1642     case UNICODE_CR:
   1643     case UNICODE_SPACE:
   1644     case UNICODE_TAB:
   1645       SourceFile->FileBufferPtr++;
   1646       break;
   1647 
   1648     case UNICODE_LF:
   1649       SourceFile->FileBufferPtr++;
   1650       SourceFile->LineNum++;
   1651       if (mGlobals.Verbose) {
   1652         printf ("%d: %S\n", SourceFile->LineNum, SourceFile->FileBufferPtr);
   1653       }
   1654       break;
   1655 
   1656     default:
   1657       return Count - 1;
   1658     }
   1659   }
   1660   //
   1661   // Some tokens require trailing whitespace. If we're at the end of the
   1662   // file, then we count that as well.
   1663   //
   1664   if ((Count == 0) && (EndOfFile (SourceFile))) {
   1665     Count++;
   1666   }
   1667 
   1668   return Count;
   1669 }
   1670 
   1671 static
   1672 UINT32
   1673 wstrcmp (
   1674   WCHAR *Buffer,
   1675   WCHAR *Str
   1676   )
   1677 {
   1678   UINT32  Len;
   1679 
   1680   Len = 0;
   1681   while (*Str == *Buffer) {
   1682     Buffer++;
   1683     Str++;
   1684     Len++;
   1685   }
   1686 
   1687   if (*Str) {
   1688     return 0;
   1689   }
   1690 
   1691   return Len;
   1692 }
   1693 
   1694 static
   1695 WCHAR *
   1696 wstrcatenate (
   1697   WCHAR *Dst,
   1698   WCHAR *Src
   1699   )
   1700 {
   1701   UINT32 Len  = 0;
   1702   WCHAR  *Bak = Dst;
   1703 
   1704   if (Src == NULL) {
   1705     return Dst;
   1706   }
   1707 
   1708   if (Dst != NULL) {
   1709     Len = wcslen (Dst);
   1710   }
   1711   Len += wcslen (Src);
   1712   Dst = (WCHAR *) malloc ((Len + 1) * 2);
   1713   if (Dst == NULL) {
   1714     return NULL;
   1715   }
   1716 
   1717   Dst[0] = L'\0';
   1718   if (Bak != NULL) {
   1719     wcscpy (Dst, Bak);
   1720     FREE (Bak);
   1721   }
   1722   wcscat (Dst, Src);
   1723   return Dst;
   1724 }
   1725 
   1726 //
   1727 // Given a filename, try to find it along the include paths.
   1728 //
   1729 static
   1730 FILE *
   1731 FindFile (
   1732   IN INT8    *FileName,
   1733   OUT INT8   *FoundFileName,
   1734   IN UINT32  FoundFileNameLen
   1735   )
   1736 {
   1737   FILE              *Fptr;
   1738   TEXT_STRING_LIST  *List;
   1739 
   1740   //
   1741   // Traverse the list of paths and try to find the file
   1742   //
   1743   List = mGlobals.IncludePaths;
   1744   while (List != NULL) {
   1745     //
   1746     // Put the path and filename together
   1747     //
   1748     if (strlen (List->Str) + strlen (FileName) + 1 > FoundFileNameLen) {
   1749       Error (UTILITY_NAME, 0, 0, NULL, "internal error - cannot concatenate path+filename");
   1750       return NULL;
   1751     }
   1752     //
   1753     // Append the filename to this include path and try to open the file.
   1754     //
   1755     strcpy (FoundFileName, List->Str);
   1756     strcat (FoundFileName, FileName);
   1757     if ((Fptr = fopen (FoundFileName, "rb")) != NULL) {
   1758       //
   1759       // Return the file pointer
   1760       //
   1761       return Fptr;
   1762     }
   1763 
   1764     List = List->Next;
   1765   }
   1766   //
   1767   // Not found
   1768   //
   1769   FoundFileName[0] = 0;
   1770   return NULL;
   1771 }
   1772 //
   1773 // Process the command-line arguments
   1774 //
   1775 static
   1776 STATUS
   1777 ProcessArgs (
   1778   int   Argc,
   1779   char  *Argv[]
   1780   )
   1781 {
   1782   TEXT_STRING_LIST  *NewList;
   1783   char              *Cptr;
   1784   char              *Cptr2;
   1785 
   1786   //
   1787   // Clear our globals
   1788   //
   1789   memset ((char *) &mGlobals, 0, sizeof (mGlobals));
   1790   strcpy (mGlobals.BaseName, DEFAULT_BASE_NAME);
   1791   //
   1792   // Skip program name
   1793   //
   1794   Argc--;
   1795   Argv++;
   1796 
   1797   if (Argc == 0) {
   1798     Usage ();
   1799     return STATUS_ERROR;
   1800   }
   1801 
   1802   mGlobals.Mode = MODE_UNKNOWN;
   1803   //
   1804   // Process until no more -args.
   1805   //
   1806   while ((Argc > 0) && (Argv[0][0] == '-')) {
   1807     //
   1808     // -parse option
   1809     //
   1810     if (_stricmp (Argv[0], "-parse") == 0) {
   1811       if (mGlobals.Mode != MODE_UNKNOWN) {
   1812         Error (NULL, 0, 0, "only one of -parse/-scan/-dump allowed", NULL);
   1813         return STATUS_ERROR;
   1814       }
   1815 
   1816       mGlobals.Mode = MODE_PARSE;
   1817       //
   1818       // -scan option
   1819       //
   1820     } else if (_stricmp (Argv[0], "-scan") == 0) {
   1821       if (mGlobals.Mode != MODE_UNKNOWN) {
   1822         Error (NULL, 0, 0, "only one of -parse/-scan/-dump allowed", NULL);
   1823         return STATUS_ERROR;
   1824       }
   1825 
   1826       mGlobals.Mode = MODE_SCAN;
   1827       //
   1828       // -vscan verbose scanning option
   1829       //
   1830     } else if (_stricmp (Argv[0], "-vscan") == 0) {
   1831       mGlobals.VerboseScan = TRUE;
   1832       //
   1833       // -dump option
   1834       //
   1835     } else if (_stricmp (Argv[0], "-dump") == 0) {
   1836       if (mGlobals.Mode != MODE_UNKNOWN) {
   1837         Error (NULL, 0, 0, "only one of -parse/-scan/-dump allowed", NULL);
   1838         return STATUS_ERROR;
   1839       }
   1840 
   1841       mGlobals.Mode = MODE_DUMP;
   1842     } else if (_stricmp (Argv[0], "-uqs") == 0) {
   1843       mGlobals.UnquotedStrings = TRUE;
   1844       //
   1845       // -i path    add include search path when parsing
   1846       //
   1847     } else if (_stricmp (Argv[0], "-i") == 0) {
   1848       //
   1849       // check for one more arg
   1850       //
   1851       if ((Argc <= 1) || (Argv[1][0] == '-')) {
   1852         Error (UTILITY_NAME, 0, 0, Argv[0], "missing include path");
   1853         return STATUS_ERROR;
   1854       }
   1855       //
   1856       // Allocate memory for a new list element, fill it in, and
   1857       // add it to our list of include paths. Always make sure it
   1858       // has a "\" on the end of it.
   1859       //
   1860       NewList = malloc (sizeof (TEXT_STRING_LIST));
   1861       if (NewList == NULL) {
   1862         Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure");
   1863         return STATUS_ERROR;
   1864       }
   1865 
   1866       memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST));
   1867       NewList->Str = malloc (strlen (Argv[1]) + 2);
   1868       if (NewList->Str == NULL) {
   1869         free (NewList);
   1870         Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure");
   1871         return STATUS_ERROR;
   1872       }
   1873 
   1874       strcpy (NewList->Str, Argv[1]);
   1875       if (NewList->Str[strlen (NewList->Str) - 1] != '\\') {
   1876         strcat (NewList->Str, "\\");
   1877       }
   1878       //
   1879       // Add it to our linked list
   1880       //
   1881       if (mGlobals.IncludePaths == NULL) {
   1882         mGlobals.IncludePaths = NewList;
   1883       } else {
   1884         mGlobals.LastIncludePath->Next = NewList;
   1885       }
   1886 
   1887       mGlobals.LastIncludePath = NewList;
   1888       Argc--;
   1889       Argv++;
   1890     } else if (_stricmp (Argv[0], "-if") == 0) {
   1891       //
   1892       // Indirection file -- check for one more arg
   1893       //
   1894       if ((Argc <= 1) || (Argv[1][0] == '-')) {
   1895         Error (UTILITY_NAME, 0, 0, Argv[0], "missing indirection file name");
   1896         return STATUS_ERROR;
   1897       }
   1898       //
   1899       // Allocate memory for a new list element, fill it in, and
   1900       // add it to our list of include paths. Always make sure it
   1901       // has a "\" on the end of it.
   1902       //
   1903       NewList = malloc (sizeof (TEXT_STRING_LIST));
   1904       if (NewList == NULL) {
   1905         Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure");
   1906         return STATUS_ERROR;
   1907       }
   1908 
   1909       memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST));
   1910       NewList->Str = malloc (strlen (Argv[1]) + 1);
   1911       if (NewList->Str == NULL) {
   1912         free (NewList);
   1913         Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure");
   1914         return STATUS_ERROR;
   1915       }
   1916 
   1917       strcpy (NewList->Str, Argv[1]);
   1918       //
   1919       // Add it to our linked list
   1920       //
   1921       if (mGlobals.IndirectionFileName == NULL) {
   1922         mGlobals.IndirectionFileName = NewList;
   1923       } else {
   1924         mGlobals.LastIndirectionFileName->Next = NewList;
   1925       }
   1926 
   1927       mGlobals.LastIndirectionFileName = NewList;
   1928       Argc--;
   1929       Argv++;
   1930     } else if (_stricmp (Argv[0], "-db") == 0) {
   1931       //
   1932       // -db option to specify a database file.
   1933       // Check for one more arg (the database file name)
   1934       //
   1935       if ((Argc <= 1) || (Argv[1][0] == '-')) {
   1936         Error (UTILITY_NAME, 0, 0, Argv[0], "missing database file name");
   1937         return STATUS_ERROR;
   1938       }
   1939 
   1940       NewList = malloc (sizeof (TEXT_STRING_LIST));
   1941       if (NewList == NULL) {
   1942         Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure");
   1943         return STATUS_ERROR;
   1944       }
   1945 
   1946       memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST));
   1947       NewList->Str = malloc (strlen (Argv[1]) + 1);
   1948       if (NewList->Str == NULL) {
   1949         free (NewList);
   1950         Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure");
   1951         return STATUS_ERROR;
   1952       }
   1953 
   1954       strcpy (NewList->Str, Argv[1]);
   1955       //
   1956       // Add it to our linked list
   1957       //
   1958       if (mGlobals.DatabaseFileName == NULL) {
   1959         mGlobals.DatabaseFileName = NewList;
   1960       } else {
   1961         mGlobals.LastDatabaseFileName->Next = NewList;
   1962       }
   1963 
   1964       mGlobals.LastDatabaseFileName = NewList;
   1965       Argc--;
   1966       Argv++;
   1967     } else if (_stricmp (Argv[0], "-ou") == 0) {
   1968       //
   1969       // -ou option to specify an output unicode file to
   1970       // which we can dump our database.
   1971       //
   1972       if ((Argc <= 1) || (Argv[1][0] == '-')) {
   1973         Error (UTILITY_NAME, 0, 0, Argv[0], "missing database dump output file name");
   1974         return STATUS_ERROR;
   1975       }
   1976 
   1977       if (mGlobals.DumpUFileName[0] == 0) {
   1978         strcpy (mGlobals.DumpUFileName, Argv[1]);
   1979       } else {
   1980         Error (UTILITY_NAME, 0, 0, Argv[1], "-ou option already specified with '%s'", mGlobals.DumpUFileName);
   1981         return STATUS_ERROR;
   1982       }
   1983 
   1984       Argc--;
   1985       Argv++;
   1986     } else if (_stricmp (Argv[0], "-hpk") == 0) {
   1987       //
   1988       // -hpk option to create an HII export pack of the input database file
   1989       //
   1990       if ((Argc <= 1) || (Argv[1][0] == '-')) {
   1991         Error (UTILITY_NAME, 0, 0, Argv[0], "missing raw string data dump output file name");
   1992         return STATUS_ERROR;
   1993       }
   1994 
   1995       if (mGlobals.HiiExportPackFileName[0] == 0) {
   1996         strcpy (mGlobals.HiiExportPackFileName, Argv[1]);
   1997       } else {
   1998         Error (UTILITY_NAME, 0, 0, Argv[1], "-or option already specified with '%s'", mGlobals.HiiExportPackFileName);
   1999         return STATUS_ERROR;
   2000       }
   2001 
   2002       Argc--;
   2003       Argv++;
   2004     } else if ((_stricmp (Argv[0], "-?") == 0) || (_stricmp (Argv[0], "-h") == 0)) {
   2005       Usage ();
   2006       return STATUS_ERROR;
   2007     } else if (_stricmp (Argv[0], "-v") == 0) {
   2008       mGlobals.Verbose = 1;
   2009     } else if (_stricmp (Argv[0], "-vdbw") == 0) {
   2010       mGlobals.VerboseDatabaseWrite = 1;
   2011     } else if (_stricmp (Argv[0], "-vdbr") == 0) {
   2012       mGlobals.VerboseDatabaseRead = 1;
   2013     } else if (_stricmp (Argv[0], "-newdb") == 0) {
   2014       mGlobals.NewDatabase = 1;
   2015     } else if (_stricmp (Argv[0], "-ignorenotfound") == 0) {
   2016       mGlobals.IgnoreNotFound = 1;
   2017     } else if (_stricmp (Argv[0], "-oc") == 0) {
   2018       //
   2019       // check for one more arg
   2020       //
   2021       if ((Argc <= 1) || (Argv[1][0] == '-')) {
   2022         Error (UTILITY_NAME, 0, 0, Argv[0], "missing output C filename");
   2023         return STATUS_ERROR;
   2024       }
   2025 
   2026       strcpy (mGlobals.StringCFileName, Argv[1]);
   2027       Argc--;
   2028       Argv++;
   2029     } else if (_stricmp (Argv[0], "-bn") == 0) {
   2030       //
   2031       // check for one more arg
   2032       //
   2033       if ((Argc <= 1) || (Argv[1][0] == '-')) {
   2034         Error (UTILITY_NAME, 0, 0, Argv[0], "missing base name");
   2035         Usage ();
   2036         return STATUS_ERROR;
   2037       }
   2038 
   2039       strcpy (mGlobals.BaseName, Argv[1]);
   2040       Argc--;
   2041       Argv++;
   2042     } else if (_stricmp (Argv[0], "-oh") == 0) {
   2043       //
   2044       // -oh to specify output .h defines file name
   2045       //
   2046       if ((Argc <= 1) || (Argv[1][0] == '-')) {
   2047         Error (UTILITY_NAME, 0, 0, Argv[0], "missing output .h filename");
   2048         return STATUS_ERROR;
   2049       }
   2050 
   2051       strcpy (mGlobals.StringHFileName, Argv[1]);
   2052       Argc--;
   2053       Argv++;
   2054     } else if (_stricmp (Argv[0], "-dep") == 0) {
   2055       //
   2056       // -dep to specify output dependency file name
   2057       //
   2058       if ((Argc <= 1) || (Argv[1][0] == '-')) {
   2059         Error (UTILITY_NAME, 0, 0, Argv[0], "missing output dependency filename");
   2060         return STATUS_ERROR;
   2061       }
   2062 
   2063       strcpy (mGlobals.OutputDependencyFileName, Argv[1]);
   2064       Argc--;
   2065       Argv++;
   2066     } else if (_stricmp (Argv[0], "-skipext") == 0) {
   2067       //
   2068       // -skipext to skip scanning of files with certain filename extensions
   2069       //
   2070       if ((Argc <= 1) || (Argv[1][0] == '-')) {
   2071         Error (UTILITY_NAME, 0, 0, Argv[0], "missing filename extension");
   2072         return STATUS_ERROR;
   2073       }
   2074       //
   2075       // Allocate memory for a new list element, fill it in, and
   2076       // add it to our list of excluded extensions. Always make sure it
   2077       // has a "." as the first character.
   2078       //
   2079       NewList = malloc (sizeof (TEXT_STRING_LIST));
   2080       if (NewList == NULL) {
   2081         Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure");
   2082         return STATUS_ERROR;
   2083       }
   2084 
   2085       memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST));
   2086       NewList->Str = malloc (strlen (Argv[1]) + 2);
   2087       if (NewList->Str == NULL) {
   2088         free (NewList);
   2089         Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure");
   2090         return STATUS_ERROR;
   2091       }
   2092 
   2093       if (Argv[1][0] == '.') {
   2094         strcpy (NewList->Str, Argv[1]);
   2095       } else {
   2096         NewList->Str[0] = '.';
   2097         strcpy (NewList->Str + 1, Argv[1]);
   2098       }
   2099       //
   2100       // Add it to our linked list
   2101       //
   2102       if (mGlobals.SkipExt == NULL) {
   2103         mGlobals.SkipExt = NewList;
   2104       } else {
   2105         mGlobals.LastSkipExt->Next = NewList;
   2106       }
   2107 
   2108       mGlobals.LastSkipExt = NewList;
   2109       Argc--;
   2110       Argv++;
   2111     } else if (_stricmp (Argv[0], "-lang") == 0) {
   2112       //
   2113       // "-lang zh-Hans" or "-lang en-US" to only output certain languages
   2114       //
   2115       if ((Argc <= 1) || (Argv[1][0] == '-')) {
   2116         Error (UTILITY_NAME, 0, 0, Argv[0], "missing language name");
   2117         Usage ();
   2118         return STATUS_ERROR;
   2119       }
   2120 
   2121       if (AddCommandLineLanguage (Argv[1]) != STATUS_SUCCESS) {
   2122         return STATUS_ERROR;
   2123       }
   2124 
   2125       Argc--;
   2126       Argv++;
   2127     } else if (_stricmp (Argv[0], "-od") == 0) {
   2128       //
   2129       // Output database file name -- check for another arg
   2130       //
   2131       if ((Argc <= 1) || (Argv[1][0] == '-')) {
   2132         Error (UTILITY_NAME, 0, 0, Argv[0], "missing output database file name");
   2133         return STATUS_ERROR;
   2134       }
   2135 
   2136       strcpy (mGlobals.OutputDatabaseFileName, Argv[1]);
   2137       Argv++;
   2138       Argc--;
   2139     } else if (_stricmp (Argv[0], "-ppflag") == 0) {
   2140       //
   2141       // -ppflag "Preprocess flags" -- check for another arg
   2142       //
   2143       if ((Argc <= 1) || (Argv[1][0] == '-')) {
   2144         Error (UTILITY_NAME, 0, 0, Argv[0], "missing preprocess flags");
   2145         Usage ();
   2146         return STATUS_ERROR;
   2147       }
   2148 
   2149       //
   2150       // Allocate memory for a new list element, fill it in, and
   2151       // add it to our list of preprocess flag.
   2152       //
   2153       NewList = malloc (sizeof (TEXT_STRING_LIST));
   2154       if (NewList == NULL) {
   2155         Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure");
   2156         return STATUS_ERROR;
   2157       }
   2158 
   2159       memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST));
   2160       NewList->Str = malloc (strlen (Argv[1]) * 2 + 1);
   2161       if (NewList->Str == NULL) {
   2162         free (NewList);
   2163         Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure");
   2164         return STATUS_ERROR;
   2165       }
   2166 
   2167       //
   2168       // Convert '"' to '\"' in preprocess flag
   2169       //
   2170       Cptr = Argv[1];
   2171       Cptr2 = NewList->Str;
   2172       if (*Cptr == '"') {
   2173         *Cptr2++ = '\\';
   2174         *Cptr2++ = '"';
   2175         Cptr++;
   2176       }
   2177       while (*Cptr != '\0') {
   2178         if ((*Cptr == '"') && (*(Cptr - 1) != '\\')) {
   2179           *Cptr2++ = '\\';
   2180         }
   2181         *Cptr2++ = *Cptr++;
   2182       }
   2183       *Cptr2 = '\0';
   2184 
   2185       //
   2186       // Add it to our linked list
   2187       //
   2188       if (mGlobals.PreprocessFlags == NULL) {
   2189         mGlobals.PreprocessFlags = NewList;
   2190       } else {
   2191         mGlobals.LastPreprocessFlags->Next = NewList;
   2192       }
   2193       mGlobals.LastPreprocessFlags = NewList;
   2194 
   2195       mGlobals.Preprocess = TRUE;
   2196 
   2197       Argv++;
   2198       Argc--;
   2199     } else {
   2200       //
   2201       // Unrecognized arg
   2202       //
   2203       Error (UTILITY_NAME, 0, 0, Argv[0], "unrecognized option");
   2204       Usage ();
   2205       return STATUS_ERROR;
   2206     }
   2207 
   2208     Argv++;
   2209     Argc--;
   2210   }
   2211   //
   2212   // Make sure they specified the mode parse/scan/dump
   2213   //
   2214   if (mGlobals.Mode == MODE_UNKNOWN) {
   2215     Error (NULL, 0, 0, "must specify one of -parse/-scan/-dump", NULL);
   2216     return STATUS_ERROR;
   2217   }
   2218   //
   2219   // All modes require a database filename
   2220   //
   2221   if (mGlobals.DatabaseFileName == 0) {
   2222     Error (NULL, 0, 0, "must specify a database filename using -db DbFileName", NULL);
   2223     Usage ();
   2224     return STATUS_ERROR;
   2225   }
   2226   //
   2227   // If dumping the database file, then return immediately if all
   2228   // parameters check out.
   2229   //
   2230   if (mGlobals.Mode == MODE_DUMP) {
   2231     //
   2232     // Not much use if they didn't specify -oh or -oc or -ou or -hpk
   2233     //
   2234     if ((mGlobals.DumpUFileName[0] == 0) &&
   2235         (mGlobals.StringHFileName[0] == 0) &&
   2236         (mGlobals.StringCFileName[0] == 0) &&
   2237         (mGlobals.HiiExportPackFileName[0] == 0)
   2238         ) {
   2239       Error (NULL, 0, 0, "-dump without -oc/-oh/-ou/-hpk is a NOP", NULL);
   2240       return STATUS_ERROR;
   2241     }
   2242 
   2243     return STATUS_SUCCESS;
   2244   }
   2245   //
   2246   // Had to specify source string file and output string defines header filename.
   2247   //
   2248   if (mGlobals.Mode == MODE_SCAN) {
   2249     if (Argc < 1) {
   2250       Error (UTILITY_NAME, 0, 0, NULL, "must specify at least one source file to scan with -scan");
   2251       Usage ();
   2252       return STATUS_ERROR;
   2253     }
   2254     //
   2255     // If -ppflag is specified, -oh should also be specified for preprocess
   2256     //
   2257     if (mGlobals.Preprocess && (mGlobals.StringHFileName[0] == 0)) {
   2258       Error (UTILITY_NAME, 0, 0, NULL, "must specify string defines file name to preprocess before scan");
   2259       Usage ();
   2260       return STATUS_ERROR;
   2261     }
   2262     //
   2263     // Get the list of filenames
   2264     //
   2265     while (Argc > 0) {
   2266       NewList = malloc (sizeof (TEXT_STRING_LIST));
   2267       if (NewList == NULL) {
   2268         Error (UTILITY_NAME, 0, 0, "memory allocation failure", NULL);
   2269         return STATUS_ERROR;
   2270       }
   2271 
   2272       memset (NewList, 0, sizeof (TEXT_STRING_LIST));
   2273       NewList->Str = (UINT8 *) malloc (strlen (Argv[0]) + 1);
   2274       if (NewList->Str == NULL) {
   2275         Error (UTILITY_NAME, 0, 0, "memory allocation failure", NULL);
   2276         return STATUS_ERROR;
   2277       }
   2278 
   2279       strcpy (NewList->Str, Argv[0]);
   2280       if (mGlobals.ScanFileName == NULL) {
   2281         mGlobals.ScanFileName = NewList;
   2282       } else {
   2283         mGlobals.LastScanFileName->Next = NewList;
   2284       }
   2285 
   2286       mGlobals.LastScanFileName = NewList;
   2287       Argc--;
   2288       Argv++;
   2289     }
   2290   } else {
   2291     //
   2292     // Parse mode -- must specify an input unicode file name
   2293     //
   2294     if (Argc < 1) {
   2295       Error (UTILITY_NAME, 0, 0, NULL, "must specify input unicode string file name with -parse");
   2296       Usage ();
   2297       return STATUS_ERROR;
   2298     }
   2299 
   2300     strcpy (mGlobals.SourceFiles.FileName, Argv[0]);
   2301   }
   2302 
   2303   return STATUS_SUCCESS;
   2304 }
   2305 //
   2306 // Found "-lang zh-Hans;en-US" on the command line. Parse the
   2307 // language list and save the setting for later processing.
   2308 //
   2309 static
   2310 STATUS
   2311 AddCommandLineLanguage (
   2312   IN INT8          *Language
   2313   )
   2314 {
   2315   char              Separator[] = ";";
   2316   char              *Token;
   2317   WCHAR_STRING_LIST *WNewList;
   2318 
   2319   //
   2320   // Keep processing the input string until we find the end.
   2321   //
   2322   Token = strtok (Language, Separator);
   2323   while (Token != NULL) {
   2324     WNewList = MALLOC (sizeof (WCHAR_STRING_LIST));
   2325     if (WNewList == NULL) {
   2326       Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure");
   2327       return STATUS_ERROR;
   2328     }
   2329     WNewList->Next = NULL;
   2330     WNewList->Str  = MALLOC ((strlen (Token) + 1) * sizeof (WCHAR));
   2331     if (WNewList->Str == NULL) {
   2332       free (WNewList);
   2333       Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure");
   2334       return STATUS_ERROR;
   2335     }
   2336 #ifdef USE_VC8
   2337     swprintf (WNewList->Str, (strlen (Token) + 1) * sizeof (WCHAR), L"%S", Token);
   2338 #else
   2339     swprintf (WNewList->Str, L"%S", Token);
   2340 #endif
   2341 
   2342     //
   2343     // Add it to our linked list
   2344     //
   2345     if (mGlobals.Language == NULL) {
   2346       mGlobals.Language = WNewList;
   2347     } else {
   2348       mGlobals.LastLanguage->Next = WNewList;
   2349     }
   2350 
   2351     mGlobals.LastLanguage = WNewList;
   2352     Token = strtok (NULL, Separator);
   2353   }
   2354 
   2355   return STATUS_SUCCESS;
   2356 }
   2357 //
   2358 // The contents of the text file are expected to be (one per line)
   2359 //   STRING_IDENTIFIER_NAME   ScopeName
   2360 // For example:
   2361 //   STR_ID_MY_FAVORITE_STRING   IBM
   2362 //
   2363 static
   2364 STATUS
   2365 ParseIndirectionFiles (
   2366   TEXT_STRING_LIST    *Files
   2367   )
   2368 {
   2369   FILE                        *Fptr;
   2370   INT8                        Line[200];
   2371   INT8                        *StringName;
   2372   INT8                        *ScopeName;
   2373   INT8                        *End;
   2374   UINT32                      LineCount;
   2375   WCHAR_MATCHING_STRING_LIST  *NewList;
   2376 
   2377   Line[sizeof (Line) - 1] = 0;
   2378   Fptr                    = NULL;
   2379   while (Files != NULL) {
   2380     Fptr      = fopen (Files->Str, "r");
   2381     LineCount = 0;
   2382     if (Fptr == NULL) {
   2383       Error (NULL, 0, 0, Files->Str, "failed to open input indirection file for reading");
   2384       return STATUS_ERROR;
   2385     }
   2386 
   2387     while (fgets (Line, sizeof (Line), Fptr) != NULL) {
   2388       //
   2389       // remove terminating newline for error printing purposes.
   2390       //
   2391       if (Line[strlen (Line) - 1] == '\n') {
   2392         Line[strlen (Line) - 1] = 0;
   2393       }
   2394 
   2395       LineCount++;
   2396       if (Line[sizeof (Line) - 1] != 0) {
   2397         Error (Files->Str, LineCount, 0, "line length exceeds maximum supported", NULL);
   2398         goto Done;
   2399       }
   2400 
   2401       StringName = Line;
   2402       while (*StringName && (isspace (*StringName))) {
   2403         StringName++;
   2404       }
   2405 
   2406       if (*StringName) {
   2407         if ((*StringName == '_') || isalpha (*StringName)) {
   2408           End = StringName;
   2409           while ((*End) && (*End == '_') || (isalnum (*End))) {
   2410             End++;
   2411           }
   2412 
   2413           if (isspace (*End)) {
   2414             *End = 0;
   2415             End++;
   2416             while (isspace (*End)) {
   2417               End++;
   2418             }
   2419 
   2420             if (*End) {
   2421               ScopeName = End;
   2422               while (*End && !isspace (*End)) {
   2423                 End++;
   2424               }
   2425 
   2426               *End = 0;
   2427               //
   2428               // Add the string name/scope pair
   2429               //
   2430               NewList = malloc (sizeof (WCHAR_MATCHING_STRING_LIST));
   2431               if (NewList == NULL) {
   2432                 Error (NULL, 0, 0, "memory allocation error", NULL);
   2433                 goto Done;
   2434               }
   2435 
   2436               memset (NewList, 0, sizeof (WCHAR_MATCHING_STRING_LIST));
   2437               NewList->Str1 = (WCHAR *) malloc ((strlen (StringName) + 1) * sizeof (WCHAR));
   2438               NewList->Str2 = (WCHAR *) malloc ((strlen (ScopeName) + 1) * sizeof (WCHAR));
   2439               if ((NewList->Str1 == NULL) || (NewList->Str2 == NULL)) {
   2440                 Error (NULL, 0, 0, "memory allocation error", NULL);
   2441                 goto Done;
   2442               }
   2443 
   2444 #ifdef USE_VC8
   2445               swprintf (NewList->Str1, (strlen (StringName) + 1) * sizeof (WCHAR), L"%S", StringName);
   2446               swprintf (NewList->Str2, (strlen (ScopeName) + 1) * sizeof (WCHAR), L"%S", ScopeName);
   2447 #else
   2448               swprintf (NewList->Str1, L"%S", StringName);
   2449               swprintf (NewList->Str2, L"%S", ScopeName);
   2450 #endif
   2451               if (mGlobals.IndirectionList == NULL) {
   2452                 mGlobals.IndirectionList = NewList;
   2453               } else {
   2454                 mGlobals.LastIndirectionList->Next = NewList;
   2455               }
   2456 
   2457               mGlobals.LastIndirectionList = NewList;
   2458             } else {
   2459               Error (Files->Str, LineCount, 0, StringName, "invalid line : expected 'StringIdentifier Scope'");
   2460               goto Done;
   2461             }
   2462           } else {
   2463             Error (Files->Str, LineCount, 0, StringName, "invalid line : expected 'StringIdentifier Scope'");
   2464             goto Done;
   2465           }
   2466         } else {
   2467           Error (Files->Str, LineCount, 0, StringName, "invalid string identifier");
   2468           goto Done;
   2469         }
   2470       }
   2471     }
   2472 
   2473     fclose (Fptr);
   2474     Fptr  = NULL;
   2475     Files = Files->Next;
   2476   }
   2477 
   2478 Done:
   2479   if (Fptr != NULL) {
   2480     fclose (Fptr);
   2481     return STATUS_ERROR;
   2482   }
   2483 
   2484   return STATUS_SUCCESS;
   2485 }
   2486 
   2487 static
   2488 INTN
   2489 PreprocessSourceFile (
   2490   UINT8 *SourceFileName
   2491   )
   2492 {
   2493   char              *Cptr;
   2494   FILE              *InFptr;
   2495   FILE              *OutFptr;
   2496   UINT32            CmdLen;
   2497   char              *PreProcessCmd;
   2498   char              BaseName[MAX_PATH];
   2499   char              TempFileName[MAX_PATH];
   2500   char              SourceFileDir[MAX_PATH];
   2501   char              Line[MAX_LINE_LEN];
   2502   TEXT_STRING_LIST  *List;
   2503   char              InsertLine[] = "#undef STRING_TOKEN\n";
   2504   int               Status;
   2505 
   2506   //
   2507   // Check whehter source file exist
   2508   //
   2509   InFptr = fopen (SourceFileName, "r");
   2510   if (InFptr == NULL) {
   2511     Error (NULL, 0, 0, SourceFileName, "failed to open input file for scanning");
   2512     return STATUS_ERROR;
   2513   }
   2514 
   2515   //
   2516   // Get source file directory
   2517   //
   2518   strcpy (SourceFileDir, SourceFileName);
   2519   Cptr = strrchr (SourceFileDir, '\\');
   2520   if (Cptr != NULL) {
   2521     *Cptr = '\0';
   2522   }
   2523 
   2524   //
   2525   // Generate preprocess output file name
   2526   //
   2527   strcpy (BaseName, mGlobals.OutputDatabaseFileName);
   2528   Cptr = strrchr (BaseName, '\\');
   2529   if (Cptr != NULL) {
   2530     *++Cptr = '\0';
   2531   }
   2532 
   2533   Cptr = strrchr (SourceFileName, '\\');
   2534   if (Cptr != NULL) {
   2535     Cptr++;
   2536   }
   2537   strcat (BaseName, Cptr);
   2538 
   2539   Cptr = strrchr (BaseName, '.');
   2540   if (Cptr != NULL) {
   2541     *Cptr = '\0';
   2542   }
   2543 
   2544   strcpy (mGlobals.PreprocessFileName, BaseName);
   2545   strcat (mGlobals.PreprocessFileName, PREPROCESS_OUTPUT_FILE_EXTENSION);
   2546 
   2547   strcpy (TempFileName, BaseName);
   2548   strcat (TempFileName, PREPROCESS_TEMP_FILE_EXTENSION);
   2549 
   2550   //
   2551   // Insert "#undef STRING_TOKEN" after each line of "#include ...", so as to
   2552   // preserve the STRING_TOKEN() for scanning after preprocess
   2553   //
   2554   OutFptr = fopen (TempFileName, "w");
   2555   if (OutFptr == NULL) {
   2556     Error (NULL, 0, 0, TempFileName, "failed to open file for write");
   2557     fclose (InFptr);
   2558     return STATUS_ERROR;
   2559   }
   2560   while (fgets (Line, MAX_LINE_LEN, InFptr) != NULL) {
   2561     fputs (Line, OutFptr);
   2562     Cptr = Line;
   2563 
   2564     //
   2565     // Skip leading blank space
   2566     //
   2567     while (*Cptr == ' ' || *Cptr == '\t') {
   2568       Cptr++;
   2569     }
   2570 
   2571     if (*Cptr == '#' && strncmp (Cptr + 1, "include", 7) == 0){
   2572       fputs (InsertLine, OutFptr);
   2573     }
   2574   }
   2575   fclose (InFptr);
   2576   fclose (OutFptr);
   2577 
   2578   //
   2579   // Prepare preprocess command
   2580   //
   2581   CmdLen = 1;
   2582   CmdLen += strlen (PREPROCESSOR_COMMAND);
   2583   CmdLen++;
   2584   CmdLen += strlen (PREPROCESSOR_OPTIONS);
   2585   CmdLen++;
   2586 
   2587   //
   2588   // "-I SourceFileDir "
   2589   //
   2590   CmdLen += strlen (SourceFileDir);
   2591   CmdLen += 4;
   2592 
   2593   List = mGlobals.PreprocessFlags;
   2594   while (List != NULL) {
   2595     CmdLen += strlen (List->Str);
   2596     CmdLen++;
   2597 
   2598     List = List->Next;
   2599   }
   2600 
   2601   CmdLen += strlen (TempFileName);
   2602   CmdLen += 3;
   2603   CmdLen += strlen (mGlobals.PreprocessFileName);
   2604 
   2605   PreProcessCmd = malloc (CmdLen);
   2606   if (PreProcessCmd == NULL) {
   2607     Error (NULL, 0, 0, UTILITY_NAME, "memory allocation fail (%d bytes)\n", CmdLen);
   2608     return STATUS_ERROR;
   2609   }
   2610 
   2611   strcpy (PreProcessCmd, PREPROCESSOR_COMMAND);
   2612   strcat (PreProcessCmd, " ");
   2613   strcat (PreProcessCmd, PREPROCESSOR_OPTIONS);
   2614   strcat (PreProcessCmd, " ");
   2615 
   2616 
   2617   strcat (PreProcessCmd, "-I ");
   2618   strcat (PreProcessCmd, SourceFileDir);
   2619   strcat (PreProcessCmd, " ");
   2620 
   2621   List = mGlobals.PreprocessFlags;
   2622   while (List != NULL) {
   2623     strcat (PreProcessCmd, List->Str);
   2624     strcat (PreProcessCmd, " ");
   2625 
   2626     List = List->Next;
   2627   }
   2628 
   2629   strcat (PreProcessCmd, TempFileName);
   2630   strcat (PreProcessCmd, " > ");
   2631   strcat (PreProcessCmd, mGlobals.PreprocessFileName);
   2632 
   2633   //
   2634   // Preprocess the source file
   2635   //
   2636   Status = system (PreProcessCmd);
   2637   if (Status != 0) {
   2638     Error (NULL, 0, 0, PreProcessCmd, "failed to spawn C preprocessor on source file\n");
   2639     free (PreProcessCmd);
   2640     return STATUS_ERROR;
   2641   }
   2642 
   2643   free (PreProcessCmd);
   2644   return STATUS_SUCCESS;
   2645 }
   2646 
   2647 static
   2648 STATUS
   2649 ScanFiles (
   2650   TEXT_STRING_LIST *ScanFiles
   2651   )
   2652 {
   2653   char              Line[MAX_LINE_LEN];
   2654   FILE              *Fptr;
   2655   char              *FileName;
   2656   UINT32            LineNum;
   2657   char              *Cptr;
   2658   char              *SavePtr;
   2659   char              *TermPtr;
   2660   char              *StringTokenPos;
   2661   TEXT_STRING_LIST  *SList;
   2662   BOOLEAN           SkipIt;
   2663   BOOLEAN           FileExist;
   2664 
   2665   //
   2666   // Put a null-terminator at the end of the line. If we read in
   2667   // a line longer than we support, then we can catch it.
   2668   //
   2669   Line[MAX_LINE_LEN - 1] = 0;
   2670   //
   2671   // Process each file. If they gave us a skip extension list, then
   2672   // skip it if the extension matches.
   2673   //
   2674   FileExist = FALSE;
   2675   while (ScanFiles != NULL) {
   2676     SkipIt = FALSE;
   2677     for (SList = mGlobals.SkipExt; SList != NULL; SList = SList->Next) {
   2678       if ((strlen (ScanFiles->Str) > strlen (SList->Str)) &&
   2679           (strcmp (ScanFiles->Str + strlen (ScanFiles->Str) - strlen (SList->Str), SList->Str) == 0)
   2680           ) {
   2681         SkipIt = TRUE;
   2682         //
   2683         // printf ("Match: %s : %s\n", ScanFiles->Str, SList->Str);
   2684         //
   2685         break;
   2686       }
   2687     }
   2688 
   2689     if (!SkipIt) {
   2690       if (mGlobals.VerboseScan) {
   2691         printf ("Scanning %s\n", ScanFiles->Str);
   2692       }
   2693 
   2694       if (mGlobals.Preprocess) {
   2695         //
   2696         // Create an empty string defines file for preprocessor
   2697         //
   2698         if (!FileExist) {
   2699           Fptr = fopen (mGlobals.StringHFileName, "w");
   2700           if (Fptr == NULL) {
   2701             Error (NULL, 0, 0, mGlobals.StringHFileName, "failed to open file for write");
   2702             return STATUS_ERROR;
   2703           }
   2704 
   2705           fclose (Fptr);
   2706           FileExist = TRUE;
   2707         }
   2708 
   2709         //
   2710         // Preprocess using C preprocessor
   2711         //
   2712         if (PreprocessSourceFile (ScanFiles->Str) != STATUS_SUCCESS) {
   2713           return STATUS_ERROR;
   2714         }
   2715 
   2716         FileName = mGlobals.PreprocessFileName;
   2717       } else {
   2718         FileName = ScanFiles->Str;
   2719       }
   2720 
   2721       Fptr = fopen (FileName, "r");
   2722       if (Fptr == NULL) {
   2723         Error (NULL, 0, 0, FileName, "failed to open input file for scanning");
   2724         return STATUS_ERROR;
   2725       }
   2726 
   2727       LineNum = 0;
   2728       while (fgets (Line, sizeof (Line), Fptr) != NULL) {
   2729         LineNum++;
   2730         if (Line[MAX_LINE_LEN - 1] != 0) {
   2731           Error (ScanFiles->Str, LineNum, 0, "line length exceeds maximum supported by tool", NULL);
   2732           fclose (Fptr);
   2733           return STATUS_ERROR;
   2734         }
   2735         //
   2736         // Remove the newline from the input line so we can print a warning message
   2737         //
   2738         if (Line[strlen (Line) - 1] == '\n') {
   2739           Line[strlen (Line) - 1] = 0;
   2740         }
   2741         //
   2742         // Terminate the line at // comments
   2743         //
   2744         Cptr = strstr (Line, "//");
   2745         if (Cptr != NULL) {
   2746           *Cptr = 0;
   2747         }
   2748 
   2749         Cptr = Line;
   2750         while ((Cptr = strstr (Cptr, STRING_TOKEN)) != NULL) {
   2751           //
   2752           // Found "STRING_TOKEN". Make sure we don't have NUM_STRING_TOKENS or
   2753           // something like that. Then make sure it's followed by
   2754           // an open parenthesis, a string identifier, and then a closing
   2755           // parenthesis.
   2756           //
   2757           if (mGlobals.VerboseScan) {
   2758             printf (" %d: %s", LineNum, Cptr);
   2759           }
   2760 
   2761           if (((Cptr == Line) || (!IsValidIdentifierChar (*(Cptr - 1), FALSE))) &&
   2762               (!IsValidIdentifierChar (*(Cptr + sizeof (STRING_TOKEN) - 1), FALSE))
   2763               ) {
   2764             StringTokenPos  = Cptr;
   2765             SavePtr         = Cptr;
   2766             Cptr += strlen (STRING_TOKEN);
   2767             while (*Cptr && isspace (*Cptr) && (*Cptr != '(')) {
   2768               Cptr++;
   2769             }
   2770 
   2771             if (*Cptr != '(') {
   2772               Warning (ScanFiles->Str, LineNum, 0, StringTokenPos, "expected "STRING_TOKEN "(identifier)");
   2773             } else {
   2774               //
   2775               // Skip over the open-parenthesis and find the next non-blank character
   2776               //
   2777               Cptr++;
   2778               while (isspace (*Cptr)) {
   2779                 Cptr++;
   2780               }
   2781 
   2782               SavePtr = Cptr;
   2783               if ((*Cptr == '_') || isalpha (*Cptr)) {
   2784                 while ((*Cptr == '_') || (isalnum (*Cptr))) {
   2785                   Cptr++;
   2786                 }
   2787 
   2788                 TermPtr = Cptr;
   2789                 while (*Cptr && isspace (*Cptr)) {
   2790                   Cptr++;
   2791                 }
   2792 
   2793                 if (*Cptr != ')') {
   2794                   Warning (ScanFiles->Str, LineNum, 0, StringTokenPos, "expected "STRING_TOKEN "(identifier)");
   2795                 }
   2796 
   2797                 if (*TermPtr) {
   2798                   *TermPtr  = 0;
   2799                   Cptr      = TermPtr + 1;
   2800                 } else {
   2801                   Cptr = TermPtr;
   2802                 }
   2803                 //
   2804                 // Add the string identifier to the list of used strings
   2805                 //
   2806                 ParserSetPosition (ScanFiles->Str, LineNum);
   2807                 StringDBSetStringReferenced (SavePtr, mGlobals.IgnoreNotFound);
   2808                 if (mGlobals.VerboseScan) {
   2809                   printf ("...referenced %s", SavePtr);
   2810                 }
   2811               } else {
   2812                 Warning (ScanFiles->Str, LineNum, 0, StringTokenPos, "expected valid string identifier name");
   2813               }
   2814             }
   2815           } else {
   2816             //
   2817             // Found it, but it's a substring of something else. Advance our pointer.
   2818             //
   2819             Cptr++;
   2820           }
   2821 
   2822           if (mGlobals.VerboseScan) {
   2823             printf ("\n");
   2824           }
   2825         }
   2826       }
   2827 
   2828       fclose (Fptr);
   2829     } else {
   2830       //
   2831       // Skipping this file type
   2832       //
   2833       if (mGlobals.VerboseScan) {
   2834         printf ("Skip scanning of %s\n", ScanFiles->Str);
   2835       }
   2836     }
   2837 
   2838     ScanFiles = ScanFiles->Next;
   2839   }
   2840 
   2841   //
   2842   // Remove the empty string defines file
   2843   //
   2844   if (FileExist) {
   2845     remove (mGlobals.StringHFileName);
   2846   }
   2847 
   2848   return STATUS_SUCCESS;
   2849 }
   2850 //
   2851 // Free the global string lists we allocated memory for
   2852 //
   2853 static
   2854 void
   2855 FreeLists (
   2856   VOID
   2857   )
   2858 {
   2859   TEXT_STRING_LIST  *Temp;
   2860   WCHAR_STRING_LIST *WTemp;
   2861 
   2862   //
   2863   // Traverse the include paths, freeing each
   2864   //
   2865   while (mGlobals.IncludePaths != NULL) {
   2866     Temp = mGlobals.IncludePaths->Next;
   2867     free (mGlobals.IncludePaths->Str);
   2868     free (mGlobals.IncludePaths);
   2869     mGlobals.IncludePaths = Temp;
   2870   }
   2871   //
   2872   // If we did a scan, then free up our
   2873   // list of files to scan.
   2874   //
   2875   while (mGlobals.ScanFileName != NULL) {
   2876     Temp = mGlobals.ScanFileName->Next;
   2877     free (mGlobals.ScanFileName->Str);
   2878     free (mGlobals.ScanFileName);
   2879     mGlobals.ScanFileName = Temp;
   2880   }
   2881   //
   2882   // Free up preprocess flags list
   2883   //
   2884   while (mGlobals.PreprocessFlags != NULL) {
   2885     Temp = mGlobals.PreprocessFlags->Next;
   2886     free (mGlobals.PreprocessFlags->Str);
   2887     free (mGlobals.PreprocessFlags);
   2888     mGlobals.PreprocessFlags = Temp;
   2889   }
   2890   //
   2891   // If they gave us a list of filename extensions to
   2892   // skip on scan, then free them up.
   2893   //
   2894   while (mGlobals.SkipExt != NULL) {
   2895     Temp = mGlobals.SkipExt->Next;
   2896     free (mGlobals.SkipExt->Str);
   2897     free (mGlobals.SkipExt);
   2898     mGlobals.SkipExt = Temp;
   2899   }
   2900   //
   2901   // Free up any languages specified
   2902   //
   2903   while (mGlobals.Language != NULL) {
   2904     WTemp = mGlobals.Language->Next;
   2905     free (mGlobals.Language->Str);
   2906     free (mGlobals.Language);
   2907     mGlobals.Language = WTemp;
   2908   }
   2909   //
   2910   // Free up our indirection list
   2911   //
   2912   while (mGlobals.IndirectionList != NULL) {
   2913     mGlobals.LastIndirectionList = mGlobals.IndirectionList->Next;
   2914     free (mGlobals.IndirectionList->Str1);
   2915     free (mGlobals.IndirectionList->Str2);
   2916     free (mGlobals.IndirectionList);
   2917     mGlobals.IndirectionList = mGlobals.LastIndirectionList;
   2918   }
   2919 
   2920   while (mGlobals.IndirectionFileName != NULL) {
   2921     mGlobals.LastIndirectionFileName = mGlobals.IndirectionFileName->Next;
   2922     free (mGlobals.IndirectionFileName->Str);
   2923     free (mGlobals.IndirectionFileName);
   2924     mGlobals.IndirectionFileName = mGlobals.LastIndirectionFileName;
   2925   }
   2926 }
   2927 
   2928 static
   2929 BOOLEAN
   2930 IsValidIdentifierChar (
   2931   INT8      Char,
   2932   BOOLEAN   FirstChar
   2933   )
   2934 {
   2935   //
   2936   // If it's the first character of an identifier, then
   2937   // it must be one of [A-Za-z_].
   2938   //
   2939   if (FirstChar) {
   2940     if (isalpha (Char) || (Char == '_')) {
   2941       return TRUE;
   2942     }
   2943   } else {
   2944     //
   2945     // If it's not the first character, then it can
   2946     // be one of [A-Za-z_0-9]
   2947     //
   2948     if (isalnum (Char) || (Char == '_')) {
   2949       return TRUE;
   2950     }
   2951   }
   2952 
   2953   return FALSE;
   2954 }
   2955 
   2956 static
   2957 void
   2958 RewindFile (
   2959   SOURCE_FILE *SourceFile
   2960   )
   2961 {
   2962   SourceFile->LineNum       = 1;
   2963   SourceFile->FileBufferPtr = SourceFile->FileBuffer;
   2964   SourceFile->EndOfFile     = FALSE;
   2965 }
   2966 
   2967 static
   2968 BOOLEAN
   2969 SkipTo (
   2970   SOURCE_FILE *SourceFile,
   2971   WCHAR       WChar,
   2972   BOOLEAN     StopAfterNewline
   2973   )
   2974 {
   2975   while (!EndOfFile (SourceFile)) {
   2976     //
   2977     // Check for the character of interest
   2978     //
   2979     if (SourceFile->FileBufferPtr[0] == WChar) {
   2980       return TRUE;
   2981     } else {
   2982       if (SourceFile->FileBufferPtr[0] == UNICODE_LF) {
   2983         SourceFile->LineNum++;
   2984         if (StopAfterNewline) {
   2985           SourceFile->FileBufferPtr++;
   2986           if (SourceFile->FileBufferPtr[0] == 0) {
   2987             SourceFile->FileBufferPtr++;
   2988           }
   2989 
   2990           return FALSE;
   2991         }
   2992       }
   2993 
   2994       SourceFile->FileBufferPtr++;
   2995     }
   2996   }
   2997 
   2998   return FALSE;
   2999 }
   3000 
   3001 static
   3002 void
   3003 Usage (
   3004   VOID
   3005   )
   3006 /*++
   3007 
   3008 Routine Description:
   3009 
   3010   Print usage information for this utility.
   3011 
   3012 Arguments:
   3013 
   3014   None.
   3015 
   3016 Returns:
   3017 
   3018   Nothing.
   3019 
   3020 --*/
   3021 {
   3022   int         Index;
   3023   const char  *Str[] = {
   3024     UTILITY_NAME" "UTILITY_VERSION" - Intel UEFI String Gather Utility",
   3025     "  Copyright (C), 2004 - 2008 Intel Corporation",
   3026 
   3027 #if ( defined(UTILITY_BUILD) && defined(UTILITY_VENDOR) )
   3028     "  Built from "UTILITY_BUILD", project of "UTILITY_VENDOR,
   3029 #endif
   3030     "",
   3031     "Usage:",
   3032     "  "UTILITY_NAME" -parse [OPTION] FILE",
   3033     "  "UTILITY_NAME" -scan  [OPTION] FILE",
   3034     "  "UTILITY_NAME" -dump  [OPTION]",
   3035     "Description:",
   3036     "  Process unicode strings file.",
   3037     "Common options include:",
   3038     "  -h or -?         for this help information",
   3039     "  -db Database     required name of output/input database file",
   3040     "  -bn BaseName     for use in the .h and .c output files",
   3041     "                   Default = "DEFAULT_BASE_NAME,
   3042     "  -v               for verbose output",
   3043     "  -vdbw            for verbose output when writing database",
   3044     "  -vdbr            for verbose output when reading database",
   3045     "  -od FileName     to specify an output database file name",
   3046     "Parse options include:",
   3047     "  -i IncludePath   add IncludePath to list of search paths",
   3048     "  -dep FileName    to specify an output dependency file name",
   3049     "  -newdb           to not read in existing database file",
   3050     "  -uqs             to indicate that unquoted strings are used",
   3051     "  FileNames        name of one or more unicode files to parse",
   3052     "Scan options include:",
   3053     "  -scan            scan text file(s) for STRING_TOKEN() usage",
   3054     "  -skipext .ext    to skip scan of files with .ext filename extension",
   3055     "  -ppflag \"Flags\"  to specify the C preprocessor flags",
   3056     "  -oh FileName     to specify string defines file name for preprocessor",
   3057     "  -ignorenotfound  ignore if a given STRING_TOKEN(STR) is not ",
   3058     "                   found in the database",
   3059     "  FileNames        one or more files to scan",
   3060     "Dump options include:",
   3061     "  -oc FileName     write string data to FileName",
   3062     "  -oh FileName     write string defines to FileName",
   3063     "  -ou FileName     dump database to unicode file FileName",
   3064     "  -lang Lang       only dump for the language 'Lang'",
   3065     "                   use ';' to separate multiple languages",
   3066     "  -if FileName     to specify an indirection file",
   3067     "  -hpk FileName    to create an HII export pack of the strings",
   3068     "",
   3069     "The expected process is to parse a unicode string file to create an initial",
   3070     "database of string identifier names and string definitions. Then text files",
   3071     "should be scanned for STRING_TOKEN() usages, and the referenced",
   3072     "strings will be tagged as used in the database. After all files have been",
   3073     "scanned, then the database should be dumped to create the necessary output",
   3074     "files.",
   3075     "",
   3076     NULL
   3077   };
   3078   for (Index = 0; Str[Index] != NULL; Index++) {
   3079     fprintf (stdout, "%s\n", Str[Index]);
   3080   }
   3081 }
   3082