3 Copyright (c) 2004, 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.
27 #include <Common/UefiBaseTypes.h>
29 #include "CommonLib.h"
30 #include "EfiUtilityMsgs.h"
31 #include "StrGather.h"
34 #define TOOL_VERSION "0.31"
39 #define MAX_NEST_DEPTH 20 // just in case we get in an endless loop.
40 #define MAX_STRING_IDENTIFIER_NAME 100 // number of wchars
41 #define MAX_LINE_LEN 200
42 #define STRING_TOKEN "STRING_TOKEN"
43 #define DEFAULT_BASE_NAME "BaseName"
45 // Operational modes for this utility
47 #define MODE_UNKNOWN 0
52 // Different file separater for Linux and Windows
55 #define FILE_SEP_CHAR '/'
56 #define FILE_SEP_STRING "/"
58 #define FILE_SEP_CHAR '\\'
59 #define FILE_SEP_STRING "\\"
63 // We keep a linked list of these for the source files we process
65 typedef struct _SOURCE_FILE
{
70 CHAR8 FileName
[MAX_PATH
];
74 struct _SOURCE_FILE
*Previous
;
75 struct _SOURCE_FILE
*Next
;
76 WCHAR ControlCharacter
;
79 #define DEFAULT_CONTROL_CHARACTER UNICODE_SLASH
82 // Here's all our globals. We need a linked list of include paths, a linked
83 // list of source files, a linked list of subdirectories (appended to each
84 // include path when searching), and a couple other fields.
87 SOURCE_FILE SourceFiles
;
88 TEXT_STRING_LIST
*IncludePaths
; // all include paths to search
89 TEXT_STRING_LIST
*LastIncludePath
;
90 TEXT_STRING_LIST
*ScanFileName
;
91 TEXT_STRING_LIST
*LastScanFileName
;
92 TEXT_STRING_LIST
*SkipExt
; // if -skipext .uni
93 TEXT_STRING_LIST
*LastSkipExt
;
94 TEXT_STRING_LIST
*IndirectionFileName
;
95 TEXT_STRING_LIST
*LastIndirectionFileName
;
96 TEXT_STRING_LIST
*DatabaseFileName
;
97 TEXT_STRING_LIST
*LastDatabaseFileName
;
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 CHAR8 OutputDatabaseFileName
[MAX_PATH
];
110 CHAR8 StringHFileName
[MAX_PATH
];
111 CHAR8 StringCFileName
[MAX_PATH
]; // output .C filename
112 CHAR8 DumpUFileName
[MAX_PATH
]; // output unicode dump file name
113 CHAR8 HiiExportPackFileName
[MAX_PATH
]; // HII export pack file name
114 CHAR8 BaseName
[MAX_PATH
]; // base filename of the strings file
120 IsValidIdentifierChar (
128 SOURCE_FILE
*SourceFile
134 SOURCE_FILE
*SourceFile
,
136 BOOLEAN StopAfterNewline
142 SOURCE_FILE
*SourceFile
148 SOURCE_FILE
*SourceFile
154 SOURCE_FILE
*SourceFile
160 SOURCE_FILE
*SourceFile
165 GetStringIdentifierName (
166 IN SOURCE_FILE
*SourceFile
,
167 IN OUT WCHAR
*StringIdentifierName
,
168 IN UINT32 StringIdentifierNameLen
173 GetLanguageIdentifierName (
174 IN SOURCE_FILE
*SourceFile
,
175 IN OUT WCHAR
*LanguageIdentifierName
,
176 IN UINT32 LanguageIdentifierNameLen
,
182 GetPrintableLanguageName (
183 IN SOURCE_FILE
*SourceFile
188 AddCommandLineLanguage (
195 SOURCE_FILE
*SourceFile
,
202 SOURCE_FILE
*SourceFile
,
203 SOURCE_FILE
*ParentSourceFile
209 SOURCE_FILE
*SourceFile
216 OUT CHAR8
*FoundFileName
,
217 IN UINT32 FoundFileNameLen
230 SOURCE_FILE
*SourceFile
255 SOURCE_FILE
*SourceFile
260 ProcessTokenInclude (
261 SOURCE_FILE
*SourceFile
267 SOURCE_FILE
*SourceFile
272 ProcessTokenLanguage (
273 SOURCE_FILE
*SourceFile
278 ProcessTokenLangDef (
279 SOURCE_FILE
*SourceFile
285 TEXT_STRING_LIST
*ScanFiles
290 ParseIndirectionFiles (
291 TEXT_STRING_LIST
*Files
295 StringDBCreateHiiExportPack (
296 CHAR8
*OutputFileName
308 Call the routine to parse the command-line options, then process the file.
312 Argc - Standard C main() argc and argv.
313 Argv - Standard C main() argc and argv.
324 SetUtilityName (PROGRAM_NAME
);
326 // Process the command-line arguments
328 Status
= ProcessArgs (Argc
, Argv
);
329 if (Status
!= STATUS_SUCCESS
) {
333 // Initialize the database manager
335 StringDBConstructor ();
337 // We always try to read in an existing database file. It may not
338 // exist, which is ok usually.
340 if (mGlobals
.NewDatabase
== 0) {
342 // Read all databases specified.
344 for (mGlobals
.LastDatabaseFileName
= mGlobals
.DatabaseFileName
;
345 mGlobals
.LastDatabaseFileName
!= NULL
;
346 mGlobals
.LastDatabaseFileName
= mGlobals
.LastDatabaseFileName
->Next
348 Status
= StringDBReadDatabase (mGlobals
.LastDatabaseFileName
->Str
, TRUE
, mGlobals
.VerboseDatabaseRead
);
349 if (Status
!= STATUS_SUCCESS
) {
355 // Read indirection file(s) if specified
357 if (ParseIndirectionFiles (mGlobals
.IndirectionFileName
) != STATUS_SUCCESS
) {
361 // If scanning source files, do that now
363 if (mGlobals
.Mode
== MODE_SCAN
) {
364 ScanFiles (mGlobals
.ScanFileName
);
365 } else if (mGlobals
.Mode
== MODE_PARSE
) {
367 // Parsing a unicode strings file
369 mGlobals
.SourceFiles
.ControlCharacter
= DEFAULT_CONTROL_CHARACTER
;
370 Status
= ProcessIncludeFile (&mGlobals
.SourceFiles
, NULL
);
371 if (Status
!= STATUS_SUCCESS
) {
376 // Create the string defines header file if there have been no errors.
378 ParserSetPosition (NULL
, 0);
379 if ((mGlobals
.StringHFileName
[0] != 0) && (GetUtilityStatus () < STATUS_ERROR
)) {
380 Status
= StringDBDumpStringDefines (mGlobals
.StringHFileName
, mGlobals
.BaseName
);
381 if (Status
!= EFI_SUCCESS
) {
386 // Dump the strings to a .c file if there have still been no errors.
388 if ((mGlobals
.StringCFileName
[0] != 0) && (GetUtilityStatus () < STATUS_ERROR
)) {
389 Status
= StringDBDumpCStrings (
390 mGlobals
.StringCFileName
,
393 mGlobals
.IndirectionList
395 if (Status
!= EFI_SUCCESS
) {
400 // Dump the database if requested
402 if ((mGlobals
.DumpUFileName
[0] != 0) && (GetUtilityStatus () < STATUS_ERROR
)) {
403 StringDBDumpDatabase (NULL
, mGlobals
.DumpUFileName
, FALSE
);
406 // Dump the string data as HII binary string pack if requested
408 if ((mGlobals
.HiiExportPackFileName
[0] != 0) && (GetUtilityStatus () < STATUS_ERROR
)) {
409 StringDBCreateHiiExportPack (mGlobals
.HiiExportPackFileName
);
412 // Always update the database if no errors and not in dump mode. If they specified -od
413 // for an output database file name, then use that name. Otherwise use the name of
414 // the first database file specified with -db
416 if ((mGlobals
.Mode
!= MODE_DUMP
) && (GetUtilityStatus () < STATUS_ERROR
)) {
417 if (mGlobals
.OutputDatabaseFileName
[0]) {
418 Status
= StringDBWriteDatabase (mGlobals
.OutputDatabaseFileName
, mGlobals
.VerboseDatabaseWrite
);
420 Status
= StringDBWriteDatabase (mGlobals
.DatabaseFileName
->Str
, mGlobals
.VerboseDatabaseWrite
);
423 if (Status
!= EFI_SUCCESS
) {
433 StringDBDestructor ();
434 return GetUtilityStatus ();
440 SOURCE_FILE
*SourceFile
,
441 SOURCE_FILE
*ParentSourceFile
447 Given a source file, open the file and parse it
451 SourceFile - name of file to parse
452 ParentSourceFile - for error reporting purposes, the file that #included SourceFile.
460 static UINT32 NestDepth
= 0;
461 CHAR8 FoundFileName
[MAX_PATH
];
464 Status
= STATUS_SUCCESS
;
467 // Print the file being processed. Indent so you can tell the include nesting
470 if (mGlobals
.Verbose
) {
471 fprintf (stdout
, "%*cProcessing file '%s'\n", NestDepth
* 2, ' ', SourceFile
->FileName
);
475 // Make sure we didn't exceed our maximum nesting depth
477 if (NestDepth
> MAX_NEST_DEPTH
) {
478 Error (NULL
, 0, 0, SourceFile
->FileName
, "max nesting depth (%d) exceeded", NestDepth
);
479 Status
= STATUS_ERROR
;
483 // Try to open the file locally, and if that fails try along our include paths.
485 strcpy (FoundFileName
, SourceFile
->FileName
);
486 if ((SourceFile
->Fptr
= fopen (FoundFileName
, "rb")) == NULL
) {
488 // Try to find it among the paths if it has a parent (that is, it is included
491 if (ParentSourceFile
== NULL
) {
492 Error (NULL
, 0, 0, SourceFile
->FileName
, "file not found");
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");
503 // Process the file found
505 ProcessFile (SourceFile
);
508 // Close open files and return status
510 if (SourceFile
->Fptr
!= NULL
) {
511 fclose (SourceFile
->Fptr
);
520 SOURCE_FILE
*SourceFile
524 // Get the file size, and then read the entire thing into memory.
525 // Allocate space for a terminator character.
527 fseek (SourceFile
->Fptr
, 0, SEEK_END
);
528 SourceFile
->FileSize
= ftell (SourceFile
->Fptr
);
529 fseek (SourceFile
->Fptr
, 0, SEEK_SET
);
530 SourceFile
->FileBuffer
= (WCHAR
*) malloc (SourceFile
->FileSize
+ sizeof (WCHAR
));
531 if (SourceFile
->FileBuffer
== NULL
) {
532 Error (NULL
, 0, 0, "memory allocation failure", NULL
);
536 fread ((VOID
*) SourceFile
->FileBuffer
, SourceFile
->FileSize
, 1, SourceFile
->Fptr
);
537 SourceFile
->FileBuffer
[(SourceFile
->FileSize
/ sizeof (WCHAR
))] = UNICODE_NULL
;
539 // Pre-process the file to replace comments with spaces
541 PreprocessFile (SourceFile
);
545 ParseFile (SourceFile
);
546 free (SourceFile
->FileBuffer
);
547 return STATUS_SUCCESS
;
553 SOURCE_FILE
*SourceFile
560 // First character of a unicode file is special. Make sure
562 if (SourceFile
->FileBufferPtr
[0] != UNICODE_FILE_START
) {
563 Error (SourceFile
->FileName
, 1, 0, SourceFile
->FileName
, "file does not appear to be a unicode file");
567 SourceFile
->FileBufferPtr
++;
570 // Print the first line if in verbose mode
572 if (mGlobals
.Verbose
) {
573 printf ("%d: %S\n", SourceFile
->LineNum
, SourceFile
->FileBufferPtr
);
576 // Since the syntax is relatively straightforward, just switch on the next char
578 while (!EndOfFile (SourceFile
)) {
580 // Check for whitespace
582 if (SourceFile
->FileBufferPtr
[0] == UNICODE_SPACE
) {
583 SourceFile
->FileBufferPtr
++;
584 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_TAB
) {
585 SourceFile
->FileBufferPtr
++;
586 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_CR
) {
587 SourceFile
->FileBufferPtr
++;
588 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_LF
) {
589 SourceFile
->FileBufferPtr
++;
590 SourceFile
->LineNum
++;
591 if (mGlobals
.Verbose
) {
592 printf ("%d: %S\n", SourceFile
->LineNum
, SourceFile
->FileBufferPtr
);
596 } else if (SourceFile
->FileBufferPtr
[0] == 0) {
597 SourceFile
->FileBufferPtr
++;
598 } else if (InComment
) {
599 SourceFile
->FileBufferPtr
++;
600 } else if ((SourceFile
->FileBufferPtr
[0] == UNICODE_SLASH
) && (SourceFile
->FileBufferPtr
[1] == UNICODE_SLASH
)) {
601 SourceFile
->FileBufferPtr
+= 2;
603 } else if (SourceFile
->SkipToHash
&& (SourceFile
->FileBufferPtr
[0] != SourceFile
->ControlCharacter
)) {
604 SourceFile
->FileBufferPtr
++;
606 SourceFile
->SkipToHash
= FALSE
;
607 if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
608 ((Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"include")) > 0)
610 SourceFile
->FileBufferPtr
+= Len
+ 1;
611 ProcessTokenInclude (SourceFile
);
612 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
613 (Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"scope")) > 0
615 SourceFile
->FileBufferPtr
+= Len
+ 1;
616 ProcessTokenScope (SourceFile
);
617 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
618 (Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"language")) > 0
620 SourceFile
->FileBufferPtr
+= Len
+ 1;
621 ProcessTokenLanguage (SourceFile
);
622 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
623 (Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"langdef")) > 0
625 SourceFile
->FileBufferPtr
+= Len
+ 1;
626 ProcessTokenLangDef (SourceFile
);
627 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
628 (Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"string")) > 0
630 SourceFile
->FileBufferPtr
+= Len
+ 1;
631 ProcessTokenString (SourceFile
);
632 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
633 (Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"EFI_BREAKPOINT()")) > 0
635 SourceFile
->FileBufferPtr
+= Len
;
637 // BUGBUG: Caling EFI_BREAKOINT() is breaking the link. What is the proper action for this tool
638 // in this condition?
640 // EFI_BREAKPOINT ();
641 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
642 (SourceFile
->FileBufferPtr
[1] == UNICODE_EQUAL_SIGN
)
644 SourceFile
->ControlCharacter
= SourceFile
->FileBufferPtr
[2];
645 SourceFile
->FileBufferPtr
+= 3;
647 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "unrecognized token", "%S", SourceFile
->FileBufferPtr
);
649 // Treat rest of line as a comment.
656 return STATUS_SUCCESS
;
662 SOURCE_FILE
*SourceFile
667 Preprocess a file to replace all carriage returns with NULLs so
668 we can print lines from the file to the screen.
671 SourceFile - structure that we use to keep track of an input file.
680 RewindFile (SourceFile
);
682 while (!EndOfFile (SourceFile
)) {
684 // If a line-feed, then no longer in a comment
686 if (SourceFile
->FileBufferPtr
[0] == UNICODE_LF
) {
687 SourceFile
->FileBufferPtr
++;
688 SourceFile
->LineNum
++;
690 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_CR
) {
692 // Replace all carriage returns with a NULL so we can print stuff
694 SourceFile
->FileBufferPtr
[0] = 0;
695 SourceFile
->FileBufferPtr
++;
696 } else if (InComment
) {
697 SourceFile
->FileBufferPtr
[0] = UNICODE_SPACE
;
698 SourceFile
->FileBufferPtr
++;
699 } else if ((SourceFile
->FileBufferPtr
[0] == UNICODE_SLASH
) && (SourceFile
->FileBufferPtr
[1] == UNICODE_SLASH
)) {
700 SourceFile
->FileBufferPtr
+= 2;
703 SourceFile
->FileBufferPtr
++;
707 // Could check for end-of-file and still in a comment, but
708 // should not be necessary. So just restore the file pointers.
710 RewindFile (SourceFile
);
715 GetPrintableLanguageName (
716 IN SOURCE_FILE
*SourceFile
724 SkipWhiteSpace (SourceFile
);
725 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
727 SourceFile
->FileName
,
730 "expected quoted printable language name",
732 SourceFile
->FileBufferPtr
734 SourceFile
->SkipToHash
= TRUE
;
739 SourceFile
->FileBufferPtr
++;
740 Start
= Ptr
= SourceFile
->FileBufferPtr
;
741 while (!EndOfFile (SourceFile
)) {
742 if (SourceFile
->FileBufferPtr
[0] == UNICODE_CR
) {
743 Warning (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "carriage return found in quoted string", "%S", Start
);
745 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_DOUBLE_QUOTE
) {
749 SourceFile
->FileBufferPtr
++;
753 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
755 SourceFile
->FileName
,
758 "missing closing quote on printable language name string",
763 SourceFile
->FileBufferPtr
++;
766 // Now allocate memory for the string and save it off
768 String
= (WCHAR
*) malloc ((Len
+ 1) * sizeof (WCHAR
));
769 if (String
== NULL
) {
770 Error (NULL
, 0, 0, "memory allocation failed", NULL
);
774 // Copy the string from the file buffer to the local copy.
775 // We do no reformatting of it whatsoever at this point.
787 // Now format the string to convert \wide and \narrow controls
789 StringDBFormatString (String
);
796 SOURCE_FILE
*SourceFile
,
804 BOOLEAN PreviousBackslash
;
806 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
808 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "expected quoted string", "%S", SourceFile
->FileBufferPtr
);
815 SourceFile
->FileBufferPtr
++;
816 Start
= Ptr
= SourceFile
->FileBufferPtr
;
817 PreviousBackslash
= FALSE
;
818 while (!EndOfFile (SourceFile
)) {
819 if ((SourceFile
->FileBufferPtr
[0] == UNICODE_DOUBLE_QUOTE
) && (!PreviousBackslash
)) {
821 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_CR
) {
822 Warning (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "carriage return found in quoted string", "%S", Start
);
823 PreviousBackslash
= FALSE
;
824 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_BACKSLASH
) {
825 PreviousBackslash
= TRUE
;
827 PreviousBackslash
= FALSE
;
830 SourceFile
->FileBufferPtr
++;
834 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
835 Warning (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "missing closing quote on string", "%S", Start
);
837 SourceFile
->FileBufferPtr
++;
840 // Now allocate memory for the string and save it off
842 String
= (WCHAR
*) malloc ((Len
+ 1) * sizeof (WCHAR
));
843 if (String
== NULL
) {
844 Error (NULL
, 0, 0, "memory allocation failed", NULL
);
848 // Copy the string from the file buffer to the local copy.
849 // We do no reformatting of it whatsoever at this point.
864 // #string STR_ID_NAME
866 // All we can do is call the string database to add the string identifier. Unfortunately
867 // he'll have to keep track of the last identifier we added.
872 SOURCE_FILE
*SourceFile
875 WCHAR StringIdentifier
[MAX_STRING_IDENTIFIER_NAME
];
878 // Extract the string identifier name and add it to the database.
880 if (GetStringIdentifierName (SourceFile
, StringIdentifier
, sizeof (StringIdentifier
)) > 0) {
881 StringId
= STRING_ID_INVALID
;
882 StringDBAddStringIdentifier (StringIdentifier
, &StringId
, 0);
885 // Error recovery -- skip to the next #
887 SourceFile
->SkipToHash
= TRUE
;
894 SOURCE_FILE
*SourceFile
898 // The file buffer pointer will typically get updated before the End-of-file flag in the
899 // source file structure, so check it first.
901 if (SourceFile
->FileBufferPtr
>= SourceFile
->FileBuffer
+ SourceFile
->FileSize
/ sizeof (WCHAR
)) {
902 SourceFile
->EndOfFile
= TRUE
;
906 if (SourceFile
->EndOfFile
) {
915 GetStringIdentifierName (
916 IN SOURCE_FILE
*SourceFile
,
917 IN OUT WCHAR
*StringIdentifierName
,
918 IN UINT32 StringIdentifierNameLen
928 SkipWhiteSpace (SourceFile
);
929 if (SourceFile
->EndOfFile
) {
930 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "end-of-file encountered", "expected string identifier");
934 // Verify first character of name is [A-Za-z]
937 StringIdentifierNameLen
/= 2;
938 From
= SourceFile
->FileBufferPtr
;
939 Start
= SourceFile
->FileBufferPtr
;
940 if (((SourceFile
->FileBufferPtr
[0] >= UNICODE_A
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_Z
)) ||
941 ((SourceFile
->FileBufferPtr
[0] >= UNICODE_z
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_z
))
947 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "invalid character in string identifier name", "%S", Start
);
951 while (!EndOfFile (SourceFile
)) {
952 if (((SourceFile
->FileBufferPtr
[0] >= UNICODE_A
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_Z
)) ||
953 ((SourceFile
->FileBufferPtr
[0] >= UNICODE_z
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_z
)) ||
954 ((SourceFile
->FileBufferPtr
[0] >= UNICODE_0
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_9
)) ||
955 (SourceFile
->FileBufferPtr
[0] == UNICODE_UNDERSCORE
)
958 if (Len
>= StringIdentifierNameLen
) {
959 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "string identifier name too long", "%S", Start
);
963 *StringIdentifierName
= SourceFile
->FileBufferPtr
[0];
964 StringIdentifierName
++;
965 SourceFile
->FileBufferPtr
++;
966 } else if (SkipWhiteSpace (SourceFile
) == 0) {
967 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "invalid string identifier name", "%S", Start
);
974 // Terminate the copy of the string.
976 *StringIdentifierName
= 0;
982 GetLanguageIdentifierName (
983 IN SOURCE_FILE
*SourceFile
,
984 IN OUT WCHAR
*LanguageIdentifierName
,
985 IN UINT32 LanguageIdentifierNameLen
,
995 SkipWhiteSpace (SourceFile
);
996 if (SourceFile
->EndOfFile
) {
999 SourceFile
->FileName
,
1000 SourceFile
->LineNum
,
1002 "end-of-file encountered",
1003 "expected language identifier"
1010 // This function is called to optionally get a language identifier name in:
1011 // #string STR_ID eng "the string"
1012 // If it's optional, and we find a double-quote, then return now.
1015 if (*SourceFile
->FileBufferPtr
== UNICODE_DOUBLE_QUOTE
) {
1021 LanguageIdentifierNameLen
/= 2;
1023 // Internal error if we weren't given at least 4 WCHAR's to work with.
1025 if (LanguageIdentifierNameLen
< LANGUAGE_IDENTIFIER_NAME_LEN
+ 1) {
1027 SourceFile
->FileName
,
1028 SourceFile
->LineNum
,
1030 "app error -- language identifier name length is invalid",
1035 From
= SourceFile
->FileBufferPtr
;
1036 Start
= SourceFile
->FileBufferPtr
;
1037 while (!EndOfFile (SourceFile
)) {
1038 if (((SourceFile
->FileBufferPtr
[0] >= UNICODE_a
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_z
))) {
1040 if (Len
> LANGUAGE_IDENTIFIER_NAME_LEN
) {
1041 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "language identifier name too long", "%S", Start
);
1045 *LanguageIdentifierName
= SourceFile
->FileBufferPtr
[0];
1046 SourceFile
->FileBufferPtr
++;
1047 LanguageIdentifierName
++;
1048 } else if (!IsWhiteSpace (SourceFile
)) {
1049 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "invalid language identifier name", "%S", Start
);
1056 // Terminate the copy of the string.
1058 *LanguageIdentifierName
= 0;
1064 ProcessTokenInclude (
1065 SOURCE_FILE
*SourceFile
1068 CHAR8 IncludeFileName
[MAX_PATH
];
1071 BOOLEAN ReportedError
;
1072 SOURCE_FILE IncludedSourceFile
;
1074 ReportedError
= FALSE
;
1075 if (SkipWhiteSpace (SourceFile
) == 0) {
1076 Warning (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "expected whitespace following #include keyword", NULL
);
1079 // Should be quoted file name
1081 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
1082 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "expected quoted include file name", NULL
);
1086 SourceFile
->FileBufferPtr
++;
1088 // Copy the filename as ascii to our local string
1090 To
= IncludeFileName
;
1092 while (!EndOfFile (SourceFile
)) {
1093 if ((SourceFile
->FileBufferPtr
[0] == UNICODE_CR
) || (SourceFile
->FileBufferPtr
[0] == UNICODE_LF
)) {
1094 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "end-of-line found in quoted include file name", NULL
);
1098 if (SourceFile
->FileBufferPtr
[0] == UNICODE_DOUBLE_QUOTE
) {
1099 SourceFile
->FileBufferPtr
++;
1103 // If too long, then report the error once and process until the closing quote
1106 if (!ReportedError
&& (Len
>= sizeof (IncludeFileName
))) {
1107 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "length of include file name exceeds limit", NULL
);
1108 ReportedError
= TRUE
;
1111 if (!ReportedError
) {
1112 *To
= UNICODE_TO_ASCII (SourceFile
->FileBufferPtr
[0]);
1116 SourceFile
->FileBufferPtr
++;
1119 if (!ReportedError
) {
1121 memset ((char *) &IncludedSourceFile
, 0, sizeof (SOURCE_FILE
));
1122 strcpy (IncludedSourceFile
.FileName
, IncludeFileName
);
1123 IncludedSourceFile
.ControlCharacter
= DEFAULT_CONTROL_CHARACTER
;
1124 ProcessIncludeFile (&IncludedSourceFile
, SourceFile
);
1126 // printf ("including file '%s'\n", IncludeFileName);
1133 // Error recovery -- skip to next #
1135 SourceFile
->SkipToHash
= TRUE
;
1141 SOURCE_FILE
*SourceFile
1144 WCHAR StringIdentifier
[MAX_STRING_IDENTIFIER_NAME
];
1146 // Extract the scope name
1148 if (GetStringIdentifierName (SourceFile
, StringIdentifier
, sizeof (StringIdentifier
)) > 0) {
1149 StringDBSetScope (StringIdentifier
);
1153 // Parse: #langdef eng "English"
1154 // #langdef chn "\wideChinese"
1158 ProcessTokenLangDef (
1159 SOURCE_FILE
*SourceFile
1162 WCHAR LanguageIdentifier
[MAX_STRING_IDENTIFIER_NAME
];
1164 WCHAR
*PrintableName
;
1166 // Extract the 3-character language identifier
1168 Len
= GetLanguageIdentifierName (SourceFile
, LanguageIdentifier
, sizeof (LanguageIdentifier
), FALSE
);
1169 if (Len
!= LANGUAGE_IDENTIFIER_NAME_LEN
) {
1170 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "invalid or missing language identifier", NULL
);
1173 // Extract the printable name
1175 PrintableName
= GetPrintableLanguageName (SourceFile
);
1176 if (PrintableName
!= NULL
) {
1177 ParserSetPosition (SourceFile
->FileName
, SourceFile
->LineNum
);
1178 StringDBAddLanguage (LanguageIdentifier
, PrintableName
);
1179 free (PrintableName
);
1184 // Error recovery -- skip to next #
1186 SourceFile
->SkipToHash
= TRUE
;
1191 ApparentQuotedString (
1192 SOURCE_FILE
*SourceFile
1197 // See if the first and last nonblank characters on the line are double quotes
1199 for (Ptr
= SourceFile
->FileBufferPtr
; *Ptr
&& (*Ptr
== UNICODE_SPACE
); Ptr
++)
1201 if (*Ptr
!= UNICODE_DOUBLE_QUOTE
) {
1210 for (; *Ptr
&& (*Ptr
== UNICODE_SPACE
); Ptr
--)
1212 if (*Ptr
!= UNICODE_DOUBLE_QUOTE
) {
1220 // #language eng "some string " "more string"
1224 ProcessTokenLanguage (
1225 SOURCE_FILE
*SourceFile
1229 WCHAR
*SecondString
;
1233 WCHAR Language
[LANGUAGE_IDENTIFIER_NAME_LEN
+ 1];
1235 BOOLEAN PreviousNewline
;
1237 // Get the language identifier
1240 Len
= GetLanguageIdentifierName (SourceFile
, Language
, sizeof (Language
), TRUE
);
1241 if (Len
!= LANGUAGE_IDENTIFIER_NAME_LEN
) {
1242 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "invalid or missing language identifier", "%S", Language
);
1243 SourceFile
->SkipToHash
= TRUE
;
1247 // Extract the string value. It's either a quoted string that starts on the current line, or
1248 // an unquoted string that starts on the following line and continues until the next control
1249 // character in column 1.
1250 // Look ahead to find a quote or a newline
1252 if (SkipTo (SourceFile
, UNICODE_DOUBLE_QUOTE
, TRUE
)) {
1253 String
= GetQuotedString (SourceFile
, FALSE
);
1254 if (String
!= NULL
) {
1256 // Set the position in the file of where we are parsing for error
1257 // reporting purposes. Then start looking ahead for additional
1258 // quoted strings, and concatenate them until we get a failure
1259 // back from the string parser.
1261 Len
= StrLen (String
) + 1;
1262 ParserSetPosition (SourceFile
->FileName
, SourceFile
->LineNum
);
1264 SkipWhiteSpace (SourceFile
);
1265 SecondString
= GetQuotedString (SourceFile
, TRUE
);
1266 if (SecondString
!= NULL
) {
1267 Len
+= StrLen (SecondString
);
1268 TempString
= (WCHAR
*) malloc (Len
* sizeof (WCHAR
));
1269 if (TempString
== NULL
) {
1270 Error (NULL
, 0, 0, "application error", "failed to allocate memory");
1274 StrCpy (TempString
, String
);
1275 StrCat (TempString
, SecondString
);
1277 free (SecondString
);
1278 String
= TempString
;
1280 } while (SecondString
!= NULL
);
1281 StringDBAddString (Language
, NULL
, NULL
, String
, TRUE
, 0);
1285 // Error was reported at lower level. Error recovery mode.
1287 SourceFile
->SkipToHash
= TRUE
;
1290 if (!mGlobals
.UnquotedStrings
) {
1292 // They're using unquoted strings. If the next non-blank character is a double quote, and the
1293 // last non-blank character on the line is a double quote, then more than likely they're using
1294 // quotes, so they need to put the quoted string on the end of the previous line
1296 if (ApparentQuotedString (SourceFile
)) {
1298 SourceFile
->FileName
,
1299 SourceFile
->LineNum
,
1301 "unexpected quoted string on line",
1302 "specify -uqs option if necessary"
1307 // Found end-of-line (hopefully). Skip over it and start taking in characters
1308 // until we find a control character at the start of a line.
1311 From
= SourceFile
->FileBufferPtr
;
1312 PreviousNewline
= FALSE
;
1313 while (!EndOfFile (SourceFile
)) {
1314 if (SourceFile
->FileBufferPtr
[0] == UNICODE_LF
) {
1315 PreviousNewline
= TRUE
;
1316 SourceFile
->LineNum
++;
1319 if (PreviousNewline
&& (SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
)) {
1323 PreviousNewline
= FALSE
;
1326 SourceFile
->FileBufferPtr
++;
1329 if ((Len
== 0) && EndOfFile (SourceFile
)) {
1330 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "unexpected end of file", NULL
);
1331 SourceFile
->SkipToHash
= TRUE
;
1335 // Now allocate a buffer, copy the characters, and add the string.
1337 String
= (WCHAR
*) malloc ((Len
+ 1) * sizeof (WCHAR
));
1338 if (String
== NULL
) {
1339 Error (NULL
, 0, 0, "application error", "failed to allocate memory");
1344 while (From
< SourceFile
->FileBufferPtr
) {
1363 StringDBAddString (Language
, NULL
, NULL
, String
, TRUE
, 0);
1370 SOURCE_FILE
*SourceFile
1373 switch (SourceFile
->FileBufferPtr
[0]) {
1389 SOURCE_FILE
*SourceFile
1395 while (!EndOfFile (SourceFile
)) {
1397 switch (*SourceFile
->FileBufferPtr
) {
1402 SourceFile
->FileBufferPtr
++;
1406 SourceFile
->FileBufferPtr
++;
1407 SourceFile
->LineNum
++;
1408 if (mGlobals
.Verbose
) {
1409 printf ("%d: %S\n", SourceFile
->LineNum
, SourceFile
->FileBufferPtr
);
1418 // Some tokens require trailing whitespace. If we're at the end of the
1419 // file, then we count that as well.
1421 if ((Count
== 0) && (EndOfFile (SourceFile
))) {
1438 while (*Str
== *Buffer
) {
1451 // Given a filename, try to find it along the include paths.
1457 OUT CHAR8
*FoundFileName
,
1458 IN UINT32 FoundFileNameLen
1462 TEXT_STRING_LIST
*List
;
1465 // Traverse the list of paths and try to find the file
1467 List
= mGlobals
.IncludePaths
;
1468 while (List
!= NULL
) {
1470 // Put the path and filename together
1472 if (strlen (List
->Str
) + strlen (FileName
) + 1 > FoundFileNameLen
) {
1473 Error (PROGRAM_NAME
, 0, 0, NULL
, "internal error - cannot concatenate path+filename");
1477 // Append the filename to this include path and try to open the file.
1479 strcpy (FoundFileName
, List
->Str
);
1480 strcat (FoundFileName
, FileName
);
1481 if ((Fptr
= fopen (FoundFileName
, "rb")) != NULL
) {
1483 // Return the file pointer
1493 FoundFileName
[0] = 0;
1497 // Process the command-line arguments
1506 TEXT_STRING_LIST
*NewList
;
1508 // Clear our globals
1510 memset ((char *) &mGlobals
, 0, sizeof (mGlobals
));
1511 strcpy (mGlobals
.BaseName
, DEFAULT_BASE_NAME
);
1513 // Skip program name
1520 return STATUS_ERROR
;
1523 mGlobals
.Mode
= MODE_UNKNOWN
;
1525 // Process until no more -args.
1527 while ((Argc
> 0) && (Argv
[0][0] == '-')) {
1531 if (stricmp (Argv
[0], "-parse") == 0) {
1532 if (mGlobals
.Mode
!= MODE_UNKNOWN
) {
1533 Error (NULL
, 0, 0, "only one of -parse/-scan/-dump allowed", NULL
);
1534 return STATUS_ERROR
;
1537 mGlobals
.Mode
= MODE_PARSE
;
1541 } else if (stricmp (Argv
[0], "-scan") == 0) {
1542 if (mGlobals
.Mode
!= MODE_UNKNOWN
) {
1543 Error (NULL
, 0, 0, "only one of -parse/-scan/-dump allowed", NULL
);
1544 return STATUS_ERROR
;
1547 mGlobals
.Mode
= MODE_SCAN
;
1549 // -vscan verbose scanning option
1551 } else if (stricmp (Argv
[0], "-vscan") == 0) {
1552 mGlobals
.VerboseScan
= TRUE
;
1556 } else if (stricmp (Argv
[0], "-dump") == 0) {
1557 if (mGlobals
.Mode
!= MODE_UNKNOWN
) {
1558 Error (NULL
, 0, 0, "only one of -parse/-scan/-dump allowed", NULL
);
1559 return STATUS_ERROR
;
1562 mGlobals
.Mode
= MODE_DUMP
;
1563 } else if (stricmp (Argv
[0], "-uqs") == 0) {
1564 mGlobals
.UnquotedStrings
= TRUE
;
1566 // -i path add include search path when parsing
1568 } else if (stricmp (Argv
[0], "-i") == 0) {
1570 // check for one more arg
1572 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1573 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing include path");
1574 return STATUS_ERROR
;
1577 // Allocate memory for a new list element, fill it in, and
1578 // add it to our list of include paths. Always make sure it
1579 // has a "\" on the end of it.
1581 NewList
= malloc (sizeof (TEXT_STRING_LIST
));
1582 if (NewList
== NULL
) {
1583 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1584 return STATUS_ERROR
;
1587 memset ((char *) NewList
, 0, sizeof (TEXT_STRING_LIST
));
1588 NewList
->Str
= malloc (strlen (Argv
[1]) + 2);
1589 if (NewList
->Str
== NULL
) {
1591 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1592 return STATUS_ERROR
;
1595 strcpy (NewList
->Str
, Argv
[1]);
1596 if (NewList
->Str
[strlen (NewList
->Str
) - 1] != FILE_SEP_CHAR
) {
1597 strcat (NewList
->Str
, FILE_SEP_STRING
);
1600 // Add it to our linked list
1602 if (mGlobals
.IncludePaths
== NULL
) {
1603 mGlobals
.IncludePaths
= NewList
;
1605 mGlobals
.LastIncludePath
->Next
= NewList
;
1608 mGlobals
.LastIncludePath
= NewList
;
1611 } else if (stricmp (Argv
[0], "-if") == 0) {
1613 // Indirection file -- check for one more arg
1615 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1616 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing indirection file name");
1617 return STATUS_ERROR
;
1620 // Allocate memory for a new list element, fill it in, and
1621 // add it to our list of include paths. Always make sure it
1622 // has a "\" on the end of it.
1624 NewList
= malloc (sizeof (TEXT_STRING_LIST
));
1625 if (NewList
== NULL
) {
1626 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1627 return STATUS_ERROR
;
1630 memset ((char *) NewList
, 0, sizeof (TEXT_STRING_LIST
));
1631 NewList
->Str
= malloc (strlen (Argv
[1]) + 1);
1632 if (NewList
->Str
== NULL
) {
1634 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1635 return STATUS_ERROR
;
1638 strcpy (NewList
->Str
, Argv
[1]);
1640 // Add it to our linked list
1642 if (mGlobals
.IndirectionFileName
== NULL
) {
1643 mGlobals
.IndirectionFileName
= NewList
;
1645 mGlobals
.LastIndirectionFileName
->Next
= NewList
;
1648 mGlobals
.LastIndirectionFileName
= NewList
;
1651 } else if (stricmp (Argv
[0], "-db") == 0) {
1653 // -db option to specify a database file.
1654 // Check for one more arg (the database file name)
1656 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1657 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing database file name");
1658 return STATUS_ERROR
;
1661 NewList
= malloc (sizeof (TEXT_STRING_LIST
));
1662 if (NewList
== NULL
) {
1663 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1664 return STATUS_ERROR
;
1667 memset ((char *) NewList
, 0, sizeof (TEXT_STRING_LIST
));
1668 NewList
->Str
= malloc (strlen (Argv
[1]) + 1);
1669 if (NewList
->Str
== NULL
) {
1671 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1672 return STATUS_ERROR
;
1675 strcpy (NewList
->Str
, Argv
[1]);
1677 // Add it to our linked list
1679 if (mGlobals
.DatabaseFileName
== NULL
) {
1680 mGlobals
.DatabaseFileName
= NewList
;
1682 mGlobals
.LastDatabaseFileName
->Next
= NewList
;
1685 mGlobals
.LastDatabaseFileName
= NewList
;
1688 } else if (stricmp (Argv
[0], "-ou") == 0) {
1690 // -ou option to specify an output unicode file to
1691 // which we can dump our database.
1693 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1694 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing database dump output file name");
1695 return STATUS_ERROR
;
1698 if (mGlobals
.DumpUFileName
[0] == 0) {
1699 strcpy (mGlobals
.DumpUFileName
, Argv
[1]);
1701 Error (PROGRAM_NAME
, 0, 0, Argv
[1], "-ou option already specified with '%s'", mGlobals
.DumpUFileName
);
1702 return STATUS_ERROR
;
1707 } else if (stricmp (Argv
[0], "-hpk") == 0) {
1709 // -hpk option to create an HII export pack of the input database file
1711 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1712 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing raw string data dump output file name");
1713 return STATUS_ERROR
;
1716 if (mGlobals
.HiiExportPackFileName
[0] == 0) {
1717 strcpy (mGlobals
.HiiExportPackFileName
, Argv
[1]);
1719 Error (PROGRAM_NAME
, 0, 0, Argv
[1], "-or option already specified with '%s'", mGlobals
.HiiExportPackFileName
);
1720 return STATUS_ERROR
;
1725 } else if ((stricmp (Argv
[0], "-?") == 0) || (stricmp (Argv
[0], "-h") == 0)) {
1727 return STATUS_ERROR
;
1728 } else if (stricmp (Argv
[0], "-v") == 0) {
1729 mGlobals
.Verbose
= 1;
1730 } else if (stricmp (Argv
[0], "-vdbw") == 0) {
1731 mGlobals
.VerboseDatabaseWrite
= 1;
1732 } else if (stricmp (Argv
[0], "-vdbr") == 0) {
1733 mGlobals
.VerboseDatabaseRead
= 1;
1734 } else if (stricmp (Argv
[0], "-newdb") == 0) {
1735 mGlobals
.NewDatabase
= 1;
1736 } else if (stricmp (Argv
[0], "-ignorenotfound") == 0) {
1737 mGlobals
.IgnoreNotFound
= 1;
1738 } else if (stricmp (Argv
[0], "-oc") == 0) {
1740 // check for one more arg
1742 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1743 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing output C filename");
1744 return STATUS_ERROR
;
1747 strcpy (mGlobals
.StringCFileName
, Argv
[1]);
1750 } else if (stricmp (Argv
[0], "-bn") == 0) {
1752 // check for one more arg
1754 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1755 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing base name");
1757 return STATUS_ERROR
;
1760 strcpy (mGlobals
.BaseName
, Argv
[1]);
1763 } else if (stricmp (Argv
[0], "-oh") == 0) {
1765 // -oh to specify output .h defines file name
1767 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1768 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing output .h filename");
1769 return STATUS_ERROR
;
1772 strcpy (mGlobals
.StringHFileName
, Argv
[1]);
1775 } else if (stricmp (Argv
[0], "-skipext") == 0) {
1777 // -skipext to skip scanning of files with certain filename extensions
1779 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1780 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing filename extension");
1781 return STATUS_ERROR
;
1784 // Allocate memory for a new list element, fill it in, and
1785 // add it to our list of excluded extensions. Always make sure it
1786 // has a "." as the first character.
1788 NewList
= malloc (sizeof (TEXT_STRING_LIST
));
1789 if (NewList
== NULL
) {
1790 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1791 return STATUS_ERROR
;
1794 memset ((char *) NewList
, 0, sizeof (TEXT_STRING_LIST
));
1795 NewList
->Str
= malloc (strlen (Argv
[1]) + 2);
1796 if (NewList
->Str
== NULL
) {
1798 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1799 return STATUS_ERROR
;
1802 if (Argv
[1][0] == '.') {
1803 strcpy (NewList
->Str
, Argv
[1]);
1805 NewList
->Str
[0] = '.';
1806 strcpy (NewList
->Str
+ 1, Argv
[1]);
1809 // Add it to our linked list
1811 if (mGlobals
.SkipExt
== NULL
) {
1812 mGlobals
.SkipExt
= NewList
;
1814 mGlobals
.LastSkipExt
->Next
= NewList
;
1817 mGlobals
.LastSkipExt
= NewList
;
1820 } else if (stricmp (Argv
[0], "-lang") == 0) {
1822 // "-lang eng" or "-lang spa+cat" to only output certain languages
1824 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1825 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing language name");
1827 return STATUS_ERROR
;
1830 if (AddCommandLineLanguage (Argv
[1]) != STATUS_SUCCESS
) {
1831 return STATUS_ERROR
;
1836 } else if (stricmp (Argv
[0], "-od") == 0) {
1838 // Output database file name -- check for another arg
1840 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1841 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing output database file name");
1842 return STATUS_ERROR
;
1845 strcpy (mGlobals
.OutputDatabaseFileName
, Argv
[1]);
1852 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "unrecognized option");
1854 return STATUS_ERROR
;
1861 // Make sure they specified the mode parse/scan/dump
1863 if (mGlobals
.Mode
== MODE_UNKNOWN
) {
1864 Error (NULL
, 0, 0, "must specify one of -parse/-scan/-dump", NULL
);
1865 return STATUS_ERROR
;
1868 // All modes require a database filename
1870 if (mGlobals
.DatabaseFileName
== 0) {
1871 Error (NULL
, 0, 0, "must specify a database filename using -db DbFileName", NULL
);
1873 return STATUS_ERROR
;
1876 // If dumping the database file, then return immediately if all
1877 // parameters check out.
1879 if (mGlobals
.Mode
== MODE_DUMP
) {
1881 // Not much use if they didn't specify -oh or -oc or -ou or -hpk
1883 if ((mGlobals
.DumpUFileName
[0] == 0) &&
1884 (mGlobals
.StringHFileName
[0] == 0) &&
1885 (mGlobals
.StringCFileName
[0] == 0) &&
1886 (mGlobals
.HiiExportPackFileName
[0] == 0)
1888 Error (NULL
, 0, 0, "-dump without -oc/-oh/-ou/-hpk is a NOP", NULL
);
1889 return STATUS_ERROR
;
1892 return STATUS_SUCCESS
;
1895 // Had to specify source string file and output string defines header filename.
1897 if (mGlobals
.Mode
== MODE_SCAN
) {
1899 Error (PROGRAM_NAME
, 0, 0, NULL
, "must specify at least one source file to scan with -scan");
1901 return STATUS_ERROR
;
1904 // Get the list of filenames
1907 NewList
= malloc (sizeof (TEXT_STRING_LIST
));
1908 if (NewList
== NULL
) {
1909 Error (PROGRAM_NAME
, 0, 0, "memory allocation failure", NULL
);
1910 return STATUS_ERROR
;
1913 memset (NewList
, 0, sizeof (TEXT_STRING_LIST
));
1914 NewList
->Str
= (CHAR8
*) malloc (strlen (Argv
[0]) + 1);
1915 if (NewList
->Str
== NULL
) {
1916 Error (PROGRAM_NAME
, 0, 0, "memory allocation failure", NULL
);
1917 return STATUS_ERROR
;
1920 strcpy (NewList
->Str
, Argv
[0]);
1921 if (mGlobals
.ScanFileName
== NULL
) {
1922 mGlobals
.ScanFileName
= NewList
;
1924 mGlobals
.LastScanFileName
->Next
= NewList
;
1927 mGlobals
.LastScanFileName
= NewList
;
1933 // Parse mode -- must specify an input unicode file name
1936 Error (PROGRAM_NAME
, 0, 0, NULL
, "must specify input unicode string file name with -parse");
1938 return STATUS_ERROR
;
1941 strcpy (mGlobals
.SourceFiles
.FileName
, Argv
[0]);
1944 return STATUS_SUCCESS
;
1947 // Found "-lang eng,spa+cat" on the command line. Parse the
1948 // language list and save the setting for later processing.
1952 AddCommandLineLanguage (
1956 WCHAR_STRING_LIST
*WNewList
;
1960 // Keep processing the input string until we find the end.
1964 // Allocate memory for a new list element, fill it in, and
1965 // add it to our list.
1967 WNewList
= MALLOC (sizeof (WCHAR_STRING_LIST
));
1968 if (WNewList
== NULL
) {
1969 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1970 return STATUS_ERROR
;
1973 memset ((char *) WNewList
, 0, sizeof (WCHAR_STRING_LIST
));
1974 WNewList
->Str
= malloc ((strlen (Language
) + 1) * sizeof (WCHAR
));
1975 if (WNewList
->Str
== NULL
) {
1977 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1978 return STATUS_ERROR
;
1981 // Copy it as unicode to our new structure. Then remove the
1982 // plus signs in it, and verify each language name is 3 characters
1983 // long. If we find a comma, then we're done with this group, so
1986 UnicodeSPrint (WNewList
->Str
, (strlen (Language
) + 1) * sizeof (WCHAR
), L
"%a", Language
);
1987 From
= To
= WNewList
->Str
;
1989 if (*From
== L
',') {
1993 if ((StrLen (From
) < LANGUAGE_IDENTIFIER_NAME_LEN
) ||
1995 (From
[LANGUAGE_IDENTIFIER_NAME_LEN
] != 0) &&
1996 (From
[LANGUAGE_IDENTIFIER_NAME_LEN
] != UNICODE_PLUS_SIGN
) &&
1997 (From
[LANGUAGE_IDENTIFIER_NAME_LEN
] != L
',')
2000 Error (PROGRAM_NAME
, 0, 0, Language
, "invalid format for language name on command line");
2001 FREE (WNewList
->Str
);
2003 return STATUS_ERROR
;
2006 StrnCpy (To
, From
, LANGUAGE_IDENTIFIER_NAME_LEN
);
2007 To
+= LANGUAGE_IDENTIFIER_NAME_LEN
;
2008 From
+= LANGUAGE_IDENTIFIER_NAME_LEN
;
2009 if (*From
== L
'+') {
2016 // Add it to our linked list
2018 if (mGlobals
.Language
== NULL
) {
2019 mGlobals
.Language
= WNewList
;
2021 mGlobals
.LastLanguage
->Next
= WNewList
;
2024 mGlobals
.LastLanguage
= WNewList
;
2026 // Skip to next entry (comma-separated list)
2029 if (*Language
== L
',') {
2038 return STATUS_SUCCESS
;
2041 // The contents of the text file are expected to be (one per line)
2042 // STRING_IDENTIFIER_NAME ScopeName
2044 // STR_ID_MY_FAVORITE_STRING IBM
2048 ParseIndirectionFiles (
2049 TEXT_STRING_LIST
*Files
2058 WCHAR_MATCHING_STRING_LIST
*NewList
;
2060 Line
[sizeof (Line
) - 1] = 0;
2062 while (Files
!= NULL
) {
2063 Fptr
= fopen (Files
->Str
, "r");
2066 Error (NULL
, 0, 0, Files
->Str
, "failed to open input indirection file for reading");
2067 return STATUS_ERROR
;
2070 while (fgets (Line
, sizeof (Line
), Fptr
) != NULL
) {
2072 // remove terminating newline for error printing purposes.
2074 if (Line
[strlen (Line
) - 1] == '\n') {
2075 Line
[strlen (Line
) - 1] = 0;
2079 if (Line
[sizeof (Line
) - 1] != 0) {
2080 Error (Files
->Str
, LineCount
, 0, "line length exceeds maximum supported", NULL
);
2085 while (*StringName
&& (isspace (*StringName
))) {
2090 if ((*StringName
== '_') || isalpha (*StringName
)) {
2092 while ((*End
) && (*End
== '_') || (isalnum (*End
))) {
2096 if (isspace (*End
)) {
2099 while (isspace (*End
)) {
2105 while (*End
&& !isspace (*End
)) {
2111 // Add the string name/scope pair
2113 NewList
= malloc (sizeof (WCHAR_MATCHING_STRING_LIST
));
2114 if (NewList
== NULL
) {
2115 Error (NULL
, 0, 0, "memory allocation error", NULL
);
2119 memset (NewList
, 0, sizeof (WCHAR_MATCHING_STRING_LIST
));
2120 NewList
->Str1
= (WCHAR
*) malloc ((strlen (StringName
) + 1) * sizeof (WCHAR
));
2121 NewList
->Str2
= (WCHAR
*) malloc ((strlen (ScopeName
) + 1) * sizeof (WCHAR
));
2122 if ((NewList
->Str1
== NULL
) || (NewList
->Str2
== NULL
)) {
2123 Error (NULL
, 0, 0, "memory allocation error", NULL
);
2127 UnicodeSPrint (NewList
->Str1
, strlen (StringName
) + 1, L
"%a", StringName
);
2128 UnicodeSPrint (NewList
->Str2
, strlen (ScopeName
) + 1, L
"%a", ScopeName
);
2129 if (mGlobals
.IndirectionList
== NULL
) {
2130 mGlobals
.IndirectionList
= NewList
;
2132 mGlobals
.LastIndirectionList
->Next
= NewList
;
2135 mGlobals
.LastIndirectionList
= NewList
;
2137 Error (Files
->Str
, LineCount
, 0, StringName
, "invalid line : expected 'StringIdentifier Scope'");
2141 Error (Files
->Str
, LineCount
, 0, StringName
, "invalid line : expected 'StringIdentifier Scope'");
2145 Error (Files
->Str
, LineCount
, 0, StringName
, "invalid string identifier");
2153 Files
= Files
->Next
;
2159 return STATUS_ERROR
;
2162 return STATUS_SUCCESS
;
2168 TEXT_STRING_LIST
*ScanFiles
2171 char Line
[MAX_LINE_LEN
];
2177 char *StringTokenPos
;
2178 TEXT_STRING_LIST
*SList
;
2182 // Put a null-terminator at the end of the line. If we read in
2183 // a line longer than we support, then we can catch it.
2185 Line
[MAX_LINE_LEN
- 1] = 0;
2187 // Process each file. If they gave us a skip extension list, then
2188 // skip it if the extension matches.
2190 while (ScanFiles
!= NULL
) {
2192 for (SList
= mGlobals
.SkipExt
; SList
!= NULL
; SList
= SList
->Next
) {
2193 if ((strlen (ScanFiles
->Str
) > strlen (SList
->Str
)) &&
2194 (strcmp (ScanFiles
->Str
+ strlen (ScanFiles
->Str
) - strlen (SList
->Str
), SList
->Str
) == 0)
2198 // printf ("Match: %s : %s\n", ScanFiles->Str, SList->Str);
2205 if (mGlobals
.VerboseScan
) {
2206 printf ("Scanning %s\n", ScanFiles
->Str
);
2209 Fptr
= fopen (ScanFiles
->Str
, "r");
2211 Error (NULL
, 0, 0, ScanFiles
->Str
, "failed to open input file for scanning");
2212 return STATUS_ERROR
;
2216 while (fgets (Line
, sizeof (Line
), Fptr
) != NULL
) {
2218 if (Line
[MAX_LINE_LEN
- 1] != 0) {
2219 Error (ScanFiles
->Str
, LineNum
, 0, "line length exceeds maximum supported by tool", NULL
);
2221 return STATUS_ERROR
;
2224 // Remove the newline from the input line so we can print a warning message
2226 if (Line
[strlen (Line
) - 1] == '\n') {
2227 Line
[strlen (Line
) - 1] = 0;
2230 // Terminate the line at // comments
2232 Cptr
= strstr (Line
, "//");
2238 while ((Cptr
= strstr (Cptr
, STRING_TOKEN
)) != NULL
) {
2240 // Found "STRING_TOKEN". Make sure we don't have NUM_STRING_TOKENS or
2241 // something like that. Then make sure it's followed by
2242 // an open parenthesis, a string identifier, and then a closing
2245 if (mGlobals
.VerboseScan
) {
2246 printf (" %d: %s", LineNum
, Cptr
);
2249 if (((Cptr
== Line
) || (!IsValidIdentifierChar (*(Cptr
- 1), FALSE
))) &&
2250 (!IsValidIdentifierChar (*(Cptr
+ sizeof (STRING_TOKEN
) - 1), FALSE
))
2252 StringTokenPos
= Cptr
;
2254 Cptr
+= strlen (STRING_TOKEN
);
2255 while (*Cptr
&& isspace (*Cptr
) && (*Cptr
!= '(')) {
2260 Warning (ScanFiles
->Str
, LineNum
, 0, StringTokenPos
, "expected "STRING_TOKEN
"(identifier)");
2263 // Skip over the open-parenthesis and find the next non-blank character
2266 while (isspace (*Cptr
)) {
2271 if ((*Cptr
== '_') || isalpha (*Cptr
)) {
2272 while ((*Cptr
== '_') || (isalnum (*Cptr
))) {
2277 while (*Cptr
&& isspace (*Cptr
)) {
2282 Warning (ScanFiles
->Str
, LineNum
, 0, StringTokenPos
, "expected "STRING_TOKEN
"(identifier)");
2292 // Add the string identifier to the list of used strings
2294 ParserSetPosition (ScanFiles
->Str
, LineNum
);
2295 StringDBSetStringReferenced (SavePtr
, mGlobals
.IgnoreNotFound
);
2296 if (mGlobals
.VerboseScan
) {
2297 printf ("...referenced %s", SavePtr
);
2300 Warning (ScanFiles
->Str
, LineNum
, 0, StringTokenPos
, "expected valid string identifier name");
2305 // Found it, but it's a substring of something else. Advance our pointer.
2310 if (mGlobals
.VerboseScan
) {
2319 // Skipping this file type
2321 if (mGlobals
.VerboseScan
) {
2322 printf ("Skip scanning of %s\n", ScanFiles
->Str
);
2326 ScanFiles
= ScanFiles
->Next
;
2329 return STATUS_SUCCESS
;
2332 // Free the global string lists we allocated memory for
2340 TEXT_STRING_LIST
*Temp
;
2341 WCHAR_STRING_LIST
*WTemp
;
2344 // Traverse the include paths, freeing each
2346 while (mGlobals
.IncludePaths
!= NULL
) {
2347 Temp
= mGlobals
.IncludePaths
->Next
;
2348 free (mGlobals
.IncludePaths
->Str
);
2349 free (mGlobals
.IncludePaths
);
2350 mGlobals
.IncludePaths
= Temp
;
2353 // If we did a scan, then free up our
2354 // list of files to scan.
2356 while (mGlobals
.ScanFileName
!= NULL
) {
2357 Temp
= mGlobals
.ScanFileName
->Next
;
2358 free (mGlobals
.ScanFileName
->Str
);
2359 free (mGlobals
.ScanFileName
);
2360 mGlobals
.ScanFileName
= Temp
;
2363 // If they gave us a list of filename extensions to
2364 // skip on scan, then free them up.
2366 while (mGlobals
.SkipExt
!= NULL
) {
2367 Temp
= mGlobals
.SkipExt
->Next
;
2368 free (mGlobals
.SkipExt
->Str
);
2369 free (mGlobals
.SkipExt
);
2370 mGlobals
.SkipExt
= Temp
;
2373 // Free up any languages specified
2375 while (mGlobals
.Language
!= NULL
) {
2376 WTemp
= mGlobals
.Language
->Next
;
2377 free (mGlobals
.Language
->Str
);
2378 free (mGlobals
.Language
);
2379 mGlobals
.Language
= WTemp
;
2382 // Free up our indirection list
2384 while (mGlobals
.IndirectionList
!= NULL
) {
2385 mGlobals
.LastIndirectionList
= mGlobals
.IndirectionList
->Next
;
2386 free (mGlobals
.IndirectionList
->Str1
);
2387 free (mGlobals
.IndirectionList
->Str2
);
2388 free (mGlobals
.IndirectionList
);
2389 mGlobals
.IndirectionList
= mGlobals
.LastIndirectionList
;
2392 while (mGlobals
.IndirectionFileName
!= NULL
) {
2393 mGlobals
.LastIndirectionFileName
= mGlobals
.IndirectionFileName
->Next
;
2394 free (mGlobals
.IndirectionFileName
->Str
);
2395 free (mGlobals
.IndirectionFileName
);
2396 mGlobals
.IndirectionFileName
= mGlobals
.LastIndirectionFileName
;
2402 IsValidIdentifierChar (
2408 // If it's the first character of an identifier, then
2409 // it must be one of [A-Za-z_].
2412 if (isalpha (Char
) || (Char
== '_')) {
2417 // If it's not the first character, then it can
2418 // be one of [A-Za-z_0-9]
2420 if (isalnum (Char
) || (Char
== '_')) {
2431 SOURCE_FILE
*SourceFile
2434 SourceFile
->LineNum
= 1;
2435 SourceFile
->FileBufferPtr
= SourceFile
->FileBuffer
;
2436 SourceFile
->EndOfFile
= 0;
2442 SOURCE_FILE
*SourceFile
,
2444 BOOLEAN StopAfterNewline
2447 while (!EndOfFile (SourceFile
)) {
2449 // Check for the character of interest
2451 if (SourceFile
->FileBufferPtr
[0] == WChar
) {
2454 if (SourceFile
->FileBufferPtr
[0] == UNICODE_LF
) {
2455 SourceFile
->LineNum
++;
2456 if (StopAfterNewline
) {
2457 SourceFile
->FileBufferPtr
++;
2458 if (SourceFile
->FileBufferPtr
[0] == 0) {
2459 SourceFile
->FileBufferPtr
++;
2466 SourceFile
->FileBufferPtr
++;
2480 Routine Description:
2482 Print usage information for this utility.
2495 static const char *Str
[] = {
2497 PROGRAM_NAME
" version "TOOL_VERSION
" -- process unicode strings file",
2498 " Usage: "PROGRAM_NAME
" -parse {parse options} [FileNames]",
2499 " "PROGRAM_NAME
" -scan {scan options} [FileName]",
2500 " "PROGRAM_NAME
" -dump {dump options}",
2501 " Common options include:",
2502 " -h or -? for this help information",
2503 " -db Database required name of output/input database file",
2504 " -bn BaseName for use in the .h and .c output files",
2505 " Default = "DEFAULT_BASE_NAME
,
2506 " -v for verbose output",
2507 " -vdbw for verbose output when writing database",
2508 " -vdbr for verbose output when reading database",
2509 " -od FileName to specify an output database file name",
2510 " Parse options include:",
2511 " -i IncludePath add IncludePath to list of search paths",
2512 " -newdb to not read in existing database file",
2513 " -uqs to indicate that unquoted strings are used",
2514 " FileNames name of one or more unicode files to parse",
2515 " Scan options include:",
2516 " -scan scan text file(s) for STRING_TOKEN() usage",
2517 " -skipext .ext to skip scan of files with .ext filename extension",
2518 " -ignorenotfound ignore if a given STRING_TOKEN(STR) is not ",
2519 " found in the database",
2520 " FileNames one or more files to scan",
2521 " Dump options include:",
2522 " -oc FileName write string data to FileName",
2523 " -oh FileName write string defines to FileName",
2524 " -ou FileName dump database to unicode file FileName",
2525 " -lang Lang only dump for the language 'Lang'",
2526 " -if FileName to specify an indirection file",
2527 " -hpk FileName to create an HII export pack of the strings",
2529 " The expected process is to parse a unicode string file to create an initial",
2530 " database of string identifier names and string definitions. Then text files",
2531 " should be scanned for STRING_TOKEN() usages, and the referenced",
2532 " strings will be tagged as used in the database. After all files have been",
2533 " scanned, then the database should be dumped to create the necessary output",
2538 for (Index
= 0; Str
[Index
] != NULL
; Index
++) {
2539 fprintf (stdout
, "%s\n", Str
[Index
]);