blob: 5fa5a22096e1115e27bb725fe7e7f24b52cb61a4 [file] [log] [blame]
/*++
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:
SimpleFileParsing.c
Abstract:
Generic but simple file parsing routines.
--*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <Common/UefiBaseTypes.h>
#include "EfiUtilityMsgs.h"
#include "SimpleFileParsing.h"
#define MAX_PATH 255
#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 400
#define T_CHAR_SPACE ' '
#define T_CHAR_NULL 0
#define T_CHAR_CR '\r'
#define T_CHAR_TAB '\t'
#define T_CHAR_LF '\n'
#define T_CHAR_SLASH '/'
#define T_CHAR_BACKSLASH '\\'
#define T_CHAR_DOUBLE_QUOTE '"'
#define T_CHAR_LC_X 'x'
#define T_CHAR_0 '0'
//
// We keep a linked list of these for the source files we process
//
typedef struct _SOURCE_FILE {
FILE *Fptr;
T_CHAR *FileBuffer;
T_CHAR *FileBufferPtr;
UINT32 FileSize;
INT8 FileName[MAX_PATH];
UINT32 LineNum;
BOOLEAN EndOfFile;
BOOLEAN SkipToHash;
struct _SOURCE_FILE *Previous;
struct _SOURCE_FILE *Next;
T_CHAR ControlCharacter;
} SOURCE_FILE;
//
// Here's all our module globals.
//
static struct {
SOURCE_FILE SourceFile;
BOOLEAN Verbose;
} mGlobals;
static
UINT32
t_strcmp (
T_CHAR *Buffer,
T_CHAR *Str
);
static
UINT32
t_strncmp (
T_CHAR *Str1,
T_CHAR *Str2,
UINT32 Len
);
static
UINT32
t_strlen (
T_CHAR *Str
);
static
void
RewindFile (
SOURCE_FILE *SourceFile
);
static
BOOLEAN
SkipTo (
SOURCE_FILE *SourceFile,
T_CHAR TChar,
BOOLEAN StopAfterNewline
);
static
BOOLEAN
IsWhiteSpace (
SOURCE_FILE *SourceFile
);
static
UINT32
SkipWhiteSpace (
SOURCE_FILE *SourceFile
);
static
BOOLEAN
EndOfFile (
SOURCE_FILE *SourceFile
);
static
void
PreprocessFile (
SOURCE_FILE *SourceFile
);
//
// static
// T_CHAR *
// GetQuotedString (
// SOURCE_FILE *SourceFile,
// BOOLEAN Optional
// );
//
static
T_CHAR *
t_strcpy (
T_CHAR *Dest,
T_CHAR *Src
);
static
STATUS
ProcessIncludeFile (
SOURCE_FILE *SourceFile,
SOURCE_FILE *ParentSourceFile
);
static
STATUS
ParseFile (
SOURCE_FILE *SourceFile
);
static
FILE *
FindFile (
IN INT8 *FileName,
OUT INT8 *FoundFileName,
IN UINT32 FoundFileNameLen
);
static
STATUS
ProcessFile (
SOURCE_FILE *SourceFile
);
STATUS
SFPInit (
VOID
)
{
memset ((void *) &mGlobals, 0, sizeof (mGlobals));
return STATUS_SUCCESS;
}
UINT32
SFPGetLineNumber (
VOID
)
{
return mGlobals.SourceFile.LineNum;
}
/*++
Routine Description:
Return the line number of the file we're parsing. Used
for error reporting purposes.
Arguments:
None.
Returns:
The line number, or 0 if no file is being processed
--*/
T_CHAR *
SFPGetFileName (
VOID
)
/*++
Routine Description:
Return the name of the file we're parsing. Used
for error reporting purposes.
Arguments:
None.
Returns:
A pointer to the file name. Null if no file is being
processed.
--*/
{
if (mGlobals.SourceFile.FileName[0]) {
return mGlobals.SourceFile.FileName;
}
return NULL;
}
STATUS
SFPOpenFile (
IN INT8 *FileName
)
/*++
Routine Description:
Open a file for parsing.
Arguments:
FileName - name of the file to parse
Returns:
--*/
{
STATUS Status;
t_strcpy (mGlobals.SourceFile.FileName, FileName);
Status = ProcessIncludeFile (&mGlobals.SourceFile, NULL);
return Status;
}
BOOLEAN
SFPIsToken (
T_CHAR *Str
)
/*++
Routine Description:
Check to see if the specified token is found at
the current position in the input file.
Arguments:
Str - the token to look for
Returns:
TRUE - the token is next
FALSE - the token is not next
Notes:
We do a simple string comparison on this function. It is
the responsibility of the caller to ensure that the token
is not a subset of some other token.
The file pointer is advanced past the token in the input file.
--*/
{
UINT32 Len;
SkipWhiteSpace (&mGlobals.SourceFile);
if ((Len = t_strcmp (mGlobals.SourceFile.FileBufferPtr, Str)) > 0) {
mGlobals.SourceFile.FileBufferPtr += Len;
return TRUE;
}
return FALSE;
}
BOOLEAN
SFPGetNextToken (
T_CHAR *Str,
UINT32 Len
)
{
UINT32 Index;
SkipWhiteSpace (&mGlobals.SourceFile);
Index = 0;
while (!EndOfFile (&mGlobals.SourceFile) && (Index < Len)) {
if (IsWhiteSpace (&mGlobals.SourceFile)) {
if (Index > 0) {
Str[Index] = 0;
return TRUE;
}
return FALSE;
} else {
Str[Index] = mGlobals.SourceFile.FileBufferPtr[0];
mGlobals.SourceFile.FileBufferPtr++;
Index++;
}
}
return FALSE;
}
BOOLEAN
SFPSkipToToken (
T_CHAR *Str
)
{
UINT32 Len;
T_CHAR *SavePos;
Len = t_strlen (Str);
SavePos = mGlobals.SourceFile.FileBufferPtr;
SkipWhiteSpace (&mGlobals.SourceFile);
while (!EndOfFile (&mGlobals.SourceFile)) {
if (t_strncmp (Str, mGlobals.SourceFile.FileBufferPtr, Len) == 0) {
mGlobals.SourceFile.FileBufferPtr += Len;
return TRUE;
}
mGlobals.SourceFile.FileBufferPtr++;
SkipWhiteSpace (&mGlobals.SourceFile);
}
mGlobals.SourceFile.FileBufferPtr = SavePos;
return FALSE;
}
BOOLEAN
SFPGetNumber (
UINT32 *Value
)
/*++
Routine Description:
Check the token at the current file position for a numeric value.
May be either decimal or hex.
Arguments:
Value - pointer where to store the value
Returns:
FALSE - current token is not a number
TRUE - current token is a number
--*/
{
//
// UINT32 Len;
//
SkipWhiteSpace (&mGlobals.SourceFile);
if (EndOfFile (&mGlobals.SourceFile)) {
return FALSE;
}
if (isdigit (mGlobals.SourceFile.FileBufferPtr[0])) {
//
// Check for hex value
//
if ((mGlobals.SourceFile.FileBufferPtr[0] == T_CHAR_0) && (mGlobals.SourceFile.FileBufferPtr[1] == T_CHAR_LC_X)) {
if (!isxdigit (mGlobals.SourceFile.FileBufferPtr[2])) {
return FALSE;
}
mGlobals.SourceFile.FileBufferPtr += 2;
sscanf (mGlobals.SourceFile.FileBufferPtr, "%x", Value);
while (isxdigit (mGlobals.SourceFile.FileBufferPtr[0])) {
mGlobals.SourceFile.FileBufferPtr++;
}
return TRUE;
} else {
*Value = atoi (mGlobals.SourceFile.FileBufferPtr);
while (isdigit (mGlobals.SourceFile.FileBufferPtr[0])) {
mGlobals.SourceFile.FileBufferPtr++;
}
return TRUE;
}
} else {
return FALSE;
}
}
STATUS
SFPCloseFile (
VOID
)
/*++
Routine Description:
Close the file being parsed.
Arguments:
None.
Returns:
STATUS_SUCCESS - the file was closed
STATUS_ERROR - no file is currently open
--*/
{
if (mGlobals.SourceFile.FileBuffer != NULL) {
free (mGlobals.SourceFile.FileBuffer);
memset (&mGlobals.SourceFile, 0, sizeof (mGlobals.SourceFile));
return STATUS_SUCCESS;
}
return STATUS_ERROR;
}
static
STATUS
ProcessIncludeFile (
SOURCE_FILE *SourceFile,
SOURCE_FILE *ParentSourceFile
)
/*++
Routine Description:
Given a source file, open the file and parse it
Arguments:
SourceFile - name of file to parse
ParentSourceFile - for error reporting purposes, the file that #included SourceFile.
Returns:
Standard status.
--*/
{
static UINT32 NestDepth = 0;
INT8 FoundFileName[MAX_PATH];
STATUS Status;
Status = STATUS_SUCCESS;
NestDepth++;
//
// Print the file being processed. Indent so you can tell the include nesting
// depth.
//
if (mGlobals.Verbose) {
fprintf (stdout, "%*cProcessing file '%s'\n", NestDepth * 2, ' ', SourceFile->FileName);
}
//
// Make sure we didn't exceed our maximum nesting depth
//
if (NestDepth > MAX_NEST_DEPTH) {
Error (NULL, 0, 0, SourceFile->FileName, "max nesting depth (%d) exceeded", NestDepth);
Status = STATUS_ERROR;
goto Finish;
}
//
// Try to open the file locally, and if that fails try along our include paths.
//
strcpy (FoundFileName, SourceFile->FileName);
if ((SourceFile->Fptr = fopen (FoundFileName, "r")) == NULL) {
//
// Try to find it among the paths if it has a parent (that is, it is included
// by someone else).
//
Error (NULL, 0, 0, SourceFile->FileName, "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);
SourceFile->Fptr = NULL;
}
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 = (T_CHAR *) malloc (SourceFile->FileSize + sizeof (T_CHAR));
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 (T_CHAR))] = T_CHAR_NULL;
//
// Pre-process the file to replace comments with spaces
//
PreprocessFile (SourceFile);
SourceFile->LineNum = 1;
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] == T_CHAR_LF) {
SourceFile->FileBufferPtr++;
SourceFile->LineNum++;
InComment = 0;
} else if (SourceFile->FileBufferPtr[0] == T_CHAR_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] = T_CHAR_SPACE;
SourceFile->FileBufferPtr++;
} else if ((SourceFile->FileBufferPtr[0] == T_CHAR_SLASH) && (SourceFile->FileBufferPtr[1] == T_CHAR_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);
}
#if 0
static
T_CHAR *
GetQuotedString (
SOURCE_FILE *SourceFile,
BOOLEAN Optional
)
{
T_CHAR *String;
T_CHAR *Start;
T_CHAR *Ptr;
UINT32 Len;
BOOLEAN PreviousBackslash;
if (SourceFile->FileBufferPtr[0] != T_CHAR_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] == T_CHAR_DOUBLE_QUOTE) && (!PreviousBackslash)) {
break;
} else if (SourceFile->FileBufferPtr[0] == T_CHAR_CR) {
Warning (SourceFile->FileName, SourceFile->LineNum, 0, "carriage return found in quoted string", "%S", Start);
PreviousBackslash = FALSE;
} else if (SourceFile->FileBufferPtr[0] == T_CHAR_BACKSLASH) {
PreviousBackslash = TRUE;
} else {
PreviousBackslash = FALSE;
}
SourceFile->FileBufferPtr++;
Len++;
}
if (SourceFile->FileBufferPtr[0] != T_CHAR_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 = (T_CHAR *) malloc ((Len + 1) * sizeof (T_CHAR));
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;
}
#endif
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 (T_CHAR)) {
SourceFile->EndOfFile = TRUE;
return TRUE;
}
if (SourceFile->EndOfFile) {
return TRUE;
}
return FALSE;
}
#if 0
static
void
ProcessTokenInclude (
SOURCE_FILE *SourceFile
)
{
INT8 IncludeFileName[MAX_PATH];
INT8 *To;
UINT32 Len;
BOOLEAN ReportedError;
SOURCE_FILE IncludedSourceFile;
ReportedError = FALSE;
if (SkipWhiteSpace (SourceFile) == 0) {
Warning (SourceFile->FileName, SourceFile->LineNum, 0, "expected whitespace following #include keyword", NULL);
}
//
// Should be quoted file name
//
if (SourceFile->FileBufferPtr[0] != T_CHAR_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] == T_CHAR_CR) || (SourceFile->FileBufferPtr[0] == T_CHAR_LF)) {
Error (SourceFile->FileName, SourceFile->LineNum, 0, "end-of-line found in quoted include file name", NULL);
goto FailDone;
}
if (SourceFile->FileBufferPtr[0] == T_CHAR_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 = (T_CHAR) 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;
}
#endif
static
BOOLEAN
IsWhiteSpace (
SOURCE_FILE *SourceFile
)
{
switch (*SourceFile->FileBufferPtr) {
case T_CHAR_NULL:
case T_CHAR_CR:
case T_CHAR_SPACE:
case T_CHAR_TAB:
case T_CHAR_LF:
return TRUE;
default:
return FALSE;
}
}
UINT32
SkipWhiteSpace (
SOURCE_FILE *SourceFile
)
{
UINT32 Count;
Count = 0;
while (!EndOfFile (SourceFile)) {
Count++;
switch (*SourceFile->FileBufferPtr) {
case T_CHAR_NULL:
case T_CHAR_CR:
case T_CHAR_SPACE:
case T_CHAR_TAB:
SourceFile->FileBufferPtr++;
break;
case T_CHAR_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
t_strcmp (
T_CHAR *Buffer,
T_CHAR *Str
)
{
UINT32 Len;
Len = 0;
while (*Str == *Buffer) {
Buffer++;
Str++;
Len++;
}
if (*Str) {
return 0;
}
return Len;
}
static
UINT32
t_strlen (
T_CHAR *Str
)
{
UINT32 Len;
Len = 0;
while (*Str) {
Len++;
Str++;
}
return Len;
}
static
UINT32
t_strncmp (
T_CHAR *Str1,
T_CHAR *Str2,
UINT32 Len
)
{
while (Len > 0) {
if (*Str1 != *Str2) {
return Len;
}
Len--;
Str1++;
Str2++;
}
return 0;
}
static
T_CHAR *
t_strcpy (
T_CHAR *Dest,
T_CHAR *Src
)
{
T_CHAR *SaveDest;
SaveDest = Dest;
while (*Src) {
*Dest = *Src;
Dest++;
Src++;
}
*Dest = 0;
return SaveDest;
}
#if 0
static
BOOLEAN
IsValidIdentifierChar (
INT8 Char,
BOOLEAN FirstChar
)
{
//
// If it's the first character of an identifier, then
// it must be one of [A-Za-z_].
//
if (FirstChar) {
if (isalpha (Char) || (Char == '_')) {
return TRUE;
}
} else {
//
// If it's not the first character, then it can
// be one of [A-Za-z_0-9]
//
if (isalnum (Char) || (Char == '_')) {
return TRUE;
}
}
return FALSE;
}
#endif
static
void
RewindFile (
SOURCE_FILE *SourceFile
)
{
SourceFile->LineNum = 1;
SourceFile->FileBufferPtr = SourceFile->FileBuffer;
SourceFile->EndOfFile = 0;
}
#if 0
static
BOOLEAN
SkipTo (
SOURCE_FILE *SourceFile,
T_CHAR TChar,
BOOLEAN StopAfterNewline
)
{
while (!EndOfFile (SourceFile)) {
//
// Check for the character of interest
//
if (SourceFile->FileBufferPtr[0] == TChar) {
return TRUE;
} else {
if (SourceFile->FileBufferPtr[0] == T_CHAR_LF) {
SourceFile->LineNum++;
if (StopAfterNewline) {
SourceFile->FileBufferPtr++;
if (SourceFile->FileBufferPtr[0] == 0) {
SourceFile->FileBufferPtr++;
}
return FALSE;
}
}
SourceFile->FileBufferPtr++;
}
}
return FALSE;
}
#endif