/*++ | |
Copyright (c) 2004 - 2010, Intel Corporation. All rights reserved.<BR> | |
This program and the accompanying materials | |
are licensed and made available under the terms and conditions of the BSD License | |
which accompanies this distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
Module Name: | |
StrGather.c | |
Abstract: | |
Parse a strings file and create or add to a string database file. | |
--*/ | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <ctype.h> | |
#include <Tiano.h> | |
#include <EfiUtilityMsgs.h> | |
#include <EfiHii.h> | |
#include "StrGather.h" | |
#include "StringDB.h" | |
#define UTILITY_NAME "StrGather" | |
#define UTILITY_VERSION "v1.2" | |
typedef UINT16 WCHAR; | |
#define MAX_PATH 1024 | |
#define MAX_NEST_DEPTH 20 // just in case we get in an endless loop. | |
#define MAX_STRING_IDENTIFIER_NAME 128 // number of wchars | |
#define MAX_LINE_LEN 400 | |
#define STRING_TOKEN "STRING_TOKEN" | |
#define DEFAULT_BASE_NAME "BaseName" | |
// | |
// Operational modes for this utility | |
// | |
#define MODE_UNKNOWN 0 | |
#define MODE_PARSE 1 | |
#define MODE_SCAN 2 | |
#define MODE_DUMP 3 | |
// | |
// This is how we invoke the C preprocessor on the source file | |
// to resolve #if, #else, etc. | |
// | |
#define PREPROCESSOR_COMMAND "cl" | |
#define PREPROCESSOR_OPTIONS "/nologo /EP /TC /DSTRGATHER" | |
#define PREPROCESS_TEMP_FILE_EXTENSION ".ii" | |
#define PREPROCESS_OUTPUT_FILE_EXTENSION ".iii" | |
// | |
// We keep a linked list of these for the source files we process | |
// | |
typedef struct _SOURCE_FILE { | |
FILE *Fptr; | |
WCHAR *FileBuffer; | |
WCHAR *FileBufferPtr; | |
UINT32 FileSize; | |
INT8 FileName[MAX_PATH]; | |
UINT32 LineNum; | |
BOOLEAN EndOfFile; | |
BOOLEAN SkipToHash; | |
struct _SOURCE_FILE *Previous; | |
struct _SOURCE_FILE *Next; | |
WCHAR ControlCharacter; | |
} SOURCE_FILE; | |
#define DEFAULT_CONTROL_CHARACTER UNICODE_SLASH | |
// | |
// Here's all our globals. We need a linked list of include paths, a linked | |
// list of source files, a linked list of subdirectories (appended to each | |
// include path when searching), and a couple other fields. | |
// | |
static struct { | |
SOURCE_FILE SourceFiles; | |
TEXT_STRING_LIST *IncludePaths; // all include paths to search | |
TEXT_STRING_LIST *LastIncludePath; | |
TEXT_STRING_LIST *ScanFileName; | |
TEXT_STRING_LIST *LastScanFileName; | |
TEXT_STRING_LIST *SkipExt; // if -skipext .uni | |
TEXT_STRING_LIST *LastSkipExt; | |
TEXT_STRING_LIST *IndirectionFileName; | |
TEXT_STRING_LIST *LastIndirectionFileName; | |
TEXT_STRING_LIST *DatabaseFileName; | |
TEXT_STRING_LIST *LastDatabaseFileName; | |
TEXT_STRING_LIST *PreprocessFlags; | |
TEXT_STRING_LIST *LastPreprocessFlags; | |
WCHAR_STRING_LIST *Language; | |
WCHAR_STRING_LIST *LastLanguage; | |
WCHAR_MATCHING_STRING_LIST *IndirectionList; // from indirection file(s) | |
WCHAR_MATCHING_STRING_LIST *LastIndirectionList; | |
BOOLEAN Verbose; // for more detailed output | |
BOOLEAN VerboseDatabaseWrite; // for more detailed output when writing database | |
BOOLEAN VerboseDatabaseRead; // for more detailed output when reading database | |
BOOLEAN NewDatabase; // to start from scratch | |
BOOLEAN IgnoreNotFound; // when scanning | |
BOOLEAN VerboseScan; | |
BOOLEAN UnquotedStrings; // -uqs option | |
BOOLEAN Preprocess; // -ppflag option | |
INT8 PreprocessFileName[MAX_PATH]; | |
INT8 OutputDatabaseFileName[MAX_PATH]; | |
INT8 StringHFileName[MAX_PATH]; | |
INT8 StringCFileName[MAX_PATH]; // output .C filename | |
INT8 DumpUFileName[MAX_PATH]; // output unicode dump file name | |
INT8 HiiExportPackFileName[MAX_PATH]; // HII export pack file name | |
INT8 BaseName[MAX_PATH]; // base filename of the strings file | |
INT8 OutputDependencyFileName[MAX_PATH]; | |
FILE *OutputDependencyFptr; | |
UINT32 Mode; | |
} mGlobals; | |
static | |
BOOLEAN | |
IsValidIdentifierChar ( | |
INT8 Char, | |
BOOLEAN FirstChar | |
); | |
static | |
void | |
RewindFile ( | |
SOURCE_FILE *SourceFile | |
); | |
static | |
BOOLEAN | |
SkipTo ( | |
SOURCE_FILE *SourceFile, | |
WCHAR WChar, | |
BOOLEAN StopAfterNewline | |
); | |
static | |
UINT32 | |
SkipWhiteSpace ( | |
SOURCE_FILE *SourceFile | |
); | |
static | |
BOOLEAN | |
IsWhiteSpace ( | |
SOURCE_FILE *SourceFile | |
); | |
static | |
BOOLEAN | |
EndOfFile ( | |
SOURCE_FILE *SourceFile | |
); | |
static | |
void | |
PreprocessFile ( | |
SOURCE_FILE *SourceFile | |
); | |
static | |
UINT32 | |
GetStringIdentifierName ( | |
IN SOURCE_FILE *SourceFile, | |
IN OUT WCHAR *StringIdentifierName, | |
IN UINT32 StringIdentifierNameLen | |
); | |
static | |
STATUS | |
GetLanguageIdentifierName ( | |
IN SOURCE_FILE *SourceFile, | |
IN OUT WCHAR *LanguageIdentifierName, | |
IN UINT32 LanguageIdentifierNameLen, | |
IN BOOLEAN Optional | |
); | |
static | |
WCHAR * | |
GetPrintableLanguageName ( | |
IN SOURCE_FILE *SourceFile | |
); | |
static | |
STATUS | |
AddCommandLineLanguage ( | |
IN INT8 *Language | |
); | |
static | |
WCHAR * | |
GetQuotedString ( | |
SOURCE_FILE *SourceFile, | |
BOOLEAN Optional | |
); | |
static | |
STATUS | |
ProcessIncludeFile ( | |
SOURCE_FILE *SourceFile, | |
SOURCE_FILE *ParentSourceFile | |
); | |
static | |
STATUS | |
ParseFile ( | |
SOURCE_FILE *SourceFile | |
); | |
static | |
FILE * | |
FindFile ( | |
IN INT8 *FileName, | |
OUT INT8 *FoundFileName, | |
IN UINT32 FoundFileNameLen | |
); | |
static | |
STATUS | |
ProcessArgs ( | |
int Argc, | |
char *Argv[] | |
); | |
static | |
STATUS | |
ProcessFile ( | |
SOURCE_FILE *SourceFile | |
); | |
static | |
UINT32 | |
wstrcmp ( | |
WCHAR *Buffer, | |
WCHAR *Str | |
); | |
static | |
WCHAR * | |
wstrcatenate ( | |
WCHAR *Dst, | |
WCHAR *Src | |
); | |
static | |
void | |
Usage ( | |
VOID | |
); | |
static | |
void | |
FreeLists ( | |
VOID | |
); | |
static | |
void | |
ProcessTokenString ( | |
SOURCE_FILE *SourceFile | |
); | |
static | |
void | |
ProcessTokenInclude ( | |
SOURCE_FILE *SourceFile | |
); | |
static | |
void | |
ProcessTokenScope ( | |
SOURCE_FILE *SourceFile | |
); | |
static | |
void | |
ProcessTokenLanguage ( | |
SOURCE_FILE *SourceFile | |
); | |
static | |
void | |
ProcessTokenLangDef ( | |
SOURCE_FILE *SourceFile | |
); | |
static | |
VOID | |
ProcessTokenSecondaryLangDef ( | |
SOURCE_FILE *SourceFile | |
); | |
static | |
STATUS | |
ScanFiles ( | |
TEXT_STRING_LIST *ScanFiles | |
); | |
static | |
STATUS | |
ParseIndirectionFiles ( | |
TEXT_STRING_LIST *Files | |
); | |
int | |
main ( | |
int Argc, | |
char *Argv[] | |
) | |
/*++ | |
Routine Description: | |
Call the routine to parse the command-line options, then process the file. | |
Arguments: | |
Argc - Standard C main() argc and argv. | |
Argv - Standard C main() argc and argv. | |
Returns: | |
0 if successful | |
nonzero otherwise | |
--*/ | |
{ | |
STATUS Status; | |
SetUtilityName (UTILITY_NAME); | |
// | |
// Process the command-line arguments | |
// | |
Status = ProcessArgs (Argc, Argv); | |
if (Status != STATUS_SUCCESS) { | |
return Status; | |
} | |
// | |
// Initialize the database manager | |
// | |
StringDBConstructor (); | |
// | |
// We always try to read in an existing database file. It may not | |
// exist, which is ok usually. | |
// | |
if (mGlobals.NewDatabase == 0) { | |
// | |
// Read all databases specified. | |
// | |
for (mGlobals.LastDatabaseFileName = mGlobals.DatabaseFileName; | |
mGlobals.LastDatabaseFileName != NULL; | |
mGlobals.LastDatabaseFileName = mGlobals.LastDatabaseFileName->Next | |
) { | |
Status = StringDBReadDatabase (mGlobals.LastDatabaseFileName->Str, TRUE, mGlobals.VerboseDatabaseRead); | |
if (Status != STATUS_SUCCESS) { | |
return Status; | |
} | |
} | |
} | |
// | |
// Read indirection file(s) if specified | |
// | |
if (ParseIndirectionFiles (mGlobals.IndirectionFileName) != STATUS_SUCCESS) { | |
goto Finish; | |
} | |
// | |
// If scanning source files, do that now | |
// | |
if (mGlobals.Mode == MODE_SCAN) { | |
ScanFiles (mGlobals.ScanFileName); | |
} else if (mGlobals.Mode == MODE_PARSE) { | |
// | |
// Parsing a unicode strings file | |
// | |
mGlobals.SourceFiles.ControlCharacter = DEFAULT_CONTROL_CHARACTER; | |
if (mGlobals.OutputDependencyFileName[0] != 0) { | |
if ((mGlobals.OutputDependencyFptr = fopen (mGlobals.OutputDependencyFileName, "w")) == NULL) { | |
Error (NULL, 0, 0, mGlobals.OutputDependencyFileName, "failed to open output dependency file"); | |
goto Finish; | |
} | |
} | |
Status = ProcessIncludeFile (&mGlobals.SourceFiles, NULL); | |
if (mGlobals.OutputDependencyFptr != NULL) { | |
fclose (mGlobals.OutputDependencyFptr); | |
} | |
if (Status != STATUS_SUCCESS) { | |
goto Finish; | |
} | |
} | |
// | |
// Create the string defines header file if there have been no errors. | |
// | |
ParserSetPosition (NULL, 0); | |
if ((mGlobals.StringHFileName[0] != 0) && (GetUtilityStatus () < STATUS_ERROR)) { | |
Status = StringDBDumpStringDefines (mGlobals.StringHFileName, mGlobals.BaseName); | |
if (Status != EFI_SUCCESS) { | |
goto Finish; | |
} | |
} | |
// | |
// Dump the strings to a .c file if there have still been no errors. | |
// | |
if ((mGlobals.StringCFileName[0] != 0) && (GetUtilityStatus () < STATUS_ERROR)) { | |
Status = StringDBDumpCStrings ( | |
mGlobals.BaseName, | |
mGlobals.StringCFileName, | |
mGlobals.Language | |
); | |
if (Status != EFI_SUCCESS) { | |
goto Finish; | |
} | |
} | |
// | |
// Dump the database if requested | |
// | |
if ((mGlobals.DumpUFileName[0] != 0) && (GetUtilityStatus () < STATUS_ERROR)) { | |
StringDBDumpDatabase (NULL, mGlobals.DumpUFileName, FALSE); | |
} | |
// | |
// Dump the string data as HII binary string pack if requested | |
// | |
if ((mGlobals.HiiExportPackFileName[0] != 0) && (GetUtilityStatus () < STATUS_ERROR)) { | |
StringDBCreateHiiExportPack (mGlobals.HiiExportPackFileName, mGlobals.Language); | |
} | |
// | |
// Always update the database if no errors and not in dump mode. If they specified -od | |
// for an output database file name, then use that name. Otherwise use the name of | |
// the first database file specified with -db | |
// | |
if ((mGlobals.Mode != MODE_DUMP) && (GetUtilityStatus () < STATUS_ERROR)) { | |
if (mGlobals.OutputDatabaseFileName[0]) { | |
Status = StringDBWriteDatabase (mGlobals.OutputDatabaseFileName, mGlobals.VerboseDatabaseWrite); | |
} else { | |
Status = StringDBWriteDatabase (mGlobals.DatabaseFileName->Str, mGlobals.VerboseDatabaseWrite); | |
} | |
if (Status != EFI_SUCCESS) { | |
goto Finish; | |
} | |
} | |
Finish: | |
// | |
// Free up memory | |
// | |
FreeLists (); | |
StringDBDestructor (); | |
return GetUtilityStatus (); | |
} | |
static | |
STATUS | |
ProcessIncludeFile ( | |
SOURCE_FILE *SourceFile, | |
SOURCE_FILE *ParentSourceFile | |
) | |
/*++ | |
Routine Description: | |
Given a source file, open the file and parse it | |
Arguments: | |
SourceFile - name of file to parse | |
ParentSourceFile - for error reporting purposes, the file that #included SourceFile. | |
Returns: | |
Standard status. | |
--*/ | |
{ | |
static UINT32 NestDepth = 0; | |
INT8 FoundFileName[MAX_PATH]; | |
STATUS Status; | |
Status = STATUS_SUCCESS; | |
NestDepth++; | |
// | |
// Print the file being processed. Indent so you can tell the include nesting | |
// depth. | |
// | |
if (mGlobals.Verbose) { | |
fprintf (stdout, "%*cProcessing file '%s'\n", NestDepth * 2, ' ', SourceFile->FileName); | |
} | |
// | |
// Make sure we didn't exceed our maximum nesting depth | |
// | |
if (NestDepth > MAX_NEST_DEPTH) { | |
Error (NULL, 0, 0, SourceFile->FileName, "max nesting depth (%d) exceeded", NestDepth); | |
Status = STATUS_ERROR; | |
goto Finish; | |
} | |
// | |
// Try to open the file locally, and if that fails try along our include paths. | |
// | |
strcpy (FoundFileName, SourceFile->FileName); | |
if ((SourceFile->Fptr = fopen (FoundFileName, "rb")) == NULL) { | |
// | |
// Try to find it among the paths if it has a parent (that is, it is included | |
// by someone else). | |
// | |
if (ParentSourceFile == NULL) { | |
Error (NULL, 0, 0, SourceFile->FileName, "file not found"); | |
Status = STATUS_ERROR; | |
goto Finish; | |
} | |
SourceFile->Fptr = FindFile (SourceFile->FileName, FoundFileName, sizeof (FoundFileName)); | |
if (SourceFile->Fptr == NULL) { | |
Error (ParentSourceFile->FileName, ParentSourceFile->LineNum, 0, SourceFile->FileName, "include file not found"); | |
Status = STATUS_ERROR; | |
goto Finish; | |
} | |
} | |
// | |
// Output the dependency | |
// | |
if (mGlobals.OutputDependencyFptr != NULL) { | |
fprintf (mGlobals.OutputDependencyFptr, "%s : %s\n", mGlobals.DatabaseFileName->Str, FoundFileName); | |
// | |
// Add pseudo target to avoid incremental build failure when the file is deleted | |
// | |
fprintf (mGlobals.OutputDependencyFptr, "%s : \n", FoundFileName); | |
} | |
// | |
// Process the file found | |
// | |
ProcessFile (SourceFile); | |
Finish: | |
NestDepth--; | |
// | |
// Close open files and return status | |
// | |
if (SourceFile->Fptr != NULL) { | |
fclose (SourceFile->Fptr); | |
} | |
return Status; | |
} | |
static | |
STATUS | |
ProcessFile ( | |
SOURCE_FILE *SourceFile | |
) | |
{ | |
// | |
// Get the file size, and then read the entire thing into memory. | |
// Allocate space for a terminator character. | |
// | |
fseek (SourceFile->Fptr, 0, SEEK_END); | |
SourceFile->FileSize = ftell (SourceFile->Fptr); | |
fseek (SourceFile->Fptr, 0, SEEK_SET); | |
SourceFile->FileBuffer = (WCHAR *) malloc (SourceFile->FileSize + sizeof (WCHAR)); | |
if (SourceFile->FileBuffer == NULL) { | |
Error (NULL, 0, 0, "memory allocation failure", NULL); | |
return STATUS_ERROR; | |
} | |
fread ((VOID *) SourceFile->FileBuffer, SourceFile->FileSize, 1, SourceFile->Fptr); | |
SourceFile->FileBuffer[(SourceFile->FileSize / sizeof (WCHAR))] = UNICODE_NULL; | |
// | |
// Pre-process the file to replace comments with spaces | |
// | |
PreprocessFile (SourceFile); | |
// | |
// Parse the file | |
// | |
ParseFile (SourceFile); | |
free (SourceFile->FileBuffer); | |
return STATUS_SUCCESS; | |
} | |
static | |
STATUS | |
ParseFile ( | |
SOURCE_FILE *SourceFile | |
) | |
{ | |
BOOLEAN InComment; | |
UINT32 Len; | |
// | |
// First character of a unicode file is special. Make sure | |
// | |
if (SourceFile->FileBufferPtr[0] != UNICODE_FILE_START) { | |
Error (SourceFile->FileName, 1, 0, SourceFile->FileName, "file does not appear to be a unicode file"); | |
return STATUS_ERROR; | |
} | |
SourceFile->FileBufferPtr++; | |
InComment = FALSE; | |
// | |
// Print the first line if in verbose mode | |
// | |
if (mGlobals.Verbose) { | |
printf ("%d: %S\n", SourceFile->LineNum, SourceFile->FileBufferPtr); | |
} | |
// | |
// Since the syntax is relatively straightforward, just switch on the next char | |
// | |
while (!EndOfFile (SourceFile)) { | |
// | |
// Check for whitespace | |
// | |
if (SourceFile->FileBufferPtr[0] == UNICODE_SPACE) { | |
SourceFile->FileBufferPtr++; | |
} else if (SourceFile->FileBufferPtr[0] == UNICODE_TAB) { | |
SourceFile->FileBufferPtr++; | |
} else if (SourceFile->FileBufferPtr[0] == UNICODE_CR) { | |
SourceFile->FileBufferPtr++; | |
} else if (SourceFile->FileBufferPtr[0] == UNICODE_LF) { | |
SourceFile->FileBufferPtr++; | |
SourceFile->LineNum++; | |
if (mGlobals.Verbose) { | |
printf ("%d: %S\n", SourceFile->LineNum, SourceFile->FileBufferPtr); | |
} | |
InComment = FALSE; | |
} else if (SourceFile->FileBufferPtr[0] == 0) { | |
SourceFile->FileBufferPtr++; | |
} else if (InComment) { | |
SourceFile->FileBufferPtr++; | |
} else if ((SourceFile->FileBufferPtr[0] == UNICODE_SLASH) && (SourceFile->FileBufferPtr[1] == UNICODE_SLASH)) { | |
SourceFile->FileBufferPtr += 2; | |
InComment = TRUE; | |
} else if (SourceFile->SkipToHash && (SourceFile->FileBufferPtr[0] != SourceFile->ControlCharacter)) { | |
SourceFile->FileBufferPtr++; | |
} else { | |
SourceFile->SkipToHash = FALSE; | |
if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && | |
((Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"include")) > 0) | |
) { | |
SourceFile->FileBufferPtr += Len + 1; | |
ProcessTokenInclude (SourceFile); | |
} else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && | |
(Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"scope")) > 0 | |
) { | |
SourceFile->FileBufferPtr += Len + 1; | |
ProcessTokenScope (SourceFile); | |
} else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && | |
(Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"language")) > 0 | |
) { | |
SourceFile->FileBufferPtr += Len + 1; | |
ProcessTokenLanguage (SourceFile); | |
} else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && | |
(Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"langdef")) > 0 | |
) { | |
SourceFile->FileBufferPtr += Len + 1; | |
ProcessTokenLangDef (SourceFile); | |
} else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && | |
(Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"secondarylang")) > 0 | |
) { | |
SourceFile->FileBufferPtr += Len + 1; | |
ProcessTokenSecondaryLangDef (SourceFile); | |
} else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && | |
(Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"string")) > 0 | |
) { | |
SourceFile->FileBufferPtr += Len + 1; | |
ProcessTokenString (SourceFile); | |
} else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && | |
(Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"EFI_BREAKPOINT()")) > 0 | |
) { | |
SourceFile->FileBufferPtr += Len; | |
EFI_BREAKPOINT (); | |
} else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && | |
(SourceFile->FileBufferPtr[1] == UNICODE_EQUAL_SIGN) | |
) { | |
SourceFile->ControlCharacter = SourceFile->FileBufferPtr[2]; | |
SourceFile->FileBufferPtr += 3; | |
} else { | |
Error (SourceFile->FileName, SourceFile->LineNum, 0, "unrecognized token", "%S", SourceFile->FileBufferPtr); | |
// | |
// Treat rest of line as a comment. | |
// | |
InComment = TRUE; | |
} | |
} | |
} | |
return STATUS_SUCCESS; | |
} | |
static | |
void | |
PreprocessFile ( | |
SOURCE_FILE *SourceFile | |
) | |
/*++ | |
Routine Description: | |
Preprocess a file to replace all carriage returns with NULLs so | |
we can print lines from the file to the screen. | |
Arguments: | |
SourceFile - structure that we use to keep track of an input file. | |
Returns: | |
Nothing. | |
--*/ | |
{ | |
BOOLEAN InComment; | |
RewindFile (SourceFile); | |
InComment = FALSE; | |
while (!EndOfFile (SourceFile)) { | |
// | |
// If a line-feed, then no longer in a comment | |
// | |
if (SourceFile->FileBufferPtr[0] == UNICODE_LF) { | |
SourceFile->FileBufferPtr++; | |
SourceFile->LineNum++; | |
InComment = 0; | |
} else if (SourceFile->FileBufferPtr[0] == UNICODE_CR) { | |
// | |
// Replace all carriage returns with a NULL so we can print stuff | |
// | |
SourceFile->FileBufferPtr[0] = 0; | |
SourceFile->FileBufferPtr++; | |
} else if (InComment) { | |
SourceFile->FileBufferPtr[0] = UNICODE_SPACE; | |
SourceFile->FileBufferPtr++; | |
} else if ((SourceFile->FileBufferPtr[0] == UNICODE_SLASH) && (SourceFile->FileBufferPtr[1] == UNICODE_SLASH)) { | |
SourceFile->FileBufferPtr += 2; | |
InComment = TRUE; | |
} else { | |
SourceFile->FileBufferPtr++; | |
} | |
} | |
// | |
// Could check for end-of-file and still in a comment, but | |
// should not be necessary. So just restore the file pointers. | |
// | |
RewindFile (SourceFile); | |
} | |
static | |
WCHAR * | |
GetPrintableLanguageName ( | |
IN SOURCE_FILE *SourceFile | |
) | |
{ | |
WCHAR *String; | |
WCHAR *Start; | |
WCHAR *Ptr; | |
UINT32 Len; | |
SkipWhiteSpace (SourceFile); | |
if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) { | |
Error (SourceFile->FileName, SourceFile->LineNum, 0, "expected quoted printable language name", "%S", SourceFile->FileBufferPtr); | |
SourceFile->SkipToHash = TRUE; | |
return NULL; | |
} | |
Len = 0; | |
SourceFile->FileBufferPtr++; | |
Start = Ptr = SourceFile->FileBufferPtr; | |
while (!EndOfFile (SourceFile)) { | |
if (SourceFile->FileBufferPtr[0] == UNICODE_CR) { | |
Warning (SourceFile->FileName, SourceFile->LineNum, 0, "carriage return found in quoted string", "%S", Start); | |
break; | |
} else if (SourceFile->FileBufferPtr[0] == UNICODE_DOUBLE_QUOTE) { | |
break; | |
} | |
SourceFile->FileBufferPtr++; | |
Len++; | |
} | |
if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) { | |
Warning ( | |
SourceFile->FileName, | |
SourceFile->LineNum, | |
0, | |
"missing closing quote on printable language name string", | |
"%S", | |
Start | |
); | |
} else { | |
SourceFile->FileBufferPtr++; | |
} | |
// | |
// Now allocate memory for the string and save it off | |
// | |
String = (WCHAR *) malloc ((Len + 1) * sizeof (WCHAR)); | |
if (String == NULL) { | |
Error (NULL, 0, 0, "memory allocation failed", NULL); | |
return NULL; | |
} | |
// | |
// Copy the string from the file buffer to the local copy. | |
// We do no reformatting of it whatsoever at this point. | |
// | |
Ptr = String; | |
while (Len > 0) { | |
*Ptr = *Start; | |
Start++; | |
Ptr++; | |
Len--; | |
} | |
*Ptr = 0; | |
// | |
// Now format the string to convert \wide and \narrow controls | |
// | |
StringDBFormatString (String); | |
return String; | |
} | |
static struct { | |
WCHAR *ISO639; | |
WCHAR *RFC3066; | |
} LanguageConvertTable[] = { | |
{ L"eng", L"en-US" }, | |
{ L"fra", L"fr-FR" }, | |
{ L"spa", L"es-ES" }, | |
{ NULL, NULL } | |
}; | |
WCHAR * | |
GetLangCode ( | |
IN WCHAR *Lang | |
) | |
{ | |
UINT32 Index; | |
WCHAR *LangCode; | |
LangCode = NULL; | |
// | |
// The Lang is xx-XX format and return. | |
// | |
if (wcschr (Lang, L'-') != NULL) { | |
LangCode = (WCHAR *) malloc ((wcslen (Lang) + 1) * sizeof(WCHAR)); | |
if (LangCode != NULL) { | |
wcscpy (LangCode, Lang); | |
} | |
return LangCode; | |
} | |
// | |
// Convert the language according to the table. | |
// | |
for (Index = 0; LanguageConvertTable[Index].ISO639 != NULL; Index++) { | |
if (wcscmp(LanguageConvertTable[Index].ISO639, Lang) == 0) { | |
LangCode = (WCHAR *) malloc ((wcslen (LanguageConvertTable[Index].RFC3066) + 1) * sizeof (WCHAR)); | |
if (LangCode != NULL) { | |
wcscpy (LangCode, LanguageConvertTable[Index].RFC3066); | |
} | |
return LangCode; | |
} | |
} | |
return NULL; | |
} | |
WCHAR * | |
GetLangCodeList ( | |
IN WCHAR *SecondaryLangList | |
) | |
{ | |
WCHAR *CodeBeg, *CodeEnd; | |
WCHAR *CodeRet; | |
WCHAR *LangCodeList = NULL; | |
WCHAR *TempLangCodeList = NULL; | |
TempLangCodeList = (WCHAR *) malloc ((wcslen(SecondaryLangList) + 1) * sizeof(WCHAR)); | |
if (TempLangCodeList == NULL) { | |
return NULL; | |
} | |
wcscpy (TempLangCodeList, SecondaryLangList); | |
CodeBeg = TempLangCodeList; | |
while (CodeBeg != NULL) { | |
CodeEnd = wcschr (CodeBeg, L';'); | |
if (CodeEnd != NULL) { | |
*CodeEnd = L'\0'; | |
CodeEnd++; | |
} | |
CodeRet = GetLangCode (CodeBeg); | |
if (CodeRet != NULL) { | |
if (LangCodeList != NULL) { | |
LangCodeList = wstrcatenate (LangCodeList, L";"); | |
} | |
LangCodeList = wstrcatenate (LangCodeList, CodeRet); | |
} | |
CodeBeg = CodeEnd; | |
FREE (CodeRet); | |
} | |
free (TempLangCodeList); | |
return LangCodeList; | |
} | |
static | |
WCHAR * | |
GetSecondaryLanguageList ( | |
IN SOURCE_FILE *SourceFile | |
) | |
{ | |
WCHAR *SecondaryLangList = NULL; | |
WCHAR SecondaryLang[MAX_STRING_IDENTIFIER_NAME + 1]; | |
WCHAR *LangCodeList; | |
WCHAR *Start; | |
WCHAR *Ptr; | |
UINT32 Index; | |
SkipWhiteSpace (SourceFile); | |
if (SourceFile->FileBufferPtr[0] != UNICODE_OPEN_PAREN) { | |
Error (SourceFile->FileName, SourceFile->LineNum, 0, "expected open bracket", "%S", SourceFile->FileBufferPtr); | |
SourceFile->SkipToHash = TRUE; | |
return NULL; | |
} | |
Index = 0; | |
SecondaryLang [0] = L'\0'; | |
SourceFile->FileBufferPtr++; | |
Start = Ptr = SourceFile->FileBufferPtr; | |
while (!EndOfFile (SourceFile)) { | |
if (((SourceFile->FileBufferPtr[0] >= UNICODE_a) && (SourceFile->FileBufferPtr[0] <= UNICODE_z)) || | |
((SourceFile->FileBufferPtr[0] >= UNICODE_A) && (SourceFile->FileBufferPtr[0] <= UNICODE_Z)) || | |
(SourceFile->FileBufferPtr[0] == UNICODE_MINUS)) { | |
if (Index > MAX_STRING_IDENTIFIER_NAME) { | |
Error (SourceFile->FileName, SourceFile->LineNum, 0, "secondary language length is too lang", "%S", SourceFile->FileBufferPtr); | |
goto Err; | |
} | |
SecondaryLang[Index] = SourceFile->FileBufferPtr[0]; | |
Index++; | |
} else if (SourceFile->FileBufferPtr[0] == UNICODE_SPACE) { | |
SecondaryLang[Index] = L'\0'; | |
if (SecondaryLang[0] != L'\0') { | |
if (SecondaryLangList != NULL) { | |
SecondaryLangList = wstrcatenate (SecondaryLangList, L";"); | |
} | |
SecondaryLangList = wstrcatenate (SecondaryLangList, SecondaryLang); | |
Index = 0; | |
SecondaryLang [0] = L'\0'; | |
SourceFile->FileBufferPtr++; | |
continue; | |
} | |
} else if (SourceFile->FileBufferPtr[0] == UNICODE_CLOSE_PAREN) { | |
if (SecondaryLangList != NULL) { | |
SecondaryLangList = wstrcatenate (SecondaryLangList, L";"); | |
} | |
SecondaryLangList = wstrcatenate (SecondaryLangList, SecondaryLang); | |
break; | |
} else { | |
Error (SourceFile->FileName, SourceFile->LineNum, 0, "can not recognize the secondary language", "%S", SourceFile->FileBufferPtr); | |
goto Err; | |
} | |
SourceFile->FileBufferPtr++; | |
} | |
if (SourceFile->FileBufferPtr[0] != UNICODE_CLOSE_PAREN) { | |
Error (SourceFile->FileName, SourceFile->LineNum, 0, "missing closing bracket", "%S", Start); | |
} else { | |
SourceFile->FileBufferPtr++; | |
} | |
LangCodeList = GetLangCodeList (SecondaryLangList); | |
FREE (SecondaryLangList); | |
return LangCodeList; | |
Err: | |
FREE(SecondaryLangList); | |
return NULL; | |
} | |
static | |
WCHAR * | |
GetQuotedString ( | |
SOURCE_FILE *SourceFile, | |
BOOLEAN Optional | |
) | |
{ | |
WCHAR *String; | |
WCHAR *Start; | |
WCHAR *Ptr; | |
UINT32 Len; | |
BOOLEAN PreviousBackslash; | |
if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) { | |
if (!Optional) { | |
Error (SourceFile->FileName, SourceFile->LineNum, 0, "expected quoted string", "%S", SourceFile->FileBufferPtr); | |
} | |
return NULL; | |
} | |
Len = 0; | |
SourceFile->FileBufferPtr++; | |
Start = Ptr = SourceFile->FileBufferPtr; | |
PreviousBackslash = FALSE; | |
while (!EndOfFile (SourceFile)) { | |
if ((SourceFile->FileBufferPtr[0] == UNICODE_DOUBLE_QUOTE) && (!PreviousBackslash)) { | |
break; | |
} else if (SourceFile->FileBufferPtr[0] == UNICODE_CR) { | |
Warning (SourceFile->FileName, SourceFile->LineNum, 0, "carriage return found in quoted string", "%S", Start); | |
PreviousBackslash = FALSE; | |
} else if (SourceFile->FileBufferPtr[0] == UNICODE_BACKSLASH) { | |
PreviousBackslash = TRUE; | |
} else { | |
PreviousBackslash = FALSE; | |
} | |
SourceFile->FileBufferPtr++; | |
Len++; | |
} | |
if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) { | |
Warning (SourceFile->FileName, SourceFile->LineNum, 0, "missing closing quote on string", "%S", Start); | |
} else { | |
SourceFile->FileBufferPtr++; | |
} | |
// | |
// Now allocate memory for the string and save it off | |
// | |
String = (WCHAR *) malloc ((Len + 1) * sizeof (WCHAR)); | |
if (String == NULL) { | |
Error (NULL, 0, 0, "memory allocation failed", NULL); | |
return NULL; | |
} | |
// | |
// Copy the string from the file buffer to the local copy. | |
// We do no reformatting of it whatsoever at this point. | |
// | |
Ptr = String; | |
while (Len > 0) { | |
*Ptr = *Start; | |
Start++; | |
Ptr++; | |
Len--; | |
} | |
*Ptr = 0; | |
return String; | |
} | |
// | |
// Parse: | |
// #string STR_ID_NAME | |
// | |
// All we can do is call the string database to add the string identifier. Unfortunately | |
// he'll have to keep track of the last identifier we added. | |
// | |
static | |
void | |
ProcessTokenString ( | |
SOURCE_FILE *SourceFile | |
) | |
{ | |
WCHAR StringIdentifier[MAX_STRING_IDENTIFIER_NAME + 1]; | |
UINT16 StringId; | |
// | |
// Extract the string identifier name and add it to the database. | |
// | |
if (GetStringIdentifierName (SourceFile, StringIdentifier, sizeof (StringIdentifier)) > 0) { | |
StringId = STRING_ID_INVALID; | |
StringDBAddStringIdentifier (StringIdentifier, &StringId, 0); | |
} else { | |
// | |
// Error recovery -- skip to the next # | |
// | |
SourceFile->SkipToHash = TRUE; | |
} | |
} | |
static | |
BOOLEAN | |
EndOfFile ( | |
SOURCE_FILE *SourceFile | |
) | |
{ | |
// | |
// The file buffer pointer will typically get updated before the End-of-file flag in the | |
// source file structure, so check it first. | |
// | |
if (SourceFile->FileBufferPtr >= SourceFile->FileBuffer + SourceFile->FileSize / sizeof (WCHAR)) { | |
SourceFile->EndOfFile = TRUE; | |
return TRUE; | |
} | |
if (SourceFile->EndOfFile) { | |
return TRUE; | |
} | |
return FALSE; | |
} | |
static | |
UINT32 | |
GetStringIdentifierName ( | |
IN SOURCE_FILE *SourceFile, | |
IN OUT WCHAR *StringIdentifierName, | |
IN UINT32 StringIdentifierNameLen | |
) | |
{ | |
UINT32 Len; | |
WCHAR *From; | |
WCHAR *Start; | |
// | |
// Skip whitespace | |
// | |
SkipWhiteSpace (SourceFile); | |
if (SourceFile->EndOfFile) { | |
Error (SourceFile->FileName, SourceFile->LineNum, 0, "end-of-file encountered", "expected string identifier"); | |
return 0; | |
} | |
// | |
// Verify first character of name is [A-Za-z] | |
// | |
Len = 0; | |
StringIdentifierNameLen /= 2; | |
From = SourceFile->FileBufferPtr; | |
Start = SourceFile->FileBufferPtr; | |
if (((SourceFile->FileBufferPtr[0] >= UNICODE_A) && (SourceFile->FileBufferPtr[0] <= UNICODE_Z)) || | |
((SourceFile->FileBufferPtr[0] >= UNICODE_z) && (SourceFile->FileBufferPtr[0] <= UNICODE_z)) | |
) { | |
// | |
// Do nothing | |
// | |
} else { | |
Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid character in string identifier name", "%S", Start); | |
return 0; | |
} | |
while (!EndOfFile (SourceFile)) { | |
if (((SourceFile->FileBufferPtr[0] >= UNICODE_A) && (SourceFile->FileBufferPtr[0] <= UNICODE_Z)) || | |
((SourceFile->FileBufferPtr[0] >= UNICODE_z) && (SourceFile->FileBufferPtr[0] <= UNICODE_z)) || | |
((SourceFile->FileBufferPtr[0] >= UNICODE_0) && (SourceFile->FileBufferPtr[0] <= UNICODE_9)) || | |
(SourceFile->FileBufferPtr[0] == UNICODE_UNDERSCORE) | |
) { | |
Len++; | |
if (Len >= StringIdentifierNameLen) { | |
Error (SourceFile->FileName, SourceFile->LineNum, 0, "string identifier name too long", "%S", Start); | |
return 0; | |
} | |
*StringIdentifierName = SourceFile->FileBufferPtr[0]; | |
StringIdentifierName++; | |
SourceFile->FileBufferPtr++; | |
} else if (SkipWhiteSpace (SourceFile) == 0) { | |
Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid string identifier name", "%S", Start); | |
return 0; | |
} else { | |
break; | |
} | |
} | |
// | |
// Terminate the copy of the string. | |
// | |
*StringIdentifierName = 0; | |
return Len; | |
} | |
static | |
STATUS | |
GetLanguageIdentifierName ( | |
IN SOURCE_FILE *SourceFile, | |
IN OUT WCHAR *LanguageIdentifierName, | |
IN UINT32 LanguageIdentifierNameLen, | |
IN BOOLEAN Optional | |
) | |
{ | |
UINT32 Len; | |
WCHAR *Start; | |
WCHAR *LangCode; | |
WCHAR *LanguageIdentifier; | |
LanguageIdentifier = LanguageIdentifierName; | |
// | |
// Skip whitespace | |
// | |
SkipWhiteSpace (SourceFile); | |
if (SourceFile->EndOfFile) { | |
if (!Optional) { | |
Error (SourceFile->FileName, SourceFile->LineNum, 0, "end-of-file encountered", "expected language identifier"); | |
return STATUS_ERROR; | |
} | |
return STATUS_SUCCESS; | |
} | |
// | |
// This function is called to optionally get a language identifier name in: | |
// #string STR_ID eng "the string" | |
// If it's optional, and we find a double-quote, then return now. | |
// | |
if (Optional) { | |
if (*SourceFile->FileBufferPtr == UNICODE_DOUBLE_QUOTE) { | |
return STATUS_SUCCESS; | |
} | |
} | |
LanguageIdentifierNameLen /= 2; | |
// | |
// Internal error if we weren't given at least 4 WCHAR's to work with. | |
// | |
if (LanguageIdentifierNameLen < LANGUAGE_IDENTIFIER_NAME_LEN + 1) { | |
Error ( | |
SourceFile->FileName, | |
SourceFile->LineNum, | |
0, | |
"app error -- language identifier name length is invalid", | |
NULL | |
); | |
} | |
Len = 0; | |
Start = SourceFile->FileBufferPtr; | |
while (!EndOfFile (SourceFile)) { | |
if (((SourceFile->FileBufferPtr[0] >= UNICODE_a) && (SourceFile->FileBufferPtr[0] <= UNICODE_z)) || | |
((SourceFile->FileBufferPtr[0] >= UNICODE_A) && (SourceFile->FileBufferPtr[0] <= UNICODE_Z)) || | |
(SourceFile->FileBufferPtr[0] == UNICODE_MINUS)) { | |
Len++; | |
if (Len > LANGUAGE_IDENTIFIER_NAME_LEN) { | |
Error (SourceFile->FileName, SourceFile->LineNum, 0, "language identifier name too long", "%S", Start); | |
return STATUS_ERROR; | |
} | |
*LanguageIdentifierName = SourceFile->FileBufferPtr[0]; | |
SourceFile->FileBufferPtr++; | |
LanguageIdentifierName++; | |
} else if (!IsWhiteSpace (SourceFile)) { | |
Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid language identifier name", "%S", Start); | |
return STATUS_ERROR; | |
} else { | |
break; | |
} | |
} | |
// | |
// Terminate the copy of the string. | |
// | |
*LanguageIdentifierName = 0; | |
LangCode = GetLangCode (LanguageIdentifier); | |
if (LangCode != NULL) { | |
wcscpy (LanguageIdentifier, LangCode); | |
FREE (LangCode); | |
} | |
return STATUS_SUCCESS; | |
} | |
static | |
void | |
ProcessTokenInclude ( | |
SOURCE_FILE *SourceFile | |
) | |
{ | |
INT8 IncludeFileName[MAX_PATH]; | |
INT8 *To; | |
UINT32 Len; | |
BOOLEAN ReportedError; | |
SOURCE_FILE IncludedSourceFile; | |
ReportedError = FALSE; | |
if (SkipWhiteSpace (SourceFile) == 0) { | |
Warning (SourceFile->FileName, SourceFile->LineNum, 0, "expected whitespace following #include keyword", NULL); | |
} | |
// | |
// Should be quoted file name | |
// | |
if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) { | |
Error (SourceFile->FileName, SourceFile->LineNum, 0, "expected quoted include file name", NULL); | |
goto FailDone; | |
} | |
SourceFile->FileBufferPtr++; | |
// | |
// Copy the filename as ascii to our local string | |
// | |
To = IncludeFileName; | |
Len = 0; | |
while (!EndOfFile (SourceFile)) { | |
if ((SourceFile->FileBufferPtr[0] == UNICODE_CR) || (SourceFile->FileBufferPtr[0] == UNICODE_LF)) { | |
Error (SourceFile->FileName, SourceFile->LineNum, 0, "end-of-line found in quoted include file name", NULL); | |
goto FailDone; | |
} | |
if (SourceFile->FileBufferPtr[0] == UNICODE_DOUBLE_QUOTE) { | |
SourceFile->FileBufferPtr++; | |
break; | |
} | |
// | |
// If too long, then report the error once and process until the closing quote | |
// | |
Len++; | |
if (!ReportedError && (Len >= sizeof (IncludeFileName))) { | |
Error (SourceFile->FileName, SourceFile->LineNum, 0, "length of include file name exceeds limit", NULL); | |
ReportedError = TRUE; | |
} | |
if (!ReportedError) { | |
*To = UNICODE_TO_ASCII (SourceFile->FileBufferPtr[0]); | |
To++; | |
} | |
SourceFile->FileBufferPtr++; | |
} | |
if (!ReportedError) { | |
*To = 0; | |
memset ((char *) &IncludedSourceFile, 0, sizeof (SOURCE_FILE)); | |
strcpy (IncludedSourceFile.FileName, IncludeFileName); | |
IncludedSourceFile.ControlCharacter = DEFAULT_CONTROL_CHARACTER; | |
ProcessIncludeFile (&IncludedSourceFile, SourceFile); | |
// | |
// printf ("including file '%s'\n", IncludeFileName); | |
// | |
} | |
return ; | |
FailDone: | |
// | |
// Error recovery -- skip to next # | |
// | |
SourceFile->SkipToHash = TRUE; | |
} | |
static | |
void | |
ProcessTokenScope ( | |
SOURCE_FILE *SourceFile | |
) | |
{ | |
WCHAR StringIdentifier[MAX_STRING_IDENTIFIER_NAME + 1]; | |
// | |
// Extract the scope name | |
// | |
if (GetStringIdentifierName (SourceFile, StringIdentifier, sizeof (StringIdentifier)) > 0) { | |
StringDBSetScope (StringIdentifier); | |
} | |
} | |
// | |
// Parse: #langdef eng "English" | |
// #langdef chn "\wideChinese" | |
// | |
static | |
void | |
ProcessTokenLangDef ( | |
SOURCE_FILE *SourceFile | |
) | |
{ | |
STATUS Status; | |
WCHAR LanguageIdentifier[MAX_STRING_IDENTIFIER_NAME + 1]; | |
WCHAR *PrintableName; | |
Status = GetLanguageIdentifierName (SourceFile, LanguageIdentifier, sizeof (LanguageIdentifier), FALSE); | |
if (Status != STATUS_SUCCESS) { | |
return; | |
} | |
// | |
// Extract the printable name | |
// | |
PrintableName = GetPrintableLanguageName (SourceFile); | |
if (PrintableName != NULL) { | |
ParserSetPosition (SourceFile->FileName, SourceFile->LineNum); | |
StringDBAddLanguage (LanguageIdentifier, PrintableName, NULL); | |
FREE (PrintableName); | |
return ; | |
} | |
// | |
// Error recovery -- skip to next # | |
// | |
SourceFile->SkipToHash = TRUE; | |
} | |
static | |
VOID | |
ProcessTokenSecondaryLangDef ( | |
SOURCE_FILE *SourceFile | |
) | |
{ | |
STATUS Status; | |
LANGUAGE_LIST *Lang; | |
WCHAR LanguageIdentifier[MAX_STRING_IDENTIFIER_NAME + 1]; | |
WCHAR *LangCode; | |
WCHAR *SecondaryLangList = NULL; | |
Status = GetLanguageIdentifierName (SourceFile, LanguageIdentifier, sizeof (LanguageIdentifier), FALSE); | |
if (Status != STATUS_SUCCESS) { | |
return; | |
} | |
LangCode = GetLangCode(LanguageIdentifier); | |
if (LangCode == NULL) { | |
return ; | |
} | |
Lang = StringDBFindLanguageList (LanguageIdentifier); | |
if (Lang == NULL) { | |
return; | |
} | |
SecondaryLangList = GetSecondaryLanguageList (SourceFile); | |
if (SecondaryLangList != NULL) { | |
ParserSetPosition (SourceFile->FileName, SourceFile->LineNum); | |
Status = StringDBAddSecondaryLanguage (LangCode, GetLangCodeList(SecondaryLangList)); | |
if (Status != STATUS_SUCCESS) { | |
SourceFile->SkipToHash = TRUE; | |
} | |
FREE (LangCode); | |
FREE (SecondaryLangList); | |
return ; | |
} | |
FREE (LangCode); | |
SourceFile->SkipToHash = TRUE; | |
} | |
static | |
BOOLEAN | |
ApparentQuotedString ( | |
SOURCE_FILE *SourceFile | |
) | |
{ | |
WCHAR *Ptr; | |
// | |
// See if the first and last nonblank characters on the line are double quotes | |
// | |
for (Ptr = SourceFile->FileBufferPtr; *Ptr && (*Ptr == UNICODE_SPACE); Ptr++) | |
; | |
if (*Ptr != UNICODE_DOUBLE_QUOTE) { | |
return FALSE; | |
} | |
while (*Ptr) { | |
Ptr++; | |
} | |
Ptr--; | |
for (; *Ptr && (*Ptr == UNICODE_SPACE); Ptr--) | |
; | |
if (*Ptr != UNICODE_DOUBLE_QUOTE) { | |
return FALSE; | |
} | |
return TRUE; | |
} | |
// | |
// Parse: | |
// #language eng "some string " "more string" | |
// | |
static | |
void | |
ProcessTokenLanguage ( | |
SOURCE_FILE *SourceFile | |
) | |
{ | |
STATUS Status; | |
WCHAR *String; | |
WCHAR *SecondString; | |
WCHAR *TempString; | |
WCHAR *From; | |
WCHAR *To; | |
WCHAR Language[LANGUAGE_IDENTIFIER_NAME_LEN + 1]; | |
UINT32 Len; | |
BOOLEAN PreviousNewline; | |
// | |
// Get the language identifier | |
// | |
Language[0] = 0; | |
Status = GetLanguageIdentifierName (SourceFile, Language, sizeof (Language), TRUE); | |
if (Status != STATUS_SUCCESS) { | |
return; | |
} | |
// | |
// Extract the string value. It's either a quoted string that starts on the current line, or | |
// an unquoted string that starts on the following line and continues until the next control | |
// character in column 1. | |
// Look ahead to find a quote or a newline | |
// | |
if (SkipTo (SourceFile, UNICODE_DOUBLE_QUOTE, TRUE)) { | |
String = GetQuotedString (SourceFile, FALSE); | |
if (String != NULL) { | |
// | |
// Set the position in the file of where we are parsing for error | |
// reporting purposes. Then start looking ahead for additional | |
// quoted strings, and concatenate them until we get a failure | |
// back from the string parser. | |
// | |
Len = wcslen (String) + 1; | |
ParserSetPosition (SourceFile->FileName, SourceFile->LineNum); | |
do { | |
SkipWhiteSpace (SourceFile); | |
SecondString = GetQuotedString (SourceFile, TRUE); | |
if (SecondString != NULL) { | |
Len += wcslen (SecondString); | |
TempString = (WCHAR *) malloc (Len * sizeof (WCHAR)); | |
if (TempString == NULL) { | |
Error (NULL, 0, 0, "application error", "failed to allocate memory"); | |
return ; | |
} | |
wcscpy (TempString, String); | |
wcscat (TempString, SecondString); | |
free (String); | |
free (SecondString); | |
String = TempString; | |
} | |
} while (SecondString != NULL); | |
StringDBAddString (Language, NULL, NULL, String, TRUE, 0); | |
free (String); | |
} else { | |
// | |
// Error was reported at lower level. Error recovery mode. | |
// | |
SourceFile->SkipToHash = TRUE; | |
} | |
} else { | |
if (!mGlobals.UnquotedStrings) { | |
// | |
// They're using unquoted strings. If the next non-blank character is a double quote, and the | |
// last non-blank character on the line is a double quote, then more than likely they're using | |
// quotes, so they need to put the quoted string on the end of the previous line | |
// | |
if (ApparentQuotedString (SourceFile)) { | |
Warning ( | |
SourceFile->FileName, | |
SourceFile->LineNum, | |
0, | |
"unexpected quoted string on line", | |
"specify -uqs option if necessary" | |
); | |
} | |
} | |
// | |
// Found end-of-line (hopefully). Skip over it and start taking in characters | |
// until we find a control character at the start of a line. | |
// | |
Len = 0; | |
From = SourceFile->FileBufferPtr; | |
PreviousNewline = FALSE; | |
while (!EndOfFile (SourceFile)) { | |
if (SourceFile->FileBufferPtr[0] == UNICODE_LF) { | |
PreviousNewline = TRUE; | |
SourceFile->LineNum++; | |
} else { | |
Len++; | |
if (PreviousNewline && (SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter)) { | |
break; | |
} | |
PreviousNewline = FALSE; | |
} | |
SourceFile->FileBufferPtr++; | |
} | |
if ((Len == 0) && EndOfFile (SourceFile)) { | |
Error (SourceFile->FileName, SourceFile->LineNum, 0, "unexpected end of file", NULL); | |
SourceFile->SkipToHash = TRUE; | |
return ; | |
} | |
// | |
// Now allocate a buffer, copy the characters, and add the string. | |
// | |
String = (WCHAR *) malloc ((Len + 1) * sizeof (WCHAR)); | |
if (String == NULL) { | |
Error (NULL, 0, 0, "application error", "failed to allocate memory"); | |
return ; | |
} | |
To = String; | |
while (From < SourceFile->FileBufferPtr) { | |
switch (*From) { | |
case UNICODE_LF: | |
case 0: | |
break; | |
default: | |
*To = *From; | |
To++; | |
break; | |
} | |
From++; | |
} | |
// | |
// String[Len] = 0; | |
// | |
*To = 0; | |
StringDBAddString (Language, NULL, NULL, String, TRUE, 0); | |
} | |
} | |
static | |
BOOLEAN | |
IsWhiteSpace ( | |
SOURCE_FILE *SourceFile | |
) | |
{ | |
switch (SourceFile->FileBufferPtr[0]) { | |
case UNICODE_NULL: | |
case UNICODE_CR: | |
case UNICODE_SPACE: | |
case UNICODE_TAB: | |
case UNICODE_LF: | |
return TRUE; | |
default: | |
return FALSE; | |
} | |
} | |
static | |
UINT32 | |
SkipWhiteSpace ( | |
SOURCE_FILE *SourceFile | |
) | |
{ | |
UINT32 Count; | |
Count = 0; | |
while (!EndOfFile (SourceFile)) { | |
Count++; | |
switch (*SourceFile->FileBufferPtr) { | |
case UNICODE_NULL: | |
case UNICODE_CR: | |
case UNICODE_SPACE: | |
case UNICODE_TAB: | |
SourceFile->FileBufferPtr++; | |
break; | |
case UNICODE_LF: | |
SourceFile->FileBufferPtr++; | |
SourceFile->LineNum++; | |
if (mGlobals.Verbose) { | |
printf ("%d: %S\n", SourceFile->LineNum, SourceFile->FileBufferPtr); | |
} | |
break; | |
default: | |
return Count - 1; | |
} | |
} | |
// | |
// Some tokens require trailing whitespace. If we're at the end of the | |
// file, then we count that as well. | |
// | |
if ((Count == 0) && (EndOfFile (SourceFile))) { | |
Count++; | |
} | |
return Count; | |
} | |
static | |
UINT32 | |
wstrcmp ( | |
WCHAR *Buffer, | |
WCHAR *Str | |
) | |
{ | |
UINT32 Len; | |
Len = 0; | |
while (*Str == *Buffer) { | |
Buffer++; | |
Str++; | |
Len++; | |
} | |
if (*Str) { | |
return 0; | |
} | |
return Len; | |
} | |
static | |
WCHAR * | |
wstrcatenate ( | |
WCHAR *Dst, | |
WCHAR *Src | |
) | |
{ | |
UINT32 Len = 0; | |
WCHAR *Bak = Dst; | |
if (Src == NULL) { | |
return Dst; | |
} | |
if (Dst != NULL) { | |
Len = wcslen (Dst); | |
} | |
Len += wcslen (Src); | |
Dst = (WCHAR *) malloc ((Len + 1) * 2); | |
if (Dst == NULL) { | |
return NULL; | |
} | |
Dst[0] = L'\0'; | |
if (Bak != NULL) { | |
wcscpy (Dst, Bak); | |
FREE (Bak); | |
} | |
wcscat (Dst, Src); | |
return Dst; | |
} | |
// | |
// Given a filename, try to find it along the include paths. | |
// | |
static | |
FILE * | |
FindFile ( | |
IN INT8 *FileName, | |
OUT INT8 *FoundFileName, | |
IN UINT32 FoundFileNameLen | |
) | |
{ | |
FILE *Fptr; | |
TEXT_STRING_LIST *List; | |
// | |
// Traverse the list of paths and try to find the file | |
// | |
List = mGlobals.IncludePaths; | |
while (List != NULL) { | |
// | |
// Put the path and filename together | |
// | |
if (strlen (List->Str) + strlen (FileName) + 1 > FoundFileNameLen) { | |
Error (UTILITY_NAME, 0, 0, NULL, "internal error - cannot concatenate path+filename"); | |
return NULL; | |
} | |
// | |
// Append the filename to this include path and try to open the file. | |
// | |
strcpy (FoundFileName, List->Str); | |
strcat (FoundFileName, FileName); | |
if ((Fptr = fopen (FoundFileName, "rb")) != NULL) { | |
// | |
// Return the file pointer | |
// | |
return Fptr; | |
} | |
List = List->Next; | |
} | |
// | |
// Not found | |
// | |
FoundFileName[0] = 0; | |
return NULL; | |
} | |
// | |
// Process the command-line arguments | |
// | |
static | |
STATUS | |
ProcessArgs ( | |
int Argc, | |
char *Argv[] | |
) | |
{ | |
TEXT_STRING_LIST *NewList; | |
char *Cptr; | |
char *Cptr2; | |
// | |
// Clear our globals | |
// | |
memset ((char *) &mGlobals, 0, sizeof (mGlobals)); | |
strcpy (mGlobals.BaseName, DEFAULT_BASE_NAME); | |
// | |
// Skip program name | |
// | |
Argc--; | |
Argv++; | |
if (Argc == 0) { | |
Usage (); | |
return STATUS_ERROR; | |
} | |
mGlobals.Mode = MODE_UNKNOWN; | |
// | |
// Process until no more -args. | |
// | |
while ((Argc > 0) && (Argv[0][0] == '-')) { | |
// | |
// -parse option | |
// | |
if (_stricmp (Argv[0], "-parse") == 0) { | |
if (mGlobals.Mode != MODE_UNKNOWN) { | |
Error (NULL, 0, 0, "only one of -parse/-scan/-dump allowed", NULL); | |
return STATUS_ERROR; | |
} | |
mGlobals.Mode = MODE_PARSE; | |
// | |
// -scan option | |
// | |
} else if (_stricmp (Argv[0], "-scan") == 0) { | |
if (mGlobals.Mode != MODE_UNKNOWN) { | |
Error (NULL, 0, 0, "only one of -parse/-scan/-dump allowed", NULL); | |
return STATUS_ERROR; | |
} | |
mGlobals.Mode = MODE_SCAN; | |
// | |
// -vscan verbose scanning option | |
// | |
} else if (_stricmp (Argv[0], "-vscan") == 0) { | |
mGlobals.VerboseScan = TRUE; | |
// | |
// -dump option | |
// | |
} else if (_stricmp (Argv[0], "-dump") == 0) { | |
if (mGlobals.Mode != MODE_UNKNOWN) { | |
Error (NULL, 0, 0, "only one of -parse/-scan/-dump allowed", NULL); | |
return STATUS_ERROR; | |
} | |
mGlobals.Mode = MODE_DUMP; | |
} else if (_stricmp (Argv[0], "-uqs") == 0) { | |
mGlobals.UnquotedStrings = TRUE; | |
// | |
// -i path add include search path when parsing | |
// | |
} else if (_stricmp (Argv[0], "-i") == 0) { | |
// | |
// check for one more arg | |
// | |
if ((Argc <= 1) || (Argv[1][0] == '-')) { | |
Error (UTILITY_NAME, 0, 0, Argv[0], "missing include path"); | |
return STATUS_ERROR; | |
} | |
// | |
// Allocate memory for a new list element, fill it in, and | |
// add it to our list of include paths. Always make sure it | |
// has a "\" on the end of it. | |
// | |
NewList = malloc (sizeof (TEXT_STRING_LIST)); | |
if (NewList == NULL) { | |
Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); | |
return STATUS_ERROR; | |
} | |
memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST)); | |
NewList->Str = malloc (strlen (Argv[1]) + 2); | |
if (NewList->Str == NULL) { | |
free (NewList); | |
Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); | |
return STATUS_ERROR; | |
} | |
strcpy (NewList->Str, Argv[1]); | |
if (NewList->Str[strlen (NewList->Str) - 1] != '\\') { | |
strcat (NewList->Str, "\\"); | |
} | |
// | |
// Add it to our linked list | |
// | |
if (mGlobals.IncludePaths == NULL) { | |
mGlobals.IncludePaths = NewList; | |
} else { | |
mGlobals.LastIncludePath->Next = NewList; | |
} | |
mGlobals.LastIncludePath = NewList; | |
Argc--; | |
Argv++; | |
} else if (_stricmp (Argv[0], "-if") == 0) { | |
// | |
// Indirection file -- check for one more arg | |
// | |
if ((Argc <= 1) || (Argv[1][0] == '-')) { | |
Error (UTILITY_NAME, 0, 0, Argv[0], "missing indirection file name"); | |
return STATUS_ERROR; | |
} | |
// | |
// Allocate memory for a new list element, fill it in, and | |
// add it to our list of include paths. Always make sure it | |
// has a "\" on the end of it. | |
// | |
NewList = malloc (sizeof (TEXT_STRING_LIST)); | |
if (NewList == NULL) { | |
Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); | |
return STATUS_ERROR; | |
} | |
memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST)); | |
NewList->Str = malloc (strlen (Argv[1]) + 1); | |
if (NewList->Str == NULL) { | |
free (NewList); | |
Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); | |
return STATUS_ERROR; | |
} | |
strcpy (NewList->Str, Argv[1]); | |
// | |
// Add it to our linked list | |
// | |
if (mGlobals.IndirectionFileName == NULL) { | |
mGlobals.IndirectionFileName = NewList; | |
} else { | |
mGlobals.LastIndirectionFileName->Next = NewList; | |
} | |
mGlobals.LastIndirectionFileName = NewList; | |
Argc--; | |
Argv++; | |
} else if (_stricmp (Argv[0], "-db") == 0) { | |
// | |
// -db option to specify a database file. | |
// Check for one more arg (the database file name) | |
// | |
if ((Argc <= 1) || (Argv[1][0] == '-')) { | |
Error (UTILITY_NAME, 0, 0, Argv[0], "missing database file name"); | |
return STATUS_ERROR; | |
} | |
NewList = malloc (sizeof (TEXT_STRING_LIST)); | |
if (NewList == NULL) { | |
Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); | |
return STATUS_ERROR; | |
} | |
memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST)); | |
NewList->Str = malloc (strlen (Argv[1]) + 1); | |
if (NewList->Str == NULL) { | |
free (NewList); | |
Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); | |
return STATUS_ERROR; | |
} | |
strcpy (NewList->Str, Argv[1]); | |
// | |
// Add it to our linked list | |
// | |
if (mGlobals.DatabaseFileName == NULL) { | |
mGlobals.DatabaseFileName = NewList; | |
} else { | |
mGlobals.LastDatabaseFileName->Next = NewList; | |
} | |
mGlobals.LastDatabaseFileName = NewList; | |
Argc--; | |
Argv++; | |
} else if (_stricmp (Argv[0], "-ou") == 0) { | |
// | |
// -ou option to specify an output unicode file to | |
// which we can dump our database. | |
// | |
if ((Argc <= 1) || (Argv[1][0] == '-')) { | |
Error (UTILITY_NAME, 0, 0, Argv[0], "missing database dump output file name"); | |
return STATUS_ERROR; | |
} | |
if (mGlobals.DumpUFileName[0] == 0) { | |
strcpy (mGlobals.DumpUFileName, Argv[1]); | |
} else { | |
Error (UTILITY_NAME, 0, 0, Argv[1], "-ou option already specified with '%s'", mGlobals.DumpUFileName); | |
return STATUS_ERROR; | |
} | |
Argc--; | |
Argv++; | |
} else if (_stricmp (Argv[0], "-hpk") == 0) { | |
// | |
// -hpk option to create an HII export pack of the input database file | |
// | |
if ((Argc <= 1) || (Argv[1][0] == '-')) { | |
Error (UTILITY_NAME, 0, 0, Argv[0], "missing raw string data dump output file name"); | |
return STATUS_ERROR; | |
} | |
if (mGlobals.HiiExportPackFileName[0] == 0) { | |
strcpy (mGlobals.HiiExportPackFileName, Argv[1]); | |
} else { | |
Error (UTILITY_NAME, 0, 0, Argv[1], "-or option already specified with '%s'", mGlobals.HiiExportPackFileName); | |
return STATUS_ERROR; | |
} | |
Argc--; | |
Argv++; | |
} else if ((_stricmp (Argv[0], "-?") == 0) || (_stricmp (Argv[0], "-h") == 0)) { | |
Usage (); | |
return STATUS_ERROR; | |
} else if (_stricmp (Argv[0], "-v") == 0) { | |
mGlobals.Verbose = 1; | |
} else if (_stricmp (Argv[0], "-vdbw") == 0) { | |
mGlobals.VerboseDatabaseWrite = 1; | |
} else if (_stricmp (Argv[0], "-vdbr") == 0) { | |
mGlobals.VerboseDatabaseRead = 1; | |
} else if (_stricmp (Argv[0], "-newdb") == 0) { | |
mGlobals.NewDatabase = 1; | |
} else if (_stricmp (Argv[0], "-ignorenotfound") == 0) { | |
mGlobals.IgnoreNotFound = 1; | |
} else if (_stricmp (Argv[0], "-oc") == 0) { | |
// | |
// check for one more arg | |
// | |
if ((Argc <= 1) || (Argv[1][0] == '-')) { | |
Error (UTILITY_NAME, 0, 0, Argv[0], "missing output C filename"); | |
return STATUS_ERROR; | |
} | |
strcpy (mGlobals.StringCFileName, Argv[1]); | |
Argc--; | |
Argv++; | |
} else if (_stricmp (Argv[0], "-bn") == 0) { | |
// | |
// check for one more arg | |
// | |
if ((Argc <= 1) || (Argv[1][0] == '-')) { | |
Error (UTILITY_NAME, 0, 0, Argv[0], "missing base name"); | |
Usage (); | |
return STATUS_ERROR; | |
} | |
strcpy (mGlobals.BaseName, Argv[1]); | |
Argc--; | |
Argv++; | |
} else if (_stricmp (Argv[0], "-oh") == 0) { | |
// | |
// -oh to specify output .h defines file name | |
// | |
if ((Argc <= 1) || (Argv[1][0] == '-')) { | |
Error (UTILITY_NAME, 0, 0, Argv[0], "missing output .h filename"); | |
return STATUS_ERROR; | |
} | |
strcpy (mGlobals.StringHFileName, Argv[1]); | |
Argc--; | |
Argv++; | |
} else if (_stricmp (Argv[0], "-dep") == 0) { | |
// | |
// -dep to specify output dependency file name | |
// | |
if ((Argc <= 1) || (Argv[1][0] == '-')) { | |
Error (UTILITY_NAME, 0, 0, Argv[0], "missing output dependency filename"); | |
return STATUS_ERROR; | |
} | |
strcpy (mGlobals.OutputDependencyFileName, Argv[1]); | |
Argc--; | |
Argv++; | |
} else if (_stricmp (Argv[0], "-skipext") == 0) { | |
// | |
// -skipext to skip scanning of files with certain filename extensions | |
// | |
if ((Argc <= 1) || (Argv[1][0] == '-')) { | |
Error (UTILITY_NAME, 0, 0, Argv[0], "missing filename extension"); | |
return STATUS_ERROR; | |
} | |
// | |
// Allocate memory for a new list element, fill it in, and | |
// add it to our list of excluded extensions. Always make sure it | |
// has a "." as the first character. | |
// | |
NewList = malloc (sizeof (TEXT_STRING_LIST)); | |
if (NewList == NULL) { | |
Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); | |
return STATUS_ERROR; | |
} | |
memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST)); | |
NewList->Str = malloc (strlen (Argv[1]) + 2); | |
if (NewList->Str == NULL) { | |
free (NewList); | |
Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); | |
return STATUS_ERROR; | |
} | |
if (Argv[1][0] == '.') { | |
strcpy (NewList->Str, Argv[1]); | |
} else { | |
NewList->Str[0] = '.'; | |
strcpy (NewList->Str + 1, Argv[1]); | |
} | |
// | |
// Add it to our linked list | |
// | |
if (mGlobals.SkipExt == NULL) { | |
mGlobals.SkipExt = NewList; | |
} else { | |
mGlobals.LastSkipExt->Next = NewList; | |
} | |
mGlobals.LastSkipExt = NewList; | |
Argc--; | |
Argv++; | |
} else if (_stricmp (Argv[0], "-lang") == 0) { | |
// | |
// "-lang zh-Hans" or "-lang en-US" to only output certain languages | |
// | |
if ((Argc <= 1) || (Argv[1][0] == '-')) { | |
Error (UTILITY_NAME, 0, 0, Argv[0], "missing language name"); | |
Usage (); | |
return STATUS_ERROR; | |
} | |
if (AddCommandLineLanguage (Argv[1]) != STATUS_SUCCESS) { | |
return STATUS_ERROR; | |
} | |
Argc--; | |
Argv++; | |
} else if (_stricmp (Argv[0], "-od") == 0) { | |
// | |
// Output database file name -- check for another arg | |
// | |
if ((Argc <= 1) || (Argv[1][0] == '-')) { | |
Error (UTILITY_NAME, 0, 0, Argv[0], "missing output database file name"); | |
return STATUS_ERROR; | |
} | |
strcpy (mGlobals.OutputDatabaseFileName, Argv[1]); | |
Argv++; | |
Argc--; | |
} else if (_stricmp (Argv[0], "-ppflag") == 0) { | |
// | |
// -ppflag "Preprocess flags" -- check for another arg | |
// | |
if ((Argc <= 1) || (Argv[1][0] == '-')) { | |
Error (UTILITY_NAME, 0, 0, Argv[0], "missing preprocess flags"); | |
Usage (); | |
return STATUS_ERROR; | |
} | |
// | |
// Allocate memory for a new list element, fill it in, and | |
// add it to our list of preprocess flag. | |
// | |
NewList = malloc (sizeof (TEXT_STRING_LIST)); | |
if (NewList == NULL) { | |
Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); | |
return STATUS_ERROR; | |
} | |
memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST)); | |
NewList->Str = malloc (strlen (Argv[1]) * 2 + 1); | |
if (NewList->Str == NULL) { | |
free (NewList); | |
Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); | |
return STATUS_ERROR; | |
} | |
// | |
// Convert '"' to '\"' in preprocess flag | |
// | |
Cptr = Argv[1]; | |
Cptr2 = NewList->Str; | |
if (*Cptr == '"') { | |
*Cptr2++ = '\\'; | |
*Cptr2++ = '"'; | |
Cptr++; | |
} | |
while (*Cptr != '\0') { | |
if ((*Cptr == '"') && (*(Cptr - 1) != '\\')) { | |
*Cptr2++ = '\\'; | |
} | |
*Cptr2++ = *Cptr++; | |
} | |
*Cptr2 = '\0'; | |
// | |
// Add it to our linked list | |
// | |
if (mGlobals.PreprocessFlags == NULL) { | |
mGlobals.PreprocessFlags = NewList; | |
} else { | |
mGlobals.LastPreprocessFlags->Next = NewList; | |
} | |
mGlobals.LastPreprocessFlags = NewList; | |
mGlobals.Preprocess = TRUE; | |
Argv++; | |
Argc--; | |
} else { | |
// | |
// Unrecognized arg | |
// | |
Error (UTILITY_NAME, 0, 0, Argv[0], "unrecognized option"); | |
Usage (); | |
return STATUS_ERROR; | |
} | |
Argv++; | |
Argc--; | |
} | |
// | |
// Make sure they specified the mode parse/scan/dump | |
// | |
if (mGlobals.Mode == MODE_UNKNOWN) { | |
Error (NULL, 0, 0, "must specify one of -parse/-scan/-dump", NULL); | |
return STATUS_ERROR; | |
} | |
// | |
// All modes require a database filename | |
// | |
if (mGlobals.DatabaseFileName == 0) { | |
Error (NULL, 0, 0, "must specify a database filename using -db DbFileName", NULL); | |
Usage (); | |
return STATUS_ERROR; | |
} | |
// | |
// If dumping the database file, then return immediately if all | |
// parameters check out. | |
// | |
if (mGlobals.Mode == MODE_DUMP) { | |
// | |
// Not much use if they didn't specify -oh or -oc or -ou or -hpk | |
// | |
if ((mGlobals.DumpUFileName[0] == 0) && | |
(mGlobals.StringHFileName[0] == 0) && | |
(mGlobals.StringCFileName[0] == 0) && | |
(mGlobals.HiiExportPackFileName[0] == 0) | |
) { | |
Error (NULL, 0, 0, "-dump without -oc/-oh/-ou/-hpk is a NOP", NULL); | |
return STATUS_ERROR; | |
} | |
return STATUS_SUCCESS; | |
} | |
// | |
// Had to specify source string file and output string defines header filename. | |
// | |
if (mGlobals.Mode == MODE_SCAN) { | |
if (Argc < 1) { | |
Error (UTILITY_NAME, 0, 0, NULL, "must specify at least one source file to scan with -scan"); | |
Usage (); | |
return STATUS_ERROR; | |
} | |
// | |
// If -ppflag is specified, -oh should also be specified for preprocess | |
// | |
if (mGlobals.Preprocess && (mGlobals.StringHFileName[0] == 0)) { | |
Error (UTILITY_NAME, 0, 0, NULL, "must specify string defines file name to preprocess before scan"); | |
Usage (); | |
return STATUS_ERROR; | |
} | |
// | |
// Get the list of filenames | |
// | |
while (Argc > 0) { | |
NewList = malloc (sizeof (TEXT_STRING_LIST)); | |
if (NewList == NULL) { | |
Error (UTILITY_NAME, 0, 0, "memory allocation failure", NULL); | |
return STATUS_ERROR; | |
} | |
memset (NewList, 0, sizeof (TEXT_STRING_LIST)); | |
NewList->Str = (UINT8 *) malloc (strlen (Argv[0]) + 1); | |
if (NewList->Str == NULL) { | |
Error (UTILITY_NAME, 0, 0, "memory allocation failure", NULL); | |
return STATUS_ERROR; | |
} | |
strcpy (NewList->Str, Argv[0]); | |
if (mGlobals.ScanFileName == NULL) { | |
mGlobals.ScanFileName = NewList; | |
} else { | |
mGlobals.LastScanFileName->Next = NewList; | |
} | |
mGlobals.LastScanFileName = NewList; | |
Argc--; | |
Argv++; | |
} | |
} else { | |
// | |
// Parse mode -- must specify an input unicode file name | |
// | |
if (Argc < 1) { | |
Error (UTILITY_NAME, 0, 0, NULL, "must specify input unicode string file name with -parse"); | |
Usage (); | |
return STATUS_ERROR; | |
} | |
strcpy (mGlobals.SourceFiles.FileName, Argv[0]); | |
} | |
return STATUS_SUCCESS; | |
} | |
// | |
// Found "-lang zh-Hans;en-US" on the command line. Parse the | |
// language list and save the setting for later processing. | |
// | |
static | |
STATUS | |
AddCommandLineLanguage ( | |
IN INT8 *Language | |
) | |
{ | |
char Separator[] = ";"; | |
char *Token; | |
WCHAR_STRING_LIST *WNewList; | |
// | |
// Keep processing the input string until we find the end. | |
// | |
Token = strtok (Language, Separator); | |
while (Token != NULL) { | |
WNewList = MALLOC (sizeof (WCHAR_STRING_LIST)); | |
if (WNewList == NULL) { | |
Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); | |
return STATUS_ERROR; | |
} | |
WNewList->Next = NULL; | |
WNewList->Str = MALLOC ((strlen (Token) + 1) * sizeof (WCHAR)); | |
if (WNewList->Str == NULL) { | |
free (WNewList); | |
Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); | |
return STATUS_ERROR; | |
} | |
#ifdef USE_VC8 | |
swprintf (WNewList->Str, (strlen (Token) + 1) * sizeof (WCHAR), L"%S", Token); | |
#else | |
swprintf (WNewList->Str, L"%S", Token); | |
#endif | |
// | |
// Add it to our linked list | |
// | |
if (mGlobals.Language == NULL) { | |
mGlobals.Language = WNewList; | |
} else { | |
mGlobals.LastLanguage->Next = WNewList; | |
} | |
mGlobals.LastLanguage = WNewList; | |
Token = strtok (NULL, Separator); | |
} | |
return STATUS_SUCCESS; | |
} | |
// | |
// The contents of the text file are expected to be (one per line) | |
// STRING_IDENTIFIER_NAME ScopeName | |
// For example: | |
// STR_ID_MY_FAVORITE_STRING IBM | |
// | |
static | |
STATUS | |
ParseIndirectionFiles ( | |
TEXT_STRING_LIST *Files | |
) | |
{ | |
FILE *Fptr; | |
INT8 Line[200]; | |
INT8 *StringName; | |
INT8 *ScopeName; | |
INT8 *End; | |
UINT32 LineCount; | |
WCHAR_MATCHING_STRING_LIST *NewList; | |
Line[sizeof (Line) - 1] = 0; | |
Fptr = NULL; | |
while (Files != NULL) { | |
Fptr = fopen (Files->Str, "r"); | |
LineCount = 0; | |
if (Fptr == NULL) { | |
Error (NULL, 0, 0, Files->Str, "failed to open input indirection file for reading"); | |
return STATUS_ERROR; | |
} | |
while (fgets (Line, sizeof (Line), Fptr) != NULL) { | |
// | |
// remove terminating newline for error printing purposes. | |
// | |
if (Line[strlen (Line) - 1] == '\n') { | |
Line[strlen (Line) - 1] = 0; | |
} | |
LineCount++; | |
if (Line[sizeof (Line) - 1] != 0) { | |
Error (Files->Str, LineCount, 0, "line length exceeds maximum supported", NULL); | |
goto Done; | |
} | |
StringName = Line; | |
while (*StringName && (isspace (*StringName))) { | |
StringName++; | |
} | |
if (*StringName) { | |
if ((*StringName == '_') || isalpha (*StringName)) { | |
End = StringName; | |
while ((*End) && (*End == '_') || (isalnum (*End))) { | |
End++; | |
} | |
if (isspace (*End)) { | |
*End = 0; | |
End++; | |
while (isspace (*End)) { | |
End++; | |
} | |
if (*End) { | |
ScopeName = End; | |
while (*End && !isspace (*End)) { | |
End++; | |
} | |
*End = 0; | |
// | |
// Add the string name/scope pair | |
// | |
NewList = malloc (sizeof (WCHAR_MATCHING_STRING_LIST)); | |
if (NewList == NULL) { | |
Error (NULL, 0, 0, "memory allocation error", NULL); | |
goto Done; | |
} | |
memset (NewList, 0, sizeof (WCHAR_MATCHING_STRING_LIST)); | |
NewList->Str1 = (WCHAR *) malloc ((strlen (StringName) + 1) * sizeof (WCHAR)); | |
NewList->Str2 = (WCHAR *) malloc ((strlen (ScopeName) + 1) * sizeof (WCHAR)); | |
if ((NewList->Str1 == NULL) || (NewList->Str2 == NULL)) { | |
Error (NULL, 0, 0, "memory allocation error", NULL); | |
goto Done; | |
} | |
#ifdef USE_VC8 | |
swprintf (NewList->Str1, (strlen (StringName) + 1) * sizeof (WCHAR), L"%S", StringName); | |
swprintf (NewList->Str2, (strlen (ScopeName) + 1) * sizeof (WCHAR), L"%S", ScopeName); | |
#else | |
swprintf (NewList->Str1, L"%S", StringName); | |
swprintf (NewList->Str2, L"%S", ScopeName); | |
#endif | |
if (mGlobals.IndirectionList == NULL) { | |
mGlobals.IndirectionList = NewList; | |
} else { | |
mGlobals.LastIndirectionList->Next = NewList; | |
} | |
mGlobals.LastIndirectionList = NewList; | |
} else { | |
Error (Files->Str, LineCount, 0, StringName, "invalid line : expected 'StringIdentifier Scope'"); | |
goto Done; | |
} | |
} else { | |
Error (Files->Str, LineCount, 0, StringName, "invalid line : expected 'StringIdentifier Scope'"); | |
goto Done; | |
} | |
} else { | |
Error (Files->Str, LineCount, 0, StringName, "invalid string identifier"); | |
goto Done; | |
} | |
} | |
} | |
fclose (Fptr); | |
Fptr = NULL; | |
Files = Files->Next; | |
} | |
Done: | |
if (Fptr != NULL) { | |
fclose (Fptr); | |
return STATUS_ERROR; | |
} | |
return STATUS_SUCCESS; | |
} | |
static | |
INTN | |
PreprocessSourceFile ( | |
UINT8 *SourceFileName | |
) | |
{ | |
char *Cptr; | |
FILE *InFptr; | |
FILE *OutFptr; | |
UINT32 CmdLen; | |
char *PreProcessCmd; | |
char BaseName[MAX_PATH]; | |
char TempFileName[MAX_PATH]; | |
char SourceFileDir[MAX_PATH]; | |
char Line[MAX_LINE_LEN]; | |
TEXT_STRING_LIST *List; | |
char InsertLine[] = "#undef STRING_TOKEN\n"; | |
int Status; | |
// | |
// Check whehter source file exist | |
// | |
InFptr = fopen (SourceFileName, "r"); | |
if (InFptr == NULL) { | |
Error (NULL, 0, 0, SourceFileName, "failed to open input file for scanning"); | |
return STATUS_ERROR; | |
} | |
// | |
// Get source file directory | |
// | |
strcpy (SourceFileDir, SourceFileName); | |
Cptr = strrchr (SourceFileDir, '\\'); | |
if (Cptr != NULL) { | |
*Cptr = '\0'; | |
} | |
// | |
// Generate preprocess output file name | |
// | |
strcpy (BaseName, mGlobals.OutputDatabaseFileName); | |
Cptr = strrchr (BaseName, '\\'); | |
if (Cptr != NULL) { | |
*++Cptr = '\0'; | |
} | |
Cptr = strrchr (SourceFileName, '\\'); | |
if (Cptr != NULL) { | |
Cptr++; | |
} | |
strcat (BaseName, Cptr); | |
Cptr = strrchr (BaseName, '.'); | |
if (Cptr != NULL) { | |
*Cptr = '\0'; | |
} | |
strcpy (mGlobals.PreprocessFileName, BaseName); | |
strcat (mGlobals.PreprocessFileName, PREPROCESS_OUTPUT_FILE_EXTENSION); | |
strcpy (TempFileName, BaseName); | |
strcat (TempFileName, PREPROCESS_TEMP_FILE_EXTENSION); | |
// | |
// Insert "#undef STRING_TOKEN" after each line of "#include ...", so as to | |
// preserve the STRING_TOKEN() for scanning after preprocess | |
// | |
OutFptr = fopen (TempFileName, "w"); | |
if (OutFptr == NULL) { | |
Error (NULL, 0, 0, TempFileName, "failed to open file for write"); | |
fclose (InFptr); | |
return STATUS_ERROR; | |
} | |
while (fgets (Line, MAX_LINE_LEN, InFptr) != NULL) { | |
fputs (Line, OutFptr); | |
Cptr = Line; | |
// | |
// Skip leading blank space | |
// | |
while (*Cptr == ' ' || *Cptr == '\t') { | |
Cptr++; | |
} | |
if (*Cptr == '#' && strncmp (Cptr + 1, "include", 7) == 0){ | |
fputs (InsertLine, OutFptr); | |
} | |
} | |
fclose (InFptr); | |
fclose (OutFptr); | |
// | |
// Prepare preprocess command | |
// | |
CmdLen = 1; | |
CmdLen += strlen (PREPROCESSOR_COMMAND); | |
CmdLen++; | |
CmdLen += strlen (PREPROCESSOR_OPTIONS); | |
CmdLen++; | |
// | |
// "-I SourceFileDir " | |
// | |
CmdLen += strlen (SourceFileDir); | |
CmdLen += 4; | |
List = mGlobals.PreprocessFlags; | |
while (List != NULL) { | |
CmdLen += strlen (List->Str); | |
CmdLen++; | |
List = List->Next; | |
} | |
CmdLen += strlen (TempFileName); | |
CmdLen += 3; | |
CmdLen += strlen (mGlobals.PreprocessFileName); | |
PreProcessCmd = malloc (CmdLen); | |
if (PreProcessCmd == NULL) { | |
Error (NULL, 0, 0, UTILITY_NAME, "memory allocation fail (%d bytes)\n", CmdLen); | |
return STATUS_ERROR; | |
} | |
strcpy (PreProcessCmd, PREPROCESSOR_COMMAND); | |
strcat (PreProcessCmd, " "); | |
strcat (PreProcessCmd, PREPROCESSOR_OPTIONS); | |
strcat (PreProcessCmd, " "); | |
strcat (PreProcessCmd, "-I "); | |
strcat (PreProcessCmd, SourceFileDir); | |
strcat (PreProcessCmd, " "); | |
List = mGlobals.PreprocessFlags; | |
while (List != NULL) { | |
strcat (PreProcessCmd, List->Str); | |
strcat (PreProcessCmd, " "); | |
List = List->Next; | |
} | |
strcat (PreProcessCmd, TempFileName); | |
strcat (PreProcessCmd, " > "); | |
strcat (PreProcessCmd, mGlobals.PreprocessFileName); | |
// | |
// Preprocess the source file | |
// | |
Status = system (PreProcessCmd); | |
if (Status != 0) { | |
Error (NULL, 0, 0, PreProcessCmd, "failed to spawn C preprocessor on source file\n"); | |
free (PreProcessCmd); | |
return STATUS_ERROR; | |
} | |
free (PreProcessCmd); | |
return STATUS_SUCCESS; | |
} | |
static | |
STATUS | |
ScanFiles ( | |
TEXT_STRING_LIST *ScanFiles | |
) | |
{ | |
char Line[MAX_LINE_LEN]; | |
FILE *Fptr; | |
char *FileName; | |
UINT32 LineNum; | |
char *Cptr; | |
char *SavePtr; | |
char *TermPtr; | |
char *StringTokenPos; | |
TEXT_STRING_LIST *SList; | |
BOOLEAN SkipIt; | |
BOOLEAN FileExist; | |
// | |
// Put a null-terminator at the end of the line. If we read in | |
// a line longer than we support, then we can catch it. | |
// | |
Line[MAX_LINE_LEN - 1] = 0; | |
// | |
// Process each file. If they gave us a skip extension list, then | |
// skip it if the extension matches. | |
// | |
FileExist = FALSE; | |
while (ScanFiles != NULL) { | |
SkipIt = FALSE; | |
for (SList = mGlobals.SkipExt; SList != NULL; SList = SList->Next) { | |
if ((strlen (ScanFiles->Str) > strlen (SList->Str)) && | |
(strcmp (ScanFiles->Str + strlen (ScanFiles->Str) - strlen (SList->Str), SList->Str) == 0) | |
) { | |
SkipIt = TRUE; | |
// | |
// printf ("Match: %s : %s\n", ScanFiles->Str, SList->Str); | |
// | |
break; | |
} | |
} | |
if (!SkipIt) { | |
if (mGlobals.VerboseScan) { | |
printf ("Scanning %s\n", ScanFiles->Str); | |
} | |
if (mGlobals.Preprocess) { | |
// | |
// Create an empty string defines file for preprocessor | |
// | |
if (!FileExist) { | |
Fptr = fopen (mGlobals.StringHFileName, "w"); | |
if (Fptr == NULL) { | |
Error (NULL, 0, 0, mGlobals.StringHFileName, "failed to open file for write"); | |
return STATUS_ERROR; | |
} | |
fclose (Fptr); | |
FileExist = TRUE; | |
} | |
// | |
// Preprocess using C preprocessor | |
// | |
if (PreprocessSourceFile (ScanFiles->Str) != STATUS_SUCCESS) { | |
return STATUS_ERROR; | |
} | |
FileName = mGlobals.PreprocessFileName; | |
} else { | |
FileName = ScanFiles->Str; | |
} | |
Fptr = fopen (FileName, "r"); | |
if (Fptr == NULL) { | |
Error (NULL, 0, 0, FileName, "failed to open input file for scanning"); | |
return STATUS_ERROR; | |
} | |
LineNum = 0; | |
while (fgets (Line, sizeof (Line), Fptr) != NULL) { | |
LineNum++; | |
if (Line[MAX_LINE_LEN - 1] != 0) { | |
Error (ScanFiles->Str, LineNum, 0, "line length exceeds maximum supported by tool", NULL); | |
fclose (Fptr); | |
return STATUS_ERROR; | |
} | |
// | |
// Remove the newline from the input line so we can print a warning message | |
// | |
if (Line[strlen (Line) - 1] == '\n') { | |
Line[strlen (Line) - 1] = 0; | |
} | |
// | |
// Terminate the line at // comments | |
// | |
Cptr = strstr (Line, "//"); | |
if (Cptr != NULL) { | |
*Cptr = 0; | |
} | |
Cptr = Line; | |
while ((Cptr = strstr (Cptr, STRING_TOKEN)) != NULL) { | |
// | |
// Found "STRING_TOKEN". Make sure we don't have NUM_STRING_TOKENS or | |
// something like that. Then make sure it's followed by | |
// an open parenthesis, a string identifier, and then a closing | |
// parenthesis. | |
// | |
if (mGlobals.VerboseScan) { | |
printf (" %d: %s", LineNum, Cptr); | |
} | |
if (((Cptr == Line) || (!IsValidIdentifierChar (*(Cptr - 1), FALSE))) && | |
(!IsValidIdentifierChar (*(Cptr + sizeof (STRING_TOKEN) - 1), FALSE)) | |
) { | |
StringTokenPos = Cptr; | |
SavePtr = Cptr; | |
Cptr += strlen (STRING_TOKEN); | |
while (*Cptr && isspace (*Cptr) && (*Cptr != '(')) { | |
Cptr++; | |
} | |
if (*Cptr != '(') { | |
Warning (ScanFiles->Str, LineNum, 0, StringTokenPos, "expected "STRING_TOKEN "(identifier)"); | |
} else { | |
// | |
// Skip over the open-parenthesis and find the next non-blank character | |
// | |
Cptr++; | |
while (isspace (*Cptr)) { | |
Cptr++; | |
} | |
SavePtr = Cptr; | |
if ((*Cptr == '_') || isalpha (*Cptr)) { | |
while ((*Cptr == '_') || (isalnum (*Cptr))) { | |
Cptr++; | |
} | |
TermPtr = Cptr; | |
while (*Cptr && isspace (*Cptr)) { | |
Cptr++; | |
} | |
if (*Cptr != ')') { | |
Warning (ScanFiles->Str, LineNum, 0, StringTokenPos, "expected "STRING_TOKEN "(identifier)"); | |
} | |
if (*TermPtr) { | |
*TermPtr = 0; | |
Cptr = TermPtr + 1; | |
} else { | |
Cptr = TermPtr; | |
} | |
// | |
// Add the string identifier to the list of used strings | |
// | |
ParserSetPosition (ScanFiles->Str, LineNum); | |
StringDBSetStringReferenced (SavePtr, mGlobals.IgnoreNotFound); | |
if (mGlobals.VerboseScan) { | |
printf ("...referenced %s", SavePtr); | |
} | |
} else { | |
Warning (ScanFiles->Str, LineNum, 0, StringTokenPos, "expected valid string identifier name"); | |
} | |
} | |
} else { | |
// | |
// Found it, but it's a substring of something else. Advance our pointer. | |
// | |
Cptr++; | |
} | |
if (mGlobals.VerboseScan) { | |
printf ("\n"); | |
} | |
} | |
} | |
fclose (Fptr); | |
} else { | |
// | |
// Skipping this file type | |
// | |
if (mGlobals.VerboseScan) { | |
printf ("Skip scanning of %s\n", ScanFiles->Str); | |
} | |
} | |
ScanFiles = ScanFiles->Next; | |
} | |
// | |
// Remove the empty string defines file | |
// | |
if (FileExist) { | |
remove (mGlobals.StringHFileName); | |
} | |
return STATUS_SUCCESS; | |
} | |
// | |
// Free the global string lists we allocated memory for | |
// | |
static | |
void | |
FreeLists ( | |
VOID | |
) | |
{ | |
TEXT_STRING_LIST *Temp; | |
WCHAR_STRING_LIST *WTemp; | |
// | |
// Traverse the include paths, freeing each | |
// | |
while (mGlobals.IncludePaths != NULL) { | |
Temp = mGlobals.IncludePaths->Next; | |
free (mGlobals.IncludePaths->Str); | |
free (mGlobals.IncludePaths); | |
mGlobals.IncludePaths = Temp; | |
} | |
// | |
// If we did a scan, then free up our | |
// list of files to scan. | |
// | |
while (mGlobals.ScanFileName != NULL) { | |
Temp = mGlobals.ScanFileName->Next; | |
free (mGlobals.ScanFileName->Str); | |
free (mGlobals.ScanFileName); | |
mGlobals.ScanFileName = Temp; | |
} | |
// | |
// Free up preprocess flags list | |
// | |
while (mGlobals.PreprocessFlags != NULL) { | |
Temp = mGlobals.PreprocessFlags->Next; | |
free (mGlobals.PreprocessFlags->Str); | |
free (mGlobals.PreprocessFlags); | |
mGlobals.PreprocessFlags = Temp; | |
} | |
// | |
// If they gave us a list of filename extensions to | |
// skip on scan, then free them up. | |
// | |
while (mGlobals.SkipExt != NULL) { | |
Temp = mGlobals.SkipExt->Next; | |
free (mGlobals.SkipExt->Str); | |
free (mGlobals.SkipExt); | |
mGlobals.SkipExt = Temp; | |
} | |
// | |
// Free up any languages specified | |
// | |
while (mGlobals.Language != NULL) { | |
WTemp = mGlobals.Language->Next; | |
free (mGlobals.Language->Str); | |
free (mGlobals.Language); | |
mGlobals.Language = WTemp; | |
} | |
// | |
// Free up our indirection list | |
// | |
while (mGlobals.IndirectionList != NULL) { | |
mGlobals.LastIndirectionList = mGlobals.IndirectionList->Next; | |
free (mGlobals.IndirectionList->Str1); | |
free (mGlobals.IndirectionList->Str2); | |
free (mGlobals.IndirectionList); | |
mGlobals.IndirectionList = mGlobals.LastIndirectionList; | |
} | |
while (mGlobals.IndirectionFileName != NULL) { | |
mGlobals.LastIndirectionFileName = mGlobals.IndirectionFileName->Next; | |
free (mGlobals.IndirectionFileName->Str); | |
free (mGlobals.IndirectionFileName); | |
mGlobals.IndirectionFileName = mGlobals.LastIndirectionFileName; | |
} | |
} | |
static | |
BOOLEAN | |
IsValidIdentifierChar ( | |
INT8 Char, | |
BOOLEAN FirstChar | |
) | |
{ | |
// | |
// If it's the first character of an identifier, then | |
// it must be one of [A-Za-z_]. | |
// | |
if (FirstChar) { | |
if (isalpha (Char) || (Char == '_')) { | |
return TRUE; | |
} | |
} else { | |
// | |
// If it's not the first character, then it can | |
// be one of [A-Za-z_0-9] | |
// | |
if (isalnum (Char) || (Char == '_')) { | |
return TRUE; | |
} | |
} | |
return FALSE; | |
} | |
static | |
void | |
RewindFile ( | |
SOURCE_FILE *SourceFile | |
) | |
{ | |
SourceFile->LineNum = 1; | |
SourceFile->FileBufferPtr = SourceFile->FileBuffer; | |
SourceFile->EndOfFile = FALSE; | |
} | |
static | |
BOOLEAN | |
SkipTo ( | |
SOURCE_FILE *SourceFile, | |
WCHAR WChar, | |
BOOLEAN StopAfterNewline | |
) | |
{ | |
while (!EndOfFile (SourceFile)) { | |
// | |
// Check for the character of interest | |
// | |
if (SourceFile->FileBufferPtr[0] == WChar) { | |
return TRUE; | |
} else { | |
if (SourceFile->FileBufferPtr[0] == UNICODE_LF) { | |
SourceFile->LineNum++; | |
if (StopAfterNewline) { | |
SourceFile->FileBufferPtr++; | |
if (SourceFile->FileBufferPtr[0] == 0) { | |
SourceFile->FileBufferPtr++; | |
} | |
return FALSE; | |
} | |
} | |
SourceFile->FileBufferPtr++; | |
} | |
} | |
return FALSE; | |
} | |
static | |
void | |
Usage ( | |
VOID | |
) | |
/*++ | |
Routine Description: | |
Print usage information for this utility. | |
Arguments: | |
None. | |
Returns: | |
Nothing. | |
--*/ | |
{ | |
int Index; | |
const char *Str[] = { | |
UTILITY_NAME" "UTILITY_VERSION" - Intel UEFI String Gather Utility", | |
" Copyright (C), 2004 - 2008 Intel Corporation", | |
#if ( defined(UTILITY_BUILD) && defined(UTILITY_VENDOR) ) | |
" Built from "UTILITY_BUILD", project of "UTILITY_VENDOR, | |
#endif | |
"", | |
"Usage:", | |
" "UTILITY_NAME" -parse [OPTION] FILE", | |
" "UTILITY_NAME" -scan [OPTION] FILE", | |
" "UTILITY_NAME" -dump [OPTION]", | |
"Description:", | |
" Process unicode strings file.", | |
"Common options include:", | |
" -h or -? for this help information", | |
" -db Database required name of output/input database file", | |
" -bn BaseName for use in the .h and .c output files", | |
" Default = "DEFAULT_BASE_NAME, | |
" -v for verbose output", | |
" -vdbw for verbose output when writing database", | |
" -vdbr for verbose output when reading database", | |
" -od FileName to specify an output database file name", | |
"Parse options include:", | |
" -i IncludePath add IncludePath to list of search paths", | |
" -dep FileName to specify an output dependency file name", | |
" -newdb to not read in existing database file", | |
" -uqs to indicate that unquoted strings are used", | |
" FileNames name of one or more unicode files to parse", | |
"Scan options include:", | |
" -scan scan text file(s) for STRING_TOKEN() usage", | |
" -skipext .ext to skip scan of files with .ext filename extension", | |
" -ppflag \"Flags\" to specify the C preprocessor flags", | |
" -oh FileName to specify string defines file name for preprocessor", | |
" -ignorenotfound ignore if a given STRING_TOKEN(STR) is not ", | |
" found in the database", | |
" FileNames one or more files to scan", | |
"Dump options include:", | |
" -oc FileName write string data to FileName", | |
" -oh FileName write string defines to FileName", | |
" -ou FileName dump database to unicode file FileName", | |
" -lang Lang only dump for the language 'Lang'", | |
" use ';' to separate multiple languages", | |
" -if FileName to specify an indirection file", | |
" -hpk FileName to create an HII export pack of the strings", | |
"", | |
"The expected process is to parse a unicode string file to create an initial", | |
"database of string identifier names and string definitions. Then text files", | |
"should be scanned for STRING_TOKEN() usages, and the referenced", | |
"strings will be tagged as used in the database. After all files have been", | |
"scanned, then the database should be dumped to create the necessary output", | |
"files.", | |
"", | |
NULL | |
}; | |
for (Index = 0; Str[Index] != NULL; Index++) { | |
fprintf (stdout, "%s\n", Str[Index]); | |
} | |
} |