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
53 // We keep a linked list of these for the source files we process
55 typedef struct _SOURCE_FILE
{
60 CHAR8 FileName
[MAX_PATH
];
64 struct _SOURCE_FILE
*Previous
;
65 struct _SOURCE_FILE
*Next
;
66 WCHAR ControlCharacter
;
69 #define DEFAULT_CONTROL_CHARACTER UNICODE_SLASH
72 // Here's all our globals. We need a linked list of include paths, a linked
73 // list of source files, a linked list of subdirectories (appended to each
74 // include path when searching), and a couple other fields.
77 SOURCE_FILE SourceFiles
;
78 TEXT_STRING_LIST
*IncludePaths
; // all include paths to search
79 TEXT_STRING_LIST
*LastIncludePath
;
80 TEXT_STRING_LIST
*ScanFileName
;
81 TEXT_STRING_LIST
*LastScanFileName
;
82 TEXT_STRING_LIST
*SkipExt
; // if -skipext .uni
83 TEXT_STRING_LIST
*LastSkipExt
;
84 TEXT_STRING_LIST
*IndirectionFileName
;
85 TEXT_STRING_LIST
*LastIndirectionFileName
;
86 TEXT_STRING_LIST
*DatabaseFileName
;
87 TEXT_STRING_LIST
*LastDatabaseFileName
;
88 WCHAR_STRING_LIST
*Language
;
89 WCHAR_STRING_LIST
*LastLanguage
;
90 WCHAR_MATCHING_STRING_LIST
*IndirectionList
; // from indirection file(s)
91 WCHAR_MATCHING_STRING_LIST
*LastIndirectionList
;
92 BOOLEAN Verbose
; // for more detailed output
93 BOOLEAN VerboseDatabaseWrite
; // for more detailed output when writing database
94 BOOLEAN VerboseDatabaseRead
; // for more detailed output when reading database
95 BOOLEAN NewDatabase
; // to start from scratch
96 BOOLEAN IgnoreNotFound
; // when scanning
98 BOOLEAN UnquotedStrings
; // -uqs option
99 CHAR8 OutputDatabaseFileName
[MAX_PATH
];
100 CHAR8 StringHFileName
[MAX_PATH
];
101 CHAR8 StringCFileName
[MAX_PATH
]; // output .C filename
102 CHAR8 DumpUFileName
[MAX_PATH
]; // output unicode dump file name
103 CHAR8 HiiExportPackFileName
[MAX_PATH
]; // HII export pack file name
104 CHAR8 BaseName
[MAX_PATH
]; // base filename of the strings file
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 CHAR8
*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 (
286 CHAR8
*OutputFileName
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 Status
= ProcessIncludeFile (&mGlobals
.SourceFiles
, NULL
);
361 if (Status
!= STATUS_SUCCESS
) {
366 // Create the string defines header file if there have been no errors.
368 ParserSetPosition (NULL
, 0);
369 if ((mGlobals
.StringHFileName
[0] != 0) && (GetUtilityStatus () < STATUS_ERROR
)) {
370 Status
= StringDBDumpStringDefines (mGlobals
.StringHFileName
, mGlobals
.BaseName
);
371 if (Status
!= EFI_SUCCESS
) {
376 // Dump the strings to a .c file if there have still been no errors.
378 if ((mGlobals
.StringCFileName
[0] != 0) && (GetUtilityStatus () < STATUS_ERROR
)) {
379 Status
= StringDBDumpCStrings (
380 mGlobals
.StringCFileName
,
383 mGlobals
.IndirectionList
385 if (Status
!= EFI_SUCCESS
) {
390 // Dump the database if requested
392 if ((mGlobals
.DumpUFileName
[0] != 0) && (GetUtilityStatus () < STATUS_ERROR
)) {
393 StringDBDumpDatabase (NULL
, mGlobals
.DumpUFileName
, FALSE
);
396 // Dump the string data as HII binary string pack if requested
398 if ((mGlobals
.HiiExportPackFileName
[0] != 0) && (GetUtilityStatus () < STATUS_ERROR
)) {
399 StringDBCreateHiiExportPack (mGlobals
.HiiExportPackFileName
);
402 // Always update the database if no errors and not in dump mode. If they specified -od
403 // for an output database file name, then use that name. Otherwise use the name of
404 // the first database file specified with -db
406 if ((mGlobals
.Mode
!= MODE_DUMP
) && (GetUtilityStatus () < STATUS_ERROR
)) {
407 if (mGlobals
.OutputDatabaseFileName
[0]) {
408 Status
= StringDBWriteDatabase (mGlobals
.OutputDatabaseFileName
, mGlobals
.VerboseDatabaseWrite
);
410 Status
= StringDBWriteDatabase (mGlobals
.DatabaseFileName
->Str
, mGlobals
.VerboseDatabaseWrite
);
413 if (Status
!= EFI_SUCCESS
) {
423 StringDBDestructor ();
424 return GetUtilityStatus ();
430 SOURCE_FILE
*SourceFile
,
431 SOURCE_FILE
*ParentSourceFile
437 Given a source file, open the file and parse it
441 SourceFile - name of file to parse
442 ParentSourceFile - for error reporting purposes, the file that #included SourceFile.
450 static UINT32 NestDepth
= 0;
451 CHAR8 FoundFileName
[MAX_PATH
];
454 Status
= STATUS_SUCCESS
;
457 // Print the file being processed. Indent so you can tell the include nesting
460 if (mGlobals
.Verbose
) {
461 fprintf (stdout
, "%*cProcessing file '%s'\n", NestDepth
* 2, ' ', SourceFile
->FileName
);
465 // Make sure we didn't exceed our maximum nesting depth
467 if (NestDepth
> MAX_NEST_DEPTH
) {
468 Error (NULL
, 0, 0, SourceFile
->FileName
, "max nesting depth (%d) exceeded", NestDepth
);
469 Status
= STATUS_ERROR
;
473 // Try to open the file locally, and if that fails try along our include paths.
475 strcpy (FoundFileName
, SourceFile
->FileName
);
476 if ((SourceFile
->Fptr
= fopen (FoundFileName
, "rb")) == NULL
) {
478 // Try to find it among the paths if it has a parent (that is, it is included
481 if (ParentSourceFile
== NULL
) {
482 Error (NULL
, 0, 0, SourceFile
->FileName
, "file not found");
486 SourceFile
->Fptr
= FindFile (SourceFile
->FileName
, FoundFileName
, sizeof (FoundFileName
));
487 if (SourceFile
->Fptr
== NULL
) {
488 Error (ParentSourceFile
->FileName
, ParentSourceFile
->LineNum
, 0, SourceFile
->FileName
, "include file not found");
493 // Process the file found
495 ProcessFile (SourceFile
);
498 // Close open files and return status
500 if (SourceFile
->Fptr
!= NULL
) {
501 fclose (SourceFile
->Fptr
);
510 SOURCE_FILE
*SourceFile
514 // Get the file size, and then read the entire thing into memory.
515 // Allocate space for a terminator character.
517 fseek (SourceFile
->Fptr
, 0, SEEK_END
);
518 SourceFile
->FileSize
= ftell (SourceFile
->Fptr
);
519 fseek (SourceFile
->Fptr
, 0, SEEK_SET
);
520 SourceFile
->FileBuffer
= (WCHAR
*) malloc (SourceFile
->FileSize
+ sizeof (WCHAR
));
521 if (SourceFile
->FileBuffer
== NULL
) {
522 Error (NULL
, 0, 0, "memory allocation failure", NULL
);
526 fread ((VOID
*) SourceFile
->FileBuffer
, SourceFile
->FileSize
, 1, SourceFile
->Fptr
);
527 SourceFile
->FileBuffer
[(SourceFile
->FileSize
/ sizeof (WCHAR
))] = UNICODE_NULL
;
529 // Pre-process the file to replace comments with spaces
531 PreprocessFile (SourceFile
);
535 ParseFile (SourceFile
);
536 free (SourceFile
->FileBuffer
);
537 return STATUS_SUCCESS
;
543 SOURCE_FILE
*SourceFile
550 // First character of a unicode file is special. Make sure
552 if (SourceFile
->FileBufferPtr
[0] != UNICODE_FILE_START
) {
553 Error (SourceFile
->FileName
, 1, 0, SourceFile
->FileName
, "file does not appear to be a unicode file");
557 SourceFile
->FileBufferPtr
++;
560 // Print the first line if in verbose mode
562 if (mGlobals
.Verbose
) {
563 printf ("%d: %S\n", SourceFile
->LineNum
, SourceFile
->FileBufferPtr
);
566 // Since the syntax is relatively straightforward, just switch on the next char
568 while (!EndOfFile (SourceFile
)) {
570 // Check for whitespace
572 if (SourceFile
->FileBufferPtr
[0] == UNICODE_SPACE
) {
573 SourceFile
->FileBufferPtr
++;
574 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_TAB
) {
575 SourceFile
->FileBufferPtr
++;
576 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_CR
) {
577 SourceFile
->FileBufferPtr
++;
578 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_LF
) {
579 SourceFile
->FileBufferPtr
++;
580 SourceFile
->LineNum
++;
581 if (mGlobals
.Verbose
) {
582 printf ("%d: %S\n", SourceFile
->LineNum
, SourceFile
->FileBufferPtr
);
586 } else if (SourceFile
->FileBufferPtr
[0] == 0) {
587 SourceFile
->FileBufferPtr
++;
588 } else if (InComment
) {
589 SourceFile
->FileBufferPtr
++;
590 } else if ((SourceFile
->FileBufferPtr
[0] == UNICODE_SLASH
) && (SourceFile
->FileBufferPtr
[1] == UNICODE_SLASH
)) {
591 SourceFile
->FileBufferPtr
+= 2;
593 } else if (SourceFile
->SkipToHash
&& (SourceFile
->FileBufferPtr
[0] != SourceFile
->ControlCharacter
)) {
594 SourceFile
->FileBufferPtr
++;
596 SourceFile
->SkipToHash
= FALSE
;
597 if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
598 ((Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"include")) > 0)
600 SourceFile
->FileBufferPtr
+= Len
+ 1;
601 ProcessTokenInclude (SourceFile
);
602 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
603 (Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"scope")) > 0
605 SourceFile
->FileBufferPtr
+= Len
+ 1;
606 ProcessTokenScope (SourceFile
);
607 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
608 (Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"language")) > 0
610 SourceFile
->FileBufferPtr
+= Len
+ 1;
611 ProcessTokenLanguage (SourceFile
);
612 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
613 (Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"langdef")) > 0
615 SourceFile
->FileBufferPtr
+= Len
+ 1;
616 ProcessTokenLangDef (SourceFile
);
617 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
618 (Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"string")) > 0
620 SourceFile
->FileBufferPtr
+= Len
+ 1;
621 ProcessTokenString (SourceFile
);
622 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
623 (Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"EFI_BREAKPOINT()")) > 0
625 SourceFile
->FileBufferPtr
+= Len
;
627 // BUGBUG: Caling EFI_BREAKOINT() is breaking the link. What is the proper action for this tool
628 // in this condition?
630 // EFI_BREAKPOINT ();
631 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
632 (SourceFile
->FileBufferPtr
[1] == UNICODE_EQUAL_SIGN
)
634 SourceFile
->ControlCharacter
= SourceFile
->FileBufferPtr
[2];
635 SourceFile
->FileBufferPtr
+= 3;
637 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "unrecognized token", "%S", SourceFile
->FileBufferPtr
);
639 // Treat rest of line as a comment.
646 return STATUS_SUCCESS
;
652 SOURCE_FILE
*SourceFile
657 Preprocess a file to replace all carriage returns with NULLs so
658 we can print lines from the file to the screen.
661 SourceFile - structure that we use to keep track of an input file.
670 RewindFile (SourceFile
);
672 while (!EndOfFile (SourceFile
)) {
674 // If a line-feed, then no longer in a comment
676 if (SourceFile
->FileBufferPtr
[0] == UNICODE_LF
) {
677 SourceFile
->FileBufferPtr
++;
678 SourceFile
->LineNum
++;
680 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_CR
) {
682 // Replace all carriage returns with a NULL so we can print stuff
684 SourceFile
->FileBufferPtr
[0] = 0;
685 SourceFile
->FileBufferPtr
++;
686 } else if (InComment
) {
687 SourceFile
->FileBufferPtr
[0] = UNICODE_SPACE
;
688 SourceFile
->FileBufferPtr
++;
689 } else if ((SourceFile
->FileBufferPtr
[0] == UNICODE_SLASH
) && (SourceFile
->FileBufferPtr
[1] == UNICODE_SLASH
)) {
690 SourceFile
->FileBufferPtr
+= 2;
693 SourceFile
->FileBufferPtr
++;
697 // Could check for end-of-file and still in a comment, but
698 // should not be necessary. So just restore the file pointers.
700 RewindFile (SourceFile
);
705 GetPrintableLanguageName (
706 IN SOURCE_FILE
*SourceFile
714 SkipWhiteSpace (SourceFile
);
715 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
717 SourceFile
->FileName
,
720 "expected quoted printable language name",
722 SourceFile
->FileBufferPtr
724 SourceFile
->SkipToHash
= TRUE
;
729 SourceFile
->FileBufferPtr
++;
730 Start
= Ptr
= SourceFile
->FileBufferPtr
;
731 while (!EndOfFile (SourceFile
)) {
732 if (SourceFile
->FileBufferPtr
[0] == UNICODE_CR
) {
733 Warning (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "carriage return found in quoted string", "%S", Start
);
735 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_DOUBLE_QUOTE
) {
739 SourceFile
->FileBufferPtr
++;
743 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
745 SourceFile
->FileName
,
748 "missing closing quote on printable language name string",
753 SourceFile
->FileBufferPtr
++;
756 // Now allocate memory for the string and save it off
758 String
= (WCHAR
*) malloc ((Len
+ 1) * sizeof (WCHAR
));
759 if (String
== NULL
) {
760 Error (NULL
, 0, 0, "memory allocation failed", NULL
);
764 // Copy the string from the file buffer to the local copy.
765 // We do no reformatting of it whatsoever at this point.
777 // Now format the string to convert \wide and \narrow controls
779 StringDBFormatString (String
);
786 SOURCE_FILE
*SourceFile
,
794 BOOLEAN PreviousBackslash
;
796 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
798 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "expected quoted string", "%S", SourceFile
->FileBufferPtr
);
805 SourceFile
->FileBufferPtr
++;
806 Start
= Ptr
= SourceFile
->FileBufferPtr
;
807 PreviousBackslash
= FALSE
;
808 while (!EndOfFile (SourceFile
)) {
809 if ((SourceFile
->FileBufferPtr
[0] == UNICODE_DOUBLE_QUOTE
) && (!PreviousBackslash
)) {
811 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_CR
) {
812 Warning (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "carriage return found in quoted string", "%S", Start
);
813 PreviousBackslash
= FALSE
;
814 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_BACKSLASH
) {
815 PreviousBackslash
= TRUE
;
817 PreviousBackslash
= FALSE
;
820 SourceFile
->FileBufferPtr
++;
824 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
825 Warning (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "missing closing quote on string", "%S", Start
);
827 SourceFile
->FileBufferPtr
++;
830 // Now allocate memory for the string and save it off
832 String
= (WCHAR
*) malloc ((Len
+ 1) * sizeof (WCHAR
));
833 if (String
== NULL
) {
834 Error (NULL
, 0, 0, "memory allocation failed", NULL
);
838 // Copy the string from the file buffer to the local copy.
839 // We do no reformatting of it whatsoever at this point.
854 // #string STR_ID_NAME
856 // All we can do is call the string database to add the string identifier. Unfortunately
857 // he'll have to keep track of the last identifier we added.
862 SOURCE_FILE
*SourceFile
865 WCHAR StringIdentifier
[MAX_STRING_IDENTIFIER_NAME
];
868 // Extract the string identifier name and add it to the database.
870 if (GetStringIdentifierName (SourceFile
, StringIdentifier
, sizeof (StringIdentifier
)) > 0) {
871 StringId
= STRING_ID_INVALID
;
872 StringDBAddStringIdentifier (StringIdentifier
, &StringId
, 0);
875 // Error recovery -- skip to the next #
877 SourceFile
->SkipToHash
= TRUE
;
884 SOURCE_FILE
*SourceFile
888 // The file buffer pointer will typically get updated before the End-of-file flag in the
889 // source file structure, so check it first.
891 if (SourceFile
->FileBufferPtr
>= SourceFile
->FileBuffer
+ SourceFile
->FileSize
/ sizeof (WCHAR
)) {
892 SourceFile
->EndOfFile
= TRUE
;
896 if (SourceFile
->EndOfFile
) {
905 GetStringIdentifierName (
906 IN SOURCE_FILE
*SourceFile
,
907 IN OUT WCHAR
*StringIdentifierName
,
908 IN UINT32 StringIdentifierNameLen
918 SkipWhiteSpace (SourceFile
);
919 if (SourceFile
->EndOfFile
) {
920 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "end-of-file encountered", "expected string identifier");
924 // Verify first character of name is [A-Za-z]
927 StringIdentifierNameLen
/= 2;
928 From
= SourceFile
->FileBufferPtr
;
929 Start
= SourceFile
->FileBufferPtr
;
930 if (((SourceFile
->FileBufferPtr
[0] >= UNICODE_A
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_Z
)) ||
931 ((SourceFile
->FileBufferPtr
[0] >= UNICODE_z
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_z
))
937 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "invalid character in string identifier name", "%S", Start
);
941 while (!EndOfFile (SourceFile
)) {
942 if (((SourceFile
->FileBufferPtr
[0] >= UNICODE_A
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_Z
)) ||
943 ((SourceFile
->FileBufferPtr
[0] >= UNICODE_z
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_z
)) ||
944 ((SourceFile
->FileBufferPtr
[0] >= UNICODE_0
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_9
)) ||
945 (SourceFile
->FileBufferPtr
[0] == UNICODE_UNDERSCORE
)
948 if (Len
>= StringIdentifierNameLen
) {
949 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "string identifier name too long", "%S", Start
);
953 *StringIdentifierName
= SourceFile
->FileBufferPtr
[0];
954 StringIdentifierName
++;
955 SourceFile
->FileBufferPtr
++;
956 } else if (SkipWhiteSpace (SourceFile
) == 0) {
957 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "invalid string identifier name", "%S", Start
);
964 // Terminate the copy of the string.
966 *StringIdentifierName
= 0;
972 GetLanguageIdentifierName (
973 IN SOURCE_FILE
*SourceFile
,
974 IN OUT WCHAR
*LanguageIdentifierName
,
975 IN UINT32 LanguageIdentifierNameLen
,
985 SkipWhiteSpace (SourceFile
);
986 if (SourceFile
->EndOfFile
) {
989 SourceFile
->FileName
,
992 "end-of-file encountered",
993 "expected language identifier"
1000 // This function is called to optionally get a language identifier name in:
1001 // #string STR_ID eng "the string"
1002 // If it's optional, and we find a double-quote, then return now.
1005 if (*SourceFile
->FileBufferPtr
== UNICODE_DOUBLE_QUOTE
) {
1011 LanguageIdentifierNameLen
/= 2;
1013 // Internal error if we weren't given at least 4 WCHAR's to work with.
1015 if (LanguageIdentifierNameLen
< LANGUAGE_IDENTIFIER_NAME_LEN
+ 1) {
1017 SourceFile
->FileName
,
1018 SourceFile
->LineNum
,
1020 "app error -- language identifier name length is invalid",
1025 From
= SourceFile
->FileBufferPtr
;
1026 Start
= SourceFile
->FileBufferPtr
;
1027 while (!EndOfFile (SourceFile
)) {
1028 if (((SourceFile
->FileBufferPtr
[0] >= UNICODE_a
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_z
))) {
1030 if (Len
> LANGUAGE_IDENTIFIER_NAME_LEN
) {
1031 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "language identifier name too long", "%S", Start
);
1035 *LanguageIdentifierName
= SourceFile
->FileBufferPtr
[0];
1036 SourceFile
->FileBufferPtr
++;
1037 LanguageIdentifierName
++;
1038 } else if (!IsWhiteSpace (SourceFile
)) {
1039 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "invalid language identifier name", "%S", Start
);
1046 // Terminate the copy of the string.
1048 *LanguageIdentifierName
= 0;
1054 ProcessTokenInclude (
1055 SOURCE_FILE
*SourceFile
1058 CHAR8 IncludeFileName
[MAX_PATH
];
1061 BOOLEAN ReportedError
;
1062 SOURCE_FILE IncludedSourceFile
;
1064 ReportedError
= FALSE
;
1065 if (SkipWhiteSpace (SourceFile
) == 0) {
1066 Warning (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "expected whitespace following #include keyword", NULL
);
1069 // Should be quoted file name
1071 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
1072 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "expected quoted include file name", NULL
);
1076 SourceFile
->FileBufferPtr
++;
1078 // Copy the filename as ascii to our local string
1080 To
= IncludeFileName
;
1082 while (!EndOfFile (SourceFile
)) {
1083 if ((SourceFile
->FileBufferPtr
[0] == UNICODE_CR
) || (SourceFile
->FileBufferPtr
[0] == UNICODE_LF
)) {
1084 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "end-of-line found in quoted include file name", NULL
);
1088 if (SourceFile
->FileBufferPtr
[0] == UNICODE_DOUBLE_QUOTE
) {
1089 SourceFile
->FileBufferPtr
++;
1093 // If too long, then report the error once and process until the closing quote
1096 if (!ReportedError
&& (Len
>= sizeof (IncludeFileName
))) {
1097 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "length of include file name exceeds limit", NULL
);
1098 ReportedError
= TRUE
;
1101 if (!ReportedError
) {
1102 *To
= UNICODE_TO_ASCII (SourceFile
->FileBufferPtr
[0]);
1106 SourceFile
->FileBufferPtr
++;
1109 if (!ReportedError
) {
1111 memset ((char *) &IncludedSourceFile
, 0, sizeof (SOURCE_FILE
));
1112 strcpy (IncludedSourceFile
.FileName
, IncludeFileName
);
1113 IncludedSourceFile
.ControlCharacter
= DEFAULT_CONTROL_CHARACTER
;
1114 ProcessIncludeFile (&IncludedSourceFile
, SourceFile
);
1116 // printf ("including file '%s'\n", IncludeFileName);
1123 // Error recovery -- skip to next #
1125 SourceFile
->SkipToHash
= TRUE
;
1131 SOURCE_FILE
*SourceFile
1134 WCHAR StringIdentifier
[MAX_STRING_IDENTIFIER_NAME
];
1136 // Extract the scope name
1138 if (GetStringIdentifierName (SourceFile
, StringIdentifier
, sizeof (StringIdentifier
)) > 0) {
1139 StringDBSetScope (StringIdentifier
);
1143 // Parse: #langdef eng "English"
1144 // #langdef chn "\wideChinese"
1148 ProcessTokenLangDef (
1149 SOURCE_FILE
*SourceFile
1152 WCHAR LanguageIdentifier
[MAX_STRING_IDENTIFIER_NAME
];
1154 WCHAR
*PrintableName
;
1156 // Extract the 3-character language identifier
1158 Len
= GetLanguageIdentifierName (SourceFile
, LanguageIdentifier
, sizeof (LanguageIdentifier
), FALSE
);
1159 if (Len
!= LANGUAGE_IDENTIFIER_NAME_LEN
) {
1160 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "invalid or missing language identifier", NULL
);
1163 // Extract the printable name
1165 PrintableName
= GetPrintableLanguageName (SourceFile
);
1166 if (PrintableName
!= NULL
) {
1167 ParserSetPosition (SourceFile
->FileName
, SourceFile
->LineNum
);
1168 StringDBAddLanguage (LanguageIdentifier
, PrintableName
);
1169 free (PrintableName
);
1174 // Error recovery -- skip to next #
1176 SourceFile
->SkipToHash
= TRUE
;
1181 ApparentQuotedString (
1182 SOURCE_FILE
*SourceFile
1187 // See if the first and last nonblank characters on the line are double quotes
1189 for (Ptr
= SourceFile
->FileBufferPtr
; *Ptr
&& (*Ptr
== UNICODE_SPACE
); Ptr
++)
1191 if (*Ptr
!= UNICODE_DOUBLE_QUOTE
) {
1200 for (; *Ptr
&& (*Ptr
== UNICODE_SPACE
); Ptr
--)
1202 if (*Ptr
!= UNICODE_DOUBLE_QUOTE
) {
1210 // #language eng "some string " "more string"
1214 ProcessTokenLanguage (
1215 SOURCE_FILE
*SourceFile
1219 WCHAR
*SecondString
;
1223 WCHAR Language
[LANGUAGE_IDENTIFIER_NAME_LEN
+ 1];
1225 BOOLEAN PreviousNewline
;
1227 // Get the language identifier
1230 Len
= GetLanguageIdentifierName (SourceFile
, Language
, sizeof (Language
), TRUE
);
1231 if (Len
!= LANGUAGE_IDENTIFIER_NAME_LEN
) {
1232 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "invalid or missing language identifier", "%S", Language
);
1233 SourceFile
->SkipToHash
= TRUE
;
1237 // Extract the string value. It's either a quoted string that starts on the current line, or
1238 // an unquoted string that starts on the following line and continues until the next control
1239 // character in column 1.
1240 // Look ahead to find a quote or a newline
1242 if (SkipTo (SourceFile
, UNICODE_DOUBLE_QUOTE
, TRUE
)) {
1243 String
= GetQuotedString (SourceFile
, FALSE
);
1244 if (String
!= NULL
) {
1246 // Set the position in the file of where we are parsing for error
1247 // reporting purposes. Then start looking ahead for additional
1248 // quoted strings, and concatenate them until we get a failure
1249 // back from the string parser.
1251 Len
= StrLen (String
) + 1;
1252 ParserSetPosition (SourceFile
->FileName
, SourceFile
->LineNum
);
1254 SkipWhiteSpace (SourceFile
);
1255 SecondString
= GetQuotedString (SourceFile
, TRUE
);
1256 if (SecondString
!= NULL
) {
1257 Len
+= StrLen (SecondString
);
1258 TempString
= (WCHAR
*) malloc (Len
* sizeof (WCHAR
));
1259 if (TempString
== NULL
) {
1260 Error (NULL
, 0, 0, "application error", "failed to allocate memory");
1264 StrCpy (TempString
, String
);
1265 StrCat (TempString
, SecondString
);
1267 free (SecondString
);
1268 String
= TempString
;
1270 } while (SecondString
!= NULL
);
1271 StringDBAddString (Language
, NULL
, NULL
, String
, TRUE
, 0);
1275 // Error was reported at lower level. Error recovery mode.
1277 SourceFile
->SkipToHash
= TRUE
;
1280 if (!mGlobals
.UnquotedStrings
) {
1282 // They're using unquoted strings. If the next non-blank character is a double quote, and the
1283 // last non-blank character on the line is a double quote, then more than likely they're using
1284 // quotes, so they need to put the quoted string on the end of the previous line
1286 if (ApparentQuotedString (SourceFile
)) {
1288 SourceFile
->FileName
,
1289 SourceFile
->LineNum
,
1291 "unexpected quoted string on line",
1292 "specify -uqs option if necessary"
1297 // Found end-of-line (hopefully). Skip over it and start taking in characters
1298 // until we find a control character at the start of a line.
1301 From
= SourceFile
->FileBufferPtr
;
1302 PreviousNewline
= FALSE
;
1303 while (!EndOfFile (SourceFile
)) {
1304 if (SourceFile
->FileBufferPtr
[0] == UNICODE_LF
) {
1305 PreviousNewline
= TRUE
;
1306 SourceFile
->LineNum
++;
1309 if (PreviousNewline
&& (SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
)) {
1313 PreviousNewline
= FALSE
;
1316 SourceFile
->FileBufferPtr
++;
1319 if ((Len
== 0) && EndOfFile (SourceFile
)) {
1320 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "unexpected end of file", NULL
);
1321 SourceFile
->SkipToHash
= TRUE
;
1325 // Now allocate a buffer, copy the characters, and add the string.
1327 String
= (WCHAR
*) malloc ((Len
+ 1) * sizeof (WCHAR
));
1328 if (String
== NULL
) {
1329 Error (NULL
, 0, 0, "application error", "failed to allocate memory");
1334 while (From
< SourceFile
->FileBufferPtr
) {
1353 StringDBAddString (Language
, NULL
, NULL
, String
, TRUE
, 0);
1360 SOURCE_FILE
*SourceFile
1363 switch (SourceFile
->FileBufferPtr
[0]) {
1379 SOURCE_FILE
*SourceFile
1385 while (!EndOfFile (SourceFile
)) {
1387 switch (*SourceFile
->FileBufferPtr
) {
1392 SourceFile
->FileBufferPtr
++;
1396 SourceFile
->FileBufferPtr
++;
1397 SourceFile
->LineNum
++;
1398 if (mGlobals
.Verbose
) {
1399 printf ("%d: %S\n", SourceFile
->LineNum
, SourceFile
->FileBufferPtr
);
1408 // Some tokens require trailing whitespace. If we're at the end of the
1409 // file, then we count that as well.
1411 if ((Count
== 0) && (EndOfFile (SourceFile
))) {
1428 while (*Str
== *Buffer
) {
1441 // Given a filename, try to find it along the include paths.
1447 OUT CHAR8
*FoundFileName
,
1448 IN UINT32 FoundFileNameLen
1452 TEXT_STRING_LIST
*List
;
1455 // Traverse the list of paths and try to find the file
1457 List
= mGlobals
.IncludePaths
;
1458 while (List
!= NULL
) {
1460 // Put the path and filename together
1462 if (strlen (List
->Str
) + strlen (FileName
) + 1 > FoundFileNameLen
) {
1463 Error (PROGRAM_NAME
, 0, 0, NULL
, "internal error - cannot concatenate path+filename");
1467 // Append the filename to this include path and try to open the file.
1469 strcpy (FoundFileName
, List
->Str
);
1470 strcat (FoundFileName
, FileName
);
1471 if ((Fptr
= fopen (FoundFileName
, "rb")) != NULL
) {
1473 // Return the file pointer
1483 FoundFileName
[0] = 0;
1487 // Process the command-line arguments
1496 TEXT_STRING_LIST
*NewList
;
1498 // Clear our globals
1500 memset ((char *) &mGlobals
, 0, sizeof (mGlobals
));
1501 strcpy (mGlobals
.BaseName
, DEFAULT_BASE_NAME
);
1503 // Skip program name
1510 return STATUS_ERROR
;
1513 mGlobals
.Mode
= MODE_UNKNOWN
;
1515 // Process until no more -args.
1517 while ((Argc
> 0) && (Argv
[0][0] == '-')) {
1521 if (stricmp (Argv
[0], "-parse") == 0) {
1522 if (mGlobals
.Mode
!= MODE_UNKNOWN
) {
1523 Error (NULL
, 0, 0, "only one of -parse/-scan/-dump allowed", NULL
);
1524 return STATUS_ERROR
;
1527 mGlobals
.Mode
= MODE_PARSE
;
1531 } else if (stricmp (Argv
[0], "-scan") == 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_SCAN
;
1539 // -vscan verbose scanning option
1541 } else if (stricmp (Argv
[0], "-vscan") == 0) {
1542 mGlobals
.VerboseScan
= TRUE
;
1546 } else if (stricmp (Argv
[0], "-dump") == 0) {
1547 if (mGlobals
.Mode
!= MODE_UNKNOWN
) {
1548 Error (NULL
, 0, 0, "only one of -parse/-scan/-dump allowed", NULL
);
1549 return STATUS_ERROR
;
1552 mGlobals
.Mode
= MODE_DUMP
;
1553 } else if (stricmp (Argv
[0], "-uqs") == 0) {
1554 mGlobals
.UnquotedStrings
= TRUE
;
1556 // -i path add include search path when parsing
1558 } else if (stricmp (Argv
[0], "-i") == 0) {
1560 // check for one more arg
1562 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1563 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing include path");
1564 return STATUS_ERROR
;
1567 // Allocate memory for a new list element, fill it in, and
1568 // add it to our list of include paths. Always make sure it
1569 // has a "\" on the end of it.
1571 NewList
= malloc (sizeof (TEXT_STRING_LIST
));
1572 if (NewList
== NULL
) {
1573 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1574 return STATUS_ERROR
;
1577 memset ((char *) NewList
, 0, sizeof (TEXT_STRING_LIST
));
1578 NewList
->Str
= malloc (strlen (Argv
[1]) + 2);
1579 if (NewList
->Str
== NULL
) {
1581 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1582 return STATUS_ERROR
;
1585 strcpy (NewList
->Str
, Argv
[1]);
1586 if (NewList
->Str
[strlen (NewList
->Str
) - 1] != '\\') {
1587 strcat (NewList
->Str
, "\\");
1590 // Add it to our linked list
1592 if (mGlobals
.IncludePaths
== NULL
) {
1593 mGlobals
.IncludePaths
= NewList
;
1595 mGlobals
.LastIncludePath
->Next
= NewList
;
1598 mGlobals
.LastIncludePath
= NewList
;
1601 } else if (stricmp (Argv
[0], "-if") == 0) {
1603 // Indirection file -- check for one more arg
1605 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1606 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing indirection file name");
1607 return STATUS_ERROR
;
1610 // Allocate memory for a new list element, fill it in, and
1611 // add it to our list of include paths. Always make sure it
1612 // has a "\" on the end of it.
1614 NewList
= malloc (sizeof (TEXT_STRING_LIST
));
1615 if (NewList
== NULL
) {
1616 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1617 return STATUS_ERROR
;
1620 memset ((char *) NewList
, 0, sizeof (TEXT_STRING_LIST
));
1621 NewList
->Str
= malloc (strlen (Argv
[1]) + 1);
1622 if (NewList
->Str
== NULL
) {
1624 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1625 return STATUS_ERROR
;
1628 strcpy (NewList
->Str
, Argv
[1]);
1630 // Add it to our linked list
1632 if (mGlobals
.IndirectionFileName
== NULL
) {
1633 mGlobals
.IndirectionFileName
= NewList
;
1635 mGlobals
.LastIndirectionFileName
->Next
= NewList
;
1638 mGlobals
.LastIndirectionFileName
= NewList
;
1641 } else if (stricmp (Argv
[0], "-db") == 0) {
1643 // -db option to specify a database file.
1644 // Check for one more arg (the database file name)
1646 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1647 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing database file name");
1648 return STATUS_ERROR
;
1651 NewList
= malloc (sizeof (TEXT_STRING_LIST
));
1652 if (NewList
== NULL
) {
1653 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1654 return STATUS_ERROR
;
1657 memset ((char *) NewList
, 0, sizeof (TEXT_STRING_LIST
));
1658 NewList
->Str
= malloc (strlen (Argv
[1]) + 1);
1659 if (NewList
->Str
== NULL
) {
1661 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1662 return STATUS_ERROR
;
1665 strcpy (NewList
->Str
, Argv
[1]);
1667 // Add it to our linked list
1669 if (mGlobals
.DatabaseFileName
== NULL
) {
1670 mGlobals
.DatabaseFileName
= NewList
;
1672 mGlobals
.LastDatabaseFileName
->Next
= NewList
;
1675 mGlobals
.LastDatabaseFileName
= NewList
;
1678 } else if (stricmp (Argv
[0], "-ou") == 0) {
1680 // -ou option to specify an output unicode file to
1681 // which we can dump our database.
1683 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1684 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing database dump output file name");
1685 return STATUS_ERROR
;
1688 if (mGlobals
.DumpUFileName
[0] == 0) {
1689 strcpy (mGlobals
.DumpUFileName
, Argv
[1]);
1691 Error (PROGRAM_NAME
, 0, 0, Argv
[1], "-ou option already specified with '%s'", mGlobals
.DumpUFileName
);
1692 return STATUS_ERROR
;
1697 } else if (stricmp (Argv
[0], "-hpk") == 0) {
1699 // -hpk option to create an HII export pack of the input database file
1701 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1702 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing raw string data dump output file name");
1703 return STATUS_ERROR
;
1706 if (mGlobals
.HiiExportPackFileName
[0] == 0) {
1707 strcpy (mGlobals
.HiiExportPackFileName
, Argv
[1]);
1709 Error (PROGRAM_NAME
, 0, 0, Argv
[1], "-or option already specified with '%s'", mGlobals
.HiiExportPackFileName
);
1710 return STATUS_ERROR
;
1715 } else if ((stricmp (Argv
[0], "-?") == 0) || (stricmp (Argv
[0], "-h") == 0)) {
1717 return STATUS_ERROR
;
1718 } else if (stricmp (Argv
[0], "-v") == 0) {
1719 mGlobals
.Verbose
= 1;
1720 } else if (stricmp (Argv
[0], "-vdbw") == 0) {
1721 mGlobals
.VerboseDatabaseWrite
= 1;
1722 } else if (stricmp (Argv
[0], "-vdbr") == 0) {
1723 mGlobals
.VerboseDatabaseRead
= 1;
1724 } else if (stricmp (Argv
[0], "-newdb") == 0) {
1725 mGlobals
.NewDatabase
= 1;
1726 } else if (stricmp (Argv
[0], "-ignorenotfound") == 0) {
1727 mGlobals
.IgnoreNotFound
= 1;
1728 } else if (stricmp (Argv
[0], "-oc") == 0) {
1730 // check for one more arg
1732 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1733 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing output C filename");
1734 return STATUS_ERROR
;
1737 strcpy (mGlobals
.StringCFileName
, Argv
[1]);
1740 } else if (stricmp (Argv
[0], "-bn") == 0) {
1742 // check for one more arg
1744 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1745 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing base name");
1747 return STATUS_ERROR
;
1750 strcpy (mGlobals
.BaseName
, Argv
[1]);
1753 } else if (stricmp (Argv
[0], "-oh") == 0) {
1755 // -oh to specify output .h defines file name
1757 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1758 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing output .h filename");
1759 return STATUS_ERROR
;
1762 strcpy (mGlobals
.StringHFileName
, Argv
[1]);
1765 } else if (stricmp (Argv
[0], "-skipext") == 0) {
1767 // -skipext to skip scanning of files with certain filename extensions
1769 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1770 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing filename extension");
1771 return STATUS_ERROR
;
1774 // Allocate memory for a new list element, fill it in, and
1775 // add it to our list of excluded extensions. Always make sure it
1776 // has a "." as the first character.
1778 NewList
= malloc (sizeof (TEXT_STRING_LIST
));
1779 if (NewList
== NULL
) {
1780 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1781 return STATUS_ERROR
;
1784 memset ((char *) NewList
, 0, sizeof (TEXT_STRING_LIST
));
1785 NewList
->Str
= malloc (strlen (Argv
[1]) + 2);
1786 if (NewList
->Str
== NULL
) {
1788 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1789 return STATUS_ERROR
;
1792 if (Argv
[1][0] == '.') {
1793 strcpy (NewList
->Str
, Argv
[1]);
1795 NewList
->Str
[0] = '.';
1796 strcpy (NewList
->Str
+ 1, Argv
[1]);
1799 // Add it to our linked list
1801 if (mGlobals
.SkipExt
== NULL
) {
1802 mGlobals
.SkipExt
= NewList
;
1804 mGlobals
.LastSkipExt
->Next
= NewList
;
1807 mGlobals
.LastSkipExt
= NewList
;
1810 } else if (stricmp (Argv
[0], "-lang") == 0) {
1812 // "-lang eng" or "-lang spa+cat" to only output certain languages
1814 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1815 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing language name");
1817 return STATUS_ERROR
;
1820 if (AddCommandLineLanguage (Argv
[1]) != STATUS_SUCCESS
) {
1821 return STATUS_ERROR
;
1826 } else if (stricmp (Argv
[0], "-od") == 0) {
1828 // Output database file name -- check for another arg
1830 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1831 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "missing output database file name");
1832 return STATUS_ERROR
;
1835 strcpy (mGlobals
.OutputDatabaseFileName
, Argv
[1]);
1842 Error (PROGRAM_NAME
, 0, 0, Argv
[0], "unrecognized option");
1844 return STATUS_ERROR
;
1851 // Make sure they specified the mode parse/scan/dump
1853 if (mGlobals
.Mode
== MODE_UNKNOWN
) {
1854 Error (NULL
, 0, 0, "must specify one of -parse/-scan/-dump", NULL
);
1855 return STATUS_ERROR
;
1858 // All modes require a database filename
1860 if (mGlobals
.DatabaseFileName
== 0) {
1861 Error (NULL
, 0, 0, "must specify a database filename using -db DbFileName", NULL
);
1863 return STATUS_ERROR
;
1866 // If dumping the database file, then return immediately if all
1867 // parameters check out.
1869 if (mGlobals
.Mode
== MODE_DUMP
) {
1871 // Not much use if they didn't specify -oh or -oc or -ou or -hpk
1873 if ((mGlobals
.DumpUFileName
[0] == 0) &&
1874 (mGlobals
.StringHFileName
[0] == 0) &&
1875 (mGlobals
.StringCFileName
[0] == 0) &&
1876 (mGlobals
.HiiExportPackFileName
[0] == 0)
1878 Error (NULL
, 0, 0, "-dump without -oc/-oh/-ou/-hpk is a NOP", NULL
);
1879 return STATUS_ERROR
;
1882 return STATUS_SUCCESS
;
1885 // Had to specify source string file and output string defines header filename.
1887 if (mGlobals
.Mode
== MODE_SCAN
) {
1889 Error (PROGRAM_NAME
, 0, 0, NULL
, "must specify at least one source file to scan with -scan");
1891 return STATUS_ERROR
;
1894 // Get the list of filenames
1897 NewList
= malloc (sizeof (TEXT_STRING_LIST
));
1898 if (NewList
== NULL
) {
1899 Error (PROGRAM_NAME
, 0, 0, "memory allocation failure", NULL
);
1900 return STATUS_ERROR
;
1903 memset (NewList
, 0, sizeof (TEXT_STRING_LIST
));
1904 NewList
->Str
= (CHAR8
*) malloc (strlen (Argv
[0]) + 1);
1905 if (NewList
->Str
== NULL
) {
1906 Error (PROGRAM_NAME
, 0, 0, "memory allocation failure", NULL
);
1907 return STATUS_ERROR
;
1910 strcpy (NewList
->Str
, Argv
[0]);
1911 if (mGlobals
.ScanFileName
== NULL
) {
1912 mGlobals
.ScanFileName
= NewList
;
1914 mGlobals
.LastScanFileName
->Next
= NewList
;
1917 mGlobals
.LastScanFileName
= NewList
;
1923 // Parse mode -- must specify an input unicode file name
1926 Error (PROGRAM_NAME
, 0, 0, NULL
, "must specify input unicode string file name with -parse");
1928 return STATUS_ERROR
;
1931 strcpy (mGlobals
.SourceFiles
.FileName
, Argv
[0]);
1934 return STATUS_SUCCESS
;
1937 // Found "-lang eng,spa+cat" on the command line. Parse the
1938 // language list and save the setting for later processing.
1942 AddCommandLineLanguage (
1946 WCHAR_STRING_LIST
*WNewList
;
1950 // Keep processing the input string until we find the end.
1954 // Allocate memory for a new list element, fill it in, and
1955 // add it to our list.
1957 WNewList
= MALLOC (sizeof (WCHAR_STRING_LIST
));
1958 if (WNewList
== NULL
) {
1959 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1960 return STATUS_ERROR
;
1963 memset ((char *) WNewList
, 0, sizeof (WCHAR_STRING_LIST
));
1964 WNewList
->Str
= malloc ((strlen (Language
) + 1) * sizeof (WCHAR
));
1965 if (WNewList
->Str
== NULL
) {
1967 Error (PROGRAM_NAME
, 0, 0, NULL
, "memory allocation failure");
1968 return STATUS_ERROR
;
1971 // Copy it as unicode to our new structure. Then remove the
1972 // plus signs in it, and verify each language name is 3 characters
1973 // long. If we find a comma, then we're done with this group, so
1976 UnicodeSPrint (WNewList
->Str
, (strlen (Language
) + 1) * sizeof (WCHAR
), L
"%a", Language
);
1977 From
= To
= WNewList
->Str
;
1979 if (*From
== L
',') {
1983 if ((StrLen (From
) < LANGUAGE_IDENTIFIER_NAME_LEN
) ||
1985 (From
[LANGUAGE_IDENTIFIER_NAME_LEN
] != 0) &&
1986 (From
[LANGUAGE_IDENTIFIER_NAME_LEN
] != UNICODE_PLUS_SIGN
) &&
1987 (From
[LANGUAGE_IDENTIFIER_NAME_LEN
] != L
',')
1990 Error (PROGRAM_NAME
, 0, 0, Language
, "invalid format for language name on command line");
1991 FREE (WNewList
->Str
);
1993 return STATUS_ERROR
;
1996 StrnCpy (To
, From
, LANGUAGE_IDENTIFIER_NAME_LEN
);
1997 To
+= LANGUAGE_IDENTIFIER_NAME_LEN
;
1998 From
+= LANGUAGE_IDENTIFIER_NAME_LEN
;
1999 if (*From
== L
'+') {
2006 // Add it to our linked list
2008 if (mGlobals
.Language
== NULL
) {
2009 mGlobals
.Language
= WNewList
;
2011 mGlobals
.LastLanguage
->Next
= WNewList
;
2014 mGlobals
.LastLanguage
= WNewList
;
2016 // Skip to next entry (comma-separated list)
2019 if (*Language
== L
',') {
2028 return STATUS_SUCCESS
;
2031 // The contents of the text file are expected to be (one per line)
2032 // STRING_IDENTIFIER_NAME ScopeName
2034 // STR_ID_MY_FAVORITE_STRING IBM
2038 ParseIndirectionFiles (
2039 TEXT_STRING_LIST
*Files
2048 WCHAR_MATCHING_STRING_LIST
*NewList
;
2050 Line
[sizeof (Line
) - 1] = 0;
2052 while (Files
!= NULL
) {
2053 Fptr
= fopen (Files
->Str
, "r");
2056 Error (NULL
, 0, 0, Files
->Str
, "failed to open input indirection file for reading");
2057 return STATUS_ERROR
;
2060 while (fgets (Line
, sizeof (Line
), Fptr
) != NULL
) {
2062 // remove terminating newline for error printing purposes.
2064 if (Line
[strlen (Line
) - 1] == '\n') {
2065 Line
[strlen (Line
) - 1] = 0;
2069 if (Line
[sizeof (Line
) - 1] != 0) {
2070 Error (Files
->Str
, LineCount
, 0, "line length exceeds maximum supported", NULL
);
2075 while (*StringName
&& (isspace (*StringName
))) {
2080 if ((*StringName
== '_') || isalpha (*StringName
)) {
2082 while ((*End
) && (*End
== '_') || (isalnum (*End
))) {
2086 if (isspace (*End
)) {
2089 while (isspace (*End
)) {
2095 while (*End
&& !isspace (*End
)) {
2101 // Add the string name/scope pair
2103 NewList
= malloc (sizeof (WCHAR_MATCHING_STRING_LIST
));
2104 if (NewList
== NULL
) {
2105 Error (NULL
, 0, 0, "memory allocation error", NULL
);
2109 memset (NewList
, 0, sizeof (WCHAR_MATCHING_STRING_LIST
));
2110 NewList
->Str1
= (WCHAR
*) malloc ((strlen (StringName
) + 1) * sizeof (WCHAR
));
2111 NewList
->Str2
= (WCHAR
*) malloc ((strlen (ScopeName
) + 1) * sizeof (WCHAR
));
2112 if ((NewList
->Str1
== NULL
) || (NewList
->Str2
== NULL
)) {
2113 Error (NULL
, 0, 0, "memory allocation error", NULL
);
2117 UnicodeSPrint (NewList
->Str1
, strlen (StringName
) + 1, L
"%a", StringName
);
2118 UnicodeSPrint (NewList
->Str2
, strlen (ScopeName
) + 1, L
"%a", ScopeName
);
2119 if (mGlobals
.IndirectionList
== NULL
) {
2120 mGlobals
.IndirectionList
= NewList
;
2122 mGlobals
.LastIndirectionList
->Next
= NewList
;
2125 mGlobals
.LastIndirectionList
= NewList
;
2127 Error (Files
->Str
, LineCount
, 0, StringName
, "invalid line : expected 'StringIdentifier Scope'");
2131 Error (Files
->Str
, LineCount
, 0, StringName
, "invalid line : expected 'StringIdentifier Scope'");
2135 Error (Files
->Str
, LineCount
, 0, StringName
, "invalid string identifier");
2143 Files
= Files
->Next
;
2149 return STATUS_ERROR
;
2152 return STATUS_SUCCESS
;
2158 TEXT_STRING_LIST
*ScanFiles
2161 char Line
[MAX_LINE_LEN
];
2167 char *StringTokenPos
;
2168 TEXT_STRING_LIST
*SList
;
2172 // Put a null-terminator at the end of the line. If we read in
2173 // a line longer than we support, then we can catch it.
2175 Line
[MAX_LINE_LEN
- 1] = 0;
2177 // Process each file. If they gave us a skip extension list, then
2178 // skip it if the extension matches.
2180 while (ScanFiles
!= NULL
) {
2182 for (SList
= mGlobals
.SkipExt
; SList
!= NULL
; SList
= SList
->Next
) {
2183 if ((strlen (ScanFiles
->Str
) > strlen (SList
->Str
)) &&
2184 (strcmp (ScanFiles
->Str
+ strlen (ScanFiles
->Str
) - strlen (SList
->Str
), SList
->Str
) == 0)
2188 // printf ("Match: %s : %s\n", ScanFiles->Str, SList->Str);
2195 if (mGlobals
.VerboseScan
) {
2196 printf ("Scanning %s\n", ScanFiles
->Str
);
2199 Fptr
= fopen (ScanFiles
->Str
, "r");
2201 Error (NULL
, 0, 0, ScanFiles
->Str
, "failed to open input file for scanning");
2202 return STATUS_ERROR
;
2206 while (fgets (Line
, sizeof (Line
), Fptr
) != NULL
) {
2208 if (Line
[MAX_LINE_LEN
- 1] != 0) {
2209 Error (ScanFiles
->Str
, LineNum
, 0, "line length exceeds maximum supported by tool", NULL
);
2211 return STATUS_ERROR
;
2214 // Remove the newline from the input line so we can print a warning message
2216 if (Line
[strlen (Line
) - 1] == '\n') {
2217 Line
[strlen (Line
) - 1] = 0;
2220 // Terminate the line at // comments
2222 Cptr
= strstr (Line
, "//");
2228 while ((Cptr
= strstr (Cptr
, STRING_TOKEN
)) != NULL
) {
2230 // Found "STRING_TOKEN". Make sure we don't have NUM_STRING_TOKENS or
2231 // something like that. Then make sure it's followed by
2232 // an open parenthesis, a string identifier, and then a closing
2235 if (mGlobals
.VerboseScan
) {
2236 printf (" %d: %s", LineNum
, Cptr
);
2239 if (((Cptr
== Line
) || (!IsValidIdentifierChar (*(Cptr
- 1), FALSE
))) &&
2240 (!IsValidIdentifierChar (*(Cptr
+ sizeof (STRING_TOKEN
) - 1), FALSE
))
2242 StringTokenPos
= Cptr
;
2244 Cptr
+= strlen (STRING_TOKEN
);
2245 while (*Cptr
&& isspace (*Cptr
) && (*Cptr
!= '(')) {
2250 Warning (ScanFiles
->Str
, LineNum
, 0, StringTokenPos
, "expected "STRING_TOKEN
"(identifier)");
2253 // Skip over the open-parenthesis and find the next non-blank character
2256 while (isspace (*Cptr
)) {
2261 if ((*Cptr
== '_') || isalpha (*Cptr
)) {
2262 while ((*Cptr
== '_') || (isalnum (*Cptr
))) {
2267 while (*Cptr
&& isspace (*Cptr
)) {
2272 Warning (ScanFiles
->Str
, LineNum
, 0, StringTokenPos
, "expected "STRING_TOKEN
"(identifier)");
2282 // Add the string identifier to the list of used strings
2284 ParserSetPosition (ScanFiles
->Str
, LineNum
);
2285 StringDBSetStringReferenced (SavePtr
, mGlobals
.IgnoreNotFound
);
2286 if (mGlobals
.VerboseScan
) {
2287 printf ("...referenced %s", SavePtr
);
2290 Warning (ScanFiles
->Str
, LineNum
, 0, StringTokenPos
, "expected valid string identifier name");
2295 // Found it, but it's a substring of something else. Advance our pointer.
2300 if (mGlobals
.VerboseScan
) {
2309 // Skipping this file type
2311 if (mGlobals
.VerboseScan
) {
2312 printf ("Skip scanning of %s\n", ScanFiles
->Str
);
2316 ScanFiles
= ScanFiles
->Next
;
2319 return STATUS_SUCCESS
;
2322 // Free the global string lists we allocated memory for
2330 TEXT_STRING_LIST
*Temp
;
2331 WCHAR_STRING_LIST
*WTemp
;
2334 // Traverse the include paths, freeing each
2336 while (mGlobals
.IncludePaths
!= NULL
) {
2337 Temp
= mGlobals
.IncludePaths
->Next
;
2338 free (mGlobals
.IncludePaths
->Str
);
2339 free (mGlobals
.IncludePaths
);
2340 mGlobals
.IncludePaths
= Temp
;
2343 // If we did a scan, then free up our
2344 // list of files to scan.
2346 while (mGlobals
.ScanFileName
!= NULL
) {
2347 Temp
= mGlobals
.ScanFileName
->Next
;
2348 free (mGlobals
.ScanFileName
->Str
);
2349 free (mGlobals
.ScanFileName
);
2350 mGlobals
.ScanFileName
= Temp
;
2353 // If they gave us a list of filename extensions to
2354 // skip on scan, then free them up.
2356 while (mGlobals
.SkipExt
!= NULL
) {
2357 Temp
= mGlobals
.SkipExt
->Next
;
2358 free (mGlobals
.SkipExt
->Str
);
2359 free (mGlobals
.SkipExt
);
2360 mGlobals
.SkipExt
= Temp
;
2363 // Free up any languages specified
2365 while (mGlobals
.Language
!= NULL
) {
2366 WTemp
= mGlobals
.Language
->Next
;
2367 free (mGlobals
.Language
->Str
);
2368 free (mGlobals
.Language
);
2369 mGlobals
.Language
= WTemp
;
2372 // Free up our indirection list
2374 while (mGlobals
.IndirectionList
!= NULL
) {
2375 mGlobals
.LastIndirectionList
= mGlobals
.IndirectionList
->Next
;
2376 free (mGlobals
.IndirectionList
->Str1
);
2377 free (mGlobals
.IndirectionList
->Str2
);
2378 free (mGlobals
.IndirectionList
);
2379 mGlobals
.IndirectionList
= mGlobals
.LastIndirectionList
;
2382 while (mGlobals
.IndirectionFileName
!= NULL
) {
2383 mGlobals
.LastIndirectionFileName
= mGlobals
.IndirectionFileName
->Next
;
2384 free (mGlobals
.IndirectionFileName
->Str
);
2385 free (mGlobals
.IndirectionFileName
);
2386 mGlobals
.IndirectionFileName
= mGlobals
.LastIndirectionFileName
;
2392 IsValidIdentifierChar (
2398 // If it's the first character of an identifier, then
2399 // it must be one of [A-Za-z_].
2402 if (isalpha (Char
) || (Char
== '_')) {
2407 // If it's not the first character, then it can
2408 // be one of [A-Za-z_0-9]
2410 if (isalnum (Char
) || (Char
== '_')) {
2421 SOURCE_FILE
*SourceFile
2424 SourceFile
->LineNum
= 1;
2425 SourceFile
->FileBufferPtr
= SourceFile
->FileBuffer
;
2426 SourceFile
->EndOfFile
= 0;
2432 SOURCE_FILE
*SourceFile
,
2434 BOOLEAN StopAfterNewline
2437 while (!EndOfFile (SourceFile
)) {
2439 // Check for the character of interest
2441 if (SourceFile
->FileBufferPtr
[0] == WChar
) {
2444 if (SourceFile
->FileBufferPtr
[0] == UNICODE_LF
) {
2445 SourceFile
->LineNum
++;
2446 if (StopAfterNewline
) {
2447 SourceFile
->FileBufferPtr
++;
2448 if (SourceFile
->FileBufferPtr
[0] == 0) {
2449 SourceFile
->FileBufferPtr
++;
2456 SourceFile
->FileBufferPtr
++;
2470 Routine Description:
2472 Print usage information for this utility.
2485 static const char *Str
[] = {
2487 PROGRAM_NAME
" version "TOOL_VERSION
" -- process unicode strings file",
2488 " Usage: "PROGRAM_NAME
" -parse {parse options} [FileNames]",
2489 " "PROGRAM_NAME
" -scan {scan options} [FileName]",
2490 " "PROGRAM_NAME
" -dump {dump options}",
2491 " Common options include:",
2492 " -h or -? for this help information",
2493 " -db Database required name of output/input database file",
2494 " -bn BaseName for use in the .h and .c output files",
2495 " Default = "DEFAULT_BASE_NAME
,
2496 " -v for verbose output",
2497 " -vdbw for verbose output when writing database",
2498 " -vdbr for verbose output when reading database",
2499 " -od FileName to specify an output database file name",
2500 " Parse options include:",
2501 " -i IncludePath add IncludePath to list of search paths",
2502 " -newdb to not read in existing database file",
2503 " -uqs to indicate that unquoted strings are used",
2504 " FileNames name of one or more unicode files to parse",
2505 " Scan options include:",
2506 " -scan scan text file(s) for STRING_TOKEN() usage",
2507 " -skipext .ext to skip scan of files with .ext filename extension",
2508 " -ignorenotfound ignore if a given STRING_TOKEN(STR) is not ",
2509 " found in the database",
2510 " FileNames one or more files to scan",
2511 " Dump options include:",
2512 " -oc FileName write string data to FileName",
2513 " -oh FileName write string defines to FileName",
2514 " -ou FileName dump database to unicode file FileName",
2515 " -lang Lang only dump for the language 'Lang'",
2516 " -if FileName to specify an indirection file",
2517 " -hpk FileName to create an HII export pack of the strings",
2519 " The expected process is to parse a unicode string file to create an initial",
2520 " database of string identifier names and string definitions. Then text files",
2521 " should be scanned for STRING_TOKEN() usages, and the referenced",
2522 " strings will be tagged as used in the database. After all files have been",
2523 " scanned, then the database should be dumped to create the necessary output",
2528 for (Index
= 0; Str
[Index
] != NULL
; Index
++) {
2529 fprintf (stdout
, "%s\n", Str
[Index
]);