3 Copyright (c) 2004-2007, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 Parse a strings file and create or add to a string database file.
27 #include <Common/UefiBaseTypes.h>
29 #include "CommonLib.h"
30 #include "EfiUtilityMsgs.h"
31 #include "StrGather.h"
37 #define MAX_NEST_DEPTH 20 // just in case we get in an endless loop.
38 #define MAX_STRING_IDENTIFIER_NAME 100 // number of wchars
39 #define MAX_LINE_LEN 200
40 #define STRING_TOKEN "STRING_TOKEN"
41 #define DEFAULT_BASE_NAME "BaseName"
43 // Operational modes for this utility
45 #define MODE_UNKNOWN 0
50 // Different file separater for Linux and Windows
53 #define FILE_SEP_CHAR '/'
54 #define FILE_SEP_STRING "/"
56 #define FILE_SEP_CHAR '\\'
57 #define FILE_SEP_STRING "\\"
61 // We keep a linked list of these for the source files we process
63 typedef struct _SOURCE_FILE
{
68 CHAR8 FileName
[MAX_PATH
];
72 struct _SOURCE_FILE
*Previous
;
73 struct _SOURCE_FILE
*Next
;
74 WCHAR ControlCharacter
;
77 #define DEFAULT_CONTROL_CHARACTER UNICODE_SLASH
80 // Here's all our globals. We need a linked list of include paths, a linked
81 // list of source files, a linked list of subdirectories (appended to each
82 // include path when searching), and a couple other fields.
85 SOURCE_FILE SourceFiles
;
86 TEXT_STRING_LIST
*IncludePaths
; // all include paths to search
87 TEXT_STRING_LIST
*LastIncludePath
;
88 TEXT_STRING_LIST
*ScanFileName
;
89 TEXT_STRING_LIST
*LastScanFileName
;
90 TEXT_STRING_LIST
*SkipExt
; // if -skipext .uni
91 TEXT_STRING_LIST
*LastSkipExt
;
92 TEXT_STRING_LIST
*IndirectionFileName
;
93 TEXT_STRING_LIST
*LastIndirectionFileName
;
94 TEXT_STRING_LIST
*DatabaseFileName
;
95 TEXT_STRING_LIST
*LastDatabaseFileName
;
96 WCHAR_STRING_LIST
*Language
;
97 WCHAR_STRING_LIST
*LastLanguage
;
98 WCHAR_MATCHING_STRING_LIST
*IndirectionList
; // from indirection file(s)
99 WCHAR_MATCHING_STRING_LIST
*LastIndirectionList
;
100 BOOLEAN Verbose
; // for more detailed output
101 BOOLEAN VerboseDatabaseWrite
; // for more detailed output when writing database
102 BOOLEAN VerboseDatabaseRead
; // for more detailed output when reading database
103 BOOLEAN NewDatabase
; // to start from scratch
104 BOOLEAN IgnoreNotFound
; // when scanning
106 BOOLEAN UnquotedStrings
; // -uqs option
107 CHAR8 OutputDatabaseFileName
[MAX_PATH
];
108 CHAR8 StringHFileName
[MAX_PATH
];
109 CHAR8 StringCFileName
[MAX_PATH
]; // output .C filename
110 CHAR8 DumpUFileName
[MAX_PATH
]; // output unicode dump file name
111 CHAR8 HiiExportPackFileName
[MAX_PATH
]; // HII export pack file name
112 CHAR8 BaseName
[MAX_PATH
]; // base filename of the strings file
118 IsValidIdentifierChar (
126 SOURCE_FILE
*SourceFile
132 SOURCE_FILE
*SourceFile
,
134 BOOLEAN StopAfterNewline
140 SOURCE_FILE
*SourceFile
146 SOURCE_FILE
*SourceFile
152 SOURCE_FILE
*SourceFile
158 SOURCE_FILE
*SourceFile
163 GetStringIdentifierName (
164 IN SOURCE_FILE
*SourceFile
,
165 IN OUT WCHAR
*StringIdentifierName
,
166 IN UINT32 StringIdentifierNameLen
171 GetLanguageIdentifierName (
172 IN SOURCE_FILE
*SourceFile
,
173 IN OUT WCHAR
*LanguageIdentifierName
,
174 IN UINT32 LanguageIdentifierNameLen
,
180 GetPrintableLanguageName (
181 IN SOURCE_FILE
*SourceFile
186 AddCommandLineLanguage (
193 SOURCE_FILE
*SourceFile
,
200 SOURCE_FILE
*SourceFile
,
201 SOURCE_FILE
*ParentSourceFile
207 SOURCE_FILE
*SourceFile
214 OUT CHAR8
*FoundFileName
,
215 IN UINT32 FoundFileNameLen
228 SOURCE_FILE
*SourceFile
259 SOURCE_FILE
*SourceFile
264 ProcessTokenInclude (
265 SOURCE_FILE
*SourceFile
271 SOURCE_FILE
*SourceFile
276 ProcessTokenLanguage (
277 SOURCE_FILE
*SourceFile
282 ProcessTokenLangDef (
283 SOURCE_FILE
*SourceFile
289 TEXT_STRING_LIST
*ScanFiles
294 ParseIndirectionFiles (
295 TEXT_STRING_LIST
*Files
299 StringDBCreateHiiExportPack (
300 CHAR8
*OutputFileName
312 Call the routine to parse the command-line options, then process the file.
316 Argc - Standard C main() argc and argv.
317 Argv - Standard C main() argc and argv.
328 SetUtilityName (UTILITY_NAME
);
330 // Process the command-line arguments
332 Status
= ProcessArgs (Argc
, Argv
);
333 if (Status
!= STATUS_SUCCESS
) {
337 // Initialize the database manager
339 StringDBConstructor ();
341 // We always try to read in an existing database file. It may not
342 // exist, which is ok usually.
344 if (mGlobals
.NewDatabase
== 0) {
346 // Read all databases specified.
348 for (mGlobals
.LastDatabaseFileName
= mGlobals
.DatabaseFileName
;
349 mGlobals
.LastDatabaseFileName
!= NULL
;
350 mGlobals
.LastDatabaseFileName
= mGlobals
.LastDatabaseFileName
->Next
352 Status
= StringDBReadDatabase (mGlobals
.LastDatabaseFileName
->Str
, TRUE
, mGlobals
.VerboseDatabaseRead
);
353 if (Status
!= STATUS_SUCCESS
) {
359 // Read indirection file(s) if specified
361 if (ParseIndirectionFiles (mGlobals
.IndirectionFileName
) != STATUS_SUCCESS
) {
365 // If scanning source files, do that now
367 if (mGlobals
.Mode
== MODE_SCAN
) {
368 ScanFiles (mGlobals
.ScanFileName
);
369 } else if (mGlobals
.Mode
== MODE_PARSE
) {
371 // Parsing a unicode strings file
373 mGlobals
.SourceFiles
.ControlCharacter
= DEFAULT_CONTROL_CHARACTER
;
374 Status
= ProcessIncludeFile (&mGlobals
.SourceFiles
, NULL
);
375 if (Status
!= STATUS_SUCCESS
) {
380 // Create the string defines header file if there have been no errors.
382 ParserSetPosition (NULL
, 0);
383 if ((mGlobals
.StringHFileName
[0] != 0) && (GetUtilityStatus () < STATUS_ERROR
)) {
384 Status
= StringDBDumpStringDefines (mGlobals
.StringHFileName
, mGlobals
.BaseName
);
385 if (Status
!= EFI_SUCCESS
) {
390 // Dump the strings to a .c file if there have still been no errors.
392 if ((mGlobals
.StringCFileName
[0] != 0) && (GetUtilityStatus () < STATUS_ERROR
)) {
393 Status
= StringDBDumpCStrings (
394 mGlobals
.StringCFileName
,
397 mGlobals
.IndirectionList
399 if (Status
!= EFI_SUCCESS
) {
404 // Dump the database if requested
406 if ((mGlobals
.DumpUFileName
[0] != 0) && (GetUtilityStatus () < STATUS_ERROR
)) {
407 StringDBDumpDatabase (NULL
, mGlobals
.DumpUFileName
, FALSE
);
410 // Dump the string data as HII binary string pack if requested
412 if ((mGlobals
.HiiExportPackFileName
[0] != 0) && (GetUtilityStatus () < STATUS_ERROR
)) {
413 StringDBCreateHiiExportPack (mGlobals
.HiiExportPackFileName
);
416 // Always update the database if no errors and not in dump mode. If they specified -od
417 // for an output database file name, then use that name. Otherwise use the name of
418 // the first database file specified with -db
420 if ((mGlobals
.Mode
!= MODE_DUMP
) && (GetUtilityStatus () < STATUS_ERROR
)) {
421 if (mGlobals
.OutputDatabaseFileName
[0]) {
422 Status
= StringDBWriteDatabase (mGlobals
.OutputDatabaseFileName
, mGlobals
.VerboseDatabaseWrite
);
424 Status
= StringDBWriteDatabase (mGlobals
.DatabaseFileName
->Str
, mGlobals
.VerboseDatabaseWrite
);
427 if (Status
!= EFI_SUCCESS
) {
437 StringDBDestructor ();
438 return GetUtilityStatus ();
444 SOURCE_FILE
*SourceFile
,
445 SOURCE_FILE
*ParentSourceFile
451 Given a source file, open the file and parse it
455 SourceFile - name of file to parse
456 ParentSourceFile - for error reporting purposes, the file that #included SourceFile.
464 static UINT32 NestDepth
= 0;
465 CHAR8 FoundFileName
[MAX_PATH
];
468 Status
= STATUS_SUCCESS
;
471 // Print the file being processed. Indent so you can tell the include nesting
474 if (mGlobals
.Verbose
) {
475 fprintf (stdout
, "%*cProcessing file '%s'\n", NestDepth
* 2, ' ', SourceFile
->FileName
);
479 // Make sure we didn't exceed our maximum nesting depth
481 if (NestDepth
> MAX_NEST_DEPTH
) {
482 Error (NULL
, 0, 0, SourceFile
->FileName
, "max nesting depth (%d) exceeded", NestDepth
);
483 Status
= STATUS_ERROR
;
487 // Try to open the file locally, and if that fails try along our include paths.
489 strcpy (FoundFileName
, SourceFile
->FileName
);
490 if ((SourceFile
->Fptr
= fopen (FoundFileName
, "rb")) == NULL
) {
492 // Try to find it among the paths if it has a parent (that is, it is included
495 if (ParentSourceFile
== NULL
) {
496 Error (NULL
, 0, 0, SourceFile
->FileName
, "file not found");
500 SourceFile
->Fptr
= FindFile (SourceFile
->FileName
, FoundFileName
, sizeof (FoundFileName
));
501 if (SourceFile
->Fptr
== NULL
) {
502 Error (ParentSourceFile
->FileName
, ParentSourceFile
->LineNum
, 0, SourceFile
->FileName
, "include file not found");
507 // Process the file found
509 ProcessFile (SourceFile
);
512 // Close open files and return status
514 if (SourceFile
->Fptr
!= NULL
) {
515 fclose (SourceFile
->Fptr
);
524 SOURCE_FILE
*SourceFile
528 // Get the file size, and then read the entire thing into memory.
529 // Allocate space for a terminator character.
531 fseek (SourceFile
->Fptr
, 0, SEEK_END
);
532 SourceFile
->FileSize
= ftell (SourceFile
->Fptr
);
533 fseek (SourceFile
->Fptr
, 0, SEEK_SET
);
534 SourceFile
->FileBuffer
= (WCHAR
*) malloc (SourceFile
->FileSize
+ sizeof (WCHAR
));
535 if (SourceFile
->FileBuffer
== NULL
) {
536 Error (NULL
, 0, 0, "memory allocation failure", NULL
);
540 fread ((VOID
*) SourceFile
->FileBuffer
, SourceFile
->FileSize
, 1, SourceFile
->Fptr
);
541 SourceFile
->FileBuffer
[(SourceFile
->FileSize
/ sizeof (WCHAR
))] = UNICODE_NULL
;
543 // Pre-process the file to replace comments with spaces
545 PreprocessFile (SourceFile
);
549 ParseFile (SourceFile
);
550 free (SourceFile
->FileBuffer
);
551 return STATUS_SUCCESS
;
557 SOURCE_FILE
*SourceFile
564 // First character of a unicode file is special. Make sure
566 if (SourceFile
->FileBufferPtr
[0] != UNICODE_FILE_START
) {
567 Error (SourceFile
->FileName
, 1, 0, SourceFile
->FileName
, "file does not appear to be a unicode file");
571 SourceFile
->FileBufferPtr
++;
574 // Print the first line if in verbose mode
576 if (mGlobals
.Verbose
) {
577 printf ("%d: %S\n", SourceFile
->LineNum
, SourceFile
->FileBufferPtr
);
580 // Since the syntax is relatively straightforward, just switch on the next char
582 while (!EndOfFile (SourceFile
)) {
584 // Check for whitespace
586 if (SourceFile
->FileBufferPtr
[0] == UNICODE_SPACE
) {
587 SourceFile
->FileBufferPtr
++;
588 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_TAB
) {
589 SourceFile
->FileBufferPtr
++;
590 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_CR
) {
591 SourceFile
->FileBufferPtr
++;
592 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_LF
) {
593 SourceFile
->FileBufferPtr
++;
594 SourceFile
->LineNum
++;
595 if (mGlobals
.Verbose
) {
596 printf ("%d: %S\n", SourceFile
->LineNum
, SourceFile
->FileBufferPtr
);
600 } else if (SourceFile
->FileBufferPtr
[0] == 0) {
601 SourceFile
->FileBufferPtr
++;
602 } else if (InComment
) {
603 SourceFile
->FileBufferPtr
++;
604 } else if ((SourceFile
->FileBufferPtr
[0] == UNICODE_SLASH
) && (SourceFile
->FileBufferPtr
[1] == UNICODE_SLASH
)) {
605 SourceFile
->FileBufferPtr
+= 2;
607 } else if (SourceFile
->SkipToHash
&& (SourceFile
->FileBufferPtr
[0] != SourceFile
->ControlCharacter
)) {
608 SourceFile
->FileBufferPtr
++;
610 SourceFile
->SkipToHash
= FALSE
;
611 if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
612 ((Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"include")) > 0)
614 SourceFile
->FileBufferPtr
+= Len
+ 1;
615 ProcessTokenInclude (SourceFile
);
616 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
617 (Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"scope")) > 0
619 SourceFile
->FileBufferPtr
+= Len
+ 1;
620 ProcessTokenScope (SourceFile
);
621 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
622 (Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"language")) > 0
624 SourceFile
->FileBufferPtr
+= Len
+ 1;
625 ProcessTokenLanguage (SourceFile
);
626 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
627 (Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"langdef")) > 0
629 SourceFile
->FileBufferPtr
+= Len
+ 1;
630 ProcessTokenLangDef (SourceFile
);
631 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
632 (Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"string")) > 0
634 SourceFile
->FileBufferPtr
+= Len
+ 1;
635 ProcessTokenString (SourceFile
);
636 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
637 (Len
= wstrcmp (SourceFile
->FileBufferPtr
+ 1, L
"EFI_BREAKPOINT()")) > 0
639 SourceFile
->FileBufferPtr
+= Len
;
641 // BUGBUG: Caling EFI_BREAKOINT() is breaking the link. What is the proper action for this tool
642 // in this condition?
644 // EFI_BREAKPOINT ();
645 } else if ((SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
) &&
646 (SourceFile
->FileBufferPtr
[1] == UNICODE_EQUAL_SIGN
)
648 SourceFile
->ControlCharacter
= SourceFile
->FileBufferPtr
[2];
649 SourceFile
->FileBufferPtr
+= 3;
651 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "unrecognized token", "%S", SourceFile
->FileBufferPtr
);
653 // Treat rest of line as a comment.
660 return STATUS_SUCCESS
;
666 SOURCE_FILE
*SourceFile
671 Preprocess a file to replace all carriage returns with NULLs so
672 we can print lines from the file to the screen.
675 SourceFile - structure that we use to keep track of an input file.
684 RewindFile (SourceFile
);
686 while (!EndOfFile (SourceFile
)) {
688 // If a line-feed, then no longer in a comment
690 if (SourceFile
->FileBufferPtr
[0] == UNICODE_LF
) {
691 SourceFile
->FileBufferPtr
++;
692 SourceFile
->LineNum
++;
694 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_CR
) {
696 // Replace all carriage returns with a NULL so we can print stuff
698 SourceFile
->FileBufferPtr
[0] = 0;
699 SourceFile
->FileBufferPtr
++;
700 } else if (InComment
) {
701 SourceFile
->FileBufferPtr
[0] = UNICODE_SPACE
;
702 SourceFile
->FileBufferPtr
++;
703 } else if ((SourceFile
->FileBufferPtr
[0] == UNICODE_SLASH
) && (SourceFile
->FileBufferPtr
[1] == UNICODE_SLASH
)) {
704 SourceFile
->FileBufferPtr
+= 2;
707 SourceFile
->FileBufferPtr
++;
711 // Could check for end-of-file and still in a comment, but
712 // should not be necessary. So just restore the file pointers.
714 RewindFile (SourceFile
);
719 GetPrintableLanguageName (
720 IN SOURCE_FILE
*SourceFile
728 SkipWhiteSpace (SourceFile
);
729 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
731 SourceFile
->FileName
,
734 "expected quoted printable language name",
736 SourceFile
->FileBufferPtr
738 SourceFile
->SkipToHash
= TRUE
;
743 SourceFile
->FileBufferPtr
++;
744 Start
= Ptr
= SourceFile
->FileBufferPtr
;
745 while (!EndOfFile (SourceFile
)) {
746 if (SourceFile
->FileBufferPtr
[0] == UNICODE_CR
) {
747 Warning (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "carriage return found in quoted string", "%S", Start
);
749 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_DOUBLE_QUOTE
) {
753 SourceFile
->FileBufferPtr
++;
757 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
759 SourceFile
->FileName
,
762 "missing closing quote on printable language name string",
767 SourceFile
->FileBufferPtr
++;
770 // Now allocate memory for the string and save it off
772 String
= (WCHAR
*) malloc ((Len
+ 1) * sizeof (WCHAR
));
773 if (String
== NULL
) {
774 Error (NULL
, 0, 0, "memory allocation failed", NULL
);
778 // Copy the string from the file buffer to the local copy.
779 // We do no reformatting of it whatsoever at this point.
791 // Now format the string to convert \wide and \narrow controls
793 StringDBFormatString (String
);
800 SOURCE_FILE
*SourceFile
,
808 BOOLEAN PreviousBackslash
;
810 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
812 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "expected quoted string", "%S", SourceFile
->FileBufferPtr
);
819 SourceFile
->FileBufferPtr
++;
820 Start
= Ptr
= SourceFile
->FileBufferPtr
;
821 PreviousBackslash
= FALSE
;
822 while (!EndOfFile (SourceFile
)) {
823 if ((SourceFile
->FileBufferPtr
[0] == UNICODE_DOUBLE_QUOTE
) && (!PreviousBackslash
)) {
825 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_CR
) {
826 Warning (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "carriage return found in quoted string", "%S", Start
);
827 PreviousBackslash
= FALSE
;
828 } else if (SourceFile
->FileBufferPtr
[0] == UNICODE_BACKSLASH
) {
829 PreviousBackslash
= TRUE
;
831 PreviousBackslash
= FALSE
;
834 SourceFile
->FileBufferPtr
++;
838 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
839 Warning (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "missing closing quote on string", "%S", Start
);
841 SourceFile
->FileBufferPtr
++;
844 // Now allocate memory for the string and save it off
846 String
= (WCHAR
*) malloc ((Len
+ 1) * sizeof (WCHAR
));
847 if (String
== NULL
) {
848 Error (NULL
, 0, 0, "memory allocation failed", NULL
);
852 // Copy the string from the file buffer to the local copy.
853 // We do no reformatting of it whatsoever at this point.
868 // #string STR_ID_NAME
870 // All we can do is call the string database to add the string identifier. Unfortunately
871 // he'll have to keep track of the last identifier we added.
876 SOURCE_FILE
*SourceFile
879 WCHAR StringIdentifier
[MAX_STRING_IDENTIFIER_NAME
];
882 // Extract the string identifier name and add it to the database.
884 if (GetStringIdentifierName (SourceFile
, StringIdentifier
, sizeof (StringIdentifier
)) > 0) {
885 StringId
= STRING_ID_INVALID
;
886 StringDBAddStringIdentifier (StringIdentifier
, &StringId
, 0);
889 // Error recovery -- skip to the next #
891 SourceFile
->SkipToHash
= TRUE
;
898 SOURCE_FILE
*SourceFile
902 // The file buffer pointer will typically get updated before the End-of-file flag in the
903 // source file structure, so check it first.
905 if (SourceFile
->FileBufferPtr
>= SourceFile
->FileBuffer
+ SourceFile
->FileSize
/ sizeof (WCHAR
)) {
906 SourceFile
->EndOfFile
= TRUE
;
910 if (SourceFile
->EndOfFile
) {
919 GetStringIdentifierName (
920 IN SOURCE_FILE
*SourceFile
,
921 IN OUT WCHAR
*StringIdentifierName
,
922 IN UINT32 StringIdentifierNameLen
932 SkipWhiteSpace (SourceFile
);
933 if (SourceFile
->EndOfFile
) {
934 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "end-of-file encountered", "expected string identifier");
938 // Verify first character of name is [A-Za-z]
941 StringIdentifierNameLen
/= 2;
942 From
= SourceFile
->FileBufferPtr
;
943 Start
= SourceFile
->FileBufferPtr
;
944 if (((SourceFile
->FileBufferPtr
[0] >= UNICODE_A
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_Z
)) ||
945 ((SourceFile
->FileBufferPtr
[0] >= UNICODE_z
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_z
))
951 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "invalid character in string identifier name", "%S", Start
);
955 while (!EndOfFile (SourceFile
)) {
956 if (((SourceFile
->FileBufferPtr
[0] >= UNICODE_A
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_Z
)) ||
957 ((SourceFile
->FileBufferPtr
[0] >= UNICODE_z
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_z
)) ||
958 ((SourceFile
->FileBufferPtr
[0] >= UNICODE_0
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_9
)) ||
959 (SourceFile
->FileBufferPtr
[0] == UNICODE_UNDERSCORE
)
962 if (Len
>= StringIdentifierNameLen
) {
963 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "string identifier name too long", "%S", Start
);
967 *StringIdentifierName
= SourceFile
->FileBufferPtr
[0];
968 StringIdentifierName
++;
969 SourceFile
->FileBufferPtr
++;
970 } else if (SkipWhiteSpace (SourceFile
) == 0) {
971 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "invalid string identifier name", "%S", Start
);
978 // Terminate the copy of the string.
980 *StringIdentifierName
= 0;
986 GetLanguageIdentifierName (
987 IN SOURCE_FILE
*SourceFile
,
988 IN OUT WCHAR
*LanguageIdentifierName
,
989 IN UINT32 LanguageIdentifierNameLen
,
999 SkipWhiteSpace (SourceFile
);
1000 if (SourceFile
->EndOfFile
) {
1003 SourceFile
->FileName
,
1004 SourceFile
->LineNum
,
1006 "end-of-file encountered",
1007 "expected language identifier"
1014 // This function is called to optionally get a language identifier name in:
1015 // #string STR_ID eng "the string"
1016 // If it's optional, and we find a double-quote, then return now.
1019 if (*SourceFile
->FileBufferPtr
== UNICODE_DOUBLE_QUOTE
) {
1025 LanguageIdentifierNameLen
/= 2;
1027 // Internal error if we weren't given at least 4 WCHAR's to work with.
1029 if (LanguageIdentifierNameLen
< LANGUAGE_IDENTIFIER_NAME_LEN
+ 1) {
1031 SourceFile
->FileName
,
1032 SourceFile
->LineNum
,
1034 "app error -- language identifier name length is invalid",
1039 From
= SourceFile
->FileBufferPtr
;
1040 Start
= SourceFile
->FileBufferPtr
;
1041 while (!EndOfFile (SourceFile
)) {
1042 if (((SourceFile
->FileBufferPtr
[0] >= UNICODE_a
) && (SourceFile
->FileBufferPtr
[0] <= UNICODE_z
))) {
1044 if (Len
> LANGUAGE_IDENTIFIER_NAME_LEN
) {
1045 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "language identifier name too long", "%S", Start
);
1049 *LanguageIdentifierName
= SourceFile
->FileBufferPtr
[0];
1050 SourceFile
->FileBufferPtr
++;
1051 LanguageIdentifierName
++;
1052 } else if (!IsWhiteSpace (SourceFile
)) {
1053 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "invalid language identifier name", "%S", Start
);
1060 // Terminate the copy of the string.
1062 *LanguageIdentifierName
= 0;
1068 ProcessTokenInclude (
1069 SOURCE_FILE
*SourceFile
1072 CHAR8 IncludeFileName
[MAX_PATH
];
1075 BOOLEAN ReportedError
;
1076 SOURCE_FILE IncludedSourceFile
;
1078 ReportedError
= FALSE
;
1079 if (SkipWhiteSpace (SourceFile
) == 0) {
1080 Warning (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "expected whitespace following #include keyword", NULL
);
1083 // Should be quoted file name
1085 if (SourceFile
->FileBufferPtr
[0] != UNICODE_DOUBLE_QUOTE
) {
1086 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "expected quoted include file name", NULL
);
1090 SourceFile
->FileBufferPtr
++;
1092 // Copy the filename as ascii to our local string
1094 To
= IncludeFileName
;
1096 while (!EndOfFile (SourceFile
)) {
1097 if ((SourceFile
->FileBufferPtr
[0] == UNICODE_CR
) || (SourceFile
->FileBufferPtr
[0] == UNICODE_LF
)) {
1098 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "end-of-line found in quoted include file name", NULL
);
1102 if (SourceFile
->FileBufferPtr
[0] == UNICODE_DOUBLE_QUOTE
) {
1103 SourceFile
->FileBufferPtr
++;
1107 // If too long, then report the error once and process until the closing quote
1110 if (!ReportedError
&& (Len
>= sizeof (IncludeFileName
))) {
1111 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "length of include file name exceeds limit", NULL
);
1112 ReportedError
= TRUE
;
1115 if (!ReportedError
) {
1116 *To
= UNICODE_TO_ASCII (SourceFile
->FileBufferPtr
[0]);
1120 SourceFile
->FileBufferPtr
++;
1123 if (!ReportedError
) {
1125 memset ((char *) &IncludedSourceFile
, 0, sizeof (SOURCE_FILE
));
1126 strcpy (IncludedSourceFile
.FileName
, IncludeFileName
);
1127 IncludedSourceFile
.ControlCharacter
= DEFAULT_CONTROL_CHARACTER
;
1128 ProcessIncludeFile (&IncludedSourceFile
, SourceFile
);
1130 // printf ("including file '%s'\n", IncludeFileName);
1137 // Error recovery -- skip to next #
1139 SourceFile
->SkipToHash
= TRUE
;
1145 SOURCE_FILE
*SourceFile
1148 WCHAR StringIdentifier
[MAX_STRING_IDENTIFIER_NAME
];
1150 // Extract the scope name
1152 if (GetStringIdentifierName (SourceFile
, StringIdentifier
, sizeof (StringIdentifier
)) > 0) {
1153 StringDBSetScope (StringIdentifier
);
1157 // Parse: #langdef eng "English"
1158 // #langdef chn "\wideChinese"
1162 ProcessTokenLangDef (
1163 SOURCE_FILE
*SourceFile
1166 WCHAR LanguageIdentifier
[MAX_STRING_IDENTIFIER_NAME
];
1168 WCHAR
*PrintableName
;
1170 // Extract the 3-character language identifier
1172 Len
= GetLanguageIdentifierName (SourceFile
, LanguageIdentifier
, sizeof (LanguageIdentifier
), FALSE
);
1173 if (Len
!= LANGUAGE_IDENTIFIER_NAME_LEN
) {
1174 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "invalid or missing language identifier", NULL
);
1177 // Extract the printable name
1179 PrintableName
= GetPrintableLanguageName (SourceFile
);
1180 if (PrintableName
!= NULL
) {
1181 ParserSetPosition (SourceFile
->FileName
, SourceFile
->LineNum
);
1182 StringDBAddLanguage (LanguageIdentifier
, PrintableName
);
1183 free (PrintableName
);
1188 // Error recovery -- skip to next #
1190 SourceFile
->SkipToHash
= TRUE
;
1195 ApparentQuotedString (
1196 SOURCE_FILE
*SourceFile
1201 // See if the first and last nonblank characters on the line are double quotes
1203 for (Ptr
= SourceFile
->FileBufferPtr
; *Ptr
&& (*Ptr
== UNICODE_SPACE
); Ptr
++)
1205 if (*Ptr
!= UNICODE_DOUBLE_QUOTE
) {
1214 for (; *Ptr
&& (*Ptr
== UNICODE_SPACE
); Ptr
--)
1216 if (*Ptr
!= UNICODE_DOUBLE_QUOTE
) {
1224 // #language eng "some string " "more string"
1228 ProcessTokenLanguage (
1229 SOURCE_FILE
*SourceFile
1233 WCHAR
*SecondString
;
1237 WCHAR Language
[LANGUAGE_IDENTIFIER_NAME_LEN
+ 1];
1239 BOOLEAN PreviousNewline
;
1241 // Get the language identifier
1244 Len
= GetLanguageIdentifierName (SourceFile
, Language
, sizeof (Language
), TRUE
);
1245 if (Len
!= LANGUAGE_IDENTIFIER_NAME_LEN
) {
1246 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "invalid or missing language identifier", "%S", Language
);
1247 SourceFile
->SkipToHash
= TRUE
;
1251 // Extract the string value. It's either a quoted string that starts on the current line, or
1252 // an unquoted string that starts on the following line and continues until the next control
1253 // character in column 1.
1254 // Look ahead to find a quote or a newline
1256 if (SkipTo (SourceFile
, UNICODE_DOUBLE_QUOTE
, TRUE
)) {
1257 String
= GetQuotedString (SourceFile
, FALSE
);
1258 if (String
!= NULL
) {
1260 // Set the position in the file of where we are parsing for error
1261 // reporting purposes. Then start looking ahead for additional
1262 // quoted strings, and concatenate them until we get a failure
1263 // back from the string parser.
1265 Len
= StrLen (String
) + 1;
1266 ParserSetPosition (SourceFile
->FileName
, SourceFile
->LineNum
);
1268 SkipWhiteSpace (SourceFile
);
1269 SecondString
= GetQuotedString (SourceFile
, TRUE
);
1270 if (SecondString
!= NULL
) {
1271 Len
+= StrLen (SecondString
);
1272 TempString
= (WCHAR
*) malloc (Len
* sizeof (WCHAR
));
1273 if (TempString
== NULL
) {
1274 Error (NULL
, 0, 0, "application error", "failed to allocate memory");
1278 StrCpy (TempString
, String
);
1279 StrCat (TempString
, SecondString
);
1281 free (SecondString
);
1282 String
= TempString
;
1284 } while (SecondString
!= NULL
);
1285 StringDBAddString (Language
, NULL
, NULL
, String
, TRUE
, 0);
1289 // Error was reported at lower level. Error recovery mode.
1291 SourceFile
->SkipToHash
= TRUE
;
1294 if (!mGlobals
.UnquotedStrings
) {
1296 // They're using unquoted strings. If the next non-blank character is a double quote, and the
1297 // last non-blank character on the line is a double quote, then more than likely they're using
1298 // quotes, so they need to put the quoted string on the end of the previous line
1300 if (ApparentQuotedString (SourceFile
)) {
1302 SourceFile
->FileName
,
1303 SourceFile
->LineNum
,
1305 "unexpected quoted string on line",
1306 "specify -uqs option if necessary"
1311 // Found end-of-line (hopefully). Skip over it and start taking in characters
1312 // until we find a control character at the start of a line.
1315 From
= SourceFile
->FileBufferPtr
;
1316 PreviousNewline
= FALSE
;
1317 while (!EndOfFile (SourceFile
)) {
1318 if (SourceFile
->FileBufferPtr
[0] == UNICODE_LF
) {
1319 PreviousNewline
= TRUE
;
1320 SourceFile
->LineNum
++;
1323 if (PreviousNewline
&& (SourceFile
->FileBufferPtr
[0] == SourceFile
->ControlCharacter
)) {
1327 PreviousNewline
= FALSE
;
1330 SourceFile
->FileBufferPtr
++;
1333 if ((Len
== 0) && EndOfFile (SourceFile
)) {
1334 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "unexpected end of file", NULL
);
1335 SourceFile
->SkipToHash
= TRUE
;
1339 // Now allocate a buffer, copy the characters, and add the string.
1341 String
= (WCHAR
*) malloc ((Len
+ 1) * sizeof (WCHAR
));
1342 if (String
== NULL
) {
1343 Error (NULL
, 0, 0, "application error", "failed to allocate memory");
1348 while (From
< SourceFile
->FileBufferPtr
) {
1367 StringDBAddString (Language
, NULL
, NULL
, String
, TRUE
, 0);
1374 SOURCE_FILE
*SourceFile
1377 switch (SourceFile
->FileBufferPtr
[0]) {
1393 SOURCE_FILE
*SourceFile
1399 while (!EndOfFile (SourceFile
)) {
1401 switch (*SourceFile
->FileBufferPtr
) {
1406 SourceFile
->FileBufferPtr
++;
1410 SourceFile
->FileBufferPtr
++;
1411 SourceFile
->LineNum
++;
1412 if (mGlobals
.Verbose
) {
1413 printf ("%d: %S\n", SourceFile
->LineNum
, SourceFile
->FileBufferPtr
);
1422 // Some tokens require trailing whitespace. If we're at the end of the
1423 // file, then we count that as well.
1425 if ((Count
== 0) && (EndOfFile (SourceFile
))) {
1442 while (*Str
== *Buffer
) {
1455 // Given a filename, try to find it along the include paths.
1461 OUT CHAR8
*FoundFileName
,
1462 IN UINT32 FoundFileNameLen
1466 TEXT_STRING_LIST
*List
;
1469 // Traverse the list of paths and try to find the file
1471 List
= mGlobals
.IncludePaths
;
1472 while (List
!= NULL
) {
1474 // Put the path and filename together
1476 if (strlen (List
->Str
) + strlen (FileName
) + 1 > FoundFileNameLen
) {
1477 Error (UTILITY_NAME
, 0, 0, NULL
, "internal error - cannot concatenate path+filename");
1481 // Append the filename to this include path and try to open the file.
1483 strcpy (FoundFileName
, List
->Str
);
1484 strcat (FoundFileName
, FileName
);
1485 if ((Fptr
= fopen (FoundFileName
, "rb")) != NULL
) {
1487 // Return the file pointer
1497 FoundFileName
[0] = 0;
1501 // Process the command-line arguments
1510 TEXT_STRING_LIST
*NewList
;
1512 // Clear our globals
1514 memset ((char *) &mGlobals
, 0, sizeof (mGlobals
));
1515 strcpy (mGlobals
.BaseName
, DEFAULT_BASE_NAME
);
1517 // Skip program name
1524 return STATUS_ERROR
;
1527 if ((strcmp(Argv
[0], "-h") == 0) || (strcmp(Argv
[0], "--help") == 0) ||
1528 (strcmp(Argv
[0], "-?") == 0) || (strcmp(Argv
[0], "/?") == 0)) {
1530 return STATUS_ERROR
;
1533 if ((strcmp(Argv
[0], "-V") == 0) || (strcmp(Argv
[0], "--version") == 0)) {
1535 return STATUS_ERROR
;
1538 mGlobals
.Mode
= MODE_UNKNOWN
;
1540 // Process until no more -args.
1542 while ((Argc
> 0) && (Argv
[0][0] == '-')) {
1546 if (stricmp (Argv
[0], "-parse") == 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_PARSE
;
1556 } else if (stricmp (Argv
[0], "-scan") == 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_SCAN
;
1564 // -vscan verbose scanning option
1566 } else if (stricmp (Argv
[0], "-vscan") == 0) {
1567 mGlobals
.VerboseScan
= TRUE
;
1571 } else if (stricmp (Argv
[0], "-dump") == 0) {
1572 if (mGlobals
.Mode
!= MODE_UNKNOWN
) {
1573 Error (NULL
, 0, 0, "only one of -parse/-scan/-dump allowed", NULL
);
1574 return STATUS_ERROR
;
1577 mGlobals
.Mode
= MODE_DUMP
;
1578 } else if (stricmp (Argv
[0], "-uqs") == 0) {
1579 mGlobals
.UnquotedStrings
= TRUE
;
1581 // -i path add include search path when parsing
1583 } else if (stricmp (Argv
[0], "-i") == 0) {
1585 // check for one more arg
1587 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1588 Error (UTILITY_NAME
, 0, 0, Argv
[0], "missing include path");
1589 return STATUS_ERROR
;
1592 // Allocate memory for a new list element, fill it in, and
1593 // add it to our list of include paths. Always make sure it
1594 // has a "\" on the end of it.
1596 NewList
= malloc (sizeof (TEXT_STRING_LIST
));
1597 if (NewList
== NULL
) {
1598 Error (UTILITY_NAME
, 0, 0, NULL
, "memory allocation failure");
1599 return STATUS_ERROR
;
1602 memset ((char *) NewList
, 0, sizeof (TEXT_STRING_LIST
));
1603 NewList
->Str
= malloc (strlen (Argv
[1]) + 2);
1604 if (NewList
->Str
== NULL
) {
1606 Error (UTILITY_NAME
, 0, 0, NULL
, "memory allocation failure");
1607 return STATUS_ERROR
;
1610 strcpy (NewList
->Str
, Argv
[1]);
1611 if (NewList
->Str
[strlen (NewList
->Str
) - 1] != FILE_SEP_CHAR
) {
1612 strcat (NewList
->Str
, FILE_SEP_STRING
);
1615 // Add it to our linked list
1617 if (mGlobals
.IncludePaths
== NULL
) {
1618 mGlobals
.IncludePaths
= NewList
;
1620 mGlobals
.LastIncludePath
->Next
= NewList
;
1623 mGlobals
.LastIncludePath
= NewList
;
1626 } else if (stricmp (Argv
[0], "-if") == 0) {
1628 // Indirection file -- check for one more arg
1630 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1631 Error (UTILITY_NAME
, 0, 0, Argv
[0], "missing indirection file name");
1632 return STATUS_ERROR
;
1635 // Allocate memory for a new list element, fill it in, and
1636 // add it to our list of include paths. Always make sure it
1637 // has a "\" on the end of it.
1639 NewList
= malloc (sizeof (TEXT_STRING_LIST
));
1640 if (NewList
== NULL
) {
1641 Error (UTILITY_NAME
, 0, 0, NULL
, "memory allocation failure");
1642 return STATUS_ERROR
;
1645 memset ((char *) NewList
, 0, sizeof (TEXT_STRING_LIST
));
1646 NewList
->Str
= malloc (strlen (Argv
[1]) + 1);
1647 if (NewList
->Str
== NULL
) {
1649 Error (UTILITY_NAME
, 0, 0, NULL
, "memory allocation failure");
1650 return STATUS_ERROR
;
1653 strcpy (NewList
->Str
, Argv
[1]);
1655 // Add it to our linked list
1657 if (mGlobals
.IndirectionFileName
== NULL
) {
1658 mGlobals
.IndirectionFileName
= NewList
;
1660 mGlobals
.LastIndirectionFileName
->Next
= NewList
;
1663 mGlobals
.LastIndirectionFileName
= NewList
;
1666 } else if (stricmp (Argv
[0], "-db") == 0) {
1668 // -db option to specify a database file.
1669 // Check for one more arg (the database file name)
1671 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1672 Error (UTILITY_NAME
, 0, 0, Argv
[0], "missing database file name");
1673 return STATUS_ERROR
;
1676 NewList
= malloc (sizeof (TEXT_STRING_LIST
));
1677 if (NewList
== NULL
) {
1678 Error (UTILITY_NAME
, 0, 0, NULL
, "memory allocation failure");
1679 return STATUS_ERROR
;
1682 memset ((char *) NewList
, 0, sizeof (TEXT_STRING_LIST
));
1683 NewList
->Str
= malloc (strlen (Argv
[1]) + 1);
1684 if (NewList
->Str
== NULL
) {
1686 Error (UTILITY_NAME
, 0, 0, NULL
, "memory allocation failure");
1687 return STATUS_ERROR
;
1690 strcpy (NewList
->Str
, Argv
[1]);
1692 // Add it to our linked list
1694 if (mGlobals
.DatabaseFileName
== NULL
) {
1695 mGlobals
.DatabaseFileName
= NewList
;
1697 mGlobals
.LastDatabaseFileName
->Next
= NewList
;
1700 mGlobals
.LastDatabaseFileName
= NewList
;
1703 } else if (stricmp (Argv
[0], "-ou") == 0) {
1705 // -ou option to specify an output unicode file to
1706 // which we can dump our database.
1708 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1709 Error (UTILITY_NAME
, 0, 0, Argv
[0], "missing database dump output file name");
1710 return STATUS_ERROR
;
1713 if (mGlobals
.DumpUFileName
[0] == 0) {
1714 strcpy (mGlobals
.DumpUFileName
, Argv
[1]);
1716 Error (UTILITY_NAME
, 0, 0, Argv
[1], "-ou option already specified with '%s'", mGlobals
.DumpUFileName
);
1717 return STATUS_ERROR
;
1722 } else if (stricmp (Argv
[0], "-hpk") == 0) {
1724 // -hpk option to create an HII export pack of the input database file
1726 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1727 Error (UTILITY_NAME
, 0, 0, Argv
[0], "missing raw string data dump output file name");
1728 return STATUS_ERROR
;
1731 if (mGlobals
.HiiExportPackFileName
[0] == 0) {
1732 strcpy (mGlobals
.HiiExportPackFileName
, Argv
[1]);
1734 Error (UTILITY_NAME
, 0, 0, Argv
[1], "-or option already specified with '%s'", mGlobals
.HiiExportPackFileName
);
1735 return STATUS_ERROR
;
1740 } else if ((stricmp (Argv
[0], "-?") == 0) || (stricmp (Argv
[0], "-h") == 0)) {
1742 return STATUS_ERROR
;
1743 } else if (stricmp (Argv
[0], "-v") == 0) {
1744 mGlobals
.Verbose
= 1;
1745 } else if (stricmp (Argv
[0], "-vdbw") == 0) {
1746 mGlobals
.VerboseDatabaseWrite
= 1;
1747 } else if (stricmp (Argv
[0], "-vdbr") == 0) {
1748 mGlobals
.VerboseDatabaseRead
= 1;
1749 } else if (stricmp (Argv
[0], "-newdb") == 0) {
1750 mGlobals
.NewDatabase
= 1;
1751 } else if (stricmp (Argv
[0], "-ignorenotfound") == 0) {
1752 mGlobals
.IgnoreNotFound
= 1;
1753 } else if (stricmp (Argv
[0], "-oc") == 0) {
1755 // check for one more arg
1757 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1758 Error (UTILITY_NAME
, 0, 0, Argv
[0], "missing output C filename");
1759 return STATUS_ERROR
;
1762 strcpy (mGlobals
.StringCFileName
, Argv
[1]);
1765 } else if (stricmp (Argv
[0], "-bn") == 0) {
1767 // check for one more arg
1769 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1770 Error (UTILITY_NAME
, 0, 0, Argv
[0], "missing base name");
1772 return STATUS_ERROR
;
1775 strcpy (mGlobals
.BaseName
, Argv
[1]);
1778 } else if (stricmp (Argv
[0], "-oh") == 0) {
1780 // -oh to specify output .h defines file name
1782 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1783 Error (UTILITY_NAME
, 0, 0, Argv
[0], "missing output .h filename");
1784 return STATUS_ERROR
;
1787 strcpy (mGlobals
.StringHFileName
, Argv
[1]);
1790 } else if (stricmp (Argv
[0], "-skipext") == 0) {
1792 // -skipext to skip scanning of files with certain filename extensions
1794 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1795 Error (UTILITY_NAME
, 0, 0, Argv
[0], "missing filename extension");
1796 return STATUS_ERROR
;
1799 // Allocate memory for a new list element, fill it in, and
1800 // add it to our list of excluded extensions. Always make sure it
1801 // has a "." as the first character.
1803 NewList
= malloc (sizeof (TEXT_STRING_LIST
));
1804 if (NewList
== NULL
) {
1805 Error (UTILITY_NAME
, 0, 0, NULL
, "memory allocation failure");
1806 return STATUS_ERROR
;
1809 memset ((char *) NewList
, 0, sizeof (TEXT_STRING_LIST
));
1810 NewList
->Str
= malloc (strlen (Argv
[1]) + 2);
1811 if (NewList
->Str
== NULL
) {
1813 Error (UTILITY_NAME
, 0, 0, NULL
, "memory allocation failure");
1814 return STATUS_ERROR
;
1817 if (Argv
[1][0] == '.') {
1818 strcpy (NewList
->Str
, Argv
[1]);
1820 NewList
->Str
[0] = '.';
1821 strcpy (NewList
->Str
+ 1, Argv
[1]);
1824 // Add it to our linked list
1826 if (mGlobals
.SkipExt
== NULL
) {
1827 mGlobals
.SkipExt
= NewList
;
1829 mGlobals
.LastSkipExt
->Next
= NewList
;
1832 mGlobals
.LastSkipExt
= NewList
;
1835 } else if (stricmp (Argv
[0], "-lang") == 0) {
1837 // "-lang eng" or "-lang spa+cat" to only output certain languages
1839 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1840 Error (UTILITY_NAME
, 0, 0, Argv
[0], "missing language name");
1842 return STATUS_ERROR
;
1845 if (AddCommandLineLanguage (Argv
[1]) != STATUS_SUCCESS
) {
1846 return STATUS_ERROR
;
1851 } else if (stricmp (Argv
[0], "-od") == 0) {
1853 // Output database file name -- check for another arg
1855 if ((Argc
<= 1) || (Argv
[1][0] == '-')) {
1856 Error (UTILITY_NAME
, 0, 0, Argv
[0], "missing output database file name");
1857 return STATUS_ERROR
;
1860 strcpy (mGlobals
.OutputDatabaseFileName
, Argv
[1]);
1867 Error (UTILITY_NAME
, 0, 0, Argv
[0], "unrecognized option");
1869 return STATUS_ERROR
;
1876 // Make sure they specified the mode parse/scan/dump
1878 if (mGlobals
.Mode
== MODE_UNKNOWN
) {
1879 Error (NULL
, 0, 0, "must specify one of -parse/-scan/-dump", NULL
);
1880 return STATUS_ERROR
;
1883 // All modes require a database filename
1885 if (mGlobals
.DatabaseFileName
== 0) {
1886 Error (NULL
, 0, 0, "must specify a database filename using -db DbFileName", NULL
);
1888 return STATUS_ERROR
;
1891 // If dumping the database file, then return immediately if all
1892 // parameters check out.
1894 if (mGlobals
.Mode
== MODE_DUMP
) {
1896 // Not much use if they didn't specify -oh or -oc or -ou or -hpk
1898 if ((mGlobals
.DumpUFileName
[0] == 0) &&
1899 (mGlobals
.StringHFileName
[0] == 0) &&
1900 (mGlobals
.StringCFileName
[0] == 0) &&
1901 (mGlobals
.HiiExportPackFileName
[0] == 0)
1903 Error (NULL
, 0, 0, "-dump without -oc/-oh/-ou/-hpk is a NOP", NULL
);
1904 return STATUS_ERROR
;
1907 return STATUS_SUCCESS
;
1910 // Had to specify source string file and output string defines header filename.
1912 if (mGlobals
.Mode
== MODE_SCAN
) {
1914 Error (UTILITY_NAME
, 0, 0, NULL
, "must specify at least one source file to scan with -scan");
1916 return STATUS_ERROR
;
1919 // Get the list of filenames
1922 NewList
= malloc (sizeof (TEXT_STRING_LIST
));
1923 if (NewList
== NULL
) {
1924 Error (UTILITY_NAME
, 0, 0, "memory allocation failure", NULL
);
1925 return STATUS_ERROR
;
1928 memset (NewList
, 0, sizeof (TEXT_STRING_LIST
));
1929 NewList
->Str
= (CHAR8
*) malloc (strlen (Argv
[0]) + 1);
1930 if (NewList
->Str
== NULL
) {
1931 Error (UTILITY_NAME
, 0, 0, "memory allocation failure", NULL
);
1932 return STATUS_ERROR
;
1935 strcpy (NewList
->Str
, Argv
[0]);
1936 if (mGlobals
.ScanFileName
== NULL
) {
1937 mGlobals
.ScanFileName
= NewList
;
1939 mGlobals
.LastScanFileName
->Next
= NewList
;
1942 mGlobals
.LastScanFileName
= NewList
;
1948 // Parse mode -- must specify an input unicode file name
1951 Error (UTILITY_NAME
, 0, 0, NULL
, "must specify input unicode string file name with -parse");
1953 return STATUS_ERROR
;
1956 strcpy (mGlobals
.SourceFiles
.FileName
, Argv
[0]);
1959 return STATUS_SUCCESS
;
1962 // Found "-lang eng,spa+cat" on the command line. Parse the
1963 // language list and save the setting for later processing.
1967 AddCommandLineLanguage (
1971 WCHAR_STRING_LIST
*WNewList
;
1975 // Keep processing the input string until we find the end.
1979 // Allocate memory for a new list element, fill it in, and
1980 // add it to our list.
1982 WNewList
= MALLOC (sizeof (WCHAR_STRING_LIST
));
1983 if (WNewList
== NULL
) {
1984 Error (UTILITY_NAME
, 0, 0, NULL
, "memory allocation failure");
1985 return STATUS_ERROR
;
1988 memset ((char *) WNewList
, 0, sizeof (WCHAR_STRING_LIST
));
1989 WNewList
->Str
= malloc ((strlen (Language
) + 1) * sizeof (WCHAR
));
1990 if (WNewList
->Str
== NULL
) {
1992 Error (UTILITY_NAME
, 0, 0, NULL
, "memory allocation failure");
1993 return STATUS_ERROR
;
1996 // Copy it as unicode to our new structure. Then remove the
1997 // plus signs in it, and verify each language name is 3 characters
1998 // long. If we find a comma, then we're done with this group, so
2001 UnicodeSPrint (WNewList
->Str
, (strlen (Language
) + 1) * sizeof (WCHAR
), L
"%a", Language
);
2002 From
= To
= WNewList
->Str
;
2004 if (*From
== L
',') {
2008 if ((StrLen (From
) < LANGUAGE_IDENTIFIER_NAME_LEN
) ||
2010 (From
[LANGUAGE_IDENTIFIER_NAME_LEN
] != 0) &&
2011 (From
[LANGUAGE_IDENTIFIER_NAME_LEN
] != UNICODE_PLUS_SIGN
) &&
2012 (From
[LANGUAGE_IDENTIFIER_NAME_LEN
] != L
',')
2015 Error (UTILITY_NAME
, 0, 0, Language
, "invalid format for language name on command line");
2016 FREE (WNewList
->Str
);
2018 return STATUS_ERROR
;
2021 StrnCpy (To
, From
, LANGUAGE_IDENTIFIER_NAME_LEN
);
2022 To
+= LANGUAGE_IDENTIFIER_NAME_LEN
;
2023 From
+= LANGUAGE_IDENTIFIER_NAME_LEN
;
2024 if (*From
== L
'+') {
2031 // Add it to our linked list
2033 if (mGlobals
.Language
== NULL
) {
2034 mGlobals
.Language
= WNewList
;
2036 mGlobals
.LastLanguage
->Next
= WNewList
;
2039 mGlobals
.LastLanguage
= WNewList
;
2041 // Skip to next entry (comma-separated list)
2044 if (*Language
== L
',') {
2053 return STATUS_SUCCESS
;
2056 // The contents of the text file are expected to be (one per line)
2057 // STRING_IDENTIFIER_NAME ScopeName
2059 // STR_ID_MY_FAVORITE_STRING IBM
2063 ParseIndirectionFiles (
2064 TEXT_STRING_LIST
*Files
2073 WCHAR_MATCHING_STRING_LIST
*NewList
;
2075 Line
[sizeof (Line
) - 1] = 0;
2077 while (Files
!= NULL
) {
2078 Fptr
= fopen (Files
->Str
, "r");
2081 Error (NULL
, 0, 0, Files
->Str
, "failed to open input indirection file for reading");
2082 return STATUS_ERROR
;
2085 while (fgets (Line
, sizeof (Line
), Fptr
) != NULL
) {
2087 // remove terminating newline for error printing purposes.
2089 if (Line
[strlen (Line
) - 1] == '\n') {
2090 Line
[strlen (Line
) - 1] = 0;
2094 if (Line
[sizeof (Line
) - 1] != 0) {
2095 Error (Files
->Str
, LineCount
, 0, "line length exceeds maximum supported", NULL
);
2100 while (*StringName
&& (isspace (*StringName
))) {
2105 if ((*StringName
== '_') || isalpha (*StringName
)) {
2107 while ((*End
) && (*End
== '_') || (isalnum (*End
))) {
2111 if (isspace (*End
)) {
2114 while (isspace (*End
)) {
2120 while (*End
&& !isspace (*End
)) {
2126 // Add the string name/scope pair
2128 NewList
= malloc (sizeof (WCHAR_MATCHING_STRING_LIST
));
2129 if (NewList
== NULL
) {
2130 Error (NULL
, 0, 0, "memory allocation error", NULL
);
2134 memset (NewList
, 0, sizeof (WCHAR_MATCHING_STRING_LIST
));
2135 NewList
->Str1
= (WCHAR
*) malloc ((strlen (StringName
) + 1) * sizeof (WCHAR
));
2136 NewList
->Str2
= (WCHAR
*) malloc ((strlen (ScopeName
) + 1) * sizeof (WCHAR
));
2137 if ((NewList
->Str1
== NULL
) || (NewList
->Str2
== NULL
)) {
2138 Error (NULL
, 0, 0, "memory allocation error", NULL
);
2142 UnicodeSPrint (NewList
->Str1
, strlen (StringName
) + 1, L
"%a", StringName
);
2143 UnicodeSPrint (NewList
->Str2
, strlen (ScopeName
) + 1, L
"%a", ScopeName
);
2144 if (mGlobals
.IndirectionList
== NULL
) {
2145 mGlobals
.IndirectionList
= NewList
;
2147 mGlobals
.LastIndirectionList
->Next
= NewList
;
2150 mGlobals
.LastIndirectionList
= NewList
;
2152 Error (Files
->Str
, LineCount
, 0, StringName
, "invalid line : expected 'StringIdentifier Scope'");
2156 Error (Files
->Str
, LineCount
, 0, StringName
, "invalid line : expected 'StringIdentifier Scope'");
2160 Error (Files
->Str
, LineCount
, 0, StringName
, "invalid string identifier");
2168 Files
= Files
->Next
;
2174 return STATUS_ERROR
;
2177 return STATUS_SUCCESS
;
2183 TEXT_STRING_LIST
*ScanFiles
2186 char Line
[MAX_LINE_LEN
];
2192 char *StringTokenPos
;
2193 TEXT_STRING_LIST
*SList
;
2197 // Put a null-terminator at the end of the line. If we read in
2198 // a line longer than we support, then we can catch it.
2200 Line
[MAX_LINE_LEN
- 1] = 0;
2202 // Process each file. If they gave us a skip extension list, then
2203 // skip it if the extension matches.
2205 while (ScanFiles
!= NULL
) {
2207 for (SList
= mGlobals
.SkipExt
; SList
!= NULL
; SList
= SList
->Next
) {
2208 if ((strlen (ScanFiles
->Str
) > strlen (SList
->Str
)) &&
2209 (strcmp (ScanFiles
->Str
+ strlen (ScanFiles
->Str
) - strlen (SList
->Str
), SList
->Str
) == 0)
2213 // printf ("Match: %s : %s\n", ScanFiles->Str, SList->Str);
2220 if (mGlobals
.VerboseScan
) {
2221 printf ("Scanning %s\n", ScanFiles
->Str
);
2224 Fptr
= fopen (ScanFiles
->Str
, "r");
2226 Error (NULL
, 0, 0, ScanFiles
->Str
, "failed to open input file for scanning");
2227 return STATUS_ERROR
;
2231 while (fgets (Line
, sizeof (Line
), Fptr
) != NULL
) {
2233 if (Line
[MAX_LINE_LEN
- 1] != 0) {
2234 Error (ScanFiles
->Str
, LineNum
, 0, "line length exceeds maximum supported by tool", NULL
);
2236 return STATUS_ERROR
;
2239 // Remove the newline from the input line so we can print a warning message
2241 if (Line
[strlen (Line
) - 1] == '\n') {
2242 Line
[strlen (Line
) - 1] = 0;
2245 // Terminate the line at // comments
2247 Cptr
= strstr (Line
, "//");
2253 while ((Cptr
= strstr (Cptr
, STRING_TOKEN
)) != NULL
) {
2255 // Found "STRING_TOKEN". Make sure we don't have NUM_STRING_TOKENS or
2256 // something like that. Then make sure it's followed by
2257 // an open parenthesis, a string identifier, and then a closing
2260 if (mGlobals
.VerboseScan
) {
2261 printf (" %d: %s", LineNum
, Cptr
);
2264 if (((Cptr
== Line
) || (!IsValidIdentifierChar (*(Cptr
- 1), FALSE
))) &&
2265 (!IsValidIdentifierChar (*(Cptr
+ sizeof (STRING_TOKEN
) - 1), FALSE
))
2267 StringTokenPos
= Cptr
;
2269 Cptr
+= strlen (STRING_TOKEN
);
2270 while (*Cptr
&& isspace (*Cptr
) && (*Cptr
!= '(')) {
2275 Warning (ScanFiles
->Str
, LineNum
, 0, StringTokenPos
, "expected "STRING_TOKEN
"(identifier)");
2278 // Skip over the open-parenthesis and find the next non-blank character
2281 while (isspace (*Cptr
)) {
2286 if ((*Cptr
== '_') || isalpha (*Cptr
)) {
2287 while ((*Cptr
== '_') || (isalnum (*Cptr
))) {
2292 while (*Cptr
&& isspace (*Cptr
)) {
2297 Warning (ScanFiles
->Str
, LineNum
, 0, StringTokenPos
, "expected "STRING_TOKEN
"(identifier)");
2307 // Add the string identifier to the list of used strings
2309 ParserSetPosition (ScanFiles
->Str
, LineNum
);
2310 StringDBSetStringReferenced (SavePtr
, mGlobals
.IgnoreNotFound
);
2311 if (mGlobals
.VerboseScan
) {
2312 printf ("...referenced %s", SavePtr
);
2315 Warning (ScanFiles
->Str
, LineNum
, 0, StringTokenPos
, "expected valid string identifier name");
2320 // Found it, but it's a substring of something else. Advance our pointer.
2325 if (mGlobals
.VerboseScan
) {
2334 // Skipping this file type
2336 if (mGlobals
.VerboseScan
) {
2337 printf ("Skip scanning of %s\n", ScanFiles
->Str
);
2341 ScanFiles
= ScanFiles
->Next
;
2344 return STATUS_SUCCESS
;
2347 // Free the global string lists we allocated memory for
2355 TEXT_STRING_LIST
*Temp
;
2356 WCHAR_STRING_LIST
*WTemp
;
2359 // Traverse the include paths, freeing each
2361 while (mGlobals
.IncludePaths
!= NULL
) {
2362 Temp
= mGlobals
.IncludePaths
->Next
;
2363 free (mGlobals
.IncludePaths
->Str
);
2364 free (mGlobals
.IncludePaths
);
2365 mGlobals
.IncludePaths
= Temp
;
2368 // If we did a scan, then free up our
2369 // list of files to scan.
2371 while (mGlobals
.ScanFileName
!= NULL
) {
2372 Temp
= mGlobals
.ScanFileName
->Next
;
2373 free (mGlobals
.ScanFileName
->Str
);
2374 free (mGlobals
.ScanFileName
);
2375 mGlobals
.ScanFileName
= Temp
;
2378 // If they gave us a list of filename extensions to
2379 // skip on scan, then free them up.
2381 while (mGlobals
.SkipExt
!= NULL
) {
2382 Temp
= mGlobals
.SkipExt
->Next
;
2383 free (mGlobals
.SkipExt
->Str
);
2384 free (mGlobals
.SkipExt
);
2385 mGlobals
.SkipExt
= Temp
;
2388 // Free up any languages specified
2390 while (mGlobals
.Language
!= NULL
) {
2391 WTemp
= mGlobals
.Language
->Next
;
2392 free (mGlobals
.Language
->Str
);
2393 free (mGlobals
.Language
);
2394 mGlobals
.Language
= WTemp
;
2397 // Free up our indirection list
2399 while (mGlobals
.IndirectionList
!= NULL
) {
2400 mGlobals
.LastIndirectionList
= mGlobals
.IndirectionList
->Next
;
2401 free (mGlobals
.IndirectionList
->Str1
);
2402 free (mGlobals
.IndirectionList
->Str2
);
2403 free (mGlobals
.IndirectionList
);
2404 mGlobals
.IndirectionList
= mGlobals
.LastIndirectionList
;
2407 while (mGlobals
.IndirectionFileName
!= NULL
) {
2408 mGlobals
.LastIndirectionFileName
= mGlobals
.IndirectionFileName
->Next
;
2409 free (mGlobals
.IndirectionFileName
->Str
);
2410 free (mGlobals
.IndirectionFileName
);
2411 mGlobals
.IndirectionFileName
= mGlobals
.LastIndirectionFileName
;
2417 IsValidIdentifierChar (
2423 // If it's the first character of an identifier, then
2424 // it must be one of [A-Za-z_].
2427 if (isalpha (Char
) || (Char
== '_')) {
2432 // If it's not the first character, then it can
2433 // be one of [A-Za-z_0-9]
2435 if (isalnum (Char
) || (Char
== '_')) {
2446 SOURCE_FILE
*SourceFile
2449 SourceFile
->LineNum
= 1;
2450 SourceFile
->FileBufferPtr
= SourceFile
->FileBuffer
;
2451 SourceFile
->EndOfFile
= 0;
2457 SOURCE_FILE
*SourceFile
,
2459 BOOLEAN StopAfterNewline
2462 while (!EndOfFile (SourceFile
)) {
2464 // Check for the character of interest
2466 if (SourceFile
->FileBufferPtr
[0] == WChar
) {
2469 if (SourceFile
->FileBufferPtr
[0] == UNICODE_LF
) {
2470 SourceFile
->LineNum
++;
2471 if (StopAfterNewline
) {
2472 SourceFile
->FileBufferPtr
++;
2473 if (SourceFile
->FileBufferPtr
[0] == 0) {
2474 SourceFile
->FileBufferPtr
++;
2481 SourceFile
->FileBufferPtr
++;
2495 Routine Description:
2497 Displays the standard utility information to SDTOUT
2509 printf ("%s v%d.%d -Utility to process unicode strings file..\n", UTILITY_NAME
, UTILITY_MAJOR_VERSION
, UTILITY_MINOR_VERSION
);
2510 printf ("Copyright (c) 1999-2007 Intel Corporation. All rights reserved.\n");
2520 Routine Description:
2522 Print usage information for this utility.
2535 static const char *Str
[] = {
2537 " Usage: "UTILITY_NAME
" -parse {parse options} [FileNames]",
2538 " "UTILITY_NAME
" -scan {scan options} [FileName]",
2539 " "UTILITY_NAME
" -dump {dump options}",
2540 " Common options include:",
2541 " -h,--help,-?,/? display help messages",
2542 " -V,--version display version information",
2543 " -db Database required name of output/input database file",
2544 " -bn BaseName for use in the .h and .c output files",
2545 " Default = "DEFAULT_BASE_NAME
,
2546 " -v for verbose output",
2547 " -vdbw for verbose output when writing database",
2548 " -vdbr for verbose output when reading database",
2549 " -od FileName to specify an output database file name",
2550 " Parse options include:",
2551 " -i IncludePath add IncludePath to list of search paths",
2552 " -newdb to not read in existing database file",
2553 " -uqs to indicate that unquoted strings are used",
2554 " FileNames name of one or more unicode files to parse",
2555 " Scan options include:",
2556 " -scan scan text file(s) for STRING_TOKEN() usage",
2557 " -skipext .ext to skip scan of files with .ext filename extension",
2558 " -ignorenotfound ignore if a given STRING_TOKEN(STR) is not ",
2559 " found in the database",
2560 " FileNames one or more files to scan",
2561 " Dump options include:",
2562 " -oc FileName write string data to FileName",
2563 " -oh FileName write string defines to FileName",
2564 " -ou FileName dump database to unicode file FileName",
2565 " -lang Lang only dump for the language 'Lang'",
2566 " -if FileName to specify an indirection file",
2567 " -hpk FileName to create an HII export pack of the strings",
2569 " The expected process is to parse a unicode string file to create an initial",
2570 " database of string identifier names and string definitions. Then text files",
2571 " should be scanned for STRING_TOKEN() usages, and the referenced",
2572 " strings will be tagged as used in the database. After all files have been",
2573 " scanned, then the database should be dumped to create the necessary output",
2581 for (Index
= 0; Str
[Index
] != NULL
; Index
++) {
2582 fprintf (stdout
, "%s\n", Str
[Index
]);