mirror of
https://github.com/HyperDbg/HyperDbg.git
synced 2026-07-10 01:29:59 +00:00
1545 lines
32 KiB
C
1545 lines
32 KiB
C
/**
|
|
* @file common.c
|
|
* @author M.H. Gholamrezaei (mh@hyperdbg.org)
|
|
*
|
|
* @details Common routines
|
|
* @version 0.1
|
|
* @date 2020-10-22
|
|
*
|
|
* @copyright This project is released under the GNU Public License v3.
|
|
*
|
|
*/
|
|
#include "pch.h"
|
|
|
|
/**
|
|
* @brief Allocates a new token
|
|
*
|
|
* @return PSCRIPT_ENGINE_TOKEN the allocated new unknown token
|
|
*/
|
|
PSCRIPT_ENGINE_TOKEN
|
|
NewUnknownToken()
|
|
{
|
|
PSCRIPT_ENGINE_TOKEN Token;
|
|
|
|
//
|
|
// Allocate memory for token and its value
|
|
//
|
|
Token = (PSCRIPT_ENGINE_TOKEN)malloc(sizeof(SCRIPT_ENGINE_TOKEN));
|
|
|
|
if (Token == NULL)
|
|
{
|
|
//
|
|
// There was an error allocating buffer
|
|
//
|
|
return NULL;
|
|
}
|
|
|
|
Token->Value = (char *)calloc(TOKEN_VALUE_MAX_LEN + 1, sizeof(char));
|
|
|
|
if (Token->Value == NULL)
|
|
{
|
|
//
|
|
// There was an error allocating buffer
|
|
//
|
|
free(Token);
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Init fields
|
|
//
|
|
strcpy(Token->Value, "");
|
|
Token->Type = UNKNOWN;
|
|
Token->Len = 0;
|
|
Token->MaxLen = TOKEN_VALUE_MAX_LEN;
|
|
Token->VariableType = (VARIABLE_TYPE *)VARIABLE_TYPE_LONG;
|
|
Token->VariableMemoryIdx = 0;
|
|
|
|
return Token;
|
|
}
|
|
|
|
/**
|
|
* @brief Allocates a new token with given type and value
|
|
*
|
|
* @param Type the type of the token
|
|
* @param Value the value string of the token
|
|
* @return PSCRIPT_ENGINE_TOKEN the allocated new token
|
|
*/
|
|
PSCRIPT_ENGINE_TOKEN
|
|
NewToken(SCRIPT_ENGINE_TOKEN_TYPE Type, char * Value)
|
|
{
|
|
//
|
|
// Allocate memory for token]
|
|
//
|
|
PSCRIPT_ENGINE_TOKEN Token = (PSCRIPT_ENGINE_TOKEN)malloc(sizeof(SCRIPT_ENGINE_TOKEN));
|
|
|
|
if (Token == NULL)
|
|
{
|
|
//
|
|
// There was an error allocating buffer
|
|
//
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Init fields
|
|
//
|
|
unsigned int Len = (unsigned int)strlen(Value);
|
|
Token->Type = Type;
|
|
Token->Len = Len;
|
|
Token->MaxLen = Len;
|
|
Token->Value = (char *)calloc(Token->MaxLen + 1, sizeof(char));
|
|
Token->VariableType = (VARIABLE_TYPE *)VARIABLE_TYPE_LONG;
|
|
Token->VariableMemoryIdx = 0;
|
|
|
|
if (Token->Value == NULL)
|
|
{
|
|
//
|
|
// There was an error allocating buffer
|
|
//
|
|
free(Token);
|
|
return NULL;
|
|
}
|
|
|
|
strcpy(Token->Value, Value);
|
|
|
|
return Token;
|
|
}
|
|
|
|
/**
|
|
* @brief Removes allocated memory of a token
|
|
*
|
|
* @param Token
|
|
* @return void
|
|
*/
|
|
void
|
|
RemoveToken(PSCRIPT_ENGINE_TOKEN * Token)
|
|
{
|
|
free((*Token)->Value);
|
|
free(*Token);
|
|
*Token = NULL;
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief Prints token
|
|
* @details prints value and type of token
|
|
*
|
|
* @param Token the token to print
|
|
* @return void
|
|
*/
|
|
void
|
|
PrintToken(PSCRIPT_ENGINE_TOKEN Token)
|
|
{
|
|
//
|
|
// Prints value of the Token
|
|
//
|
|
if (Token->Type == WHITE_SPACE)
|
|
{
|
|
printf("< :");
|
|
}
|
|
else
|
|
{
|
|
printf("<'%s' : ", Token->Value);
|
|
}
|
|
|
|
//
|
|
// Prints type of the Token
|
|
//
|
|
switch (Token->Type)
|
|
{
|
|
case GLOBAL_ID:
|
|
printf(" GLOBAL_ID>\n");
|
|
break;
|
|
case GLOBAL_UNRESOLVED_ID:
|
|
printf(" GLOBAL_UNRESOLVED_ID>\n");
|
|
break;
|
|
case LOCAL_ID:
|
|
printf(" LOCAL_ID>\n");
|
|
break;
|
|
case LOCAL_UNRESOLVED_ID:
|
|
printf(" LOCAL_UNRESOLVED_ID>\n");
|
|
break;
|
|
case STATE_ID:
|
|
printf(" STATE_ID>\n");
|
|
break;
|
|
case DECIMAL:
|
|
printf(" DECIMAL>\n");
|
|
break;
|
|
case HEX:
|
|
printf(" HEX>\n");
|
|
break;
|
|
case OCTAL:
|
|
printf(" OCTAL>\n");
|
|
break;
|
|
case BINARY:
|
|
printf(" BINARY>\n");
|
|
break;
|
|
case SPECIAL_TOKEN:
|
|
printf(" SPECIAL_TOKEN>\n");
|
|
break;
|
|
case KEYWORD:
|
|
printf(" KEYWORD>\n");
|
|
break;
|
|
case WHITE_SPACE:
|
|
printf(" WHITE_SPACE>\n");
|
|
break;
|
|
case COMMENT:
|
|
printf(" COMMENT>\n");
|
|
break;
|
|
case REGISTER:
|
|
printf(" REGISTER>\n");
|
|
break;
|
|
case PSEUDO_REGISTER:
|
|
printf(" PSEUDO_REGISTER>\n");
|
|
break;
|
|
case SEMANTIC_RULE:
|
|
printf(" SEMANTIC_RULE>\n");
|
|
break;
|
|
case NON_TERMINAL:
|
|
printf(" NON_TERMINAL>\n");
|
|
break;
|
|
case END_OF_STACK:
|
|
printf(" END_OF_STACK>\n");
|
|
break;
|
|
case STRING:
|
|
printf(" STRING>\n");
|
|
break;
|
|
case WSTRING:
|
|
printf(" WSTRING>\n");
|
|
break;
|
|
case TEMP:
|
|
printf(" TEMP>\n");
|
|
break;
|
|
case UNKNOWN:
|
|
printf(" UNKNOWN>\n");
|
|
break;
|
|
case SCRIPT_VARIABLE_TYPE:
|
|
printf(" SCRIPT_VARIABLE_TYPE>\n");
|
|
break;
|
|
case FUNCTION_ID:
|
|
printf(" FUNCTION_ID>\n");
|
|
break;
|
|
case FUNCTION_PARAMETER_ID:
|
|
printf(" FUNCTION_PARAMETER_ID>\n");
|
|
break;
|
|
case DEFERENCE_TEMP:
|
|
printf(" DEFERENCE_TEMP>\n");
|
|
break;
|
|
default:
|
|
printf(" ERROR>\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Appends char to the token value
|
|
*
|
|
* @param Token
|
|
* @param c the character to append
|
|
* @return void
|
|
*/
|
|
void
|
|
AppendByte(PSCRIPT_ENGINE_TOKEN Token, char c)
|
|
{
|
|
//
|
|
// Check overflow of the string
|
|
//
|
|
if (Token->Len >= Token->MaxLen - 1)
|
|
{
|
|
//
|
|
// Double the length of the allocated space for the string
|
|
//
|
|
Token->MaxLen *= 2;
|
|
char * NewValue = (char *)calloc(Token->MaxLen + 1, sizeof(char));
|
|
|
|
if (NewValue == NULL)
|
|
{
|
|
printf("err, could not allocate buffer");
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Free Old buffer and update the pointer
|
|
//
|
|
memcpy(NewValue, Token->Value, Token->Len);
|
|
free(Token->Value);
|
|
Token->Value = NewValue;
|
|
}
|
|
|
|
//
|
|
// Append the new character to the string
|
|
//
|
|
Token->Value[Token->Len] = c;
|
|
Token->Len++;
|
|
}
|
|
|
|
/**
|
|
* @brief Appends wchar_t to the token value
|
|
*
|
|
* @param Token
|
|
* @param c the wide character to append
|
|
* @return void
|
|
*/
|
|
void
|
|
AppendWchar(PSCRIPT_ENGINE_TOKEN Token, wchar_t c)
|
|
{
|
|
//
|
|
// Check overflow of the string
|
|
//
|
|
if (Token->Len >= Token->MaxLen - 2)
|
|
{
|
|
//
|
|
// Double the length of the allocated space for the wstring
|
|
//
|
|
Token->MaxLen *= 2;
|
|
char * NewValue = (char *)calloc(Token->MaxLen + 2, sizeof(char));
|
|
|
|
if (NewValue == NULL)
|
|
{
|
|
printf("err, could not allocate buffer");
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Free Old buffer and update the pointer
|
|
//
|
|
memcpy(NewValue, Token->Value, Token->Len);
|
|
free(Token->Value);
|
|
Token->Value = NewValue;
|
|
}
|
|
|
|
//
|
|
// Append the new character to the wstring
|
|
//
|
|
*((wchar_t *)(Token->Value) + Token->Len / 2) = c;
|
|
Token->Len += 2;
|
|
}
|
|
|
|
/**
|
|
* @brief Copies a PTOKEN
|
|
*
|
|
* @param Token the token to copy
|
|
* @return PSCRIPT_ENGINE_TOKEN the copied token
|
|
*/
|
|
PSCRIPT_ENGINE_TOKEN
|
|
CopyToken(PSCRIPT_ENGINE_TOKEN Token)
|
|
{
|
|
PSCRIPT_ENGINE_TOKEN TokenCopy = (PSCRIPT_ENGINE_TOKEN)malloc(sizeof(SCRIPT_ENGINE_TOKEN));
|
|
|
|
if (TokenCopy == NULL)
|
|
{
|
|
//
|
|
// There was an error allocating buffer
|
|
//
|
|
return NULL;
|
|
}
|
|
|
|
TokenCopy->Type = Token->Type;
|
|
TokenCopy->MaxLen = Token->MaxLen;
|
|
TokenCopy->Len = Token->Len;
|
|
TokenCopy->Value = (char *)calloc(strlen(Token->Value) + 1, sizeof(char));
|
|
TokenCopy->VariableType = Token->VariableType;
|
|
|
|
if (TokenCopy->Value == NULL)
|
|
{
|
|
//
|
|
// There was an error allocating buffer
|
|
//
|
|
free(TokenCopy);
|
|
return NULL;
|
|
}
|
|
|
|
strcpy(TokenCopy->Value, Token->Value);
|
|
|
|
return TokenCopy;
|
|
}
|
|
|
|
/**
|
|
* @brief Allocates a new SCRIPT_ENGINE_TOKEN_LIST
|
|
*
|
|
* @return PSCRIPT_ENGINE_TOKEN_LIST the allocated token list
|
|
*/
|
|
PSCRIPT_ENGINE_TOKEN_LIST
|
|
NewTokenList(void)
|
|
{
|
|
PSCRIPT_ENGINE_TOKEN_LIST TokenList = NULL;
|
|
|
|
//
|
|
// Allocation of memory for SCRIPT_ENGINE_TOKEN_LIST structure
|
|
//
|
|
TokenList = (PSCRIPT_ENGINE_TOKEN_LIST)malloc(sizeof(*TokenList));
|
|
|
|
if (TokenList == NULL)
|
|
{
|
|
//
|
|
// There was an error allocating buffer
|
|
//
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Initialize fields of SCRIPT_ENGINE_TOKEN_LIST
|
|
//
|
|
TokenList->Pointer = 0;
|
|
TokenList->Size = TOKEN_LIST_INIT_SIZE;
|
|
|
|
//
|
|
// Allocation of memory for SCRIPT_ENGINE_TOKEN_LIST buffer
|
|
//
|
|
TokenList->Head = (PSCRIPT_ENGINE_TOKEN *)malloc(TokenList->Size * sizeof(PSCRIPT_ENGINE_TOKEN));
|
|
|
|
return TokenList;
|
|
}
|
|
|
|
/**
|
|
* @brief Removes allocated memory of a SCRIPT_ENGINE_TOKEN_LIST
|
|
*
|
|
* @param TokenList
|
|
* @return void
|
|
*/
|
|
void
|
|
RemoveTokenList(PSCRIPT_ENGINE_TOKEN_LIST TokenList)
|
|
{
|
|
PSCRIPT_ENGINE_TOKEN Token;
|
|
for (uintptr_t i = 0; i < TokenList->Pointer; i++)
|
|
{
|
|
Token = *(TokenList->Head + i);
|
|
RemoveToken(&Token);
|
|
}
|
|
free(TokenList->Head);
|
|
free(TokenList);
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief Prints each Token inside a TokenList
|
|
*
|
|
* @param TokenList
|
|
* @return void
|
|
*/
|
|
void
|
|
PrintTokenList(PSCRIPT_ENGINE_TOKEN_LIST TokenList)
|
|
{
|
|
PSCRIPT_ENGINE_TOKEN Token;
|
|
for (uintptr_t i = 0; i < TokenList->Pointer; i++)
|
|
{
|
|
Token = *(TokenList->Head + i);
|
|
PrintToken(Token);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Adds Token to the last empty position of TokenList
|
|
*
|
|
* @param TokenList the token list to push into
|
|
* @param Token the token to add
|
|
* @return PSCRIPT_ENGINE_TOKEN_LIST
|
|
*/
|
|
PSCRIPT_ENGINE_TOKEN_LIST
|
|
Push(PSCRIPT_ENGINE_TOKEN_LIST TokenList, PSCRIPT_ENGINE_TOKEN Token)
|
|
{
|
|
//
|
|
// Calculate address to write new token
|
|
//
|
|
uintptr_t Head = (uintptr_t)TokenList->Head;
|
|
uintptr_t Pointer = (uintptr_t)TokenList->Pointer;
|
|
PSCRIPT_ENGINE_TOKEN * WriteAddr = (PSCRIPT_ENGINE_TOKEN *)(Head + Pointer * sizeof(PSCRIPT_ENGINE_TOKEN));
|
|
|
|
//
|
|
// Write Token to appropriate address in TokenList
|
|
//
|
|
*WriteAddr = Token;
|
|
|
|
//
|
|
// Update Pointer
|
|
//
|
|
TokenList->Pointer++;
|
|
|
|
//
|
|
// Handle overflow
|
|
//
|
|
if (Pointer == TokenList->Size - 1)
|
|
{
|
|
//
|
|
// Allocate a new buffer for string list with doubled length
|
|
//
|
|
PSCRIPT_ENGINE_TOKEN * NewHead = (PSCRIPT_ENGINE_TOKEN *)malloc(2 * TokenList->Size * sizeof(PSCRIPT_ENGINE_TOKEN));
|
|
|
|
if (NewHead == NULL)
|
|
{
|
|
printf("err, could not allocate buffer");
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Copy old buffer to new buffer
|
|
//
|
|
memcpy(NewHead, TokenList->Head, TokenList->Size * sizeof(PSCRIPT_ENGINE_TOKEN));
|
|
|
|
//
|
|
// Free old buffer
|
|
//
|
|
free(TokenList->Head);
|
|
|
|
//
|
|
// Update Head and size of TokenList
|
|
//
|
|
TokenList->Size = TokenList->Size * 2;
|
|
TokenList->Head = NewHead;
|
|
}
|
|
|
|
return TokenList;
|
|
}
|
|
/**
|
|
* @brief Removes last Token of a TokenList and returns it
|
|
*
|
|
* @param TokenList
|
|
* @return PSCRIPT_ENGINE_TOKEN
|
|
*/
|
|
PSCRIPT_ENGINE_TOKEN
|
|
Pop(PSCRIPT_ENGINE_TOKEN_LIST TokenList)
|
|
{
|
|
//
|
|
// Calculate address to read most recent token
|
|
//
|
|
if (TokenList->Pointer > 0)
|
|
TokenList->Pointer--; // not consider what if the token's type is string or wstring
|
|
uintptr_t Head = (uintptr_t)TokenList->Head;
|
|
uintptr_t Pointer = (uintptr_t)TokenList->Pointer;
|
|
PSCRIPT_ENGINE_TOKEN * ReadAddr = (PSCRIPT_ENGINE_TOKEN *)(Head + Pointer * sizeof(PSCRIPT_ENGINE_TOKEN));
|
|
|
|
return *ReadAddr;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns last Token of a TokenList
|
|
*
|
|
* @param TokenList
|
|
* @return PSCRIPT_ENGINE_TOKEN
|
|
*/
|
|
PSCRIPT_ENGINE_TOKEN
|
|
Top(PSCRIPT_ENGINE_TOKEN_LIST TokenList)
|
|
{
|
|
//
|
|
// Calculate address to read most recent pushed token
|
|
//
|
|
uintptr_t Head = (uintptr_t)TokenList->Head;
|
|
uintptr_t Pointer = (uintptr_t)TokenList->Pointer - 1;
|
|
PSCRIPT_ENGINE_TOKEN * ReadAddr = (PSCRIPT_ENGINE_TOKEN *)(Head + Pointer * sizeof(PSCRIPT_ENGINE_TOKEN));
|
|
|
|
return *ReadAddr;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns the token at a specific index from the top of the token list
|
|
*
|
|
* @param TokenList the token list to index into
|
|
* @param Index the zero-based index from the top
|
|
* @return PSCRIPT_ENGINE_TOKEN
|
|
*/
|
|
PSCRIPT_ENGINE_TOKEN
|
|
TopIndexed(PSCRIPT_ENGINE_TOKEN_LIST TokenList, int Index)
|
|
{
|
|
uintptr_t Head = (uintptr_t)TokenList->Head;
|
|
uintptr_t Pointer = (uintptr_t)TokenList->Pointer - 1 - Index;
|
|
PSCRIPT_ENGINE_TOKEN * ReadAddr = (PSCRIPT_ENGINE_TOKEN *)(Head + Pointer * sizeof(PSCRIPT_ENGINE_TOKEN));
|
|
|
|
return *ReadAddr;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether input char belongs to hexadecimal digit-set or not
|
|
*
|
|
* @param c the character to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsHex(char c)
|
|
{
|
|
if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether input char belongs to decimal digit-set or not
|
|
*
|
|
* @param c the character to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsDecimal(char c)
|
|
{
|
|
if (c >= '0' && c <= '9')
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether input char belongs to alphabet set or not
|
|
*
|
|
* @param c the character to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsLetter(char c)
|
|
{
|
|
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
|
|
return 1;
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether input char is underscore (_) or not
|
|
*
|
|
* @param c the character to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsUnderscore(char c)
|
|
{
|
|
if (c >= '_')
|
|
return 1;
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether input char belongs to binary digit-set or not
|
|
*
|
|
* @param c the character to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsBinary(char c)
|
|
{
|
|
if (c == '0' || c == '1')
|
|
return 1;
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether input char belongs to octal digit-set or not
|
|
*
|
|
* @param c the character to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsOctal(char c)
|
|
{
|
|
if (c >= '0' && c <= '7')
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Allocates a new temporary variable and returns it
|
|
*
|
|
* @param Error the error type pointer
|
|
* @return PSCRIPT_ENGINE_TOKEN
|
|
*/
|
|
PSCRIPT_ENGINE_TOKEN
|
|
NewTemp(PSCRIPT_ENGINE_ERROR_TYPE Error)
|
|
{
|
|
static unsigned int TempID = 0;
|
|
int i;
|
|
for (i = 0; i < MAX_TEMP_COUNT; i++)
|
|
{
|
|
if (CurrentUserDefinedFunction->TempMap[i] == 0)
|
|
{
|
|
TempID = i;
|
|
CurrentUserDefinedFunction->TempMap[i] = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (i == MAX_TEMP_COUNT)
|
|
{
|
|
*Error = SCRIPT_ENGINE_ERROR_TEMP_LIST_FULL;
|
|
}
|
|
PSCRIPT_ENGINE_TOKEN Temp = NewUnknownToken();
|
|
char TempValue[8];
|
|
sprintf(TempValue, "%d", TempID);
|
|
strcpy(Temp->Value, TempValue);
|
|
Temp->Type = TEMP;
|
|
|
|
if (CurrentUserDefinedFunction->MaxTempNumber < (i + 1))
|
|
{
|
|
CurrentUserDefinedFunction->MaxTempNumber = i + 1;
|
|
}
|
|
|
|
return Temp;
|
|
}
|
|
|
|
/**
|
|
* @brief Frees the memory allocated by Temp
|
|
*
|
|
* @param Temp the token representing the temporary variable
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
FreeTemp(PSCRIPT_ENGINE_TOKEN Temp)
|
|
{
|
|
INT Id = (INT)DecimalToInt(Temp->Value);
|
|
if (Temp->Type == TEMP || Temp->Type == DEFERENCE_TEMP)
|
|
{
|
|
CurrentUserDefinedFunction->TempMap[Id] = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether this Token type is OneOpFunc1
|
|
*
|
|
* @param Operator the token to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsType1Func(PSCRIPT_ENGINE_TOKEN Operator)
|
|
{
|
|
unsigned int n = ONEOPFUNC1_LENGTH;
|
|
for (unsigned int i = 0; i < n; i++)
|
|
{
|
|
if (!strcmp(Operator->Value, OneOpFunc1[i]))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether this Token type is OneOpFunc2
|
|
*
|
|
* @param Operator the token to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsType2Func(PSCRIPT_ENGINE_TOKEN Operator)
|
|
{
|
|
unsigned int n = ONEOPFUNC2_LENGTH;
|
|
for (unsigned int i = 0; i < n; i++)
|
|
{
|
|
if (!strcmp(Operator->Value, OneOpFunc2[i]))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether this Token type is OperatorsTwoOperandList
|
|
*
|
|
* @param Operator the token to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsTwoOperandOperator(PSCRIPT_ENGINE_TOKEN Operator)
|
|
{
|
|
unsigned int n = OPERATORS_TWO_OPERAND_LIST_LENGTH;
|
|
for (unsigned int i = 0; i < n; i++)
|
|
{
|
|
if (!strcmp(Operator->Value, OperatorsTwoOperandList[i]))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether this Token type is OperatorsOneOperandList
|
|
*
|
|
* @param Operator the token to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsOneOperandOperator(PSCRIPT_ENGINE_TOKEN Operator)
|
|
{
|
|
unsigned int n = OPERATORS_ONE_OPERAND_LIST_LENGTH;
|
|
for (unsigned int i = 0; i < n; i++)
|
|
{
|
|
if (!strcmp(Operator->Value, OperatorsOneOperandList[i]))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether this Token type is VarArgFunc1
|
|
*
|
|
* @param Operator the token to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsType4Func(PSCRIPT_ENGINE_TOKEN Operator)
|
|
{
|
|
unsigned int n = VARARGFUNC1_LENGTH;
|
|
for (unsigned int i = 0; i < n; i++)
|
|
{
|
|
if (!strcmp(Operator->Value, VarArgFunc1[i]))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether this Token type is ZeroOpFunc1
|
|
*
|
|
* @param Operator the token to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsType5Func(PSCRIPT_ENGINE_TOKEN Operator)
|
|
{
|
|
unsigned int n = ZEROOPFUNC1_LENGTH;
|
|
for (unsigned int i = 0; i < n; i++)
|
|
{
|
|
if (!strcmp(Operator->Value, ZeroOpFunc1[i]))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether this Token type is TwoOpFunc1
|
|
*
|
|
* @param Operator the token to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsType6Func(PSCRIPT_ENGINE_TOKEN Operator)
|
|
{
|
|
unsigned int n = TWOOPFUNC1_LENGTH;
|
|
for (unsigned int i = 0; i < n; i++)
|
|
{
|
|
if (!strcmp(Operator->Value, TwoOpFunc1[i]))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether this Token type is TwoOpFunc2
|
|
*
|
|
* @param Operator the token to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsType7Func(PSCRIPT_ENGINE_TOKEN Operator)
|
|
{
|
|
unsigned int n = TWOOPFUNC2_LENGTH;
|
|
for (unsigned int i = 0; i < n; i++)
|
|
{
|
|
if (!strcmp(Operator->Value, TwoOpFunc2[i]))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether this Token type is ThreeOpFunc1
|
|
*
|
|
* @param Operator the token to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsType8Func(PSCRIPT_ENGINE_TOKEN Operator)
|
|
{
|
|
unsigned int n = THREEOPFUNC1_LENGTH;
|
|
for (unsigned int i = 0; i < n; i++)
|
|
{
|
|
if (!strcmp(Operator->Value, ThreeOpFunc1[i]))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether this Token type is OneOpFunc3
|
|
*
|
|
* @param Operator the token to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsType9Func(PSCRIPT_ENGINE_TOKEN Operator)
|
|
{
|
|
unsigned int n = ONEOPFUNC3_LENGTH;
|
|
for (unsigned int i = 0; i < n; i++)
|
|
{
|
|
if (!strcmp(Operator->Value, OneOpFunc3[i]))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether this Token type is TwoOpFunc3
|
|
*
|
|
* @param Operator the token to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsType10Func(PSCRIPT_ENGINE_TOKEN Operator)
|
|
{
|
|
unsigned int n = TWOOPFUNC3_LENGTH;
|
|
for (unsigned int i = 0; i < n; i++)
|
|
{
|
|
if (!strcmp(Operator->Value, TwoOpFunc3[i]))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether this Token type is ThreeOpFunc3
|
|
*
|
|
* @param Operator the token to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsType11Func(PSCRIPT_ENGINE_TOKEN Operator)
|
|
{
|
|
unsigned int n = THREEOPFUNC3_LENGTH;
|
|
for (unsigned int i = 0; i < n; i++)
|
|
{
|
|
if (!strcmp(Operator->Value, ThreeOpFunc3[i]))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether this Token type is OneOpFunc4
|
|
*
|
|
* @param Operator the token to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsType12Func(PSCRIPT_ENGINE_TOKEN Operator)
|
|
{
|
|
unsigned int n = ONEOPFUNC4_LENGTH;
|
|
for (unsigned int i = 0; i < n; i++)
|
|
{
|
|
if (!strcmp(Operator->Value, OneOpFunc4[i]))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether this Token type is TwoOpFunc4
|
|
*
|
|
* @param Operator the token to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsType13Func(PSCRIPT_ENGINE_TOKEN Operator)
|
|
{
|
|
unsigned int n = TWOOPFUNC4_LENGTH;
|
|
for (unsigned int i = 0; i < n; i++)
|
|
{
|
|
if (!strcmp(Operator->Value, TwoOpFunc4[i]))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether this Token type is ThreeOpFunc2
|
|
*
|
|
* @param Operator the token to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsType14Func(PSCRIPT_ENGINE_TOKEN Operator)
|
|
{
|
|
unsigned int n = THREEOPFUNC2_LENGTH;
|
|
for (unsigned int i = 0; i < n; i++)
|
|
{
|
|
if (!strcmp(Operator->Value, ThreeOpFunc2[i]))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether this Token type is ThreeOpFunc4
|
|
*
|
|
* @param Operator the token to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsType15Func(PSCRIPT_ENGINE_TOKEN Operator)
|
|
{
|
|
unsigned int n = THREEOPFUNC4_LENGTH;
|
|
for (unsigned int i = 0; i < n; i++)
|
|
{
|
|
if (!strcmp(Operator->Value, ThreeOpFunc4[i]))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether this Token type is ZeroOpFunc2
|
|
*
|
|
* @param Operator the token to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsType16Func(PSCRIPT_ENGINE_TOKEN Operator)
|
|
{
|
|
unsigned int n = ZEROOPFUNC2_LENGTH;
|
|
for (unsigned int i = 0; i < n; i++)
|
|
{
|
|
if (!strcmp(Operator->Value, ZeroOpFunc2[i]))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether this Token type is assignment operator
|
|
*
|
|
* @param Operator the token to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsAssignmentOperator(PSCRIPT_ENGINE_TOKEN Operator)
|
|
{
|
|
unsigned int n = ASSIGNMENT_OPERATOR_LIST_LENGTH;
|
|
for (unsigned int i = 0; i < n; i++)
|
|
{
|
|
if (!strcmp(Operator->Value, AssignmentOperatorList[i]))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether this Token is noneterminal
|
|
* NoneTerminal token starts with capital letter
|
|
*
|
|
* @param Token the token to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsNoneTerminal(PSCRIPT_ENGINE_TOKEN Token)
|
|
{
|
|
if (Token->Value[0] >= 'A' && Token->Value[0] <= 'Z')
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether this Token is semantic rule
|
|
* SemanticRule token starts with '@'
|
|
*
|
|
* @param Token the token to check
|
|
* @return char
|
|
*/
|
|
char
|
|
IsSemanticRule(PSCRIPT_ENGINE_TOKEN Token)
|
|
{
|
|
if (Token->Value[0] == '@')
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Gets the Non Terminal Id object
|
|
*
|
|
* @param Token the token to get the non-terminal ID of
|
|
* @return int the non-terminal ID or INVALID
|
|
*/
|
|
int
|
|
GetNonTerminalId(PSCRIPT_ENGINE_TOKEN Token)
|
|
{
|
|
for (int i = 0; i < NONETERMINAL_COUNT; i++)
|
|
{
|
|
if (!strcmp(Token->Value, NoneTerminalMap[i]))
|
|
return i;
|
|
}
|
|
return INVALID;
|
|
}
|
|
|
|
/**
|
|
* @brief Gets the Terminal Id object
|
|
*
|
|
* @param Token the token to get the terminal ID of
|
|
* @return int the terminal ID or INVALID
|
|
*/
|
|
int
|
|
GetTerminalId(PSCRIPT_ENGINE_TOKEN Token)
|
|
{
|
|
for (int i = 0; i < TERMINAL_COUNT; i++)
|
|
{
|
|
if (Token->Type == HEX)
|
|
{
|
|
if (!strcmp("_hex", TerminalMap[i]))
|
|
return i;
|
|
}
|
|
else if (Token->Type == GLOBAL_ID || Token->Type == GLOBAL_UNRESOLVED_ID)
|
|
{
|
|
if (!strcmp("_global_id", TerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else if (Token->Type == LOCAL_ID || Token->Type == LOCAL_UNRESOLVED_ID)
|
|
{
|
|
if (!strcmp("_local_id", TerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else if (Token->Type == FUNCTION_ID)
|
|
{
|
|
if (!strcmp("_function_id", TerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else if (Token->Type == FUNCTION_PARAMETER_ID)
|
|
{
|
|
if (!strcmp("_function_parameter_id", TerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else if (Token->Type == REGISTER)
|
|
{
|
|
if (!strcmp("_register", TerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else if (Token->Type == PSEUDO_REGISTER)
|
|
{
|
|
if (!strcmp("_pseudo_register", TerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else if (Token->Type == SCRIPT_VARIABLE_TYPE)
|
|
{
|
|
if (!strcmp("_script_variable_type", TerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else if (Token->Type == DECIMAL)
|
|
{
|
|
if (!strcmp("_decimal", TerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else if (Token->Type == BINARY)
|
|
{
|
|
if (!strcmp("_binary", TerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else if (Token->Type == OCTAL)
|
|
{
|
|
if (!strcmp("_octal", TerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else if (Token->Type == STRING)
|
|
{
|
|
if (!strcmp("_string", TerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else if (Token->Type == WSTRING)
|
|
{
|
|
if (!strcmp("_wstring", TerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else // Keyword
|
|
{
|
|
if (!strcmp(Token->Value, TerminalMap[i]))
|
|
return i;
|
|
}
|
|
}
|
|
return INVALID;
|
|
}
|
|
|
|
/**
|
|
* @brief Gets the Non Terminal Id object
|
|
*
|
|
* @param Token the token to get the non-terminal ID of
|
|
* @return int the non-terminal ID or INVALID
|
|
*/
|
|
int
|
|
LalrGetNonTerminalId(PSCRIPT_ENGINE_TOKEN Token)
|
|
{
|
|
for (int i = 0; i < LALR_NONTERMINAL_COUNT; i++)
|
|
{
|
|
if (!strcmp(Token->Value, LalrNoneTerminalMap[i]))
|
|
return i;
|
|
}
|
|
return INVALID;
|
|
}
|
|
|
|
/**
|
|
* @brief Gets the Terminal Id object
|
|
*
|
|
* @param Token the token to get the terminal ID of
|
|
* @return int the terminal ID or INVALID
|
|
*/
|
|
int
|
|
LalrGetTerminalId(PSCRIPT_ENGINE_TOKEN Token)
|
|
{
|
|
for (int i = 0; i < LALR_TERMINAL_COUNT; i++)
|
|
{
|
|
if (Token->Type == HEX)
|
|
{
|
|
if (!strcmp("_hex", LalrTerminalMap[i]))
|
|
return i;
|
|
}
|
|
else if (Token->Type == GLOBAL_ID || Token->Type == GLOBAL_UNRESOLVED_ID)
|
|
{
|
|
if (!strcmp("_global_id", LalrTerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else if (Token->Type == LOCAL_ID || Token->Type == LOCAL_UNRESOLVED_ID)
|
|
{
|
|
if (!strcmp("_local_id", LalrTerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else if (Token->Type == FUNCTION_ID)
|
|
{
|
|
if (!strcmp("_function_id", LalrTerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else if (Token->Type == FUNCTION_PARAMETER_ID)
|
|
{
|
|
if (!strcmp("_function_parameter_id", LalrTerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else if (Token->Type == REGISTER)
|
|
{
|
|
if (!strcmp("_register", LalrTerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else if (Token->Type == PSEUDO_REGISTER)
|
|
{
|
|
if (!strcmp("_pseudo_register", LalrTerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else if (Token->Type == DECIMAL)
|
|
{
|
|
if (!strcmp("_decimal", LalrTerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else if (Token->Type == BINARY)
|
|
{
|
|
if (!strcmp("_binary", LalrTerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else if (Token->Type == OCTAL)
|
|
{
|
|
if (!strcmp("_octal", LalrTerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else if (Token->Type == STRING)
|
|
{
|
|
if (!strcmp("_string", LalrTerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else if (Token->Type == WSTRING)
|
|
{
|
|
if (!strcmp("_wstring", LalrTerminalMap[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
else // Keyword
|
|
{
|
|
if (!strcmp(Token->Value, LalrTerminalMap[i]))
|
|
return i;
|
|
}
|
|
}
|
|
return INVALID;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether the value and type of Token1 and Token2 are the same
|
|
*
|
|
* @param Token1 the first token to compare
|
|
* @param Token2 the second token to compare
|
|
* @return char
|
|
*/
|
|
char
|
|
IsEqual(const PSCRIPT_ENGINE_TOKEN Token1, const PSCRIPT_ENGINE_TOKEN Token2)
|
|
{
|
|
if (Token1->Type == Token2->Type)
|
|
{
|
|
if (Token1->Type == SPECIAL_TOKEN)
|
|
{
|
|
if (!strcmp(Token1->Value, Token2->Value))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
if (Token1->Type == GLOBAL_ID && Token2->Type == GLOBAL_UNRESOLVED_ID)
|
|
{
|
|
return 1;
|
|
}
|
|
if (Token1->Type == GLOBAL_UNRESOLVED_ID && Token2->Type == GLOBAL_ID)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if (Token1->Type == LOCAL_ID && Token2->Type == LOCAL_UNRESOLVED_ID)
|
|
{
|
|
return 1;
|
|
}
|
|
if (Token1->Type == LOCAL_UNRESOLVED_ID && Token2->Type == LOCAL_ID)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Set the Type object
|
|
*
|
|
* @param Val
|
|
* @param Type
|
|
* @return void
|
|
*/
|
|
void
|
|
SetType(unsigned long long * Val, unsigned char Type)
|
|
{
|
|
*Val = (unsigned long long int)Type;
|
|
}
|
|
|
|
/**
|
|
* @brief Converts an decimal string to a integer
|
|
*
|
|
* @param str the decimal string to convert
|
|
* @return unsigned long long int
|
|
*/
|
|
unsigned long long
|
|
DecimalToInt(char * str)
|
|
{
|
|
unsigned long long Acc = 0;
|
|
SIZE_T Len;
|
|
|
|
Len = strlen(str);
|
|
for (int i = 0; i < Len; i++)
|
|
{
|
|
Acc *= 10;
|
|
Acc += (str[i] - '0');
|
|
}
|
|
return Acc;
|
|
}
|
|
|
|
/**
|
|
* @brief Converts an decimal string to a signed integer
|
|
*
|
|
* @param str the decimal string to convert
|
|
* @return unsigned long long
|
|
*/
|
|
unsigned long long
|
|
DecimalToSignedInt(char * str)
|
|
{
|
|
long long Acc = 0;
|
|
SIZE_T Len;
|
|
|
|
if (str[0] == '-')
|
|
{
|
|
Len = strlen(str);
|
|
for (int i = 1; i < Len; i++)
|
|
{
|
|
Acc *= 10;
|
|
Acc += (str[i] - '0');
|
|
}
|
|
return -Acc;
|
|
}
|
|
else
|
|
{
|
|
Len = strlen(str);
|
|
for (int i = 0; i < Len; i++)
|
|
{
|
|
Acc *= 10;
|
|
Acc += (str[i] - '0');
|
|
}
|
|
return Acc;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Converts an hexadecimal string to integer
|
|
*
|
|
* @param str the hexadecimal string to convert
|
|
* @return unsigned long long
|
|
*/
|
|
unsigned long long
|
|
HexToInt(char * str)
|
|
{
|
|
CHAR Temp;
|
|
SIZE_T Len = strlen(str);
|
|
unsigned long long Acc = 0;
|
|
for (int i = 0; i < Len; i++)
|
|
{
|
|
Acc <<= 4;
|
|
if (str[i] >= '0' && str[i] <= '9')
|
|
{
|
|
Temp = str[i] - '0';
|
|
}
|
|
else if (str[i] >= 'a' && str[i] <= 'f')
|
|
{
|
|
Temp = str[i] - 'a' + 10;
|
|
}
|
|
else
|
|
{
|
|
Temp = str[i] - 'A' + 10;
|
|
}
|
|
Acc += Temp;
|
|
}
|
|
|
|
return Acc;
|
|
}
|
|
|
|
/**
|
|
* @brief Converts an octal string to integer
|
|
*
|
|
* @param str the octal string to convert
|
|
* @return unsigned long long
|
|
*/
|
|
unsigned long long
|
|
OctalToInt(char * str)
|
|
{
|
|
SIZE_T Len;
|
|
unsigned long long Acc = 0;
|
|
|
|
Len = strlen(str);
|
|
|
|
for (int i = 0; i < Len; i++)
|
|
{
|
|
Acc <<= 3;
|
|
Acc += (str[i] - '0');
|
|
}
|
|
return Acc;
|
|
}
|
|
|
|
/**
|
|
* @brief Converts a binary string to integer
|
|
*
|
|
* @param str the binary string to convert
|
|
* @return unsigned long long
|
|
*/
|
|
unsigned long long
|
|
BinaryToInt(char * str)
|
|
{
|
|
SIZE_T Len;
|
|
unsigned long long Acc = 0;
|
|
|
|
Len = strlen(str);
|
|
|
|
for (int i = 0; i < Len; i++)
|
|
{
|
|
Acc <<= 1;
|
|
Acc += (str[i] - '0');
|
|
}
|
|
return Acc;
|
|
}
|
|
|
|
/**
|
|
* @brief Rotate a character array to the left by one time
|
|
*
|
|
* @param str the string to rotate
|
|
* @return void
|
|
*/
|
|
void
|
|
RotateLeftStringOnce(char * str)
|
|
{
|
|
INT Length = (INT)strlen(str);
|
|
CHAR Temp = str[0];
|
|
for (int i = 0; i < (Length - 1); i++)
|
|
{
|
|
str[i] = str[i + 1];
|
|
}
|
|
str[Length - 1] = Temp;
|
|
}
|