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