/** @file

  VfrCompiler error handler.

Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "VfrError.h"
#include "EfiUtilityMsgs.h"

static SVFR_ERROR_HANDLE VFR_ERROR_HANDLE_TABLE [] = {
  { VFR_RETURN_SUCCESS, NULL },
  { VFR_RETURN_ERROR_SKIPED, NULL },
  { VFR_RETURN_FATAL_ERROR, ": fatal error!!" },

  { VFR_RETURN_MISMATCHED, ": unexpected token" },
  { VFR_RETURN_INVALID_PARAMETER, ": invalid parameter" },
  { VFR_RETURN_OUT_FOR_RESOURCES, ": system out of memory" },
  { VFR_RETURN_UNSUPPORTED, ": unsupported" },
  { VFR_RETURN_REDEFINED, ": already defined" },
  { VFR_RETURN_FORMID_REDEFINED, ": form id already defined" },
  { VFR_RETURN_QUESTIONID_REDEFINED, ": question id already defined" },
  { VFR_RETURN_VARSTOREID_REDEFINED, ": varstore id already defined" },
  { VFR_RETURN_UNDEFINED, ": undefined" },
  { VFR_RETURN_VAR_NOTDEFINED_BY_QUESTION, ": some variable has not defined by a question"},
  { VFR_RETURN_VARSTORE_DATATYPE_REDEFINED_ERROR, ": Data Structure is defined by more than one varstores, it can't be referred as varstore, only varstore name could be used."},
  { VFR_RETURN_GET_EFIVARSTORE_ERROR, ": get efi varstore error"},
  { VFR_RETURN_EFIVARSTORE_USE_ERROR, ": can not use the efi varstore like this" },
  { VFR_RETURN_EFIVARSTORE_SIZE_ERROR, ": unsupport efi varstore size should be <= 8 bytes" },
  { VFR_RETURN_GET_NVVARSTORE_ERROR, ": get name value varstore error" },
  { VFR_RETURN_QVAR_REUSE, ": variable reused by more than one question" },
  { VFR_RETURN_FLAGS_UNSUPPORTED, ": flags unsupported" },
  { VFR_RETURN_ERROR_ARRARY_NUM, ": array number error, the valid value is in (0 ~ MAX_INDEX-1) for UEFI vfr and in (1 ~ MAX_INDEX) for Framework Vfr" },
  { VFR_RETURN_DATA_STRING_ERROR, ": data field string error or not support"},
  { VFR_RETURN_DEFAULT_VALUE_REDEFINED, ": default value re-defined with different value"},
  { VFR_RETURN_CONSTANT_ONLY, ": only constant is allowed in the expression"},
  { VFR_RETURN_VARSTORE_NAME_REDEFINED_ERROR, ": Varstore name is defined by more than one varstores, it can't be referred as varstore, only varstore structure name could be used."},
  { VFR_RETURN_BIT_WIDTH_ERROR, ": bit width must be <= sizeof (type) * 8 and the max width can not > 32" },
  { VFR_RETURN_STRING_TO_UINT_OVERFLOW, ": String to UINT* Overflow"},
  { VFR_RETURN_CODEUNDEFINED, ": undefined Error Code" }
};

static SVFR_WARNING_HANDLE VFR_WARNING_HANDLE_TABLE [] = {
  { VFR_WARNING_DEFAULT_VALUE_REDEFINED, ": default value re-defined with different value"},
  { VFR_WARNING_ACTION_WITH_TEXT_TWO, ": Action opcode should not have TextTwo part"},
  { VFR_WARNING_OBSOLETED_FRAMEWORK_OPCODE, ": Not recommend to use obsoleted framework opcode"},
  { VFR_WARNING_CODEUNDEFINED, ": undefined Warning Code" }
};

CVfrErrorHandle::CVfrErrorHandle (
  VOID
  )
{
  mInputFileName         = NULL;
  mScopeRecordListHead   = NULL;
  mScopeRecordListTail   = NULL;
  mVfrErrorHandleTable   = VFR_ERROR_HANDLE_TABLE;
  mVfrWarningHandleTable = VFR_WARNING_HANDLE_TABLE;
  mWarningAsError        = FALSE;
}

CVfrErrorHandle::~CVfrErrorHandle (
  VOID
  )
{
  SVfrFileScopeRecord *pNode = NULL;

  if (mInputFileName != NULL) {
    delete[] mInputFileName;
  }

  while (mScopeRecordListHead != NULL) {
    pNode = mScopeRecordListHead;
    mScopeRecordListHead = mScopeRecordListHead->mNext;
    delete pNode;
  }

  mScopeRecordListHead   = NULL;
  mScopeRecordListTail   = NULL;
  mVfrErrorHandleTable   = NULL;
  mVfrWarningHandleTable = NULL;
}

VOID
CVfrErrorHandle::SetWarningAsError (
  IN BOOLEAN  WarningAsError
  )
{
  mWarningAsError = WarningAsError;
}

VOID
CVfrErrorHandle::SetInputFile (
  IN CHAR8    *InputFile
  )
{
  if (InputFile != NULL) {
    mInputFileName = new CHAR8[strlen(InputFile) + 1];
    strcpy (mInputFileName, InputFile);
  }
}

SVfrFileScopeRecord::SVfrFileScopeRecord (
  IN CHAR8    *Record,
  IN UINT32   LineNum
  )
{
  UINT32      Index;
  CHAR8       *FileName = NULL;
  CHAR8       *Str      = NULL;

  mWholeScopeLine      = LineNum;
  mNext                = NULL;

  Str = strchr (Record, ' ');
  mScopeLineStart = atoi (++Str);

  Str = strchr (Str, '\"');
  FileName = ++Str;

  while((Str = strstr (FileName, "\\\\")) != NULL) {
    FileName = Str + 2;
  }
  if ((mFileName = new CHAR8[strlen(FileName)]) != NULL) {
    for (Index = 0; FileName[Index] != '\"'; Index++) {
      mFileName[Index] = FileName[Index];
    }
    mFileName[Index] = '\0';
  }

  return;
}

SVfrFileScopeRecord::~SVfrFileScopeRecord (
  VOID
  )
{
  if (mFileName != NULL) {
    delete[] mFileName;
  }
}

VOID
CVfrErrorHandle::ParseFileScopeRecord (
  IN CHAR8     *Record,
  IN UINT32    WholeScopeLine
  )
{
  SVfrFileScopeRecord *pNode        = NULL;

  if (Record == NULL) {
    return;
  }

  if ((pNode = new SVfrFileScopeRecord(Record, WholeScopeLine)) == NULL) {
    return;
  }

  if (mScopeRecordListHead == NULL) {
    mScopeRecordListTail = mScopeRecordListHead = pNode;
  } else {
    mScopeRecordListTail->mNext = pNode;
    mScopeRecordListTail        = pNode;
  }
}

VOID
CVfrErrorHandle::GetFileNameLineNum (
  IN  UINT32 LineNum,
  OUT CHAR8  **FileName,
  OUT UINT32 *FileLine
  )
{
  SVfrFileScopeRecord *pNode    = NULL;

  if ((FileName == NULL) || (FileLine == NULL)) {
    return;
  }

  *FileName = NULL;
  *FileLine = 0xFFFFFFFF;

  //
  // Some errors occur before scope record list been built.
  //
  if (mScopeRecordListHead == NULL) {
    *FileLine = LineNum;
    *FileName = mInputFileName;
    return ;
  }

  for (pNode = mScopeRecordListHead; pNode->mNext != NULL; pNode = pNode->mNext) {
    if ((LineNum > pNode->mWholeScopeLine) && (pNode->mNext->mWholeScopeLine > LineNum)) {
      *FileName = pNode->mFileName;
      *FileLine = LineNum - pNode->mWholeScopeLine + pNode->mScopeLineStart - 1;
      return ;
    }
  }

  *FileName = pNode->mFileName;
  *FileLine = LineNum - pNode->mWholeScopeLine + pNode->mScopeLineStart - 1;
}

VOID
CVfrErrorHandle::PrintMsg (
  IN UINT32               LineNum,
  IN CHAR8                *TokName,
  IN CONST CHAR8          *MsgType,
  IN CONST CHAR8          *ErrorMsg
  )
{
  CHAR8                  *FileName = NULL;
  UINT32                 FileLine;

  if (strncmp ("Warning", MsgType, strlen ("Warning")) == 0) {
    VerboseMsg ((CHAR8 *) ErrorMsg);
    return;
  }
  GetFileNameLineNum (LineNum, &FileName, &FileLine);
  Error (FileName, FileLine, 0x3000, TokName, (CHAR8 *) "\t%s\n", (CHAR8 *) ErrorMsg);
}

UINT8
CVfrErrorHandle::HandleError (
  IN EFI_VFR_RETURN_CODE  ErrorCode,
  IN UINT32               LineNum,
  IN CHAR8                *TokName
  )
{
  UINT32                 Index;
  CHAR8                  *FileName = NULL;
  UINT32                 FileLine;
  CONST CHAR8            *ErrorMsg = NULL;

  if (mVfrErrorHandleTable == NULL) {
    return 1;
  }

  for (Index = 0; mVfrErrorHandleTable[Index].mErrorCode != VFR_RETURN_CODEUNDEFINED; Index++) {
    if (ErrorCode == mVfrErrorHandleTable[Index].mErrorCode) {
      ErrorMsg = mVfrErrorHandleTable[Index].mErrorMsg;
      break;
    }
  }

  if (ErrorMsg != NULL) {
    GetFileNameLineNum (LineNum, &FileName, &FileLine);
    Error (FileName, FileLine, 0x3000, TokName, (CHAR8 *) "\t%s\n", (CHAR8 *) ErrorMsg);
    return 1;
  } else {
    return 0;
  }
}

UINT8
CVfrErrorHandle::HandleWarning (
  IN EFI_VFR_WARNING_CODE WarningCode,
  IN UINT32               LineNum,
  IN CHAR8                *TokName
  )
{
  UINT32                 Index;
  CHAR8                  *FileName = NULL;
  UINT32                 FileLine;
  CONST CHAR8            *WarningMsg = NULL;

  if (mVfrWarningHandleTable == NULL) {
    return 1;
  }

  GetFileNameLineNum (LineNum, &FileName, &FileLine);

  if (mWarningAsError) {
    Error (FileName, FileLine, 0x2220, (CHAR8 *) "warning treated as error", NULL);
  }

  for (Index = 0; mVfrWarningHandleTable[Index].mWarningCode != VFR_WARNING_CODEUNDEFINED; Index++) {
    if (WarningCode == mVfrWarningHandleTable[Index].mWarningCode) {
      WarningMsg = mVfrWarningHandleTable[Index].mWarningMsg;
      break;
    }
  }

  if (WarningMsg != NULL) {
    Warning (FileName, FileLine, 0, TokName, (CHAR8 *) "\t%s\n", (CHAR8 *) WarningMsg);
    return 1;
  } else {
    return 0;
  }
}

CVfrErrorHandle gCVfrErrorHandle;
