/** @file
Generic but simple file parsing routines.

Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution.  The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php

THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

--*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

#include "CommonLib.h"
#include "EfiUtilityMsgs.h"
#include "SimpleFileParsing.h"

#ifndef MAX_PATH
#define MAX_PATH  255
#endif
//
// just in case we get in an endless loop.
//
#define MAX_NEST_DEPTH  20
//
// number of wchars
//
#define MAX_STRING_IDENTIFIER_NAME  100

#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'
#define T_CHAR_STAR                 '*'

//
// We keep a linked list of these for the source files we process
//
typedef struct _SOURCE_FILE {
  FILE                *Fptr;
  CHAR8               *FileBuffer;
  CHAR8               *FileBufferPtr;
  UINTN               FileSize;
  CHAR8               FileName[MAX_PATH];
  UINTN               LineNum;
  BOOLEAN             EndOfFile;
  BOOLEAN             SkipToHash;
  struct _SOURCE_FILE *Previous;
  struct _SOURCE_FILE *Next;
  CHAR8               ControlCharacter;
} SOURCE_FILE;

typedef struct {
  CHAR8   *FileBufferPtr;
} FILE_POSITION;

//
// Keep all our module globals in this structure
//
STATIC struct {
  SOURCE_FILE SourceFile;
  BOOLEAN     VerboseFile;
  BOOLEAN     VerboseToken;
} mGlobals;

STATIC
UINTN
t_strcmp (
  CHAR8  *Buffer,
  CHAR8  *Str
  );

STATIC
UINTN
t_strncmp (
  CHAR8  *Str1,
  CHAR8  *Str2,
  INTN    Len
  );

STATIC
UINTN
t_strlen (
  CHAR8  *Str
  );

STATIC
VOID
RewindFile (
  SOURCE_FILE *SourceFile
  );

STATIC
BOOLEAN
IsWhiteSpace (
  SOURCE_FILE *SourceFile
  );

STATIC
UINTN
SkipWhiteSpace (
  SOURCE_FILE *SourceFile
  );

STATIC
BOOLEAN
EndOfFile (
  SOURCE_FILE *SourceFile
  );

STATIC
VOID
PreprocessFile (
  SOURCE_FILE *SourceFile
  );

STATIC
CHAR8   *
t_strcpy (
  CHAR8  *Dest,
  CHAR8  *Src
  );

STATIC
STATUS
ProcessIncludeFile (
  SOURCE_FILE *SourceFile,
  SOURCE_FILE *ParentSourceFile
  );

STATIC
STATUS
ProcessFile (
  SOURCE_FILE *SourceFile
  );

STATIC
STATUS
GetFilePosition (
  FILE_POSITION *Fpos
  );

STATIC
STATUS
SetFilePosition (
  FILE_POSITION *Fpos
  );

STATUS
SFPInit (
  VOID
  )
/*++

Routine Description:

Arguments:
  None.

Returns:
  STATUS_SUCCESS always

--*/
{
  memset ((VOID *) &mGlobals, 0, sizeof (mGlobals));
  return STATUS_SUCCESS;
}

UINTN
SFPGetLineNumber (
  VOID
  )
/*++

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

--*/
{
  return mGlobals.SourceFile.LineNum;
}

