3 Copyright (c) 2004 - 2007, Intel Corporation
4 All rights reserved. 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.
28 #include "EfiUtilityMsgs.h"
29 #include "StrGather.h"
32 #define TOOL_VERSION "0.31"
37 #define MAX_NEST_DEPTH 20 // just in case we get in an endless loop.
38 #define MAX_STRING_IDENTIFIER_NAME 100 // number of wchars
39 #define MAX_LINE_LEN 400
40 #define STRING_TOKEN "STRING_TOKEN"
41 #define DEFAULT_BASE_NAME "BaseName"
43 // Operational modes for this utility
45 #define MODE_UNKNOWN 0
51 // We keep a linked list of these for the source files we process
53 typedef struct _SOURCE_FILE
{
58 INT8 FileName
[MAX_PATH
];
62 struct _SOURCE_FILE
*Previous
;
63 struct _SOURCE_FILE
*Next
;
64 WCHAR ControlCharacter
;
67 #define DEFAULT_CONTROL_CHARACTER UNICODE_SLASH
70 // Here's all our globals. We need a linked list of include paths, a linked
71 // list of source files, a linked list of subdirectories (appended to each
72 // include path when searching), and a couple other fields.
75 SOURCE_FILE SourceFiles
;
76 TEXT_STRING_LIST
*IncludePaths
; // all include paths to search
77 TEXT_STRING_LIST
*LastIncludePath
;
78 TEXT_STRING_LIST
*ScanFileName
;
79 TEXT_STRING_LIST
*LastScanFileName
;
80 TEXT_STRING_LIST
*SkipExt
; // if -skipext .uni
81 TEXT_STRING_LIST
*LastSkipExt
;
82 TEXT_STRING_LIST
*IndirectionFileName
;
83 TEXT_STRING_LIST
*LastIndirectionFileName
;
84 TEXT_STRING_LIST
*DatabaseFileName
;
85 TEXT_STRING_LIST
*LastDatabaseFileName
;
86 WCHAR_STRING_LIST
*Language
;
87 WCHAR_STRING_LIST
*LastLanguage
;
88 WCHAR_MATCHING_STRING_LIST
*IndirectionList
; // from indirection file(s)
89 WCHAR_MATCHING_STRING_LIST
*LastIndirectionList
;
90 BOOLEAN Verbose
; // for more detailed output
91 BOOLEAN VerboseDatabaseWrite
; // for more detailed output when writing database
92 BOOLEAN VerboseDatabaseRead
; // for more detailed output when reading database
93 BOOLEAN NewDatabase
; // to start from scratch
94 BOOLEAN IgnoreNotFound
; // when scanning
96 BOOLEAN UnquotedStrings
; // -uqs option
97 INT8 OutputDatabaseFileName
[MAX_PATH
];
98 INT8 StringHFileName
[MAX_PATH
];
99 INT8 StringCFileName
[MAX_PATH
]; // output .C filename
100 INT8 DumpUFileName
[MAX_PATH
]; // output unicode dump file name
101 INT8 HiiExportPackFileName
[MAX_PATH
]; // HII export pack file name
102 INT8 BaseName
[MAX_PATH
]; // base filename of the strings file
103 INT8 OutputDependencyFileName
[MAX_PATH
];
104 FILE *OutputDependencyFptr
;
110 IsValidIdentifierChar (
118 SOURCE_FILE
*SourceFile
124 SOURCE_FILE
*SourceFile
,
126 BOOLEAN StopAfterNewline
132 SOURCE_FILE
*SourceFile
138 SOURCE_FILE
*SourceFile
144 SOURCE_FILE
*SourceFile
150 SOURCE_FILE
*SourceFile
155 GetStringIdentifierName (
156 IN SOURCE_FILE
*SourceFile
,
157 IN OUT WCHAR
*StringIdentifierName
,
158 IN UINT32 StringIdentifierNameLen
163 GetLanguageIdentifierName (
164 IN SOURCE_FILE
*SourceFile
,
165 IN OUT WCHAR
*LanguageIdentifierName
,
166 IN UINT32 LanguageIdentifierNameLen
,
172 GetPrintableLanguageName (
173 IN SOURCE_FILE
*SourceFile
178 AddCommandLineLanguage (
185 SOURCE_FILE
*SourceFile
,
192 SOURCE_FILE
*SourceFile
,
193 SOURCE_FILE
*ParentSourceFile
199 SOURCE_FILE
*SourceFile
206 OUT INT8
*FoundFileName
,
207 IN UINT32 FoundFileNameLen
220 SOURCE_FILE
*SourceFile
245 SOURCE_FILE
*SourceFile
250 ProcessTokenInclude (
251 SOURCE_FILE
*SourceFile
257 SOURCE_FILE
*SourceFile
262 ProcessTokenLanguage (
263 SOURCE_FILE
*SourceFile
268 ProcessTokenLangDef (
269 SOURCE_FILE
*SourceFile
275 TEXT_STRING_LIST
*ScanFiles
280 ParseIndirectionFiles (
281 TEXT_STRING_LIST
*Files
285 StringDBCreateHiiExportPack (
298 Call the routine to parse the command-line options, then process the file.
302 Argc - Standard C main() argc and argv.
303 Argv - Standard C main() argc and argv.
314 SetUtilityName (PROGRAM_NAME
);
316 // Process the command-line arguments
318 Status
= ProcessArgs (Argc
, Argv
);
319 if (Status
!= STATUS_SUCCESS
) {
323 // Initialize the database manager
325 StringDBConstructor ();
327 // We always try to read in an existing database file. It may not
328 // exist, which is ok usually.
330 if (mGlobals
.NewDatabase
== 0) {
332 // Read all databases specified.
334 for (mGlobals
.LastDatabaseFileName
= mGlobals
.DatabaseFileName
;
335 mGlobals
.LastDatabaseFileName
!= NULL
;
336 mGlobals
.LastDatabaseFileName
= mGlobals
.LastDatabaseFileName
->Next
338 Status
= StringDBReadDatabase (mGlobals
.LastDatabaseFileName
->Str
, TRUE
, mGlobals
.VerboseDatabaseRead
);
339 if (Status
!= STATUS_SUCCESS
) {
345 // Read indirection file(s) if specified
347 if (ParseIndirectionFiles (mGlobals
.IndirectionFileName
) != STATUS_SUCCESS
) {
351 // If scanning source files, do that now
353 if (mGlobals
.Mode
== MODE_SCAN
) {
354 ScanFiles (mGlobals
.ScanFileName
);
355 } else if (mGlobals
.Mode
== MODE_PARSE
) {
357 // Parsing a unicode strings file
359 mGlobals
.SourceFiles
.ControlCharacter
= DEFAULT_CONTROL_CHARACTER
;
360 if (mGlobals
.OutputDependencyFileName
[0] != 0) {
361 if ((mGlobals
.OutputDependencyFptr
= fopen (mGlobals
.OutputDependencyFileName
, "w")) == NULL
) {
362 Error (NULL
, 0, 0, mGlobals
.OutputDependencyFileName
, "failed to open output dependency file");
366 Status
= ProcessIncludeFile (&mGlobals
.SourceFiles
, NULL
);
367 if (mGlobals
.OutputDependencyFptr
!= NULL
) {
368 fclose (mGlobals
.OutputDependencyFptr
);
370 if (Status
!= STATUS_SUCCESS
) {
375 // Create the string defines header file if there have been no errors.
377 ParserSetPosition (NULL
, 0);
378 if ((mGlobals
.StringHFileName
[0] != 0) && (GetUtilityStatus () < STATUS_ERROR
)) {
379 Status
= StringDBDumpStringDefines (mGlobals
.StringHFileName
, mGlobals
.BaseName
);
380 if (Status
!= EFI_SUCCESS
) {
385 // Dump the strings to a .c file if there have still been no errors.
387 if ((mGlobals
.StringCFileName
[0] != 0) && (GetUtilityStatus () < STATUS_ERROR
)) {
388 Status
= StringDBDumpCStrings (
389 mGlobals
.StringCFileName
,
392 mGlobals
.IndirectionList
394 if (Status
!= EFI_SUCCESS
) {
399 // Dump the database if requested
401 if ((mGlobals
.DumpUFileName
[0] != 0) && (GetUtilityStatus () < STATUS_ERROR
)) {
402 StringDBDumpDatabase (NULL
, mGlobals
.DumpUFileName
, FALSE
);
405 // Dump the string data as HII binary string pack if requested
407 if ((mGlobals
.HiiExportPackFileName
[0] != 0) && (GetUtilityStatus () < STATUS_ERROR
)) {
408 StringDBCreateHiiExportPack (mGlobals
.HiiExportPackFileName
);
411 // Always update the database if no errors and not in dump mode. If they specified -od
412 // for an output database file name, then use that name. Otherwise use the name of
413 // the first database file specified with -db
415 if ((mGlobals
.Mode
!= MODE_DUMP
) && (GetUtilityStatus () < STATUS_ERROR
)) {
416 if (mGlobals
.OutputDatabaseFileName
[0]) {
417 Status
= StringDBWriteDatabase (mGlobals
.OutputDatabaseFileName
, mGlobals
.VerboseDatabaseWrite
);
419 Status
= StringDBWriteDatabase (mGlobals
.DatabaseFileName
->Str
, mGlobals
.VerboseDatabaseWrite
);
422 if (Status
!= EFI_SUCCESS
) {
432 StringDBDestructor ();
433 return GetUtilityStatus ();
439 SOURCE_FILE
*SourceFile
,
440 SOURCE_FILE
*ParentSourceFile
446 Given a source file, open the file and parse it
450 SourceFile - name of file to parse
451 ParentSourceFile - for error reporting purposes, the file that #included SourceFile.
459 static UINT32 NestDepth
= 0;
460 INT8 FoundFileName
[MAX_PATH
];
463 Status
= STATUS_SUCCESS
;
466 // Print the file being processed. Indent so you can tell the include nesting
469 if (mGlobals
.Verbose
) {
470 fprintf (stdout
, "%*cProcessing file '%s'\n", NestDepth
* 2, ' ', SourceFile
->FileName
);
474 // Make sure we didn't exceed our maximum nesting depth
476 if (NestDepth
> MAX_NEST_DEPTH
) {
477 Error (NULL
, 0, 0, SourceFile
->FileName
, "max nesting depth (%d) exceeded", NestDepth
);
478 Status
= STATUS_ERROR
;
482 // Try to open the file locally, and if that fails try along our include paths.
484 strcpy (FoundFileName
, SourceFile
->FileName
);
485 if ((SourceFile
->Fptr
= fopen (FoundFileName
, "rb")) == NULL
) {
487 // Try to find it among the paths if it has a parent (that is, it is included
490 if (ParentSourceFile
== NULL
) {
491 Error (NULL
, 0, 0, SourceFile
->FileName
, "file not found");
492 Status
= STATUS_ERROR
;
496 SourceFile
->Fptr
= FindFile (SourceFile
->FileName
, FoundFileName
, sizeof (FoundFileName
));
497 if (SourceFile
->Fptr
== NULL
) {
498 Error (ParentSourceFile
->FileName
, ParentSourceFile
->LineNum
, 0, SourceFile
->FileName
, "include file not found");
499 Status
= STATUS_ERROR
;
505 // Output the dependency
507 if (mGlobals
.OutputDependencyFptr
!= NULL
) {
508 fprintf (mGlobals
.OutputDependencyFptr
, "%s : %s\n", mGlobals
.DatabaseFileName
->Str
, FoundFileName
);
510 // Add pseudo target to avoid incremental build failure when the file is deleted
512 fprintf (mGlobals
.OutputDependencyFptr
, "%s : \n", FoundFileName
);
516 // Process the file found
518 ProcessFile (SourceFile
);
523 // Close open files and return status
525 if (SourceFile
->Fptr
!= NULL
) {
526 fclose (SourceFile
->Fptr
);
535 SOURCE_FILE
*SourceFile
539 // Get the file size, and then read the entire thing into memory.
540 // Allocate space for a terminator character.
542 fseek (SourceFile
->Fptr
, 0, SEEK_END
);
543 SourceFile
->FileSize
= ftell (SourceFile
->Fptr
);
544 fseek (SourceFile
->Fptr
, 0, SEEK_SET
);
545 SourceFile
->FileBuffer
= (WCHAR
*) malloc (SourceFile
->FileSize
+ sizeof (WCHAR
));
546 if (SourceFile
->FileBuffer
== NULL
) {
547 Error (NULL
, 0, 0, "memory allocation failure", NULL
);
551 fread ((VOID
*) SourceFile
->FileBuffer
, SourceFile
->FileSize
, 1, SourceFile
->Fptr
);
552 SourceFile
->FileBuffer
[(SourceFile
->FileSize
/ sizeof (WCHAR
))] = UNICODE_NULL
;
554 // Pre-process the file to replace comments with spaces
556 PreprocessFile (SourceFile
);
560 ParseFile (SourceFile
);
561 free (SourceFile
->FileBuffer
);
562 return STATUS_SUCCESS
;
568 SOURCE_FILE
*SourceFile
575 // First character of a unicode file is special. Make sure
577 if (SourceFile
->FileBufferPtr
[0] != UNICODE_FILE_START
) {
578 Error (SourceFile
->FileName
, 1, 0, SourceFile
->FileName
, "file does not appear to be a unicode file");
582 SourceFile
->FileBufferPtr
++;
585 // Print the first line if in verbose mode
587 if (mGlobals
.Verbose
) {
588 printf ("%d: %S\n", SourceFile
->LineNum
, SourceFile
->FileBufferPtr
);
591 // Since the syntax is relatively straightforward, just switch on the next char
593 while (!EndOfFile (SourceFile
)) {
595 // Check for whitespace
597 if (SourceFile
->FileBufferPtr
[0] == UNICODE_SPACE
) {
598 SourceFile
->FileBufferPtr
++;
599 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_TAB
) {
600 SourceFile
->FileBufferPtr
++;
601 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_CR
) {
602 SourceFile
->FileBufferPtr
++;
603 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_LF
) {
604 SourceFile
->FileBufferPtr
++;
605 SourceFile
->LineNum
++;
606 if (mGlobals
.Verbose
) {
607 printf ("%d: %S\n", SourceFile
->LineNum
, SourceFile
->FileBufferPtr
);
611 } else if (SourceFile
->FileBufferPtr
[0] == 0) {
612 SourceFile
->FileBufferPtr
++;
613 } else if (InComment
) {
614 SourceFile
->FileBufferPtr
++;
615 } else if ((SourceFile
->FileBufferPtr
[0] == UNICODE_SLASH
) && (SourceFile
->FileBufferPtr
[1] == UNICODE_SLASH
)) {
616 SourceFile
->FileBufferPtr
+= 2;
618 } else if (SourceFile
->SkipToHash
&& (SourceFile
->FileBufferPtr
[0] != SourceFile
->ControlCharacter
)) {
619 SourceFile
->FileBufferPtr
++;
621 SourceFile
->SkipToHash
= FALSE
;
622 if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
623 ((Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"include")) > 0)
625 SourceFile
->FileBufferPtr
+= Len
+ 1;
626 ProcessTokenInclude (SourceFile
);
627 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
628 (Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"scope")) > 0
630 SourceFile
->FileBufferPtr
+= Len
+ 1;
631 ProcessTokenScope (SourceFile
);
632 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
633 (Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"language")) > 0
635 SourceFile
->FileBufferPtr
+= Len
+ 1;
636 ProcessTokenLanguage (SourceFile
);
637 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
638 (Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"langdef")) > 0
640 SourceFile
->FileBufferPtr
+= Len
+ 1;
641 ProcessTokenLangDef (SourceFile
);
642 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
643 (Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"string")) > 0
645 SourceFile
->FileBufferPtr
+= Len
+ 1;
646 ProcessTokenString (SourceFile
);
647 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
648 (Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"EFI_BREAKPOINT()")) > 0
650 SourceFile
->FileBufferPtr
+= Len
;
652 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
653 (SourceFile
->FileBufferPtr
[1] == UNICODE_EQUAL_SIGN
)
655 SourceFile
->ControlCharacter
= SourceFile
->FileBufferPtr
[2];
656 SourceFile
->FileBufferPtr
+= 3;
658 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "unrecognized token", "%S", SourceFile
->FileBufferPtr
);
660 // Treat rest of line as a comment.
667 return STATUS_SUCCESS
;
673 SOURCE_FILE
*SourceFile
678 Preprocess a file to replace all carriage returns with NULLs so
679 we can print lines from the file to the screen.
682 SourceFile - structure that we use to keep track of an input file.
691 RewindFile (SourceFile
);
693 while (!EndOfFile (SourceFile
)) {
695 // If a line-feed, then no longer in a comment
697 if (SourceFile
->FileBufferPtr
[0] == UNICODE_LF
) {
698 SourceFile
->FileBufferPtr
++;
699 SourceFile
->LineNum
++;
701 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_CR
) {
703 // Replace all carriage returns with a NULL so we can print stuff
705 SourceFile
->FileBufferPtr
[0] = 0;
706 SourceFile
->FileBufferPtr
++;
707 } else if (InComment
) {
708 SourceFile
->FileBufferPtr
[0] = UNICODE_SPACE
;
709 SourceFile
->FileBufferPtr
++;
710 } else if ((SourceFile
->FileBufferPtr
[0] == UNICODE_SLASH
) && (SourceFile
->FileBufferPtr
[1] == UNICODE_SLASH
)) {
711 SourceFile
->FileBufferPtr
+= 2;
714 SourceFile
->FileBufferPtr
++;
718 // Could check for end-of-file and still in a comment, but
719 // should not be necessary. So just restore the file pointers.
721 RewindFile (SourceFile
);
726 GetPrintableLanguageName (
727 IN SOURCE_FILE
*SourceFile
735 SkipWhiteSpace (SourceFile
);
736 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
738 SourceFile
->FileName
,
741 "expected quoted printable language name",
743 SourceFile
->FileBufferPtr
745 SourceFile
->SkipToHash
= TRUE
;
750 SourceFile
->FileBufferPtr
++;
751 Start
= Ptr
= SourceFile
->FileBufferPtr
;
752 while (!EndOfFile (SourceFile
)) {
753 if (SourceFile
->FileBufferPtr
[0] == UNICODE_CR
) {
754 Warning (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "carriage return found in quoted string", "%S", Start
);
756 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_DOUBLE_QUOTE
) {
760 SourceFile
->FileBufferPtr
++;
764 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
766 SourceFile
->FileName
,
769 "missing closing quote on printable language name string",
774 SourceFile
->FileBufferPtr
++;
777 // Now allocate memory for the string and save it off
779 String
= (WCHAR
*) malloc ((Len
+ 1) * sizeof (WCHAR
));
780 if (String
== NULL
) {
781 Error (NULL
, 0, 0, "memory allocation failed", NULL
);
785 // Copy the string from the file buffer to the local copy.
786 // We do no reformatting of it whatsoever at this point.
798 // Now format the string to convert \wide and \narrow controls
800 StringDBFormatString (String
);
807 SOURCE_FILE
*SourceFile
,
815 BOOLEAN PreviousBackslash
;
817 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
819 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "expected quoted string", "%S", SourceFile
->FileBufferPtr
);
826 SourceFile
->FileBufferPtr
++;
827 Start
= Ptr
= SourceFile
->FileBufferPtr
;
828 PreviousBackslash
= FALSE
;
829 while (!EndOfFile (SourceFile
)) {
830 if ((SourceFile
->FileBufferPtr
[0] == UNICODE_DOUBLE_QUOTE
) && (!PreviousBackslash
)) {
832 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_CR
) {
833 Warning (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "carriage return found in quoted string", "%S", Start
);
834 PreviousBackslash
= FALSE
;
835 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_BACKSLASH
) {
836 PreviousBackslash
= TRUE
;
838 PreviousBackslash
= FALSE
;
841 SourceFile
->FileBufferPtr
++;
845 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
846 Warning (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "missing closing quote on string", "%S", Start
);
848 SourceFile
->FileBufferPtr
++;
851 // Now allocate memory for the string and save it off
853 String
= (WCHAR
*) malloc ((Len
+ 1) * sizeof (WCHAR
));
854 if (String
== NULL
) {
855 Error (NULL
, 0, 0, "memory allocation failed", NULL
);
859 // Copy the string from the file buffer to the local copy.
860 // We do no reformatting of it whatsoever at this point.
875 // #string STR_ID_NAME
877 // All we can do is call the string database to add the string identifier. Unfortunately
878 // he'll have to keep track of the last identifier we added.
883 SOURCE_FILE
*SourceFile
886 WCHAR StringIdentifier
[MAX_STRING_IDENTIFIER_NAME
];
889 // Extract the string identifier name and add it to the database.
891 if (GetStringIdentifierName (SourceFile
, StringIdentifier
, sizeof (StringIdentifier
)) > 0) {
892 StringId
= STRING_ID_INVALID
;
893 StringDBAddStringIdentifier (StringIdentifier
, &StringId
, 0);
896 // Error recovery -- skip to the next #
898 SourceFile
->SkipToHash
= TRUE
;
905 SOURCE_FILE
*SourceFile
909 // The file buffer pointer will typically get updated before the End-of-file flag in the
910 // source file structure, so check it first.
912 if (SourceFile
->FileBufferPtr
>= SourceFile
->FileBuffer
+ SourceFile
->FileSize
/ sizeof (WCHAR
)) {
913 SourceFile
->EndOfFile
= TRUE
;
917 if (SourceFile
->EndOfFile
) {
926 GetStringIdentifierName (
927 IN SOURCE_FILE
*SourceFile
,
928 IN OUT WCHAR
*StringIdentifierName
,
929 IN UINT32 StringIdentifierNameLen
939 SkipWhiteSpace (SourceFile
);
940 if (SourceFile
->EndOfFile
) {
941 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "end-of-file encountered", "expected string identifier");
945 // Verify first character of name is [A-Za-z]
948 StringIdentifierNameLen
/= 2;
949 From
= SourceFile
->FileBufferPtr
;
950 Start
= SourceFile
->FileBufferPtr
;
951 if (((SourceFile
->FileBufferPtr
[0] >= UNICODE_A
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_Z
)) ||
952 ((SourceFile
->FileBufferPtr
[0] >= UNICODE_z
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_z
))
958 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "invalid character in string identifier name", "%S", Start
);
962 while (!EndOfFile (SourceFile
)) {
963 if (((SourceFile
->FileBufferPtr
[0] >= UNICODE_A
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_Z
)) ||
964 ((SourceFile
->FileBufferPtr
[0] >= UNICODE_z
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_z
)) ||
965 ((SourceFile
->FileBufferPtr
[0] >= UNICODE_0
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_9
)) ||
966 (SourceFile
->FileBufferPtr
[0] == UNICODE_UNDERSCORE
)
969 if (Len
>= StringIdentifierNameLen
) {
970 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "string identifier name too long", "%S", Start
);
974 *StringIdentifierName
= SourceFile
->FileBufferPtr
[0];
975 StringIdentifierName
++;
976 SourceFile
->FileBufferPtr
++;
977 } else if (SkipWhiteSpace (SourceFile
) == 0) {
978 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "invalid string identifier name", "%S", Start
);
985 // Terminate the copy of the string.
987 *StringIdentifierName
= 0;
993 GetLanguageIdentifierName (
994 IN SOURCE_FILE
*SourceFile
,
995 IN OUT WCHAR
*LanguageIdentifierName
,
996 IN UINT32 LanguageIdentifierNameLen
,
1006 SkipWhiteSpace (SourceFile
);
1007 if (SourceFile
->EndOfFile
) {
1010 SourceFile
->FileName
,
1011 SourceFile
->LineNum
,
1013 "end-of-file encountered",
1014 "expected language identifier"
1021 // This function is called to optionally get a language identifier name in:
1022 // #string STR_ID eng "the string"
1023 // If it's optional, and we find a double-quote, then return now.
1026 if (*SourceFile
->FileBufferPtr
== UNICODE_DOUBLE_QUOTE
) {
1032 LanguageIdentifierNameLen
/= 2;
1034 // Internal error if we weren't given at least 4 WCHAR's to work with.
1036 if (LanguageIdentifierNameLen
< LANGUAGE_IDENTIFIER_NAME_LEN
+ 1) {
1038 SourceFile
->FileName
,
1039 SourceFile
->LineNum
,
1041 "app error -- language identifier name length is invalid",
1046 From
= SourceFile
->FileBufferPtr
;
1047 Start
= SourceFile
->FileBufferPtr
;
1048 while (!EndOfFile (SourceFile
)) {
1049 if (((SourceFile
->FileBufferPtr
[0] >= UNICODE_a
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_z
))) {
1051 if (Len
> LANGUAGE_IDENTIFIER_NAME_LEN
) {
1052 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "language identifier name too long", "%S", Start
);
1056 *LanguageIdentifierName
= SourceFile
->FileBufferPtr
[0];
1057 SourceFile
->FileBufferPtr
++;
1058 LanguageIdentifierName
++;
1059 } else if (!IsWhiteSpace (SourceFile
)) {
1060 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "invalid language identifier name", "%S", Start
);
1067 // Terminate the copy of the string.
1069 *LanguageIdentifierName
= 0;
1075 ProcessTokenInclude (
1076 SOURCE_FILE
*SourceFile
1079 INT8 IncludeFileName
[MAX_PATH
];
1082 BOOLEAN ReportedError
;
1083 SOURCE_FILE IncludedSourceFile
;
1085 ReportedError
= FALSE
;
1086 if (SkipWhiteSpace (SourceFile
) == 0) {
1087 Warning (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "expected whitespace following #include keyword", NULL
);
1090 // Should be quoted file name
1092 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
1093 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "expected quoted include file name", NULL
);
1097 SourceFile
->FileBufferPtr
++;
1099 // Copy the filename as ascii to our local string
1101 To
= IncludeFileName
;
1103 while (!EndOfFile (SourceFile
)) {
1104 if ((SourceFile
->FileBufferPtr
[0] == UNICODE_CR
) || (SourceFile
->FileBufferPtr
[0] == UNICODE_LF
)) {
1105 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "end-of-line found in quoted include file name", NULL
);
1109 if (SourceFile
->FileBufferPtr
[0] == UNICODE_DOUBLE_QUOTE
) {
1110 SourceFile
->FileBufferPtr
++;
1114 // If too long, then report the error once and process until the closing quote
1117 if (!ReportedError
&& (Len
>= sizeof (IncludeFileName
))) {
1118 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "length of include file name exceeds limit", NULL
);
1119 ReportedError
= TRUE
;
1122 if (!ReportedError
) {
1123 *To
= UNICODE_TO_ASCII (SourceFile
->FileBufferPtr
[0]);
1127 SourceFile
->FileBufferPtr
++;
1130 if (!ReportedError
) {
1132 memset ((char *) &IncludedSourceFile
, 0, sizeof (SOURCE_FILE
));
1133 strcpy (IncludedSourceFile
.FileName
, IncludeFileName
);
1134 IncludedSourceFile
.ControlCharacter
= DEFAULT_CONTROL_CHARACTER
;
1135 ProcessIncludeFile (&IncludedSourceFile
, SourceFile
);
1137 // printf ("including file '%s'\n", IncludeFileName);
1144 // Error recovery -- skip to next #
1146 SourceFile
->SkipToHash
= TRUE
;
1152 SOURCE_FILE
*SourceFile
1155 WCHAR StringIdentifier
[MAX_STRING_IDENTIFIER_NAME
];
1157 // Extract the scope name
1159 if (GetStringIdentifierName (SourceFile
, StringIdentifier
, sizeof (StringIdentifier
)) > 0) {
1160 StringDBSetScope (StringIdentifier
);
1164 // Parse: #langdef eng "English"
1165 // #langdef chn "\wideChinese"
1169 ProcessTokenLangDef (
1170 SOURCE_FILE
*SourceFile
1173 WCHAR LanguageIdentifier
[MAX_STRING_IDENTIFIER_NAME
];
1175 WCHAR
*PrintableName
;
1177 // Extract the 3-character language identifier
1179 Len
= GetLanguageIdentifierName (SourceFile
, LanguageIdentifier
, sizeof (LanguageIdentifier
), FALSE
);
1180 if (Len
!= LANGUAGE_IDENTIFIER_NAME_LEN
) {
1181 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "invalid or missing language identifier", NULL
);
1184 // Extract the printable name
1186 PrintableName
= GetPrintableLanguageName (SourceFile
);
1187 if (PrintableName
!= NULL
) {
1188 ParserSetPosition (SourceFile
->FileName
, SourceFile
->LineNum
);
1189 StringDBAddLanguage (LanguageIdentifier
, PrintableName
);
1190 free (PrintableName
);
1195 // Error recovery -- skip to next #
1197 SourceFile
->SkipToHash
= TRUE
;
1202 ApparentQuotedString (
1203 SOURCE_FILE
*SourceFile
1208 // See if the first and last nonblank characters on the line are double quotes
1210 for (Ptr
= SourceFile
->FileBufferPtr
; *Ptr
&& (*Ptr
== UNICODE_SPACE
); Ptr
++)
1212 if (*Ptr
!= UNICODE_DOUBLE_QUOTE
) {
1221 for (; *Ptr
&& (*Ptr
== UNICODE_SPACE
); Ptr
--)
1223 if (*Ptr
!= UNICODE_DOUBLE_QUOTE
) {
1231 // #language eng "some string " "more string"
1235 ProcessTokenLanguage (
1236 SOURCE_FILE
*SourceFile
1240 WCHAR
*SecondString
;
1244 WCHAR Language
[LANGUAGE_IDENTIFIER_NAME_LEN
+ 1];
1246 BOOLEAN PreviousNewline
;
1248 // Get the language identifier
1251 Len
= GetLanguageIdentifierName (SourceFile
, Language
, sizeof (Language
), TRUE
);
1252 if (Len
!= LANGUAGE_IDENTIFIER_NAME_LEN
) {
1253 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "invalid or missing language identifier", "%S", Language
);
1254 SourceFile
->SkipToHash
= TRUE
;
1258 // Extract the string value. It's either a quoted string that starts on the current line, or
1259 // an unquoted string that starts on the following line and continues until the next control
1260 // character in column 1.
1261 // Look ahead to find a quote or a newline
1263 if (SkipTo (SourceFile
, UNICODE_DOUBLE_QUOTE
, TRUE
)) {
1264 String
= GetQuotedString (SourceFile
, FALSE
);
1265 if (String
!= NULL
) {
1267 // Set the position in the file of where we are parsing for error
1268 // reporting purposes. Then start looking ahead for additional
1269 // quoted strings, and concatenate them until we get a failure
1270 // back from the string parser.
1272 Len
= wcslen (String
) + 1;
1273 ParserSetPosition (SourceFile
->FileName
, SourceFile
->LineNum
);
1275 SkipWhiteSpace (SourceFile
);
1276 SecondString
= GetQuotedString (SourceFile
, TRUE
);
1277 if (SecondString
!= NULL
) {
1278 Len
+= wcslen (SecondString
);
1279 TempString
= (WCHAR
*) malloc (Len
* sizeof (WCHAR
));
1280 if (TempString
== NULL
) {
1281 Error (NULL
, 0, 0, "application error", "failed to allocate memory");
1285 wcscpy (TempString
, String
);
1286 wcscat (TempString
, SecondString
);
1288 free (SecondString
);
1289 String
= TempString
;
1291 } while (SecondString
!= NULL
);
1292 StringDBAddString (Language
, NULL
, NULL
, String
, TRUE
, 0);
1296 // Error was reported at lower level. Error recovery mode.
1298 SourceFile
->SkipToHash
= TRUE
;
1301 if (!mGlobals
.UnquotedStrings
) {
1303 // They're using unquoted strings. If the next non-blank character is a double quote, and the
1304 // last non-blank character on the line is a double quote, then more than likely they're using
1305 // quotes, so they need to put the quoted string on the end of the previous line
1307 if (ApparentQuotedString (SourceFile
)) {
1309 SourceFile
->FileName
,
1310 SourceFile
->LineNum
,
1312 "unexpected quoted string on line",
1313 "specify -uqs option if necessary"
1318 // Found end-of-line (hopefully). Skip over it and start taking in characters
1319 // until we find a control character at the start of a line.
1322 From
= SourceFile
->FileBufferPtr
;
1323 PreviousNewline
= FALSE
;
1324 while (!EndOfFile (SourceFile
)) {
1325 if (SourceFile
->FileBufferPtr
[0] == UNICODE_LF
) {
1326 PreviousNewline
= TRUE
;
1327 SourceFile
->LineNum
++;
1330 if (PreviousNewline
&& (SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
)) {
1334 PreviousNewline
= FALSE
;
1337 SourceFile
->FileBufferPtr
++;
1340 if ((Len
== 0) && EndOfFile (SourceFile
)) {
1341 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "unexpected end of file", NULL
);
1342 SourceFile
->SkipToHash
= TRUE
;
1346 // Now allocate a buffer, copy the characters, and add the string.
1348 String
= (WCHAR
*) malloc ((Len
+ 1) * sizeof (WCHAR
));
1349 if (String
== NULL
) {
1350 Error (NULL
, 0, 0, "application error", "failed to allocate memory");
1355 while (From
< SourceFile
->FileBufferPtr
) {
1374 StringDBAddString (Language
, NULL
, NULL
, String
, TRUE
, 0);
1381 SOURCE_FILE
*SourceFile
1384 switch (SourceFile
->FileBufferPtr
[0]) {
1400 SOURCE_FILE
*SourceFile
1406 while (!EndOfFile (SourceFile
)) {
1408 switch (*SourceFile
->FileBufferPtr
) {
1413 SourceFile
->FileBufferPtr
++;
1417 SourceFile
->FileBufferPtr
++;
1418 SourceFile
->LineNum
++;
1419 if (mGlobals
.Verbose
) {
1420 printf ("%d: %S\n", SourceFile
->LineNum
, SourceFile
->FileBufferPtr
);
1429 // Some tokens require trailing whitespace. If we're at the end of the
1430 // file, then we count that as well.
1432 if ((Count
== 0) && (EndOfFile (SourceFile
))) {
1449 while (*Str
== *Buffer
) {
1462 // Given a filename, try to find it along the include paths.
1468 OUT INT8
*FoundFileName
,
1469 IN UINT32 FoundFileNameLen
1473 TEXT_STRING_LIST
*List
;
1476 // Traverse the list of paths and try to find the file
1478 List
= mGlobals
.IncludePaths
;
1479 while (List
!= NULL
) {
1481 // Put the path and filename together
1483 if (strlen (List
->Str
) + strlen (FileName
) + 1 > FoundFileNameLen
) {
1484 Error (PROGRAM_NAME
, 0, 0, NULL
, "internal error - cannot concatenate path+filename");
1488 // Append the filename to this include path and try to open the file.
1490 strcpy (FoundFileName
, List
->Str
);
1491 strcat (FoundFileName
, FileName
);
1492 if ((Fptr
= fopen (FoundFileName
, "rb")) != NULL
) {
1494 // Return the file pointer
1504 FoundFileName
[0] = 0;
1508 // Process the command-line arguments
1517 TEXT_STRING_LIST
*NewList
;
1519 // Clear our globals
1521 memset ((char *) &mGlobals
, 0, sizeof (mGlobals
));
1522 strcpy (mGlobals
.BaseName
, DEFAULT_BASE_NAME
);
1524 // Skip program name
1531 return STATUS_ERROR
;
1534 mGlobals
.Mode
= MODE_UNKNOWN
;
1536 // Process until no more -args.
1538 while ((Argc
> 0) && (Argv
[0][0] == '-')) {
1542 if (_stricmp (Argv
[0], "-parse") == 0) {
1543 if (mGlobals
.Mode
!= MODE_UNKNOWN
) {
1544 Error (NULL
, 0, 0, "only one of -parse/-scan/-dump allowed", NULL
);
1545 return STATUS_ERROR
;
1548 mGlobals
.Mode
= MODE_PARSE
;
1552 } else if (_stricmp (Argv
[0], "-scan") == 0) {
1553 if (mGlobals
.Mode
!= MODE_UNKNOWN
) {
1554 Error (NULL
, 0, 0, "only one of -parse/-scan/-dump allowed", NULL
);
1555 return STATUS_ERROR
;
1558 mGlobals
.Mode
= MODE_SCAN
;
1560 // -vscan verbose scanning option
1562 } else if (_stricmp (Argv
[0], "-vscan") == 0) {
1563 mGlobals
.VerboseScan
= TRUE
;
1567 } else if (_stricmp (Argv
[0], "-dump") == 0) {
1568 if (mGlobals
.Mode
!= MODE_UNKNOWN
) {
1569 Error (NULL
, 0, 0, "only one of -parse/-scan/-dump allowed", NULL
);
1570 return STATUS_ERROR
;
1573 mGlobals
.Mode
= MODE_DUMP
;
1574 } else if (_stricmp (Argv
[0], "-uqs") == 0) {
1575 mGlobals
.UnquotedStrings
= TRUE
;
1577 // -i path add include search path when parsing
1579 } else if (_stricmp (Argv
[0], "-i") == 0) {
1581 // check for one more arg
1583 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1584 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing include path");
1585 return STATUS_ERROR
;
1588 // Allocate memory for a new list element, fill it in, and
1589 // add it to our list of include paths. Always make sure it
1590 // has a "\" on the end of it.
1592 NewList
= malloc (sizeof (TEXT_STRING_LIST
));
1593 if (NewList
== NULL
) {
1594 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1595 return STATUS_ERROR
;
1598 memset ((char *) NewList
, 0, sizeof (TEXT_STRING_LIST
));
1599 NewList
->Str
= malloc (strlen (Argv
[1]) + 2);
1600 if (NewList
->Str
== NULL
) {
1602 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1603 return STATUS_ERROR
;
1606 strcpy (NewList
->Str
, Argv
[1]);
1607 if (NewList
->Str
[strlen (NewList
->Str
) - 1] != '\\') {
1608 strcat (NewList
->Str
, "\\");
1611 // Add it to our linked list
1613 if (mGlobals
.IncludePaths
== NULL
) {
1614 mGlobals
.IncludePaths
= NewList
;
1616 mGlobals
.LastIncludePath
->Next
= NewList
;
1619 mGlobals
.LastIncludePath
= NewList
;
1622 } else if (_stricmp (Argv
[0], "-if") == 0) {
1624 // Indirection file -- check for one more arg
1626 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1627 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing indirection file name");
1628 return STATUS_ERROR
;
1631 // Allocate memory for a new list element, fill it in, and
1632 // add it to our list of include paths. Always make sure it
1633 // has a "\" on the end of it.
1635 NewList
= malloc (sizeof (TEXT_STRING_LIST
));
1636 if (NewList
== NULL
) {
1637 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1638 return STATUS_ERROR
;
1641 memset ((char *) NewList
, 0, sizeof (TEXT_STRING_LIST
));
1642 NewList
->Str
= malloc (strlen (Argv
[1]) + 1);
1643 if (NewList
->Str
== NULL
) {
1645 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1646 return STATUS_ERROR
;
1649 strcpy (NewList
->Str
, Argv
[1]);
1651 // Add it to our linked list
1653 if (mGlobals
.IndirectionFileName
== NULL
) {
1654 mGlobals
.IndirectionFileName
= NewList
;
1656 mGlobals
.LastIndirectionFileName
->Next
= NewList
;
1659 mGlobals
.LastIndirectionFileName
= NewList
;
1662 } else if (_stricmp (Argv
[0], "-db") == 0) {
1664 // -db option to specify a database file.
1665 // Check for one more arg (the database file name)
1667 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1668 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing database file name");
1669 return STATUS_ERROR
;
1672 NewList
= malloc (sizeof (TEXT_STRING_LIST
));
1673 if (NewList
== NULL
) {
1674 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1675 return STATUS_ERROR
;
1678 memset ((char *) NewList
, 0, sizeof (TEXT_STRING_LIST
));
1679 NewList
->Str
= malloc (strlen (Argv
[1]) + 1);
1680 if (NewList
->Str
== NULL
) {
1682 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1683 return STATUS_ERROR
;
1686 strcpy (NewList
->Str
, Argv
[1]);
1688 // Add it to our linked list
1690 if (mGlobals
.DatabaseFileName
== NULL
) {
1691 mGlobals
.DatabaseFileName
= NewList
;
1693 mGlobals
.LastDatabaseFileName
->Next
= NewList
;
1696 mGlobals
.LastDatabaseFileName
= NewList
;
1699 } else if (_stricmp (Argv
[0], "-ou") == 0) {
1701 // -ou option to specify an output unicode file to
1702 // which we can dump our database.
1704 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1705 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing database dump output file name");
1706 return STATUS_ERROR
;
1709 if (mGlobals
.DumpUFileName
[0] == 0) {
1710 strcpy (mGlobals
.DumpUFileName
, Argv
[1]);
1712 Error (PROGRAM_NAME
, 0, 0, Argv
[1], "-ou option already specified with '%s'", mGlobals
.DumpUFileName
);
1713 return STATUS_ERROR
;
1718 } else if (_stricmp (Argv
[0], "-hpk") == 0) {
1720 // -hpk option to create an HII export pack of the input database file
1722 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1723 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing raw string data dump output file name");
1724 return STATUS_ERROR
;
1727 if (mGlobals
.HiiExportPackFileName
[0] == 0) {
1728 strcpy (mGlobals
.HiiExportPackFileName
, Argv
[1]);
1730 Error (PROGRAM_NAME
, 0, 0, Argv
[1], "-or option already specified with '%s'", mGlobals
.HiiExportPackFileName
);
1731 return STATUS_ERROR
;
1736 } else if ((_stricmp (Argv
[0], "-?") == 0) || (_stricmp (Argv
[0], "-h") == 0)) {
1738 return STATUS_ERROR
;
1739 } else if (_stricmp (Argv
[0], "-v") == 0) {
1740 mGlobals
.Verbose
= 1;
1741 } else if (_stricmp (Argv
[0], "-vdbw") == 0) {
1742 mGlobals
.VerboseDatabaseWrite
= 1;
1743 } else if (_stricmp (Argv
[0], "-vdbr") == 0) {
1744 mGlobals
.VerboseDatabaseRead
= 1;
1745 } else if (_stricmp (Argv
[0], "-newdb") == 0) {
1746 mGlobals
.NewDatabase
= 1;
1747 } else if (_stricmp (Argv
[0], "-ignorenotfound") == 0) {
1748 mGlobals
.IgnoreNotFound
= 1;
1749 } else if (_stricmp (Argv
[0], "-oc") == 0) {
1751 // check for one more arg
1753 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1754 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing output C filename");
1755 return STATUS_ERROR
;
1758 strcpy (mGlobals
.StringCFileName
, Argv
[1]);
1761 } else if (_stricmp (Argv
[0], "-bn") == 0) {
1763 // check for one more arg
1765 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1766 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing base name");
1768 return STATUS_ERROR
;
1771 strcpy (mGlobals
.BaseName
, Argv
[1]);
1774 } else if (_stricmp (Argv
[0], "-oh") == 0) {
1776 // -oh to specify output .h defines file name
1778 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1779 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing output .h filename");
1780 return STATUS_ERROR
;
1783 strcpy (mGlobals
.StringHFileName
, Argv
[1]);
1786 } else if (_stricmp (Argv
[0], "-dep") == 0) {
1788 // -dep to specify output dependency file name
1790 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1791 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing output dependency filename");
1792 return STATUS_ERROR
;
1795 strcpy (mGlobals
.OutputDependencyFileName
, Argv
[1]);
1798 } else if (_stricmp (Argv
[0], "-skipext") == 0) {
1800 // -skipext to skip scanning of files with certain filename extensions
1802 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1803 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing filename extension");
1804 return STATUS_ERROR
;
1807 // Allocate memory for a new list element, fill it in, and
1808 // add it to our list of excluded extensions. Always make sure it
1809 // has a "." as the first character.
1811 NewList
= malloc (sizeof (TEXT_STRING_LIST
));
1812 if (NewList
== NULL
) {
1813 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1814 return STATUS_ERROR
;
1817 memset ((char *) NewList
, 0, sizeof (TEXT_STRING_LIST
));
1818 NewList
->Str
= malloc (strlen (Argv
[1]) + 2);
1819 if (NewList
->Str
== NULL
) {
1821 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1822 return STATUS_ERROR
;
1825 if (Argv
[1][0] == '.') {
1826 strcpy (NewList
->Str
, Argv
[1]);
1828 NewList
->Str
[0] = '.';
1829 strcpy (NewList
->Str
+ 1, Argv
[1]);
1832 // Add it to our linked list
1834 if (mGlobals
.SkipExt
== NULL
) {
1835 mGlobals
.SkipExt
= NewList
;
1837 mGlobals
.LastSkipExt
->Next
= NewList
;
1840 mGlobals
.LastSkipExt
= NewList
;
1843 } else if (_stricmp (Argv
[0], "-lang") == 0) {
1845 // "-lang eng" or "-lang spa+cat" to only output certain languages
1847 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1848 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing language name");
1850 return STATUS_ERROR
;
1853 if (AddCommandLineLanguage (Argv
[1]) != STATUS_SUCCESS
) {
1854 return STATUS_ERROR
;
1859 } else if (_stricmp (Argv
[0], "-od") == 0) {
1861 // Output database file name -- check for another arg
1863 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1864 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing output database file name");
1865 return STATUS_ERROR
;
1868 strcpy (mGlobals
.OutputDatabaseFileName
, Argv
[1]);
1875 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "unrecognized option");
1877 return STATUS_ERROR
;
1884 // Make sure they specified the mode parse/scan/dump
1886 if (mGlobals
.Mode
== MODE_UNKNOWN
) {
1887 Error (NULL
, 0, 0, "must specify one of -parse/-scan/-dump", NULL
);
1888 return STATUS_ERROR
;
1891 // All modes require a database filename
1893 if (mGlobals
.DatabaseFileName
== 0) {
1894 Error (NULL
, 0, 0, "must specify a database filename using -db DbFileName", NULL
);
1896 return STATUS_ERROR
;
1899 // If dumping the database file, then return immediately if all
1900 // parameters check out.
1902 if (mGlobals
.Mode
== MODE_DUMP
) {
1904 // Not much use if they didn't specify -oh or -oc or -ou or -hpk
1906 if ((mGlobals
.DumpUFileName
[0] == 0) &&
1907 (mGlobals
.StringHFileName
[0] == 0) &&
1908 (mGlobals
.StringCFileName
[0] == 0) &&
1909 (mGlobals
.HiiExportPackFileName
[0] == 0)
1911 Error (NULL
, 0, 0, "-dump without -oc/-oh/-ou/-hpk is a NOP", NULL
);
1912 return STATUS_ERROR
;
1915 return STATUS_SUCCESS
;
1918 // Had to specify source string file and output string defines header filename.
1920 if (mGlobals
.Mode
== MODE_SCAN
) {
1922 Error (PROGRAM_NAME
, 0, 0, NULL
, "must specify at least one source file to scan with -scan");
1924 return STATUS_ERROR
;
1927 // Get the list of filenames
1930 NewList
= malloc (sizeof (TEXT_STRING_LIST
));
1931 if (NewList
== NULL
) {
1932 Error (PROGRAM_NAME
, 0, 0, "memory allocation failure", NULL
);
1933 return STATUS_ERROR
;
1936 memset (NewList
, 0, sizeof (TEXT_STRING_LIST
));
1937 NewList
->Str
= (UINT8
*) malloc (strlen (Argv
[0]) + 1);
1938 if (NewList
->Str
== NULL
) {
1939 Error (PROGRAM_NAME
, 0, 0, "memory allocation failure", NULL
);
1940 return STATUS_ERROR
;
1943 strcpy (NewList
->Str
, Argv
[0]);
1944 if (mGlobals
.ScanFileName
== NULL
) {
1945 mGlobals
.ScanFileName
= NewList
;
1947 mGlobals
.LastScanFileName
->Next
= NewList
;
1950 mGlobals
.LastScanFileName
= NewList
;
1956 // Parse mode -- must specify an input unicode file name
1959 Error (PROGRAM_NAME
, 0, 0, NULL
, "must specify input unicode string file name with -parse");
1961 return STATUS_ERROR
;
1964 strcpy (mGlobals
.SourceFiles
.FileName
, Argv
[0]);
1967 return STATUS_SUCCESS
;
1970 // Found "-lang eng,spa+cat" on the command line. Parse the
1971 // language list and save the setting for later processing.
1975 AddCommandLineLanguage (
1979 WCHAR_STRING_LIST
*WNewList
;
1983 // Keep processing the input string until we find the end.
1987 // Allocate memory for a new list element, fill it in, and
1988 // add it to our list.
1990 WNewList
= MALLOC (sizeof (WCHAR_STRING_LIST
));
1991 if (WNewList
== NULL
) {
1992 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1993 return STATUS_ERROR
;
1996 memset ((char *) WNewList
, 0, sizeof (WCHAR_STRING_LIST
));
1997 WNewList
->Str
= malloc ((strlen (Language
) + 1) * sizeof (WCHAR
));
1998 if (WNewList
->Str
== NULL
) {
2000 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
2001 return STATUS_ERROR
;
2004 // Copy it as unicode to our new structure. Then remove the
2005 // plus signs in it, and verify each language name is 3 characters
2006 // long. If we find a comma, then we're done with this group, so
2010 swprintf (WNewList
->Str
, (strlen (Language
) + 1) * sizeof (WCHAR
), L
"%S", Language
);
2012 swprintf (WNewList
->Str
, L
"%S", Language
);
2014 From
= To
= WNewList
->Str
;
2016 if (*From
== L
',') {
2020 if ((wcslen (From
) < LANGUAGE_IDENTIFIER_NAME_LEN
) ||
2022 (From
[LANGUAGE_IDENTIFIER_NAME_LEN
] != 0) &&
2023 (From
[LANGUAGE_IDENTIFIER_NAME_LEN
] != UNICODE_PLUS_SIGN
) &&
2024 (From
[LANGUAGE_IDENTIFIER_NAME_LEN
] != L
',')
2027 Error (PROGRAM_NAME
, 0, 0, Language
, "invalid format for language name on command line");
2028 FREE (WNewList
->Str
);
2030 return STATUS_ERROR
;
2033 wcsncpy (To
, From
, LANGUAGE_IDENTIFIER_NAME_LEN
);
2034 To
+= LANGUAGE_IDENTIFIER_NAME_LEN
;
2035 From
+= LANGUAGE_IDENTIFIER_NAME_LEN
;
2036 if (*From
== L
'+') {
2043 // Add it to our linked list
2045 if (mGlobals
.Language
== NULL
) {
2046 mGlobals
.Language
= WNewList
;
2048 mGlobals
.LastLanguage
->Next
= WNewList
;
2051 mGlobals
.LastLanguage
= WNewList
;
2053 // Skip to next entry (comma-separated list)
2056 if (*Language
== L
',') {
2065 return STATUS_SUCCESS
;
2068 // The contents of the text file are expected to be (one per line)
2069 // STRING_IDENTIFIER_NAME ScopeName
2071 // STR_ID_MY_FAVORITE_STRING IBM
2075 ParseIndirectionFiles (
2076 TEXT_STRING_LIST
*Files
2085 WCHAR_MATCHING_STRING_LIST
*NewList
;
2087 Line
[sizeof (Line
) - 1] = 0;
2089 while (Files
!= NULL
) {
2090 Fptr
= fopen (Files
->Str
, "r");
2093 Error (NULL
, 0, 0, Files
->Str
, "failed to open input indirection file for reading");
2094 return STATUS_ERROR
;
2097 while (fgets (Line
, sizeof (Line
), Fptr
) != NULL
) {
2099 // remove terminating newline for error printing purposes.
2101 if (Line
[strlen (Line
) - 1] == '\n') {
2102 Line
[strlen (Line
) - 1] = 0;
2106 if (Line
[sizeof (Line
) - 1] != 0) {
2107 Error (Files
->Str
, LineCount
, 0, "line length exceeds maximum supported", NULL
);
2112 while (*StringName
&& (isspace (*StringName
))) {
2117 if ((*StringName
== '_') || isalpha (*StringName
)) {
2119 while ((*End
) && (*End
== '_') || (isalnum (*End
))) {
2123 if (isspace (*End
)) {
2126 while (isspace (*End
)) {
2132 while (*End
&& !isspace (*End
)) {
2138 // Add the string name/scope pair
2140 NewList
= malloc (sizeof (WCHAR_MATCHING_STRING_LIST
));
2141 if (NewList
== NULL
) {
2142 Error (NULL
, 0, 0, "memory allocation error", NULL
);
2146 memset (NewList
, 0, sizeof (WCHAR_MATCHING_STRING_LIST
));
2147 NewList
->Str1
= (WCHAR
*) malloc ((strlen (StringName
) + 1) * sizeof (WCHAR
));
2148 NewList
->Str2
= (WCHAR
*) malloc ((strlen (ScopeName
) + 1) * sizeof (WCHAR
));
2149 if ((NewList
->Str1
== NULL
) || (NewList
->Str2
== NULL
)) {
2150 Error (NULL
, 0, 0, "memory allocation error", NULL
);
2155 swprintf (NewList
->Str1
, (strlen (StringName
) + 1) * sizeof (WCHAR
), L
"%S", StringName
);
2156 swprintf (NewList
->Str2
, (strlen (ScopeName
) + 1) * sizeof (WCHAR
), L
"%S", ScopeName
);
2158 swprintf (NewList
->Str1
, L
"%S", StringName
);
2159 swprintf (NewList
->Str2
, L
"%S", ScopeName
);
2161 if (mGlobals
.IndirectionList
== NULL
) {
2162 mGlobals
.IndirectionList
= NewList
;
2164 mGlobals
.LastIndirectionList
->Next
= NewList
;
2167 mGlobals
.LastIndirectionList
= NewList
;
2169 Error (Files
->Str
, LineCount
, 0, StringName
, "invalid line : expected 'StringIdentifier Scope'");
2173 Error (Files
->Str
, LineCount
, 0, StringName
, "invalid line : expected 'StringIdentifier Scope'");
2177 Error (Files
->Str
, LineCount
, 0, StringName
, "invalid string identifier");
2185 Files
= Files
->Next
;
2191 return STATUS_ERROR
;
2194 return STATUS_SUCCESS
;
2200 TEXT_STRING_LIST
*ScanFiles
2203 char Line
[MAX_LINE_LEN
];
2209 char *StringTokenPos
;
2210 TEXT_STRING_LIST
*SList
;
2214 // Put a null-terminator at the end of the line. If we read in
2215 // a line longer than we support, then we can catch it.
2217 Line
[MAX_LINE_LEN
- 1] = 0;
2219 // Process each file. If they gave us a skip extension list, then
2220 // skip it if the extension matches.
2222 while (ScanFiles
!= NULL
) {
2224 for (SList
= mGlobals
.SkipExt
; SList
!= NULL
; SList
= SList
->Next
) {
2225 if ((strlen (ScanFiles
->Str
) > strlen (SList
->Str
)) &&
2226 (strcmp (ScanFiles
->Str
+ strlen (ScanFiles
->Str
) - strlen (SList
->Str
), SList
->Str
) == 0)
2230 // printf ("Match: %s : %s\n", ScanFiles->Str, SList->Str);
2237 if (mGlobals
.VerboseScan
) {
2238 printf ("Scanning %s\n", ScanFiles
->Str
);
2241 Fptr
= fopen (ScanFiles
->Str
, "r");
2243 Error (NULL
, 0, 0, ScanFiles
->Str
, "failed to open input file for scanning");
2244 return STATUS_ERROR
;
2248 while (fgets (Line
, sizeof (Line
), Fptr
) != NULL
) {
2250 if (Line
[MAX_LINE_LEN
- 1] != 0) {
2251 Error (ScanFiles
->Str
, LineNum
, 0, "line length exceeds maximum supported by tool", NULL
);
2253 return STATUS_ERROR
;
2256 // Remove the newline from the input line so we can print a warning message
2258 if (Line
[strlen (Line
) - 1] == '\n') {
2259 Line
[strlen (Line
) - 1] = 0;
2262 // Terminate the line at // comments
2264 Cptr
= strstr (Line
, "//");
2270 while ((Cptr
= strstr (Cptr
, STRING_TOKEN
)) != NULL
) {
2272 // Found "STRING_TOKEN". Make sure we don't have NUM_STRING_TOKENS or
2273 // something like that. Then make sure it's followed by
2274 // an open parenthesis, a string identifier, and then a closing
2277 if (mGlobals
.VerboseScan
) {
2278 printf (" %d: %s", LineNum
, Cptr
);
2281 if (((Cptr
== Line
) || (!IsValidIdentifierChar (*(Cptr
- 1), FALSE
))) &&
2282 (!IsValidIdentifierChar (*(Cptr
+ sizeof (STRING_TOKEN
) - 1), FALSE
))
2284 StringTokenPos
= Cptr
;
2286 Cptr
+= strlen (STRING_TOKEN
);
2287 while (*Cptr
&& isspace (*Cptr
) && (*Cptr
!= '(')) {
2292 Warning (ScanFiles
->Str
, LineNum
, 0, StringTokenPos
, "expected "STRING_TOKEN
"(identifier)");
2295 // Skip over the open-parenthesis and find the next non-blank character
2298 while (isspace (*Cptr
)) {
2303 if ((*Cptr
== '_') || isalpha (*Cptr
)) {
2304 while ((*Cptr
== '_') || (isalnum (*Cptr
))) {
2309 while (*Cptr
&& isspace (*Cptr
)) {
2314 Warning (ScanFiles
->Str
, LineNum
, 0, StringTokenPos
, "expected "STRING_TOKEN
"(identifier)");
2324 // Add the string identifier to the list of used strings
2326 ParserSetPosition (ScanFiles
->Str
, LineNum
);
2327 StringDBSetStringReferenced (SavePtr
, mGlobals
.IgnoreNotFound
);
2328 if (mGlobals
.VerboseScan
) {
2329 printf ("...referenced %s", SavePtr
);
2332 Warning (ScanFiles
->Str
, LineNum
, 0, StringTokenPos
, "expected valid string identifier name");
2337 // Found it, but it's a substring of something else. Advance our pointer.
2342 if (mGlobals
.VerboseScan
) {
2351 // Skipping this file type
2353 if (mGlobals
.VerboseScan
) {
2354 printf ("Skip scanning of %s\n", ScanFiles
->Str
);
2358 ScanFiles
= ScanFiles
->Next
;
2361 return STATUS_SUCCESS
;
2364 // Free the global string lists we allocated memory for
2372 TEXT_STRING_LIST
*Temp
;
2373 WCHAR_STRING_LIST
*WTemp
;
2376 // Traverse the include paths, freeing each
2378 while (mGlobals
.IncludePaths
!= NULL
) {
2379 Temp
= mGlobals
.IncludePaths
->Next
;
2380 free (mGlobals
.IncludePaths
->Str
);
2381 free (mGlobals
.IncludePaths
);
2382 mGlobals
.IncludePaths
= Temp
;
2385 // If we did a scan, then free up our
2386 // list of files to scan.
2388 while (mGlobals
.ScanFileName
!= NULL
) {
2389 Temp
= mGlobals
.ScanFileName
->Next
;
2390 free (mGlobals
.ScanFileName
->Str
);
2391 free (mGlobals
.ScanFileName
);
2392 mGlobals
.ScanFileName
= Temp
;
2395 // If they gave us a list of filename extensions to
2396 // skip on scan, then free them up.
2398 while (mGlobals
.SkipExt
!= NULL
) {
2399 Temp
= mGlobals
.SkipExt
->Next
;
2400 free (mGlobals
.SkipExt
->Str
);
2401 free (mGlobals
.SkipExt
);
2402 mGlobals
.SkipExt
= Temp
;
2405 // Free up any languages specified
2407 while (mGlobals
.Language
!= NULL
) {
2408 WTemp
= mGlobals
.Language
->Next
;
2409 free (mGlobals
.Language
->Str
);
2410 free (mGlobals
.Language
);
2411 mGlobals
.Language
= WTemp
;
2414 // Free up our indirection list
2416 while (mGlobals
.IndirectionList
!= NULL
) {
2417 mGlobals
.LastIndirectionList
= mGlobals
.IndirectionList
->Next
;
2418 free (mGlobals
.IndirectionList
->Str1
);
2419 free (mGlobals
.IndirectionList
->Str2
);
2420 free (mGlobals
.IndirectionList
);
2421 mGlobals
.IndirectionList
= mGlobals
.LastIndirectionList
;
2424 while (mGlobals
.IndirectionFileName
!= NULL
) {
2425 mGlobals
.LastIndirectionFileName
= mGlobals
.IndirectionFileName
->Next
;
2426 free (mGlobals
.IndirectionFileName
->Str
);
2427 free (mGlobals
.IndirectionFileName
);
2428 mGlobals
.IndirectionFileName
= mGlobals
.LastIndirectionFileName
;
2434 IsValidIdentifierChar (
2440 // If it's the first character of an identifier, then
2441 // it must be one of [A-Za-z_].
2444 if (isalpha (Char
) || (Char
== '_')) {
2449 // If it's not the first character, then it can
2450 // be one of [A-Za-z_0-9]
2452 if (isalnum (Char
) || (Char
== '_')) {
2463 SOURCE_FILE
*SourceFile
2466 SourceFile
->LineNum
= 1;
2467 SourceFile
->FileBufferPtr
= SourceFile
->FileBuffer
;
2468 SourceFile
->EndOfFile
= 0;
2474 SOURCE_FILE
*SourceFile
,
2476 BOOLEAN StopAfterNewline
2479 while (!EndOfFile (SourceFile
)) {
2481 // Check for the character of interest
2483 if (SourceFile
->FileBufferPtr
[0] == WChar
) {
2486 if (SourceFile
->FileBufferPtr
[0] == UNICODE_LF
) {
2487 SourceFile
->LineNum
++;
2488 if (StopAfterNewline
) {
2489 SourceFile
->FileBufferPtr
++;
2490 if (SourceFile
->FileBufferPtr
[0] == 0) {
2491 SourceFile
->FileBufferPtr
++;
2498 SourceFile
->FileBufferPtr
++;
2512 Routine Description:
2514 Print usage information for this utility.
2527 static const char *Str
[] = {
2529 PROGRAM_NAME
" version "TOOL_VERSION
" -- process unicode strings file",
2530 " Usage: "PROGRAM_NAME
" -parse {parse options} [FileNames]",
2531 " "PROGRAM_NAME
" -scan {scan options} [FileName]",
2532 " "PROGRAM_NAME
" -dump {dump options}",
2533 " Common options include:",
2534 " -h or -? for this help information",
2535 " -db Database required name of output/input database file",
2536 " -bn BaseName for use in the .h and .c output files",
2537 " Default = "DEFAULT_BASE_NAME
,
2538 " -v for verbose output",
2539 " -vdbw for verbose output when writing database",
2540 " -vdbr for verbose output when reading database",
2541 " -od FileName to specify an output database file name",
2542 " Parse options include:",
2543 " -i IncludePath add IncludePath to list of search paths",
2544 " -dep FileName to specify an output dependency file name",
2545 " -newdb to not read in existing database file",
2546 " -uqs to indicate that unquoted strings are used",
2547 " FileNames name of one or more unicode files to parse",
2548 " Scan options include:",
2549 " -scan scan text file(s) for STRING_TOKEN() usage",
2550 " -skipext .ext to skip scan of files with .ext filename extension",
2551 " -ignorenotfound ignore if a given STRING_TOKEN(STR) is not ",
2552 " found in the database",
2553 " FileNames one or more files to scan",
2554 " Dump options include:",
2555 " -oc FileName write string data to FileName",
2556 " -oh FileName write string defines to FileName",
2557 " -ou FileName dump database to unicode file FileName",
2558 " -lang Lang only dump for the language 'Lang'",
2559 " -if FileName to specify an indirection file",
2560 " -hpk FileName to create an HII export pack of the strings",
2562 " The expected process is to parse a unicode string file to create an initial",
2563 " database of string identifier names and string definitions. Then text files",
2564 " should be scanned for STRING_TOKEN() usages, and the referenced",
2565 " strings will be tagged as used in the database. After all files have been",
2566 " scanned, then the database should be dumped to create the necessary output",
2571 for (Index
= 0; Str
[Index
] != NULL
; Index
++) {
2572 fprintf (stdout
, "%s\n", Str
[Index
]);