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
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.
18 Parse a strings file and create or add to a string database file.
27 #include <EfiUtilityMsgs.h>
29 #include "StrGather.h"
32 #define UTILITY_NAME "StrGather"
33 #define UTILITY_VERSION "v1.2"
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"
44 // Operational modes for this utility
46 #define MODE_UNKNOWN 0
52 // This is how we invoke the C preprocessor on the source file
53 // to resolve #if, #else, etc.
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"
61 // We keep a linked list of these for the source files we process
63 typedef struct _SOURCE_FILE
{
68 INT8 FileName
[MAX_PATH
];
72 struct _SOURCE_FILE
*Previous
;
73 struct _SOURCE_FILE
*Next
;
74 WCHAR ControlCharacter
;
77 #define DEFAULT_CONTROL_CHARACTER UNICODE_SLASH
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.
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
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
;
124 IsValidIdentifierChar (
132 SOURCE_FILE
*SourceFile
138 SOURCE_FILE
*SourceFile
,
140 BOOLEAN StopAfterNewline
146 SOURCE_FILE
*SourceFile
152 SOURCE_FILE
*SourceFile
158 SOURCE_FILE
*SourceFile
164 SOURCE_FILE
*SourceFile
169 GetStringIdentifierName (
170 IN SOURCE_FILE
*SourceFile
,
171 IN OUT WCHAR
*StringIdentifierName
,
172 IN UINT32 StringIdentifierNameLen
177 GetLanguageIdentifierName (
178 IN SOURCE_FILE
*SourceFile
,
179 IN OUT WCHAR
*LanguageIdentifierName
,
180 IN UINT32 LanguageIdentifierNameLen
,
186 GetPrintableLanguageName (
187 IN SOURCE_FILE
*SourceFile
192 AddCommandLineLanguage (
199 SOURCE_FILE
*SourceFile
,
206 SOURCE_FILE
*SourceFile
,
207 SOURCE_FILE
*ParentSourceFile
213 SOURCE_FILE
*SourceFile
220 OUT INT8
*FoundFileName
,
221 IN UINT32 FoundFileNameLen
234 SOURCE_FILE
*SourceFile
266 SOURCE_FILE
*SourceFile
271 ProcessTokenInclude (
272 SOURCE_FILE
*SourceFile
278 SOURCE_FILE
*SourceFile
283 ProcessTokenLanguage (
284 SOURCE_FILE
*SourceFile
289 ProcessTokenLangDef (
290 SOURCE_FILE
*SourceFile
295 ProcessTokenSecondaryLangDef (
296 SOURCE_FILE
*SourceFile
302 TEXT_STRING_LIST
*ScanFiles
307 ParseIndirectionFiles (
308 TEXT_STRING_LIST
*Files
320 Call the routine to parse the command-line options, then process the file.
324 Argc - Standard C main() argc and argv.
325 Argv - Standard C main() argc and argv.
336 SetUtilityName (UTILITY_NAME
);
338 // Process the command-line arguments
340 Status
= ProcessArgs (Argc
, Argv
);
341 if (Status
!= STATUS_SUCCESS
) {
345 // Initialize the database manager
347 StringDBConstructor ();
349 // We always try to read in an existing database file. It may not
350 // exist, which is ok usually.
352 if (mGlobals
.NewDatabase
== 0) {
354 // Read all databases specified.
356 for (mGlobals
.LastDatabaseFileName
= mGlobals
.DatabaseFileName
;
357 mGlobals
.LastDatabaseFileName
!= NULL
;
358 mGlobals
.LastDatabaseFileName
= mGlobals
.LastDatabaseFileName
->Next
360 Status
= StringDBReadDatabase (mGlobals
.LastDatabaseFileName
->Str
, TRUE
, mGlobals
.VerboseDatabaseRead
);
361 if (Status
!= STATUS_SUCCESS
) {
367 // Read indirection file(s) if specified
369 if (ParseIndirectionFiles (mGlobals
.IndirectionFileName
) != STATUS_SUCCESS
) {
373 // If scanning source files, do that now
375 if (mGlobals
.Mode
== MODE_SCAN
) {
376 ScanFiles (mGlobals
.ScanFileName
);
377 } else if (mGlobals
.Mode
== MODE_PARSE
) {
379 // Parsing a unicode strings file
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");
388 Status
= ProcessIncludeFile (&mGlobals
.SourceFiles
, NULL
);
389 if (mGlobals
.OutputDependencyFptr
!= NULL
) {
390 fclose (mGlobals
.OutputDependencyFptr
);
392 if (Status
!= STATUS_SUCCESS
) {
397 // Create the string defines header file if there have been no errors.
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
) {
408 // Dump the strings to a .c file if there have still been no errors.
410 if ((mGlobals
.StringCFileName
[0] != 0) && (GetUtilityStatus () < STATUS_ERROR
)) {
411 Status
= StringDBDumpCStrings (
413 mGlobals
.StringCFileName
,
416 if (Status
!= EFI_SUCCESS
) {
422 // Dump the database if requested
424 if ((mGlobals
.DumpUFileName
[0] != 0) && (GetUtilityStatus () < STATUS_ERROR
)) {
425 StringDBDumpDatabase (NULL
, mGlobals
.DumpUFileName
, FALSE
);
428 // Dump the string data as HII binary string pack if requested
430 if ((mGlobals
.HiiExportPackFileName
[0] != 0) && (GetUtilityStatus () < STATUS_ERROR
)) {
431 StringDBCreateHiiExportPack (mGlobals
.HiiExportPackFileName
, mGlobals
.Language
);
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
438 if ((mGlobals
.Mode
!= MODE_DUMP
) && (GetUtilityStatus () < STATUS_ERROR
)) {
439 if (mGlobals
.OutputDatabaseFileName
[0]) {
440 Status
= StringDBWriteDatabase (mGlobals
.OutputDatabaseFileName
, mGlobals
.VerboseDatabaseWrite
);
442 Status
= StringDBWriteDatabase (mGlobals
.DatabaseFileName
->Str
, mGlobals
.VerboseDatabaseWrite
);
445 if (Status
!= EFI_SUCCESS
) {
455 StringDBDestructor ();
456 return GetUtilityStatus ();
462 SOURCE_FILE
*SourceFile
,
463 SOURCE_FILE
*ParentSourceFile
469 Given a source file, open the file and parse it
473 SourceFile - name of file to parse
474 ParentSourceFile - for error reporting purposes, the file that #included SourceFile.
482 static UINT32 NestDepth
= 0;
483 INT8 FoundFileName
[MAX_PATH
];
486 Status
= STATUS_SUCCESS
;
489 // Print the file being processed. Indent so you can tell the include nesting
492 if (mGlobals
.Verbose
) {
493 fprintf (stdout
, "%*cProcessing file '%s'\n", NestDepth
* 2, ' ', SourceFile
->FileName
);
497 // Make sure we didn't exceed our maximum nesting depth
499 if (NestDepth
> MAX_NEST_DEPTH
) {
500 Error (NULL
, 0, 0, SourceFile
->FileName
, "max nesting depth (%d) exceeded", NestDepth
);
501 Status
= STATUS_ERROR
;
505 // Try to open the file locally, and if that fails try along our include paths.
507 strcpy (FoundFileName
, SourceFile
->FileName
);
508 if ((SourceFile
->Fptr
= fopen (FoundFileName
, "rb")) == NULL
) {
510 // Try to find it among the paths if it has a parent (that is, it is included
513 if (ParentSourceFile
== NULL
) {
514 Error (NULL
, 0, 0, SourceFile
->FileName
, "file not found");
515 Status
= STATUS_ERROR
;
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
;
528 // Output the dependency
530 if (mGlobals
.OutputDependencyFptr
!= NULL
) {
531 fprintf (mGlobals
.OutputDependencyFptr
, "%s : %s\n", mGlobals
.DatabaseFileName
->Str
, FoundFileName
);
533 // Add pseudo target to avoid incremental build failure when the file is deleted
535 fprintf (mGlobals
.OutputDependencyFptr
, "%s : \n", FoundFileName
);
539 // Process the file found
541 ProcessFile (SourceFile
);
546 // Close open files and return status
548 if (SourceFile
->Fptr
!= NULL
) {
549 fclose (SourceFile
->Fptr
);
558 SOURCE_FILE
*SourceFile
562 // Get the file size, and then read the entire thing into memory.
563 // Allocate space for a terminator character.
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
);
574 fread ((VOID
*) SourceFile
->FileBuffer
, SourceFile
->FileSize
, 1, SourceFile
->Fptr
);
575 SourceFile
->FileBuffer
[(SourceFile
->FileSize
/ sizeof (WCHAR
))] = UNICODE_NULL
;
577 // Pre-process the file to replace comments with spaces
579 PreprocessFile (SourceFile
);
583 ParseFile (SourceFile
);
584 free (SourceFile
->FileBuffer
);
585 return STATUS_SUCCESS
;
591 SOURCE_FILE
*SourceFile
598 // First character of a unicode file is special. Make sure
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");
605 SourceFile
->FileBufferPtr
++;
608 // Print the first line if in verbose mode
610 if (mGlobals
.Verbose
) {
611 printf ("%d: %S\n", SourceFile
->LineNum
, SourceFile
->FileBufferPtr
);
614 // Since the syntax is relatively straightforward, just switch on the next char
616 while (!EndOfFile (SourceFile
)) {
618 // Check for whitespace
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
);
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;
641 } else if (SourceFile
->SkipToHash
&& (SourceFile
->FileBufferPtr
[0] != SourceFile
->ControlCharacter
)) {
642 SourceFile
->FileBufferPtr
++;
644 SourceFile
->SkipToHash
= FALSE
;
645 if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
646 ((Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"include")) > 0)
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
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
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
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
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
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
678 SourceFile
->FileBufferPtr
+= Len
;
680 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
681 (SourceFile
->FileBufferPtr
[1] == UNICODE_EQUAL_SIGN
)
683 SourceFile
->ControlCharacter
= SourceFile
->FileBufferPtr
[2];
684 SourceFile
->FileBufferPtr
+= 3;
686 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "unrecognized token", "%S", SourceFile
->FileBufferPtr
);
688 // Treat rest of line as a comment.
695 return STATUS_SUCCESS
;
701 SOURCE_FILE
*SourceFile
706 Preprocess a file to replace all carriage returns with NULLs so
707 we can print lines from the file to the screen.
710 SourceFile - structure that we use to keep track of an input file.
719 RewindFile (SourceFile
);
721 while (!EndOfFile (SourceFile
)) {
723 // If a line-feed, then no longer in a comment
725 if (SourceFile
->FileBufferPtr
[0] == UNICODE_LF
) {
726 SourceFile
->FileBufferPtr
++;
727 SourceFile
->LineNum
++;
729 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_CR
) {
731 // Replace all carriage returns with a NULL so we can print stuff
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;
742 SourceFile
->FileBufferPtr
++;
746 // Could check for end-of-file and still in a comment, but
747 // should not be necessary. So just restore the file pointers.
749 RewindFile (SourceFile
);
754 GetPrintableLanguageName (
755 IN SOURCE_FILE
*SourceFile
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
;
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
);
777 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_DOUBLE_QUOTE
) {
781 SourceFile
->FileBufferPtr
++;
785 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
787 SourceFile
->FileName
,
790 "missing closing quote on printable language name string",
795 SourceFile
->FileBufferPtr
++;
798 // Now allocate memory for the string and save it off
800 String
= (WCHAR
*) malloc ((Len
+ 1) * sizeof (WCHAR
));
801 if (String
== NULL
) {
802 Error (NULL
, 0, 0, "memory allocation failed", NULL
);
806 // Copy the string from the file buffer to the local copy.
807 // We do no reformatting of it whatsoever at this point.
819 // Now format the string to convert \wide and \narrow controls
821 StringDBFormatString (String
);
828 } LanguageConvertTable
[] = {
829 { L
"eng", L
"en-US" },
830 { L
"fra", L
"fr-FR" },
831 { L
"spa", L
"es-ES" },
846 // The Lang is xx-XX format and return.
848 if (wcschr (Lang
, L
'-') != NULL
) {
849 LangCode
= (WCHAR
*) malloc ((wcslen (Lang
) + 1) * sizeof(WCHAR
));
850 if (LangCode
!= NULL
) {
851 wcscpy (LangCode
, Lang
);
857 // Convert the language accoring to the table.
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
);
874 IN WCHAR
*SecondaryLangList
877 WCHAR
*CodeBeg
, *CodeEnd
;
879 WCHAR
*LangCodeList
= NULL
;
880 WCHAR
*TempLangCodeList
= NULL
;
882 TempLangCodeList
= (WCHAR
*) malloc ((wcslen(SecondaryLangList
) + 1) * sizeof(WCHAR
));
883 if (TempLangCodeList
== NULL
) {
886 wcscpy (TempLangCodeList
, SecondaryLangList
);
887 CodeBeg
= TempLangCodeList
;
889 while (CodeBeg
!= NULL
) {
890 CodeEnd
= wcschr (CodeBeg
, L
';');
891 if (CodeEnd
!= NULL
) {
896 CodeRet
= GetLangCode (CodeBeg
);
897 if (CodeRet
!= NULL
) {
898 if (LangCodeList
!= NULL
) {
899 LangCodeList
= wstrcatenate (LangCodeList
, L
";");
901 LangCodeList
= wstrcatenate (LangCodeList
, CodeRet
);
908 free (TempLangCodeList
);
915 GetSecondaryLanguageList (
916 IN SOURCE_FILE
*SourceFile
919 WCHAR
*SecondaryLangList
= NULL
;
920 WCHAR SecondaryLang
[MAX_STRING_IDENTIFIER_NAME
+ 1];
926 SkipWhiteSpace (SourceFile
);
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
;
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
);
946 SecondaryLang
[Index
] = SourceFile
->FileBufferPtr
[0];
948 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_SPACE
) {
949 SecondaryLang
[Index
] = L
'\0';
951 if (SecondaryLang
[0] != L
'\0') {
952 if (SecondaryLangList
!= NULL
) {
953 SecondaryLangList
= wstrcatenate (SecondaryLangList
, L
";");
955 SecondaryLangList
= wstrcatenate (SecondaryLangList
, SecondaryLang
);
957 SecondaryLang
[0] = L
'\0';
958 SourceFile
->FileBufferPtr
++;
961 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_CLOSE_PAREN
) {
962 if (SecondaryLangList
!= NULL
) {
963 SecondaryLangList
= wstrcatenate (SecondaryLangList
, L
";");
965 SecondaryLangList
= wstrcatenate (SecondaryLangList
, SecondaryLang
);
968 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "can not recognize the secondary language", "%S", SourceFile
->FileBufferPtr
);
972 SourceFile
->FileBufferPtr
++;
975 if (SourceFile
->FileBufferPtr
[0] != UNICODE_CLOSE_PAREN
) {
976 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "missing closing bracket", "%S", Start
);
978 SourceFile
->FileBufferPtr
++;
981 LangCodeList
= GetLangCodeList (SecondaryLangList
);
982 FREE (SecondaryLangList
);
986 FREE(SecondaryLangList
);
993 SOURCE_FILE
*SourceFile
,
1001 BOOLEAN PreviousBackslash
;
1003 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
1005 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "expected quoted string", "%S", SourceFile
->FileBufferPtr
);
1012 SourceFile
->FileBufferPtr
++;
1013 Start
= Ptr
= SourceFile
->FileBufferPtr
;
1014 PreviousBackslash
= FALSE
;
1015 while (!EndOfFile (SourceFile
)) {
1016 if ((SourceFile
->FileBufferPtr
[0] == UNICODE_DOUBLE_QUOTE
) && (!PreviousBackslash
)) {
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
;
1024 PreviousBackslash
= FALSE
;
1027 SourceFile
->FileBufferPtr
++;
1031 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
1032 Warning (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "missing closing quote on string", "%S", Start
);
1034 SourceFile
->FileBufferPtr
++;
1037 // Now allocate memory for the string and save it off
1039 String
= (WCHAR
*) malloc ((Len
+ 1) * sizeof (WCHAR
));
1040 if (String
== NULL
) {
1041 Error (NULL
, 0, 0, "memory allocation failed", NULL
);
1045 // Copy the string from the file buffer to the local copy.
1046 // We do no reformatting of it whatsoever at this point.
1061 // #string STR_ID_NAME
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.
1068 ProcessTokenString (
1069 SOURCE_FILE
*SourceFile
1072 WCHAR StringIdentifier
[MAX_STRING_IDENTIFIER_NAME
+ 1];
1075 // Extract the string identifier name and add it to the database.
1077 if (GetStringIdentifierName (SourceFile
, StringIdentifier
, sizeof (StringIdentifier
)) > 0) {
1078 StringId
= STRING_ID_INVALID
;
1079 StringDBAddStringIdentifier (StringIdentifier
, &StringId
, 0);
1082 // Error recovery -- skip to the next #
1084 SourceFile
->SkipToHash
= TRUE
;
1091 SOURCE_FILE
*SourceFile
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.
1098 if (SourceFile
->FileBufferPtr
>= SourceFile
->FileBuffer
+ SourceFile
->FileSize
/ sizeof (WCHAR
)) {
1099 SourceFile
->EndOfFile
= TRUE
;
1103 if (SourceFile
->EndOfFile
) {
1112 GetStringIdentifierName (
1113 IN SOURCE_FILE
*SourceFile
,
1114 IN OUT WCHAR
*StringIdentifierName
,
1115 IN UINT32 StringIdentifierNameLen
1125 SkipWhiteSpace (SourceFile
);
1126 if (SourceFile
->EndOfFile
) {
1127 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "end-of-file encountered", "expected string identifier");
1131 // Verify first character of name is [A-Za-z]
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
))
1144 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "invalid character in string identifier name", "%S", Start
);
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
)
1155 if (Len
>= StringIdentifierNameLen
) {
1156 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "string identifier name too long", "%S", Start
);
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
);
1171 // Terminate the copy of the string.
1173 *StringIdentifierName
= 0;
1179 GetLanguageIdentifierName (
1180 IN SOURCE_FILE
*SourceFile
,
1181 IN OUT WCHAR
*LanguageIdentifierName
,
1182 IN UINT32 LanguageIdentifierNameLen
,
1189 WCHAR
*LanguageIdentifier
;
1191 LanguageIdentifier
= LanguageIdentifierName
;
1196 SkipWhiteSpace (SourceFile
);
1197 if (SourceFile
->EndOfFile
) {
1199 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "end-of-file encountered", "expected language identifier");
1200 return STATUS_ERROR
;
1203 return STATUS_SUCCESS
;
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.
1211 if (*SourceFile
->FileBufferPtr
== UNICODE_DOUBLE_QUOTE
) {
1212 return STATUS_SUCCESS
;
1216 LanguageIdentifierNameLen
/= 2;
1218 // Internal error if we weren't given at least 4 WCHAR's to work with.
1220 if (LanguageIdentifierNameLen
< LANGUAGE_IDENTIFIER_NAME_LEN
+ 1) {
1222 SourceFile
->FileName
,
1223 SourceFile
->LineNum
,
1225 "app error -- language identifier name length is invalid",
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
)) {
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
;
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
;
1252 // Terminate the copy of the string.
1254 *LanguageIdentifierName
= 0;
1255 LangCode
= GetLangCode (LanguageIdentifier
);
1256 if (LangCode
!= NULL
) {
1257 wcscpy (LanguageIdentifier
, LangCode
);
1260 return STATUS_SUCCESS
;
1265 ProcessTokenInclude (
1266 SOURCE_FILE
*SourceFile
1269 INT8 IncludeFileName
[MAX_PATH
];
1272 BOOLEAN ReportedError
;
1273 SOURCE_FILE IncludedSourceFile
;
1275 ReportedError
= FALSE
;
1276 if (SkipWhiteSpace (SourceFile
) == 0) {
1277 Warning (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "expected whitespace following #include keyword", NULL
);
1280 // Should be quoted file name
1282 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
1283 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "expected quoted include file name", NULL
);
1287 SourceFile
->FileBufferPtr
++;
1289 // Copy the filename as ascii to our local string
1291 To
= IncludeFileName
;
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
);
1299 if (SourceFile
->FileBufferPtr
[0] == UNICODE_DOUBLE_QUOTE
) {
1300 SourceFile
->FileBufferPtr
++;
1304 // If too long, then report the error once and process until the closing quote
1307 if (!ReportedError
&& (Len
>= sizeof (IncludeFileName
))) {
1308 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "length of include file name exceeds limit", NULL
);
1309 ReportedError
= TRUE
;
1312 if (!ReportedError
) {
1313 *To
= UNICODE_TO_ASCII (SourceFile
->FileBufferPtr
[0]);
1317 SourceFile
->FileBufferPtr
++;
1320 if (!ReportedError
) {
1322 memset ((char *) &IncludedSourceFile
, 0, sizeof (SOURCE_FILE
));
1323 strcpy (IncludedSourceFile
.FileName
, IncludeFileName
);
1324 IncludedSourceFile
.ControlCharacter
= DEFAULT_CONTROL_CHARACTER
;
1325 ProcessIncludeFile (&IncludedSourceFile
, SourceFile
);
1327 // printf ("including file '%s'\n", IncludeFileName);
1334 // Error recovery -- skip to next #
1336 SourceFile
->SkipToHash
= TRUE
;
1342 SOURCE_FILE
*SourceFile
1345 WCHAR StringIdentifier
[MAX_STRING_IDENTIFIER_NAME
+ 1];
1347 // Extract the scope name
1349 if (GetStringIdentifierName (SourceFile
, StringIdentifier
, sizeof (StringIdentifier
)) > 0) {
1350 StringDBSetScope (StringIdentifier
);
1355 // Parse: #langdef eng "English"
1356 // #langdef chn "\wideChinese"
1360 ProcessTokenLangDef (
1361 SOURCE_FILE
*SourceFile
1365 WCHAR LanguageIdentifier
[MAX_STRING_IDENTIFIER_NAME
+ 1];
1366 WCHAR
*PrintableName
;
1368 Status
= GetLanguageIdentifierName (SourceFile
, LanguageIdentifier
, sizeof (LanguageIdentifier
), FALSE
);
1369 if (Status
!= STATUS_SUCCESS
) {
1374 // Extract the printable name
1376 PrintableName
= GetPrintableLanguageName (SourceFile
);
1377 if (PrintableName
!= NULL
) {
1378 ParserSetPosition (SourceFile
->FileName
, SourceFile
->LineNum
);
1379 StringDBAddLanguage (LanguageIdentifier
, PrintableName
, NULL
);
1380 FREE (PrintableName
);
1384 // Error recovery -- skip to next #
1386 SourceFile
->SkipToHash
= TRUE
;
1391 ProcessTokenSecondaryLangDef (
1392 SOURCE_FILE
*SourceFile
1396 LANGUAGE_LIST
*Lang
;
1397 WCHAR LanguageIdentifier
[MAX_STRING_IDENTIFIER_NAME
+ 1];
1399 WCHAR
*SecondaryLangList
= NULL
;
1401 Status
= GetLanguageIdentifierName (SourceFile
, LanguageIdentifier
, sizeof (LanguageIdentifier
), FALSE
);
1402 if (Status
!= STATUS_SUCCESS
) {
1405 LangCode
= GetLangCode(LanguageIdentifier
);
1406 if (LangCode
== NULL
) {
1410 Lang
= StringDBFindLanguageList (LanguageIdentifier
);
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
;
1423 FREE (SecondaryLangList
);
1429 SourceFile
->SkipToHash
= TRUE
;
1434 ApparentQuotedString (
1435 SOURCE_FILE
*SourceFile
1440 // See if the first and last nonblank characters on the line are double quotes
1442 for (Ptr
= SourceFile
->FileBufferPtr
; *Ptr
&& (*Ptr
== UNICODE_SPACE
); Ptr
++)
1444 if (*Ptr
!= UNICODE_DOUBLE_QUOTE
) {
1453 for (; *Ptr
&& (*Ptr
== UNICODE_SPACE
); Ptr
--)
1455 if (*Ptr
!= UNICODE_DOUBLE_QUOTE
) {
1463 // #language eng "some string " "more string"
1467 ProcessTokenLanguage (
1468 SOURCE_FILE
*SourceFile
1473 WCHAR
*SecondString
;
1477 WCHAR Language
[LANGUAGE_IDENTIFIER_NAME_LEN
+ 1];
1479 BOOLEAN PreviousNewline
;
1481 // Get the language identifier
1484 Status
= GetLanguageIdentifierName (SourceFile
, Language
, sizeof (Language
), TRUE
);
1485 if (Status
!= STATUS_SUCCESS
) {
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
1495 if (SkipTo (SourceFile
, UNICODE_DOUBLE_QUOTE
, TRUE
)) {
1496 String
= GetQuotedString (SourceFile
, FALSE
);
1497 if (String
!= NULL
) {
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.
1504 Len
= wcslen (String
) + 1;
1505 ParserSetPosition (SourceFile
->FileName
, SourceFile
->LineNum
);
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");
1517 wcscpy (TempString
, String
);
1518 wcscat (TempString
, SecondString
);
1520 free (SecondString
);
1521 String
= TempString
;
1523 } while (SecondString
!= NULL
);
1524 StringDBAddString (Language
, NULL
, NULL
, String
, TRUE
, 0);
1528 // Error was reported at lower level. Error recovery mode.
1530 SourceFile
->SkipToHash
= TRUE
;
1533 if (!mGlobals
.UnquotedStrings
) {
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
1539 if (ApparentQuotedString (SourceFile
)) {
1541 SourceFile
->FileName
,
1542 SourceFile
->LineNum
,
1544 "unexpected quoted string on line",
1545 "specify -uqs option if necessary"
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.
1554 From
= SourceFile
->FileBufferPtr
;
1555 PreviousNewline
= FALSE
;
1556 while (!EndOfFile (SourceFile
)) {
1557 if (SourceFile
->FileBufferPtr
[0] == UNICODE_LF
) {
1558 PreviousNewline
= TRUE
;
1559 SourceFile
->LineNum
++;
1562 if (PreviousNewline
&& (SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
)) {
1566 PreviousNewline
= FALSE
;
1569 SourceFile
->FileBufferPtr
++;
1572 if ((Len
== 0) && EndOfFile (SourceFile
)) {
1573 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "unexpected end of file", NULL
);
1574 SourceFile
->SkipToHash
= TRUE
;
1578 // Now allocate a buffer, copy the characters, and add the string.
1580 String
= (WCHAR
*) malloc ((Len
+ 1) * sizeof (WCHAR
));
1581 if (String
== NULL
) {
1582 Error (NULL
, 0, 0, "application error", "failed to allocate memory");
1587 while (From
< SourceFile
->FileBufferPtr
) {
1606 StringDBAddString (Language
, NULL
, NULL
, String
, TRUE
, 0);
1613 SOURCE_FILE
*SourceFile
1616 switch (SourceFile
->FileBufferPtr
[0]) {
1632 SOURCE_FILE
*SourceFile
1638 while (!EndOfFile (SourceFile
)) {
1640 switch (*SourceFile
->FileBufferPtr
) {
1645 SourceFile
->FileBufferPtr
++;
1649 SourceFile
->FileBufferPtr
++;
1650 SourceFile
->LineNum
++;
1651 if (mGlobals
.Verbose
) {
1652 printf ("%d: %S\n", SourceFile
->LineNum
, SourceFile
->FileBufferPtr
);
1661 // Some tokens require trailing whitespace. If we're at the end of the
1662 // file, then we count that as well.
1664 if ((Count
== 0) && (EndOfFile (SourceFile
))) {
1681 while (*Str
== *Buffer
) {
1711 Len
+= wcslen (Src
);
1712 Dst
= (WCHAR
*) malloc ((Len
+ 1) * 2);
1727 // Given a filename, try to find it along the include paths.
1733 OUT INT8
*FoundFileName
,
1734 IN UINT32 FoundFileNameLen
1738 TEXT_STRING_LIST
*List
;
1741 // Traverse the list of paths and try to find the file
1743 List
= mGlobals
.IncludePaths
;
1744 while (List
!= NULL
) {
1746 // Put the path and filename together
1748 if (strlen (List
->Str
) + strlen (FileName
) + 1 > FoundFileNameLen
) {
1749 Error (UTILITY_NAME
, 0, 0, NULL
, "internal error - cannot concatenate path+filename");
1753 // Append the filename to this include path and try to open the file.
1755 strcpy (FoundFileName
, List
->Str
);
1756 strcat (FoundFileName
, FileName
);
1757 if ((Fptr
= fopen (FoundFileName
, "rb")) != NULL
) {
1759 // Return the file pointer
1769 FoundFileName
[0] = 0;
1773 // Process the command-line arguments
1782 TEXT_STRING_LIST
*NewList
;
1787 // Clear our globals
1789 memset ((char *) &mGlobals
, 0, sizeof (mGlobals
));
1790 strcpy (mGlobals
.BaseName
, DEFAULT_BASE_NAME
);
1792 // Skip program name
1799 return STATUS_ERROR
;
1802 mGlobals
.Mode
= MODE_UNKNOWN
;
1804 // Process until no more -args.
1806 while ((Argc
> 0) && (Argv
[0][0] == '-')) {
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
;
1816 mGlobals
.Mode
= MODE_PARSE
;
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
;
1826 mGlobals
.Mode
= MODE_SCAN
;
1828 // -vscan verbose scanning option
1830 } else if (_stricmp (Argv
[0], "-vscan") == 0) {
1831 mGlobals
.VerboseScan
= TRUE
;
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
;
1841 mGlobals
.Mode
= MODE_DUMP
;
1842 } else if (_stricmp (Argv
[0], "-uqs") == 0) {
1843 mGlobals
.UnquotedStrings
= TRUE
;
1845 // -i path add include search path when parsing
1847 } else if (_stricmp (Argv
[0], "-i") == 0) {
1849 // check for one more arg
1851 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1852 Error (UTILITY_NAME
, 0, 0, Argv
[0], "missing include path");
1853 return STATUS_ERROR
;
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.
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
;
1866 memset ((char *) NewList
, 0, sizeof (TEXT_STRING_LIST
));
1867 NewList
->Str
= malloc (strlen (Argv
[1]) + 2);
1868 if (NewList
->Str
== NULL
) {
1870 Error (UTILITY_NAME
, 0, 0, NULL
, "memory allocation failure");
1871 return STATUS_ERROR
;
1874 strcpy (NewList
->Str
, Argv
[1]);
1875 if (NewList
->Str
[strlen (NewList
->Str
) - 1] != '\\') {
1876 strcat (NewList
->Str
, "\\");
1879 // Add it to our linked list
1881 if (mGlobals
.IncludePaths
== NULL
) {
1882 mGlobals
.IncludePaths
= NewList
;
1884 mGlobals
.LastIncludePath
->Next
= NewList
;
1887 mGlobals
.LastIncludePath
= NewList
;
1890 } else if (_stricmp (Argv
[0], "-if") == 0) {
1892 // Indirection file -- check for one more arg
1894 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1895 Error (UTILITY_NAME
, 0, 0, Argv
[0], "missing indirection file name");
1896 return STATUS_ERROR
;
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.
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
;
1909 memset ((char *) NewList
, 0, sizeof (TEXT_STRING_LIST
));
1910 NewList
->Str
= malloc (strlen (Argv
[1]) + 1);
1911 if (NewList
->Str
== NULL
) {
1913 Error (UTILITY_NAME
, 0, 0, NULL
, "memory allocation failure");
1914 return STATUS_ERROR
;
1917 strcpy (NewList
->Str
, Argv
[1]);
1919 // Add it to our linked list
1921 if (mGlobals
.IndirectionFileName
== NULL
) {
1922 mGlobals
.IndirectionFileName
= NewList
;
1924 mGlobals
.LastIndirectionFileName
->Next
= NewList
;
1927 mGlobals
.LastIndirectionFileName
= NewList
;
1930 } else if (_stricmp (Argv
[0], "-db") == 0) {
1932 // -db option to specify a database file.
1933 // Check for one more arg (the database file name)
1935 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1936 Error (UTILITY_NAME
, 0, 0, Argv
[0], "missing database file name");
1937 return STATUS_ERROR
;
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
;
1946 memset ((char *) NewList
, 0, sizeof (TEXT_STRING_LIST
));
1947 NewList
->Str
= malloc (strlen (Argv
[1]) + 1);
1948 if (NewList
->Str
== NULL
) {
1950 Error (UTILITY_NAME
, 0, 0, NULL
, "memory allocation failure");
1951 return STATUS_ERROR
;
1954 strcpy (NewList
->Str
, Argv
[1]);
1956 // Add it to our linked list
1958 if (mGlobals
.DatabaseFileName
== NULL
) {
1959 mGlobals
.DatabaseFileName
= NewList
;
1961 mGlobals
.LastDatabaseFileName
->Next
= NewList
;
1964 mGlobals
.LastDatabaseFileName
= NewList
;
1967 } else if (_stricmp (Argv
[0], "-ou") == 0) {
1969 // -ou option to specify an output unicode file to
1970 // which we can dump our database.
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
;
1977 if (mGlobals
.DumpUFileName
[0] == 0) {
1978 strcpy (mGlobals
.DumpUFileName
, Argv
[1]);
1980 Error (UTILITY_NAME
, 0, 0, Argv
[1], "-ou option already specified with '%s'", mGlobals
.DumpUFileName
);
1981 return STATUS_ERROR
;
1986 } else if (_stricmp (Argv
[0], "-hpk") == 0) {
1988 // -hpk option to create an HII export pack of the input database file
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
;
1995 if (mGlobals
.HiiExportPackFileName
[0] == 0) {
1996 strcpy (mGlobals
.HiiExportPackFileName
, Argv
[1]);
1998 Error (UTILITY_NAME
, 0, 0, Argv
[1], "-or option already specified with '%s'", mGlobals
.HiiExportPackFileName
);
1999 return STATUS_ERROR
;
2004 } else if ((_stricmp (Argv
[0], "-?") == 0) || (_stricmp (Argv
[0], "-h") == 0)) {
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) {
2019 // check for one more arg
2021 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
2022 Error (UTILITY_NAME
, 0, 0, Argv
[0], "missing output C filename");
2023 return STATUS_ERROR
;
2026 strcpy (mGlobals
.StringCFileName
, Argv
[1]);
2029 } else if (_stricmp (Argv
[0], "-bn") == 0) {
2031 // check for one more arg
2033 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
2034 Error (UTILITY_NAME
, 0, 0, Argv
[0], "missing base name");
2036 return STATUS_ERROR
;
2039 strcpy (mGlobals
.BaseName
, Argv
[1]);
2042 } else if (_stricmp (Argv
[0], "-oh") == 0) {
2044 // -oh to specify output .h defines file name
2046 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
2047 Error (UTILITY_NAME
, 0, 0, Argv
[0], "missing output .h filename");
2048 return STATUS_ERROR
;
2051 strcpy (mGlobals
.StringHFileName
, Argv
[1]);
2054 } else if (_stricmp (Argv
[0], "-dep") == 0) {
2056 // -dep to specify output dependency file name
2058 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
2059 Error (UTILITY_NAME
, 0, 0, Argv
[0], "missing output dependency filename");
2060 return STATUS_ERROR
;
2063 strcpy (mGlobals
.OutputDependencyFileName
, Argv
[1]);
2066 } else if (_stricmp (Argv
[0], "-skipext") == 0) {
2068 // -skipext to skip scanning of files with certain filename extensions
2070 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
2071 Error (UTILITY_NAME
, 0, 0, Argv
[0], "missing filename extension");
2072 return STATUS_ERROR
;
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.
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
;
2085 memset ((char *) NewList
, 0, sizeof (TEXT_STRING_LIST
));
2086 NewList
->Str
= malloc (strlen (Argv
[1]) + 2);
2087 if (NewList
->Str
== NULL
) {
2089 Error (UTILITY_NAME
, 0, 0, NULL
, "memory allocation failure");
2090 return STATUS_ERROR
;
2093 if (Argv
[1][0] == '.') {
2094 strcpy (NewList
->Str
, Argv
[1]);
2096 NewList
->Str
[0] = '.';
2097 strcpy (NewList
->Str
+ 1, Argv
[1]);
2100 // Add it to our linked list
2102 if (mGlobals
.SkipExt
== NULL
) {
2103 mGlobals
.SkipExt
= NewList
;
2105 mGlobals
.LastSkipExt
->Next
= NewList
;
2108 mGlobals
.LastSkipExt
= NewList
;
2111 } else if (_stricmp (Argv
[0], "-lang") == 0) {
2113 // "-lang zh-Hans" or "-lang en-US" to only output certain languages
2115 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
2116 Error (UTILITY_NAME
, 0, 0, Argv
[0], "missing language name");
2118 return STATUS_ERROR
;
2121 if (AddCommandLineLanguage (Argv
[1]) != STATUS_SUCCESS
) {
2122 return STATUS_ERROR
;
2127 } else if (_stricmp (Argv
[0], "-od") == 0) {
2129 // Output database file name -- check for another arg
2131 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
2132 Error (UTILITY_NAME
, 0, 0, Argv
[0], "missing output database file name");
2133 return STATUS_ERROR
;
2136 strcpy (mGlobals
.OutputDatabaseFileName
, Argv
[1]);
2139 } else if (_stricmp (Argv
[0], "-ppflag") == 0) {
2141 // -ppflag "Preprocess flags" -- check for another arg
2143 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
2144 Error (UTILITY_NAME
, 0, 0, Argv
[0], "missing preprocess flags");
2146 return STATUS_ERROR
;
2150 // Allocate memory for a new list element, fill it in, and
2151 // add it to our list of preprocess flag.
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
;
2159 memset ((char *) NewList
, 0, sizeof (TEXT_STRING_LIST
));
2160 NewList
->Str
= malloc (strlen (Argv
[1]) * 2 + 1);
2161 if (NewList
->Str
== NULL
) {
2163 Error (UTILITY_NAME
, 0, 0, NULL
, "memory allocation failure");
2164 return STATUS_ERROR
;
2168 // Convert '"' to '\"' in preprocess flag
2171 Cptr2
= NewList
->Str
;
2177 while (*Cptr
!= '\0') {
2178 if ((*Cptr
== '"') && (*(Cptr
- 1) != '\\')) {
2186 // Add it to our linked list
2188 if (mGlobals
.PreprocessFlags
== NULL
) {
2189 mGlobals
.PreprocessFlags
= NewList
;
2191 mGlobals
.LastPreprocessFlags
->Next
= NewList
;
2193 mGlobals
.LastPreprocessFlags
= NewList
;
2195 mGlobals
.Preprocess
= TRUE
;
2203 Error (UTILITY_NAME
, 0, 0, Argv
[0], "unrecognized option");
2205 return STATUS_ERROR
;
2212 // Make sure they specified the mode parse/scan/dump
2214 if (mGlobals
.Mode
== MODE_UNKNOWN
) {
2215 Error (NULL
, 0, 0, "must specify one of -parse/-scan/-dump", NULL
);
2216 return STATUS_ERROR
;
2219 // All modes require a database filename
2221 if (mGlobals
.DatabaseFileName
== 0) {
2222 Error (NULL
, 0, 0, "must specify a database filename using -db DbFileName", NULL
);
2224 return STATUS_ERROR
;
2227 // If dumping the database file, then return immediately if all
2228 // parameters check out.
2230 if (mGlobals
.Mode
== MODE_DUMP
) {
2232 // Not much use if they didn't specify -oh or -oc or -ou or -hpk
2234 if ((mGlobals
.DumpUFileName
[0] == 0) &&
2235 (mGlobals
.StringHFileName
[0] == 0) &&
2236 (mGlobals
.StringCFileName
[0] == 0) &&
2237 (mGlobals
.HiiExportPackFileName
[0] == 0)
2239 Error (NULL
, 0, 0, "-dump without -oc/-oh/-ou/-hpk is a NOP", NULL
);
2240 return STATUS_ERROR
;
2243 return STATUS_SUCCESS
;
2246 // Had to specify source string file and output string defines header filename.
2248 if (mGlobals
.Mode
== MODE_SCAN
) {
2250 Error (UTILITY_NAME
, 0, 0, NULL
, "must specify at least one source file to scan with -scan");
2252 return STATUS_ERROR
;
2255 // If -ppflag is specified, -oh should also be specified for preprocess
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");
2260 return STATUS_ERROR
;
2263 // Get the list of filenames
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
;
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
;
2279 strcpy (NewList
->Str
, Argv
[0]);
2280 if (mGlobals
.ScanFileName
== NULL
) {
2281 mGlobals
.ScanFileName
= NewList
;
2283 mGlobals
.LastScanFileName
->Next
= NewList
;
2286 mGlobals
.LastScanFileName
= NewList
;
2292 // Parse mode -- must specify an input unicode file name
2295 Error (UTILITY_NAME
, 0, 0, NULL
, "must specify input unicode string file name with -parse");
2297 return STATUS_ERROR
;
2300 strcpy (mGlobals
.SourceFiles
.FileName
, Argv
[0]);
2303 return STATUS_SUCCESS
;
2306 // Found "-lang zh-Hans;en-US" on the command line. Parse the
2307 // language list and save the setting for later processing.
2311 AddCommandLineLanguage (
2315 char Separator
[] = ";";
2317 WCHAR_STRING_LIST
*WNewList
;
2320 // Keep processing the input string until we find the end.
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
;
2329 WNewList
->Next
= NULL
;
2330 WNewList
->Str
= MALLOC ((strlen (Token
) + 1) * sizeof (WCHAR
));
2331 if (WNewList
->Str
== NULL
) {
2333 Error (UTILITY_NAME
, 0, 0, NULL
, "memory allocation failure");
2334 return STATUS_ERROR
;
2337 swprintf (WNewList
->Str
, (strlen (Token
) + 1) * sizeof (WCHAR
), L
"%S", Token
);
2339 swprintf (WNewList
->Str
, L
"%S", Token
);
2343 // Add it to our linked list
2345 if (mGlobals
.Language
== NULL
) {
2346 mGlobals
.Language
= WNewList
;
2348 mGlobals
.LastLanguage
->Next
= WNewList
;
2351 mGlobals
.LastLanguage
= WNewList
;
2352 Token
= strtok (NULL
, Separator
);
2355 return STATUS_SUCCESS
;
2358 // The contents of the text file are expected to be (one per line)
2359 // STRING_IDENTIFIER_NAME ScopeName
2361 // STR_ID_MY_FAVORITE_STRING IBM
2365 ParseIndirectionFiles (
2366 TEXT_STRING_LIST
*Files
2375 WCHAR_MATCHING_STRING_LIST
*NewList
;
2377 Line
[sizeof (Line
) - 1] = 0;
2379 while (Files
!= NULL
) {
2380 Fptr
= fopen (Files
->Str
, "r");
2383 Error (NULL
, 0, 0, Files
->Str
, "failed to open input indirection file for reading");
2384 return STATUS_ERROR
;
2387 while (fgets (Line
, sizeof (Line
), Fptr
) != NULL
) {
2389 // remove terminating newline for error printing purposes.
2391 if (Line
[strlen (Line
) - 1] == '\n') {
2392 Line
[strlen (Line
) - 1] = 0;
2396 if (Line
[sizeof (Line
) - 1] != 0) {
2397 Error (Files
->Str
, LineCount
, 0, "line length exceeds maximum supported", NULL
);
2402 while (*StringName
&& (isspace (*StringName
))) {
2407 if ((*StringName
== '_') || isalpha (*StringName
)) {
2409 while ((*End
) && (*End
== '_') || (isalnum (*End
))) {
2413 if (isspace (*End
)) {
2416 while (isspace (*End
)) {
2422 while (*End
&& !isspace (*End
)) {
2428 // Add the string name/scope pair
2430 NewList
= malloc (sizeof (WCHAR_MATCHING_STRING_LIST
));
2431 if (NewList
== NULL
) {
2432 Error (NULL
, 0, 0, "memory allocation error", NULL
);
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
);
2445 swprintf (NewList
->Str1
, (strlen (StringName
) + 1) * sizeof (WCHAR
), L
"%S", StringName
);
2446 swprintf (NewList
->Str2
, (strlen (ScopeName
) + 1) * sizeof (WCHAR
), L
"%S", ScopeName
);
2448 swprintf (NewList
->Str1
, L
"%S", StringName
);
2449 swprintf (NewList
->Str2
, L
"%S", ScopeName
);
2451 if (mGlobals
.IndirectionList
== NULL
) {
2452 mGlobals
.IndirectionList
= NewList
;
2454 mGlobals
.LastIndirectionList
->Next
= NewList
;
2457 mGlobals
.LastIndirectionList
= NewList
;
2459 Error (Files
->Str
, LineCount
, 0, StringName
, "invalid line : expected 'StringIdentifier Scope'");
2463 Error (Files
->Str
, LineCount
, 0, StringName
, "invalid line : expected 'StringIdentifier Scope'");
2467 Error (Files
->Str
, LineCount
, 0, StringName
, "invalid string identifier");
2475 Files
= Files
->Next
;
2481 return STATUS_ERROR
;
2484 return STATUS_SUCCESS
;
2489 PreprocessSourceFile (
2490 UINT8
*SourceFileName
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";
2507 // Check whehter source file exist
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
;
2516 // Get source file directory
2518 strcpy (SourceFileDir
, SourceFileName
);
2519 Cptr
= strrchr (SourceFileDir
, '\\');
2525 // Generate preprocess output file name
2527 strcpy (BaseName
, mGlobals
.OutputDatabaseFileName
);
2528 Cptr
= strrchr (BaseName
, '\\');
2533 Cptr
= strrchr (SourceFileName
, '\\');
2537 strcat (BaseName
, Cptr
);
2539 Cptr
= strrchr (BaseName
, '.');
2544 strcpy (mGlobals
.PreprocessFileName
, BaseName
);
2545 strcat (mGlobals
.PreprocessFileName
, PREPROCESS_OUTPUT_FILE_EXTENSION
);
2547 strcpy (TempFileName
, BaseName
);
2548 strcat (TempFileName
, PREPROCESS_TEMP_FILE_EXTENSION
);
2551 // Insert "#undef STRING_TOKEN" after each line of "#include ...", so as to
2552 // preserve the STRING_TOKEN() for scanning after preprocess
2554 OutFptr
= fopen (TempFileName
, "w");
2555 if (OutFptr
== NULL
) {
2556 Error (NULL
, 0, 0, TempFileName
, "failed to open file for write");
2558 return STATUS_ERROR
;
2560 while (fgets (Line
, MAX_LINE_LEN
, InFptr
) != NULL
) {
2561 fputs (Line
, OutFptr
);
2565 // Skip leading blank space
2567 while (*Cptr
== ' ' || *Cptr
== '\t') {
2571 if (*Cptr
== '#' && strncmp (Cptr
+ 1, "include", 7) == 0){
2572 fputs (InsertLine
, OutFptr
);
2579 // Prepare preprocess command
2582 CmdLen
+= strlen (PREPROCESSOR_COMMAND
);
2584 CmdLen
+= strlen (PREPROCESSOR_OPTIONS
);
2588 // "-I SourceFileDir "
2590 CmdLen
+= strlen (SourceFileDir
);
2593 List
= mGlobals
.PreprocessFlags
;
2594 while (List
!= NULL
) {
2595 CmdLen
+= strlen (List
->Str
);
2601 CmdLen
+= strlen (TempFileName
);
2603 CmdLen
+= strlen (mGlobals
.PreprocessFileName
);
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
;
2611 strcpy (PreProcessCmd
, PREPROCESSOR_COMMAND
);
2612 strcat (PreProcessCmd
, " ");
2613 strcat (PreProcessCmd
, PREPROCESSOR_OPTIONS
);
2614 strcat (PreProcessCmd
, " ");
2617 strcat (PreProcessCmd
, "-I ");
2618 strcat (PreProcessCmd
, SourceFileDir
);
2619 strcat (PreProcessCmd
, " ");
2621 List
= mGlobals
.PreprocessFlags
;
2622 while (List
!= NULL
) {
2623 strcat (PreProcessCmd
, List
->Str
);
2624 strcat (PreProcessCmd
, " ");
2629 strcat (PreProcessCmd
, TempFileName
);
2630 strcat (PreProcessCmd
, " > ");
2631 strcat (PreProcessCmd
, mGlobals
.PreprocessFileName
);
2634 // Preprocess the source file
2636 Status
= system (PreProcessCmd
);
2638 Error (NULL
, 0, 0, PreProcessCmd
, "failed to spawn C preprocessor on source file\n");
2639 free (PreProcessCmd
);
2640 return STATUS_ERROR
;
2643 free (PreProcessCmd
);
2644 return STATUS_SUCCESS
;
2650 TEXT_STRING_LIST
*ScanFiles
2653 char Line
[MAX_LINE_LEN
];
2660 char *StringTokenPos
;
2661 TEXT_STRING_LIST
*SList
;
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.
2669 Line
[MAX_LINE_LEN
- 1] = 0;
2671 // Process each file. If they gave us a skip extension list, then
2672 // skip it if the extension matches.
2675 while (ScanFiles
!= NULL
) {
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)
2683 // printf ("Match: %s : %s\n", ScanFiles->Str, SList->Str);
2690 if (mGlobals
.VerboseScan
) {
2691 printf ("Scanning %s\n", ScanFiles
->Str
);
2694 if (mGlobals
.Preprocess
) {
2696 // Create an empty string defines file for preprocessor
2699 Fptr
= fopen (mGlobals
.StringHFileName
, "w");
2701 Error (NULL
, 0, 0, mGlobals
.StringHFileName
, "failed to open file for write");
2702 return STATUS_ERROR
;
2710 // Preprocess using C preprocessor
2712 if (PreprocessSourceFile (ScanFiles
->Str
) != STATUS_SUCCESS
) {
2713 return STATUS_ERROR
;
2716 FileName
= mGlobals
.PreprocessFileName
;
2718 FileName
= ScanFiles
->Str
;
2721 Fptr
= fopen (FileName
, "r");
2723 Error (NULL
, 0, 0, FileName
, "failed to open input file for scanning");
2724 return STATUS_ERROR
;
2728 while (fgets (Line
, sizeof (Line
), Fptr
) != NULL
) {
2730 if (Line
[MAX_LINE_LEN
- 1] != 0) {
2731 Error (ScanFiles
->Str
, LineNum
, 0, "line length exceeds maximum supported by tool", NULL
);
2733 return STATUS_ERROR
;
2736 // Remove the newline from the input line so we can print a warning message
2738 if (Line
[strlen (Line
) - 1] == '\n') {
2739 Line
[strlen (Line
) - 1] = 0;
2742 // Terminate the line at // comments
2744 Cptr
= strstr (Line
, "//");
2750 while ((Cptr
= strstr (Cptr
, STRING_TOKEN
)) != NULL
) {
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
2757 if (mGlobals
.VerboseScan
) {
2758 printf (" %d: %s", LineNum
, Cptr
);
2761 if (((Cptr
== Line
) || (!IsValidIdentifierChar (*(Cptr
- 1), FALSE
))) &&
2762 (!IsValidIdentifierChar (*(Cptr
+ sizeof (STRING_TOKEN
) - 1), FALSE
))
2764 StringTokenPos
= Cptr
;
2766 Cptr
+= strlen (STRING_TOKEN
);
2767 while (*Cptr
&& isspace (*Cptr
) && (*Cptr
!= '(')) {
2772 Warning (ScanFiles
->Str
, LineNum
, 0, StringTokenPos
, "expected "STRING_TOKEN
"(identifier)");
2775 // Skip over the open-parenthesis and find the next non-blank character
2778 while (isspace (*Cptr
)) {
2783 if ((*Cptr
== '_') || isalpha (*Cptr
)) {
2784 while ((*Cptr
== '_') || (isalnum (*Cptr
))) {
2789 while (*Cptr
&& isspace (*Cptr
)) {
2794 Warning (ScanFiles
->Str
, LineNum
, 0, StringTokenPos
, "expected "STRING_TOKEN
"(identifier)");
2804 // Add the string identifier to the list of used strings
2806 ParserSetPosition (ScanFiles
->Str
, LineNum
);
2807 StringDBSetStringReferenced (SavePtr
, mGlobals
.IgnoreNotFound
);
2808 if (mGlobals
.VerboseScan
) {
2809 printf ("...referenced %s", SavePtr
);
2812 Warning (ScanFiles
->Str
, LineNum
, 0, StringTokenPos
, "expected valid string identifier name");
2817 // Found it, but it's a substring of something else. Advance our pointer.
2822 if (mGlobals
.VerboseScan
) {
2831 // Skipping this file type
2833 if (mGlobals
.VerboseScan
) {
2834 printf ("Skip scanning of %s\n", ScanFiles
->Str
);
2838 ScanFiles
= ScanFiles
->Next
;
2842 // Remove the empty string defines file
2845 remove (mGlobals
.StringHFileName
);
2848 return STATUS_SUCCESS
;
2851 // Free the global string lists we allocated memory for
2859 TEXT_STRING_LIST
*Temp
;
2860 WCHAR_STRING_LIST
*WTemp
;
2863 // Traverse the include paths, freeing each
2865 while (mGlobals
.IncludePaths
!= NULL
) {
2866 Temp
= mGlobals
.IncludePaths
->Next
;
2867 free (mGlobals
.IncludePaths
->Str
);
2868 free (mGlobals
.IncludePaths
);
2869 mGlobals
.IncludePaths
= Temp
;
2872 // If we did a scan, then free up our
2873 // list of files to scan.
2875 while (mGlobals
.ScanFileName
!= NULL
) {
2876 Temp
= mGlobals
.ScanFileName
->Next
;
2877 free (mGlobals
.ScanFileName
->Str
);
2878 free (mGlobals
.ScanFileName
);
2879 mGlobals
.ScanFileName
= Temp
;
2882 // Free up preprocess flags list
2884 while (mGlobals
.PreprocessFlags
!= NULL
) {
2885 Temp
= mGlobals
.PreprocessFlags
->Next
;
2886 free (mGlobals
.PreprocessFlags
->Str
);
2887 free (mGlobals
.PreprocessFlags
);
2888 mGlobals
.PreprocessFlags
= Temp
;
2891 // If they gave us a list of filename extensions to
2892 // skip on scan, then free them up.
2894 while (mGlobals
.SkipExt
!= NULL
) {
2895 Temp
= mGlobals
.SkipExt
->Next
;
2896 free (mGlobals
.SkipExt
->Str
);
2897 free (mGlobals
.SkipExt
);
2898 mGlobals
.SkipExt
= Temp
;
2901 // Free up any languages specified
2903 while (mGlobals
.Language
!= NULL
) {
2904 WTemp
= mGlobals
.Language
->Next
;
2905 free (mGlobals
.Language
->Str
);
2906 free (mGlobals
.Language
);
2907 mGlobals
.Language
= WTemp
;
2910 // Free up our indirection list
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
;
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
;
2930 IsValidIdentifierChar (
2936 // If it's the first character of an identifier, then
2937 // it must be one of [A-Za-z_].
2940 if (isalpha (Char
) || (Char
== '_')) {
2945 // If it's not the first character, then it can
2946 // be one of [A-Za-z_0-9]
2948 if (isalnum (Char
) || (Char
== '_')) {
2959 SOURCE_FILE
*SourceFile
2962 SourceFile
->LineNum
= 1;
2963 SourceFile
->FileBufferPtr
= SourceFile
->FileBuffer
;
2964 SourceFile
->EndOfFile
= FALSE
;
2970 SOURCE_FILE
*SourceFile
,
2972 BOOLEAN StopAfterNewline
2975 while (!EndOfFile (SourceFile
)) {
2977 // Check for the character of interest
2979 if (SourceFile
->FileBufferPtr
[0] == WChar
) {
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
++;
2994 SourceFile
->FileBufferPtr
++;
3008 Routine Description:
3010 Print usage information for this utility.
3023 const char *Str
[] = {
3024 UTILITY_NAME
" "UTILITY_VERSION
" - Intel UEFI String Gather Utility",
3025 " Copyright (C), 2004 - 2008 Intel Corporation",
3027 #if ( defined(UTILITY_BUILD) && defined(UTILITY_VENDOR) )
3028 " Built from "UTILITY_BUILD
", project of "UTILITY_VENDOR
,
3032 " "UTILITY_NAME
" -parse [OPTION] FILE",
3033 " "UTILITY_NAME
" -scan [OPTION] FILE",
3034 " "UTILITY_NAME
" -dump [OPTION]",
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",
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",
3078 for (Index
= 0; Str
[Index
] != NULL
; Index
++) {
3079 fprintf (stdout
, "%s\n", Str
[Index
]);