CHAR8  *
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 (
  CHAR8      *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 (
  CHAR8  *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.

--*/
{
  UINTN  Len;
  SkipWhiteSpace (&mGlobals.SourceFile);
  if (EndOfFile (&mGlobals.SourceFile)) {
    return FALSE;
  }

  if ((Len = t_strcmp (mGlobals.SourceFile.FileBufferPtr, Str)) > 0) {
    mGlobals.SourceFile.FileBufferPtr += Len;
    if (mGlobals.VerboseToken) {
      printf ("Token: '%s'\n", Str);
    }

    return TRUE;
  }

  return FALSE;
}

BOOLEAN
SFPIsKeyword (
  CHAR8  *Str
  )
/*++

Routine Description:
  Check to see if the specified keyword is found at
  the current position in the input file.

Arguments:
  Str - keyword to look for

Returns:
  TRUE - the keyword is next
  FALSE - the keyword is not next

Notes:
  A keyword is defined as a "special" string that has a non-alphanumeric
  character following it.

--*/
{
  UINTN  Len;
  SkipWhiteSpace (&mGlobals.SourceFile);
  if (EndOfFile (&mGlobals.SourceFile)) {
    return FALSE;
  }

  if ((Len = t_strcmp (mGlobals.SourceFile.FileBufferPtr, Str)) > 0) {
    if (isalnum ((int)mGlobals.SourceFile.FileBufferPtr[Len])) {
      return FALSE;
    }

    mGlobals.SourceFile.FileBufferPtr += Len;
    if (mGlobals.VerboseToken) {
      printf ("Token: '%s'\n", Str);
    }

    return TRUE;
  }

  return FALSE;
}

BOOLEAN
SFPGetNextToken (
  CHAR8  *Str,
  UINTN  Len
  )
/*++

Routine Description:
  Get the next token from the input stream.

Arguments:
  Str - pointer to a copy of the next token
  Len - size of buffer pointed to by Str

Returns:
  TRUE  - next token successfully returned
  FALSE - otherwise

Notes:
  Preceeding white space is ignored.
  The parser's buffer pointer is advanced past the end of the
  token.

--*/
{
  UINTN  Index;
  CHAR8  TempChar;

  SkipWhiteSpace (&mGlobals.SourceFile);
  if (EndOfFile (&mGlobals.SourceFile)) {
    return FALSE;
  }
  //
  // Have to have enough string for at least one char and a null-terminator
  //
  if (Len < 2) {
    return FALSE;
  }
  //
  // Look at the first character. If it's an identifier, then treat it
  // as such
  //
  TempChar = mGlobals.SourceFile.FileBufferPtr[0];
  if (((TempChar >= 'a') && (TempChar <= 'z')) || ((TempChar >= 'A') && (TempChar <= 'Z')) || (TempChar == '_')) {
    Str[0] = TempChar;
    mGlobals.SourceFile.FileBufferPtr++;
    Index = 1;
    while (!EndOfFile (&mGlobals.SourceFile) && (Index < Len)) {
      TempChar = mGlobals.SourceFile.FileBufferPtr[0];
      if (((TempChar >= 'a') && (TempChar <= 'z')) ||
          ((TempChar >= 'A') && (TempChar <= 'Z')) ||
          ((TempChar >= '0') && (TempChar <= '9')) ||
          (TempChar == '_')
          ) {
        Str[Index] = mGlobals.SourceFile.FileBufferPtr[0];
        mGlobals.SourceFile.FileBufferPtr++;
        Index++;
      } else {
        //
        // Invalid character for symbol name, so break out
        //
        break;
      }
    }
    //
    // Null terminate and return success
    //
    Str[Index] = 0;
    return TRUE;
  } else if ((TempChar == ')') || (TempChar == '(') || (TempChar == '*')) {
    Str[0] = mGlobals.SourceFile.FileBufferPtr[0];
    mGlobals.SourceFile.FileBufferPtr++;
    Str[1] = 0;
    return TRUE;
  } else {
    //
    // Everything else is white-space (or EOF) separated
    //
    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++;
      }
    }
    //
    // See if we just ran out of file contents, but did find a token
    //
    if ((Index > 0) && EndOfFile (&mGlobals.SourceFile)) {
      Str[Index] = 0;
      return TRUE;
    }
  }

  return FALSE;
}

BOOLEAN
SFPGetGuidToken (
  CHAR8  *Str,
  UINT32 Len
  )
