mirror of https://github.com/acidanthera/audk.git
2585 lines
70 KiB
C
2585 lines
70 KiB
C
/*++
|
|
|
|
Copyright (c) 2004-2007, 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"
|
|
|
|
#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
|
|
Version (
|
|
VOID
|
|
);
|
|
|
|
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 (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;
|
|
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 (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;
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
if ((strcmp(Argv[0], "-h") == 0) || (strcmp(Argv[0], "--help") == 0) ||
|
|
(strcmp(Argv[0], "-?") == 0) || (strcmp(Argv[0], "/?") == 0)) {
|
|
Usage();
|
|
return STATUS_ERROR;
|
|
}
|
|
|
|
if ((strcmp(Argv[0], "-V") == 0) || (strcmp(Argv[0], "--version") == 0)) {
|
|
Version();
|
|
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] != 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 (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], "-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 eng" or "-lang spa+cat" 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 {
|
|
//
|
|
// 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;
|
|
}
|
|
//
|
|
// 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 = (CHAR8 *) 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 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 (UTILITY_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 (UTILITY_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 (UTILITY_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
|
|
Version (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Displays the standard utility information to SDTOUT
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Returns:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
printf ("%s v%d.%d -Utility to process unicode strings file..\n", UTILITY_NAME, UTILITY_MAJOR_VERSION, UTILITY_MINOR_VERSION);
|
|
printf ("Copyright (c) 1999-2007 Intel Corporation. All rights reserved.\n");
|
|
}
|
|
|
|
static
|
|
void
|
|
Usage (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Print usage information for this utility.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Returns:
|
|
|
|
Nothing.
|
|
|
|
--*/
|
|
{
|
|
int Index;
|
|
static const char *Str[] = {
|
|
"",
|
|
" Usage: "UTILITY_NAME " -parse {parse options} [FileNames]",
|
|
" "UTILITY_NAME " -scan {scan options} [FileName]",
|
|
" "UTILITY_NAME " -dump {dump options}",
|
|
" Common options include:",
|
|
" -h,--help,-?,/? display help messages",
|
|
" -V,--version display version 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
|
|
};
|
|
|
|
Version();
|
|
|
|
for (Index = 0; Str[Index] != NULL; Index++) {
|
|
fprintf (stdout, "%s\n", Str[Index]);
|
|
}
|
|
}
|