audk/Tools/CCode/Source/StrGather/StrGather.c

2542 lines
69 KiB
C

/*++
Copyright (c) 2004, Intel Corporation
All rights reserved. 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 <Common/UefiBaseTypes.h>
#include "CommonLib.h"
#include "EfiUtilityMsgs.h"
#include "StrGather.h"
#include "StringDB.h"
#define TOOL_VERSION "0.31"
#ifndef MAX_PATH
#define MAX_PATH 255
#endif
#define MAX_NEST_DEPTH 20 // just in case we get in an endless loop.
#define MAX_STRING_IDENTIFIER_NAME 100 // number of wchars
#define MAX_LINE_LEN 200
#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
//
// Different file separater for Linux and Windows
//
#ifdef __GNUC__
#define FILE_SEP_CHAR '/'
#define FILE_SEP_STRING "/"
#else
#define FILE_SEP_CHAR '\\'
#define FILE_SEP_STRING "\\"
#endif
//
// 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;
CHAR8 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;
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
CHAR8 OutputDatabaseFileName[MAX_PATH];
CHAR8 StringHFileName[MAX_PATH];
CHAR8 StringCFileName[MAX_PATH]; // output .C filename
CHAR8 DumpUFileName[MAX_PATH]; // output unicode dump file name
CHAR8 HiiExportPackFileName[MAX_PATH]; // HII export pack file name
CHAR8 BaseName[MAX_PATH]; // base filename of the strings file
UINT32 Mode;
} mGlobals;
static
BOOLEAN
IsValidIdentifierChar (
CHAR8 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
UINT32
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 CHAR8 *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 CHAR8 *FileName,
OUT CHAR8 *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
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
STATUS
ScanFiles (
TEXT_STRING_LIST *ScanFiles
);
static
STATUS
ParseIndirectionFiles (
TEXT_STRING_LIST *Files
);
STATUS
StringDBCreateHiiExportPack (
CHAR8 *OutputFileName
);
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 (PROGRAM_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;
Status = ProcessIncludeFile (&mGlobals.SourceFiles, NULL);
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.StringCFileName,
mGlobals.BaseName,
mGlobals.Language,
mGlobals.IndirectionList
);
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);
}
//
// 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;
CHAR8 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");
return STATUS_ERROR;
}
SourceFile->Fptr = FindFile (SourceFile->FileName, FoundFileName, sizeof (FoundFileName));
if (SourceFile->Fptr == NULL) {
Error (ParentSourceFile->FileName, ParentSourceFile->LineNum, 0, SourceFile->FileName, "include file not found");
return STATUS_ERROR;
}
}
//
// Process the file found
//
ProcessFile (SourceFile);
Finish:
//
// 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"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;
//
// BUGBUG: Caling EFI_BREAKOINT() is breaking the link. What is the proper action for this tool
// in this condition?
//
// 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
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];
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
UINT32
GetLanguageIdentifierName (
IN SOURCE_FILE *SourceFile,
IN OUT WCHAR *LanguageIdentifierName,
IN UINT32 LanguageIdentifierNameLen,
IN BOOLEAN Optional
)
{
UINT32 Len;
WCHAR *From;
WCHAR *Start;
//
// Skip whitespace
//
SkipWhiteSpace (SourceFile);
if (SourceFile->EndOfFile) {
if (!Optional) {
Error (
SourceFile->FileName,
SourceFile->LineNum,
0,
"end-of-file encountered",
"expected language identifier"
);
}
return 0;
}
//
// 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 0;
}
}
Len = 0;
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
);
}
From = SourceFile->FileBufferPtr;
Start = SourceFile->FileBufferPtr;
while (!EndOfFile (SourceFile)) {
if (((SourceFile->FileBufferPtr[0] >= UNICODE_a) && (SourceFile->FileBufferPtr[0] <= UNICODE_z))) {
Len++;
if (Len > LANGUAGE_IDENTIFIER_NAME_LEN) {
Error (SourceFile->FileName, SourceFile->LineNum, 0, "language identifier name too long", "%S", Start);
return 0;
}
*LanguageIdentifierName = SourceFile->FileBufferPtr[0];
SourceFile->FileBufferPtr++;
LanguageIdentifierName++;
} else if (!IsWhiteSpace (SourceFile)) {
Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid language identifier name", "%S", Start);
return 0;
} else {
break;
}
}
//
// Terminate the copy of the string.
//
*LanguageIdentifierName = 0;
return Len;
}
static
void
ProcessTokenInclude (
SOURCE_FILE *SourceFile
)
{
CHAR8 IncludeFileName[MAX_PATH];
CHAR8 *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];
//
// 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
)
{
WCHAR LanguageIdentifier[MAX_STRING_IDENTIFIER_NAME];
UINT32 Len;
WCHAR *PrintableName;
//
// Extract the 3-character language identifier
//
Len = GetLanguageIdentifierName (SourceFile, LanguageIdentifier, sizeof (LanguageIdentifier), FALSE);
if (Len != LANGUAGE_IDENTIFIER_NAME_LEN) {
Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid or missing language identifier", NULL);
} else {
//
// Extract the printable name
//
PrintableName = GetPrintableLanguageName (SourceFile);
if (PrintableName != NULL) {
ParserSetPosition (SourceFile->FileName, SourceFile->LineNum);
StringDBAddLanguage (LanguageIdentifier, PrintableName);
free (PrintableName);
return ;
}
}
//
// Error recovery -- skip to next #
//
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
)
{
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;
Len = GetLanguageIdentifierName (SourceFile, Language, sizeof (Language), TRUE);
if (Len != LANGUAGE_IDENTIFIER_NAME_LEN) {
Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid or missing language identifier", "%S", Language);
SourceFile->SkipToHash = TRUE;
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 = StrLen (String) + 1;
ParserSetPosition (SourceFile->FileName, SourceFile->LineNum);
do {
SkipWhiteSpace (SourceFile);
SecondString = GetQuotedString (SourceFile, TRUE);
if (SecondString != NULL) {
Len += StrLen (SecondString);
TempString = (WCHAR *) malloc (Len * sizeof (WCHAR));
if (TempString == NULL) {
Error (NULL, 0, 0, "application error", "failed to allocate memory");
return ;
}
StrCpy (TempString, String);
StrCat (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;
}
//
// Given a filename, try to find it along the include paths.
//
static
FILE *
FindFile (
IN CHAR8 *FileName,
OUT CHAR8 *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 (PROGRAM_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;
//
// 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 (PROGRAM_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 (PROGRAM_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 (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure");
return STATUS_ERROR;
}
strcpy (NewList->Str, Argv[1]);
if (NewList->Str[strlen (NewList->Str) - 1] != FILE_SEP_CHAR) {
strcat (NewList->Str, FILE_SEP_STRING);
}
//
// 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 (PROGRAM_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 (PROGRAM_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 (PROGRAM_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 (PROGRAM_NAME, 0, 0, Argv[0], "missing database file name");
return STATUS_ERROR;
}
NewList = malloc (sizeof (TEXT_STRING_LIST));
if (NewList == NULL) {
Error (PROGRAM_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 (PROGRAM_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 (PROGRAM_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 (PROGRAM_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 (PROGRAM_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 (PROGRAM_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 (PROGRAM_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 (PROGRAM_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 (PROGRAM_NAME, 0, 0, Argv[0], "missing output .h filename");
return STATUS_ERROR;
}
strcpy (mGlobals.StringHFileName, 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 (PROGRAM_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 (PROGRAM_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 (PROGRAM_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 eng" or "-lang spa+cat" to only output certain languages
//
if ((Argc <= 1) || (Argv[1][0] == '-')) {
Error (PROGRAM_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 (PROGRAM_NAME, 0, 0, Argv[0], "missing output database file name");
return STATUS_ERROR;
}
strcpy (mGlobals.OutputDatabaseFileName, Argv[1]);
Argv++;
Argc--;
} else {
//
// Unrecognized arg
//
Error (PROGRAM_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 (PROGRAM_NAME, 0, 0, NULL, "must specify at least one source file to scan with -scan");
Usage ();
return STATUS_ERROR;
}
//
// Get the list of filenames
//
while (Argc > 0) {
NewList = malloc (sizeof (TEXT_STRING_LIST));
if (NewList == NULL) {
Error (PROGRAM_NAME, 0, 0, "memory allocation failure", NULL);
return STATUS_ERROR;
}
memset (NewList, 0, sizeof (TEXT_STRING_LIST));
NewList->Str = (CHAR8 *) malloc (strlen (Argv[0]) + 1);
if (NewList->Str == NULL) {
Error (PROGRAM_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 (PROGRAM_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 eng,spa+cat" on the command line. Parse the
// language list and save the setting for later processing.
//
static
STATUS
AddCommandLineLanguage (
IN CHAR8 *Language
)
{
WCHAR_STRING_LIST *WNewList;
WCHAR *From;
WCHAR *To;
//
// Keep processing the input string until we find the end.
//
while (*Language) {
//
// Allocate memory for a new list element, fill it in, and
// add it to our list.
//
WNewList = MALLOC (sizeof (WCHAR_STRING_LIST));
if (WNewList == NULL) {
Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure");
return STATUS_ERROR;
}
memset ((char *) WNewList, 0, sizeof (WCHAR_STRING_LIST));
WNewList->Str = malloc ((strlen (Language) + 1) * sizeof (WCHAR));
if (WNewList->Str == NULL) {
free (WNewList);
Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure");
return STATUS_ERROR;
}
//
// Copy it as unicode to our new structure. Then remove the
// plus signs in it, and verify each language name is 3 characters
// long. If we find a comma, then we're done with this group, so
// break out.
//
UnicodeSPrint (WNewList->Str, (strlen (Language) + 1) * sizeof (WCHAR), L"%a", Language);
From = To = WNewList->Str;
while (*From) {
if (*From == L',') {
break;
}
if ((StrLen (From) < LANGUAGE_IDENTIFIER_NAME_LEN) ||
(
(From[LANGUAGE_IDENTIFIER_NAME_LEN] != 0) &&
(From[LANGUAGE_IDENTIFIER_NAME_LEN] != UNICODE_PLUS_SIGN) &&
(From[LANGUAGE_IDENTIFIER_NAME_LEN] != L',')
)
) {
Error (PROGRAM_NAME, 0, 0, Language, "invalid format for language name on command line");
FREE (WNewList->Str);
FREE (WNewList);
return STATUS_ERROR;
}
StrnCpy (To, From, LANGUAGE_IDENTIFIER_NAME_LEN);
To += LANGUAGE_IDENTIFIER_NAME_LEN;
From += LANGUAGE_IDENTIFIER_NAME_LEN;
if (*From == L'+') {
From++;
}
}
*To = 0;
//
// Add it to our linked list
//
if (mGlobals.Language == NULL) {
mGlobals.Language = WNewList;
} else {
mGlobals.LastLanguage->Next = WNewList;
}
mGlobals.LastLanguage = WNewList;
//
// Skip to next entry (comma-separated list)
//
while (*Language) {
if (*Language == L',') {
Language++;
break;
}
Language++;
}
}
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;
CHAR8 Line[200];
CHAR8 *StringName;
CHAR8 *ScopeName;
CHAR8 *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;
}
UnicodeSPrint (NewList->Str1, strlen (StringName) + 1, L"%a", StringName);
UnicodeSPrint (NewList->Str2, strlen (ScopeName) + 1, L"%a", ScopeName);
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
STATUS
ScanFiles (
TEXT_STRING_LIST *ScanFiles
)
{
char Line[MAX_LINE_LEN];
FILE *Fptr;
UINT32 LineNum;
char *Cptr;
char *SavePtr;
char *TermPtr;
char *StringTokenPos;
TEXT_STRING_LIST *SList;
BOOLEAN SkipIt;
//
// 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.
//
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);
}
Fptr = fopen (ScanFiles->Str, "r");
if (Fptr == NULL) {
Error (NULL, 0, 0, ScanFiles->Str, "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;
}
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;
}
//
// 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 (
CHAR8 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 = 0;
}
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;
static const char *Str[] = {
"",
PROGRAM_NAME " version "TOOL_VERSION " -- process unicode strings file",
" Usage: "PROGRAM_NAME " -parse {parse options} [FileNames]",
" "PROGRAM_NAME " -scan {scan options} [FileName]",
" "PROGRAM_NAME " -dump {dump options}",
" 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",
" -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",
" -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'",
" -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]);
}
}