/*++

Routine Description:
  Parse a GUID from the input stream. Stop when you discover white space.

Arguments:
  Str - pointer to a copy of the next token
  Len - size of buffer pointed to by Str

Returns:
  TRUE  - GUID string returned successfully
  FALSE - otherwise

--*/
{
  UINT32  Index;
  SkipWhiteSpace (&mGlobals.SourceFile);
  if (EndOfFile (&mGlobals.SourceFile)) {
    return FALSE;
  }

  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 (
  CHAR8  *Str
  )
{
  UINTN  Len;
  CHAR8         *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 (
  UINTN *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

--*/
{
  unsigned Val;

  SkipWhiteSpace (&mGlobals.SourceFile);
  if (EndOfFile (&mGlobals.SourceFile)) {
    return FALSE;
  }

  if (isdigit ((int)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 ((int)mGlobals.SourceFile.FileBufferPtr[2])) {
        return FALSE;
      }

      mGlobals.SourceFile.FileBufferPtr += 2;
      sscanf (mGlobals.SourceFile.FileBufferPtr, "%x", &Val);
      *Value = (UINT32) Val;
      while (isxdigit ((int)mGlobals.SourceFile.FileBufferPtr[0])) {
        mGlobals.SourceFile.FileBufferPtr++;
      }

      return TRUE;
    } else {
      *Value = atoi (mGlobals.SourceFile.FileBufferPtr);
      while (isdigit ((int)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 UINTN 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.VerboseFile) {
    fprintf (stdout, "%*cProcessing file '%s'\n", (int)NestDepth * 2, ' ', SourceFile->FileName);
    fprintf (stdout, "Parent source file = '%s'\n", ParentSourceFile->FileName);
  }

  //
  // Make sure we didn't exceed our maximum nesting depth
  //
  if (NestDepth > MAX_NEST_DEPTH) {
    Error (NULL, 0, 3001, "Not Supported", "%s exceeeds max nesting depth (%u)", SourceFile->FileName, (unsigned) 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 (LongFilePath (FoundFileName), "rb")) == NULL) {
    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
  )
/*++

Routine Description:

  Given a source file that's been opened, read the contents into an internal
  buffer and pre-process it to remove comments.

Arguments:

  SourceFile        - structure containing info on the file to process

Returns:

  Standard status.

--*/
{
  //
  // Get the file size, and then read the entire thing into memory.
  // Allocate extra space for a terminator character.
  //
  fseek (SourceFile->Fptr, 0, SEEK_END);
  SourceFile->FileSize = ftell (SourceFile->Fptr);
  if (mGlobals.VerboseFile) {
    printf ("FileSize = %u (0x%X)\n", (unsigned) SourceFile->FileSize, (unsigned) SourceFile->FileSize);
  }

  fseek (SourceFile->Fptr, 0, SEEK_SET);
  SourceFile->FileBuffer = (CHAR8  *) malloc (SourceFile->FileSize + sizeof (CHAR8 ));
  if (SourceFile->FileBuffer == NULL) {
    Error (NULL, 0, 4001, "Resource: memory cannot be allocated", NULL);
    return STATUS_ERROR;
  }

  fread ((VOID *) SourceFile->FileBuffer, SourceFile->FileSize, 1, SourceFile->Fptr);
  SourceFile->FileBuffer[(SourceFile->FileSize / sizeof (CHAR8 ))] = 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 (as part of error messages) from the file to the screen.

Arguments:
  SourceFile - structure that we use to keep track of an input file.

Returns:
  Nothing.

--*/
{
  BOOLEAN InComment;
  BOOLEAN SlashSlashComment;
  int     LineNum;

  RewindFile (SourceFile);
  InComment         = FALSE;
  SlashSlashComment = FALSE;
  while (!EndOfFile (SourceFile)) {
    //
    // If a line-feed, then no longer in a comment if we're in a // comment
    //
    if (SourceFile->FileBufferPtr[0] == T_CHAR_LF) {
      SourceFile->FileBufferPtr++;
      SourceFile->LineNum++;
      if (InComment && SlashSlashComment) {
        InComment         = FALSE;
        SlashSlashComment = FALSE;
      }
    } 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++;
      //
      // Check for */ comment end
      //
    } else if (InComment &&
             !SlashSlashComment &&
             (SourceFile->FileBufferPtr[0] == T_CHAR_STAR) &&
             (SourceFile->FileBufferPtr[1] == T_CHAR_SLASH)
            ) {
      SourceFile->FileBufferPtr[0] = T_CHAR_SPACE;
      SourceFile->FileBufferPtr++;
      SourceFile->FileBufferPtr[0] = T_CHAR_SPACE;
      SourceFile->FileBufferPtr++;
      InComment = FALSE;
    } else if (InComment) {
      SourceFile->FileBufferPtr[0] = T_CHAR_SPACE;
      SourceFile->FileBufferPtr++;
      //
      // Check for // comments
      //
    } else if ((SourceFile->FileBufferPtr[0] == T_CHAR_SLASH) && (SourceFile->FileBufferPtr[1] == T_CHAR_SLASH)) {
      InComment         = TRUE;
      SlashSlashComment = TRUE;
      //
      // Check for /* comment start
      //
    } else if ((SourceFile->FileBufferPtr[0] == T_CHAR_SLASH) && (SourceFile->FileBufferPtr[1] == T_CHAR_STAR)) {
      SourceFile->FileBufferPtr[0] = T_CHAR_SPACE;
      SourceFile->FileBufferPtr++;
      SourceFile->FileBufferPtr[0] = T_CHAR_SPACE;
      SourceFile->FileBufferPtr++;
      SlashSlashComment = FALSE;
      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);
  //
  // Dump the reformatted file if verbose mode
  //
  if (mGlobals.VerboseFile) {
    LineNum = 1;
    printf ("%04d: ", LineNum);
    while (!EndOfFile (SourceFile)) {
      if (SourceFile->FileBufferPtr[0] == T_CHAR_LF) {
        printf ("'\n%04d: '", ++LineNum);
      } else {
        printf ("%c", SourceFile->FileBufferPtr[0]);
      }

      SourceFile->FileBufferPtr++;
    }

    printf ("'\n");
    printf ("FileSize = %u (0x%X)\n", (unsigned)SourceFile->FileSize, (unsigned)SourceFile->FileSize);
    RewindFile (SourceFile);
  }
}

BOOLEAN
SFPGetQuotedString (
  CHAR8       *Str,
  INTN         Length
  )
/*++

Routine Description:
  Retrieve a quoted-string from the input file.

Arguments:
  Str    - pointer to a copy of the quoted string parsed
  Length - size of buffer pointed to by Str

Returns:
  TRUE    - next token in input stream was a quoted string, and
            the string value was returned in Str
  FALSE   - otherwise

--*/
{
  SkipWhiteSpace (&mGlobals.SourceFile);
  if (EndOfFile (&mGlobals.SourceFile)) {
    return FALSE;
  }

  if (mGlobals.SourceFile.FileBufferPtr[0] == T_CHAR_DOUBLE_QUOTE) {
    mGlobals.SourceFile.FileBufferPtr++;
    while (Length > 0) {
      if (EndOfFile (&mGlobals.SourceFile)) {
        return FALSE;
      }
      //
      // Check for closing quote
      //
      if (mGlobals.SourceFile.FileBufferPtr[0] == T_CHAR_DOUBLE_QUOTE) {
        mGlobals.SourceFile.FileBufferPtr++;
        *Str = 0;
        return TRUE;
      }

      *Str = mGlobals.SourceFile.FileBufferPtr[0];
      Str++;
      Length--;
      mGlobals.SourceFile.FileBufferPtr++;
    }
  }
  //
  // First character was not a quote, or the input string length was
  // insufficient to contain the quoted string, so return failure code.
  //
  return FALSE;
}

BOOLEAN
SFPIsEOF (
  VOID
  )
/*++

Routine Description:
  Return TRUE of FALSE to indicate whether or not we've reached the end of the
  file we're parsing.

Arguments:
  NA

Returns:
  TRUE    - EOF reached
  FALSE   - otherwise

--*/
{
  SkipWhiteSpace (&mGlobals.SourceFile);
  return EndOfFile (&mGlobals.SourceFile);
}

#if 0
STATIC
CHAR8  *
GetQuotedString (
  SOURCE_FILE *SourceFile,
  BOOLEAN     Optional
  )
{
  CHAR8         *String;
  CHAR8         *Start;
  CHAR8         *Ptr;
  UINTN         Len;
  BOOLEAN       PreviousBackslash;

  if (SourceFile->FileBufferPtr[0] != T_CHAR_DOUBLE_QUOTE) {
    if (Optional == FALSE) {
      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 == FALSE)) {
      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 = (CHAR8  *) malloc ((Len + 1) * sizeof (CHAR8 ));
  if (String == NULL) {
    Error (NULL, 0, 4001, "Resource: memory cannot be allocated", 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 (CHAR8 )) {
    SourceFile->EndOfFile = TRUE;
    return TRUE;
  }

  if (SourceFile->EndOfFile) {
    return TRUE;
  }

  return FALSE;
}

#if 0
STATIC
VOID
ProcessTokenInclude (
  SOURCE_FILE *SourceFile
  )
{
  CHAR8          IncludeFileName[MAX_PATH];
  CHAR8          *To;
  UINTN  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 = (CHAR8 ) SourceFile->FileBufferPtr[0];
      To++;
    }

    SourceFile->FileBufferPtr++;
  }

  if (!ReportedError) {
    *To = 0;
    memset ((CHAR8 *) &IncludedSourceFile, 0, sizeof (SOURCE_FILE));
    strcpy (IncludedSourceFile.FileName, IncludeFileName);
    ProcessIncludeFile (&IncludedSourceFile, SourceFile);
  }

  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;
  }
}

UINTN
SkipWhiteSpace (
  SOURCE_FILE *SourceFile
  )
{
  UINTN  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++;
      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
UINTN
t_strcmp (
  CHAR8  *Buffer,
  CHAR8  *Str
  )
/*++

Routine Description:
  Compare two strings for equality. The string pointed to by 'Buffer' may or may not be null-terminated,
  so only compare up to the length of Str.

Arguments:
  Buffer  - pointer to first (possibly not null-terminated) string
  Str     - pointer to null-terminated string to compare to Buffer

Returns:
  Number of bytes matched if exact match
  0 if Buffer does not start with Str

--*/
{
  UINTN  Len;

  Len = 0;
  while (*Str && (*Str == *Buffer)) {
    Buffer++;
    Str++;
    Len++;
  }

  if (*Str) {
    return 0;
  }

  return Len;
}

STATIC
UINTN
t_strlen (
  CHAR8  *Str
  )
{
  UINTN  Len;
  Len = 0;
  while (*Str) {
    Len++;
    Str++;
  }

  return Len;
}

STATIC
UINTN
t_strncmp (
  CHAR8  *Str1,
  CHAR8  *Str2,
  INTN    Len
  )
{
  while (Len > 0) {
    if (*Str1 != *Str2) {
      return Len;
    }

    Len--;
    Str1++;
    Str2++;
  }

  return 0;
}

STATIC
CHAR8  *
t_strcpy (
  CHAR8  *Dest,
  CHAR8  *Src
  )
{
  CHAR8   *SaveDest;
  SaveDest = Dest;
  while (*Src) {
    *Dest = *Src;
    Dest++;
    Src++;
  }

  *Dest = 0;
  return SaveDest;
}

STATIC
VOID
RewindFile (
  SOURCE_FILE *SourceFile
  )
{
  SourceFile->LineNum       = 1;
  SourceFile->FileBufferPtr = SourceFile->FileBuffer;
  SourceFile->EndOfFile     = 0;
}

STATIC
UINT32
GetHexChars (
  CHAR8       *Buffer,
  UINT32      BufferLen
  )
{
  UINT32  Len;
  Len = 0;
  while (!EndOfFile (&mGlobals.SourceFile) && (BufferLen > 0)) {
    if (isxdigit ((int)mGlobals.SourceFile.FileBufferPtr[0])) {
      *Buffer = mGlobals.SourceFile.FileBufferPtr[0];
      Buffer++;
      Len++;
      BufferLen--;
      mGlobals.SourceFile.FileBufferPtr++;
    } else {
      break;
    }
  }
  //
  // Null terminate if we can
  //
  if ((Len > 0) && (BufferLen > 0)) {
    *Buffer = 0;
  }

  return Len;
}

BOOLEAN
SFPGetGuid (
  INTN         GuidStyle,
  EFI_GUID    *Value
  )
/*++

Routine Description:
  Parse a GUID from the input stream. Stop when you discover white space.

Arguments:
  GuidStyle - Style of the following GUID token
  Value     - pointer to EFI_GUID struct for output

Returns:
  TRUE  - GUID string parsed successfully
  FALSE - otherwise

  GUID styles
    Style[0] 12345678-1234-5678-AAAA-BBBBCCCCDDDD

--*/
{
  unsigned      Value32;
  UINT32        Index;
  FILE_POSITION FPos;
  CHAR8         TempString[20];
  CHAR8         TempString2[3];
  CHAR8         *From;
  CHAR8         *To;
  UINT32        Len;
  BOOLEAN       Status;

  Status = FALSE;
  //
  // Skip white space, then start parsing
  //
  SkipWhiteSpace (&mGlobals.SourceFile);
  GetFilePosition (&FPos);
  if (EndOfFile (&mGlobals.SourceFile)) {
    return FALSE;
  }

  if (GuidStyle == PARSE_GUID_STYLE_5_FIELDS) {
    //
    // Style[0] 12345678-1234-5678-AAAA-BBBBCCCCDDDD
    //
    Len = GetHexChars (TempString, sizeof (TempString));
    if ((Len == 0) || (Len > 8)) {
      goto Done;
    }

    sscanf (TempString, "%x", &Value32);
    Value->Data1 = Value32;
    //
    // Next two UINT16 fields
    //
    if (mGlobals.SourceFile.FileBufferPtr[0] != '-') {
      goto Done;
    }

    mGlobals.SourceFile.FileBufferPtr++;
    Len = GetHexChars (TempString, sizeof (TempString));
    if ((Len == 0) || (Len > 4)) {
      goto Done;
    }

    sscanf (TempString, "%x", &Value32);
    Value->Data2 = (UINT16) Value32;

    if (mGlobals.SourceFile.FileBufferPtr[0] != '-') {
      goto Done;
    }

    mGlobals.SourceFile.FileBufferPtr++;
    Len = GetHexChars (TempString, sizeof (TempString));
    if ((Len == 0) || (Len > 4)) {
      goto Done;
    }

    sscanf (TempString, "%x", &Value32);
    Value->Data3 = (UINT16) Value32;
    //
    // Parse the "AAAA" as two bytes
    //
    if (mGlobals.SourceFile.FileBufferPtr[0] != '-') {
      goto Done;
    }

    mGlobals.SourceFile.FileBufferPtr++;
    Len = GetHexChars (TempString, sizeof (TempString));
    if ((Len == 0) || (Len > 4)) {
      goto Done;
    }

    sscanf (TempString, "%x", &Value32);
    Value->Data4[0] = (UINT8) (Value32 >> 8);
    Value->Data4[1] = (UINT8) Value32;
    if (mGlobals.SourceFile.FileBufferPtr[0] != '-') {
      goto Done;
    }

    mGlobals.SourceFile.FileBufferPtr++;
    //
    // Read the last 6 bytes of the GUID
    //
    //
    Len = GetHexChars (TempString, sizeof (TempString));
    if ((Len == 0) || (Len > 12)) {
      goto Done;
    }
    //
    // Insert leading 0's to make life easier
    //
    if (Len != 12) {
      From            = TempString + Len - 1;
      To              = TempString + 11;
      TempString[12]  = 0;
      while (From >= TempString) {
        *To = *From;
        To--;
        From--;
      }

      while (To >= TempString) {
        *To = '0';
        To--;
      }
    }
    //
    // Now parse each byte
    //
    TempString2[2] = 0;
    for (Index = 0; Index < 6; Index++) {
      //
      // Copy the two characters from the input string to something
      // we can parse.
      //
      TempString2[0]  = TempString[Index * 2];
      TempString2[1]  = TempString[Index * 2 + 1];
      sscanf (TempString2, "%x", &Value32);
      Value->Data4[Index + 2] = (UINT8) Value32;
    }

    Status = TRUE;
  } else {
    //
    // Unsupported GUID style
    //
    return FALSE;
  }

Done:
  if (Status == FALSE) {
    SetFilePosition (&FPos);
  }

  return Status;
}

STATIC
STATUS
GetFilePosition (
  FILE_POSITION *Fpos
  )
{
  Fpos->FileBufferPtr = mGlobals.SourceFile.FileBufferPtr;
  return STATUS_SUCCESS;
}

STATIC
STATUS
SetFilePosition (
  FILE_POSITION *Fpos
  )
{
  //
  // Should check range of pointer
  //
  mGlobals.SourceFile.FileBufferPtr = Fpos->FileBufferPtr;
  return STATUS_SUCCESS;
}
