/*++ 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 #include #include #include #include #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