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.IndirectionList->Str2);
2916 free (mGlobals.IndirectionList);
2917 mGlobals.IndirectionList = mGlobals.LastIndirectionList;
2918 }
2919
2920 while (mGlobals.IndirectionFileName != NULL) {
2921 mGlobals.LastIndirectionFileName = mGlobals.IndirectionFileName->Next;
2922 free (mGlobals.IndirectionFileName->Str);
2923 free (mGlobals.IndirectionFileName);
2924 mGlobals.IndirectionFileName = mGlobals.LastIndirectionFileName;
2925 }
2926 }
2927
2928 static
2929 BOOLEAN
2930 IsValidIdentifierChar (
2931 INT8 Char,
2932 BOOLEAN FirstChar
2933 )
2934 {
2935 //
2936 // If it's the first character of an identifier, then
2937 // it must be one of [A-Za-z_].
2938 //
2939 if (FirstChar) {
2940 if (isalpha (Char) || (Char == '_')) {
2941 return TRUE;
2942 }
2943 } else {
2944 //
2945 // If it's not the first character, then it can
2946 // be one of [A-Za-z_0-9]
2947 //
2948 if (isalnum (Char) || (Char == '_')) {
2949 return TRUE;
2950 }
2951 }
2952
2953 return FALSE;
2954 }
2955
2956 static
2957 void
2958 RewindFile (
2959 SOURCE_FILE *SourceFile
2960 )
2961 {
2962 SourceFile->LineNum = 1;
2963 SourceFile->FileBufferPtr = SourceFile->FileBuffer;
2964 SourceFile->EndOfFile = FALSE;
2965 }
2966
2967 static
2968 BOOLEAN
2969 SkipTo (
2970 SOURCE_FILE *SourceFile,
2971 WCHAR WChar,
2972 BOOLEAN StopAfterNewline
2973 )
2974 {
2975 while (!EndOfFile (SourceFile)) {
2976 //
2977 // Check for the character of interest
2978 //
2979 if (SourceFile->FileBufferPtr[0] == WChar) {
2980 return TRUE;
2981 } else {
2982 if (SourceFile->FileBufferPtr[0] == UNICODE_LF) {
2983 SourceFile->LineNum++;
2984 if (StopAfterNewline) {
2985 SourceFile->FileBufferPtr++;
2986 if (SourceFile->FileBufferPtr[0] == 0) {
2987 SourceFile->FileBufferPtr++;
2988 }
2989
2990 return FALSE;
2991 }
2992 }
2993
2994 SourceFile->FileBufferPtr++;
2995 }
2996 }
2997
2998 return FALSE;
2999 }
3000
3001 static
3002 void
3003 Usage (
3004 VOID
3005 )
3006 /*++
3007
3008 Routine Description:
3009
3010 Print usage information for this utility.
3011
3012 Arguments:
3013
3014 None.
3015
3016 Returns:
3017
3018 Nothing.
3019
3020 --*/
3021 {
3022 int Index;
3023 const char *Str[] = {
3024 UTILITY_NAME" "UTILITY_VERSION" - Intel UEFI String Gather Utility",
3025 " Copyright (C), 2004 - 2008 Intel Corporation",
3026
3027 #if ( defined(UTILITY_BUILD) && defined(UTILITY_VENDOR) )
3028 " Built from "UTILITY_BUILD", project of "UTILITY_VENDOR,
3029 #endif
3030 "",
3031 "Usage:",
3032 " "UTILITY_NAME" -parse [OPTION] FILE",
3033 " "UTILITY_NAME" -scan [OPTION] FILE",
3034 " "UTILITY_NAME" -dump [OPTION]",
3035 "Description:",
3036 " Process unicode strings file.",
3037 "Common options include:",
3038 " -h or -? for this help information",
3039 " -db Database required name of output/input database file",
3040 " -bn BaseName for use in the .h and .c output files",
3041 " Default = "DEFAULT_BASE_NAME,
3042 " -v for verbose output",
3043 " -vdbw for verbose output when writing database",
3044 " -vdbr for verbose output when reading database",
3045 " -od FileName to specify an output database file name",
3046 "Parse options include:",
3047 " -i IncludePath add IncludePath to list of search paths",
3048 " -dep FileName to specify an output dependency file name",
3049 " -newdb to not read in existing database file",
3050 " -uqs to indicate that unquoted strings are used",
3051 " FileNames name of one or more unicode files to parse",
3052 "Scan options include:",
3053 " -scan scan text file(s) for STRING_TOKEN() usage",
3054 " -skipext .ext to skip scan of files with .ext filename extension",
3055 " -ppflag \"Flags\" to specify the C preprocessor flags",
3056 " -oh FileName to specify string defines file name for preprocessor",
3057 " -ignorenotfound ignore if a given STRING_TOKEN(STR) is not ",
3058 " found in the database",
3059 " FileNames one or more files to scan",
3060 "Dump options include:",
3061 " -oc FileName write string data to FileName",
3062 " -oh FileName write string defines to FileName",
3063 " -ou FileName dump database to unicode file FileName",
3064 " -lang Lang only dump for the language 'Lang'",
3065 " use ';' to separate multiple languages",
3066 " -if FileName to specify an indirection file",
3067 " -hpk FileName to create an HII export pack of the strings",
3068 "",
3069 "The expected process is to parse a unicode string file to create an initial",
3070 "database of string identifier names and string definitions. Then text files",
3071 "should be scanned for STRING_TOKEN() usages, and the referenced",
3072 "strings will be tagged as used in the database. After all files have been",
3073 "scanned, then the database should be dumped to create the necessary output",
3074 "files.",
3075 "",
3076 NULL
3077 };
3078 for (Index = 0; Str[Index] != NULL; Index++) {
3079 fprintf (stdout, "%s\n", Str[Index]);
3080 }
3081 }