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
main(int Argc,char * Argv[])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
ProcessIncludeFile(SOURCE_FILE * SourceFile,SOURCE_FILE * ParentSourceFile)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
ProcessFile(SOURCE_FILE * SourceFile)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
ParseFile(SOURCE_FILE * SourceFile)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
PreprocessFile(SOURCE_FILE * SourceFile)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 *
GetPrintableLanguageName(IN SOURCE_FILE * SourceFile)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 *
GetLangCode(IN WCHAR * Lang)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 *
GetLangCodeList(IN WCHAR * SecondaryLangList)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 *
GetSecondaryLanguageList(IN SOURCE_FILE * SourceFile)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 *
GetQuotedString(SOURCE_FILE * SourceFile,BOOLEAN Optional)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
ProcessTokenString(SOURCE_FILE * SourceFile)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
EndOfFile(SOURCE_FILE * SourceFile)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
GetStringIdentifierName(IN SOURCE_FILE * SourceFile,IN OUT WCHAR * StringIdentifierName,IN UINT32 StringIdentifierNameLen)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
GetLanguageIdentifierName(IN SOURCE_FILE * SourceFile,IN OUT WCHAR * LanguageIdentifierName,IN UINT32 LanguageIdentifierNameLen,IN BOOLEAN Optional)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
ProcessTokenInclude(SOURCE_FILE * SourceFile)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
ProcessTokenScope(SOURCE_FILE * SourceFile)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
ProcessTokenLangDef(SOURCE_FILE * SourceFile)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
ProcessTokenSecondaryLangDef(SOURCE_FILE * SourceFile)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
ApparentQuotedString(SOURCE_FILE * SourceFile)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
ProcessTokenLanguage(SOURCE_FILE * SourceFile)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
IsWhiteSpace(SOURCE_FILE * SourceFile)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
SkipWhiteSpace(SOURCE_FILE * SourceFile)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
wstrcmp(WCHAR * Buffer,WCHAR * Str)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 *
wstrcatenate(WCHAR * Dst,WCHAR * Src)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 *
FindFile(IN INT8 * FileName,OUT INT8 * FoundFileName,IN UINT32 FoundFileNameLen)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
ProcessArgs(int Argc,char * Argv[])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
AddCommandLineLanguage(IN INT8 * Language)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
ParseIndirectionFiles(TEXT_STRING_LIST * Files)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
PreprocessSourceFile(UINT8 * SourceFileName)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
ScanFiles(TEXT_STRING_LIST * ScanFiles)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
FreeLists(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
IsValidIdentifierChar(INT8 Char,BOOLEAN FirstChar)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
RewindFile(SOURCE_FILE * SourceFile)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
SkipTo(SOURCE_FILE * SourceFile,WCHAR WChar,BOOLEAN StopAfterNewline)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
Usage(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