/*
* Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/


#include "sisutils.h"
#include "pkgfileparser.h"
#include <vector>
#include <wchar.h>

// Parse options lookups
#define MAXTOKENLEN	30
struct SParseToken
{
	wchar_t pszOpt[MAXTOKENLEN];
	unsigned dwOpt;
};

const SParseToken KTokens[] =
{
	{L"if",		IF_TOKEN},
	{L"elseif",	ELSEIF_TOKEN},
	{L"else",	ELSE_TOKEN},
	{L"endif",	ENDIF_TOKEN},
	{L"exists",	EXISTS_TOKEN},
	{L"devprop",DEVCAP_TOKEN},
	{L"appcap",	APPCAP_TOKEN},
	{L"package",DEVCAP_TOKEN},
	{L"appprop",APPCAP_TOKEN},
	{L"not",	NOT_TOKEN},
	{L"and",	AND_TOKEN},
	{L"or",		OR_TOKEN},
	{L"type",	TYPE_TOKEN},
	{L"key",	KEY_TOKEN},
};
#define NUMPARSETOKENS (sizeof(KTokens)/sizeof(SParseToken))

std::wstring string2wstring (const String& aNarrow);
String wstring2string (const std::wstring& aWide);


/**
Constructor: PkgParser class
Initilize the parameters to data members.

@internalComponent
@released

@param aFile	- Name of the package script file
*/
PkgParser::PkgParser(String aFile) : iPkgFile(aFile), m_nLineNo(0)
{
}

/**
Destructor: PkgParser class
Deallocates the memory for data members

@internalComponent
@released
*/
PkgParser::~PkgParser()
{
	if(iPkgHandle)
	{
		std::fclose(iPkgHandle);
	}

	DeleteAll();
}

/**
OpenFile: Opens the package script file

@internalComponent
@released
*/
int PkgParser::OpenFile()
{
#ifdef WIN32
	iPkgHandle = _wfopen(string2wstring(iPkgFile).c_str(),L"r");
#else
	// *nix fopen doesn't care about the filename encoding, as long as its NULL-terminated.
	iPkgHandle = std::fopen(iPkgFile.c_str(),"r");
#endif
	return iPkgHandle ? 1 : 0;

}

/**
GetEmbeddedSisList: Returns the embedded sis file list

@internalComponent
@released

@param embedSisList	- reference to sis file list structure
*/
void PkgParser::GetEmbeddedSisList(SISFILE_LIST& embedSisList)
{
	embedSisList = iEmbedSisFiles;
}

/**
GetInstallOptions: Returns the install options read from the package file

@internalComponent
@released

@param aOptions	- reference to the string list structure
*/
void PkgParser::GetInstallOptions(FILE_LIST& aOptions)
{
	aOptions = iInstallOptions;
}

/**
GetLanguageList: Returns the language list read from the package file

@internalComponent
@released

@param langList	- reference to the language list structure
*/
void PkgParser::GetLanguageList(LANGUAGE_LIST& langList)
{
	langList = iLangList;
}

/**
GetHeader: Returns the header details read from the package file

@internalComponent
@released

@param pkgHeader	- reference to the package header structure
*/
void PkgParser::GetHeader(PKG_HEADER& pkgHeader)
{
	pkgHeader = iPkgHeader;
}

/**
GetCommandList: Returns the package body details read from the package file

@internalComponent
@released

@param cmdList	- reference to the command list structure
*/
void PkgParser::GetCommandList(CMDBLOCK_LIST& cmdList)
{
	cmdList = iPkgBlock;
}

/**
ParsePkgFile: Parses the package file

@internalComponent
@released
*/
void PkgParser::ParsePkgFile()
{
	if(!OpenFile())
	{
		throw SisUtilsException((char*)iPkgFile.data(), const_cast<char *>("Could not open file"));
	}

	GetNextChar();

	// skip unicode marker if present
	if(m_pkgChar==0xFEFF) GetNextChar();

	GetNextToken ();
	while(m_token!=EOF_TOKEN)
	{
		ParseEmbeddedBlockL();
		switch (m_token)
		{
		case '&':
			GetNextToken ();
			ParseLanguagesL();
			break;
		case '#':
			GetNextToken ();
			ParseHeaderL();
			break;
		case '%':
			GetNextToken ();
			ParseVendorNameL();
			break;
		case '=':
			GetNextToken ();
			ParseLogoL();
			break;
		case '(':
			GetNextToken ();
			ParseDependencyL();
			break;
		case ':':
			GetNextToken ();
			ParseVendorUniqueNameL();
			break;
		case '[':
			GetNextToken ();
			ParseTargetDeviceL();
			break;
		case EOF_TOKEN:
			break;
		default:
			ParserError(const_cast<char *>("Unexpected token"));
			break;
		}
	}
}

/**
ParseLanguagesL: Parses the language section

@internalComponent
@released
*/
void PkgParser::ParseLanguagesL()
{
	unsigned long langCode = 0;
	unsigned long dialect = 0;
	
	while (true)
	{
		if (m_token==ALPHA_TOKEN)
		{
			langCode = PkgLanguage::GetLanguageCode(m_tokenValue.pszString);
		}
		else if (m_token==NUMERIC_TOKEN && m_tokenValue.dwNumber>=0 && m_tokenValue.dwNumber<=1000)
		{
			langCode = (m_tokenValue.dwNumber);
		}

		GetNextToken ();

		// Check if a dialect is defined
		if (m_token == '(')
		{
			GetNumericToken();
			// Modify the last added language code, combining it with dialect code
			dialect = (m_tokenValue.dwNumber);
			GetNextToken ();
			GetNextToken ();
		}
		AddLanguage(wstring2string(PkgLanguage::GetLanguageName(langCode)), langCode, dialect);

		if (m_token!=',')
			return;
		GetNextToken ();
	}
}


/**
ParseHeaderL: Parses the package header section

@internalComponent
@released
*/
void PkgParser::ParseHeaderL()
{
	if (!iLangList.size())
	{
		//No languages defined, assuming English."
		AddLanguage("EN", PkgLanguage::ELangEnglish, 0);
	}
	
	// process application names
	ExpectToken('{');
	for (unsigned short wNumLangs = 0; wNumLangs < iLangList.size(); wNumLangs++)
	{
		GetNextToken ();
		ExpectToken(QUOTED_STRING_TOKEN);
		iPkgHeader.pkgNameList.push_back(wstring2string(m_tokenValue.pszString));
		GetNextToken ();
		if (wNumLangs < (iLangList.size()-1) )
		{
			ExpectToken(',');
		}
	}
	ExpectToken('}');
	GetNextToken (); 
	
	ExpectToken(',');
	GetNextToken ();
	ExpectToken('(');
	GetNextToken ();
	
	ExpectToken(NUMERIC_TOKEN);
	iPkgHeader.pkgUid = m_tokenValue.dwNumber;
	GetNextToken ();
	
	ExpectToken(')');
	GetNextToken ();
	ExpectToken(',');
	GetNextToken ();
	
	ExpectToken(NUMERIC_TOKEN);
	iPkgHeader.vMajor = m_tokenValue.dwNumber;
	GetNextToken ();
	ExpectToken(',');
	GetNextToken ();
	
	ExpectToken(NUMERIC_TOKEN);
	iPkgHeader.vMinor = m_tokenValue.dwNumber;
	GetNextToken ();
	ExpectToken(',');
	GetNextToken ();
	
	ExpectToken(NUMERIC_TOKEN);
	iPkgHeader.vBuild = m_tokenValue.dwNumber;
	GetNextToken ();
	
	// Parse any options
	while (m_token==',')
	{
		GetNextToken ();
		if (m_token==TYPE_TOKEN)
		{
			GetNextToken ();
			ExpectToken('=');
			GetNextToken ();
			iPkgHeader.pkgType = wstring2string(m_tokenValue.pszString);
			GetNextToken ();
		}
		else
			GetNextToken ();
	}
}

/**
ParseEmbeddedBlockL: Parses the package body block

@internalComponent
@released
*/
void PkgParser::ParseEmbeddedBlockL ()
{
	while(m_token!=EOF_TOKEN)
	{
		switch (m_token)
		{
		case QUOTED_STRING_TOKEN:
			ParseFileL ();
			break;
		case '@':
			GetNextToken ();
			ParsePackageL ();
			break;
		case '!':
			GetNextToken ();
			ParseOptionsBlockL();
			break;
		case '+':
			GetNextToken ();
			ParsePropertyL ();
			break;
		case IF_TOKEN:
			GetNextToken ();
			ParseIfBlockL ();
			break;
		case ';' :
			ParseCommentL ();
			break;
		default :
			return;
		}
	}
}

/**
ParseFileL: Parses the file list section

@internalComponent
@released
*/
void PkgParser::ParseFileL()
{
	PCMD_BLOCK pCmdBlock = 0;
	PINSTALLFILE_LIST pFileList = 0;
	
	std::wstring sourceFile (m_tokenValue.pszString);
	
	// Linux and windows both support forward slashes so if source path is given '\' need to convert
	// in forward slash for compatibility.
	wchar_t *pBuffer = (wchar_t*)sourceFile.c_str();
	wchar_t *pCurrent = pBuffer;
	while (pBuffer && *pBuffer && (pCurrent = wcschr(pBuffer,L'\\')) != NULL)
	{
		*pCurrent = L'/';
		pBuffer = pCurrent + 1;
	}
	
	GetNextToken ();
	
	ExpectToken('-');
	GetNextToken ();
	
	ExpectToken(QUOTED_STRING_TOKEN);
	
	std::wstring destinationFile (m_tokenValue.pszString);
	
	// SWI only supports backward slashesh so need to convert destination path in backward slash if
	// user gives '/' in Linux.
	pBuffer = (wchar_t*)destinationFile.c_str();
	pCurrent = pBuffer;
	while (pBuffer && *pBuffer && (pCurrent = wcschr(pBuffer,L'/')) != NULL)
	{
		*pCurrent = L'\\';
		pBuffer = pCurrent + 1;
	}
	
	GetNextToken ();
	
	// Test for options
	if (m_token!=',')
	{
		pCmdBlock = new CMD_BLOCK;
		pFileList = new INSTALLFILE_LIST;

		pCmdBlock->cmdType = INSTALLFILE;
		pCmdBlock->iInstallFileList = pFileList;

		pFileList->langDepFlg = 0;
		pFileList->srcFiles.push_back(wstring2string(sourceFile));
		pFileList->destFile = wstring2string(destinationFile);

		iPkgBlock.push_back(pCmdBlock);
	}
	else
	{	
		bool needAdd = false;
		while(m_token==',')
		{
			GetNextToken ();
			std::wstring installOption = m_tokenValue.pszString;
			if((installOption == L"FF") || (installOption == L"FILE"))
			{
				needAdd = true;
			}
			GetNextToken ();
		}
		if (needAdd)
		{
			pCmdBlock = new CMD_BLOCK;
			pFileList = new INSTALLFILE_LIST;

			pCmdBlock->cmdType = INSTALLFILE;
			pCmdBlock->iInstallFileList = pFileList;

			pFileList->langDepFlg = 0;
			pFileList->srcFiles.push_back(wstring2string(sourceFile));
			pFileList->destFile = wstring2string(destinationFile);
		
			iPkgBlock.push_back(pCmdBlock);
		}
	}
}

/**
ParseIfBlockL: Parses the conditional installation body

@internalComponent
@released
*/
void PkgParser::ParseIfBlockL()
{
	PCMD_BLOCK pCmdBlock = 0; 

	//IF
	pCmdBlock = new CMD_BLOCK;
	pCmdBlock->cmdType = IF;
	ParseLogicalOp(pCmdBlock->cmdExpression);
	iPkgBlock.push_back(pCmdBlock);

	ParseEmbeddedBlockL ();
	
	while (m_token==ELSEIF_TOKEN)
	{
		GetNextToken ();
		//ELSEIF
		pCmdBlock = new CMD_BLOCK;
		pCmdBlock->cmdType = ELSEIF;
		ParseLogicalOp(pCmdBlock->cmdExpression);
		iPkgBlock.push_back(pCmdBlock);

		ParseEmbeddedBlockL ();
	}
	
	if (m_token==ELSE_TOKEN)
	{
		GetNextToken ();
		//ELSEIF
		pCmdBlock = new CMD_BLOCK;
		pCmdBlock->cmdType = ELSE;
		iPkgBlock.push_back(pCmdBlock);

		ParseEmbeddedBlockL ();
	}
	
	ExpectToken(ENDIF_TOKEN);
	//ENDIF
	pCmdBlock = new CMD_BLOCK;
	pCmdBlock->cmdType = ENDIF;
	iPkgBlock.push_back(pCmdBlock);

	GetNextToken ();
}

/**
ParseLogicalOp: Parses the logical expression

@internalComponent
@released
*/
void PkgParser::ParseLogicalOp (String& aExpression)
{
    ParseRelation (aExpression);
	switch (m_token)
	{
	case AND_TOKEN:
	case OR_TOKEN:
		{
			if (m_token==AND_TOKEN)
				aExpression.append(" && ");
			else
				aExpression.append(" || ");
			GetNextToken ();
			ParseLogicalOp (aExpression);
		}
		break;
	}
}

/**
ParseRelation: Parses the relational expression

@internalComponent
@released
*/
void PkgParser::ParseRelation(String& aExpression)
{
    ParseUnary (aExpression);
	switch (m_token)
	{
	case '=':
	case '>':
	case '<':
	case GE_TOKEN:
	case LE_TOKEN:
	case NE_TOKEN:
	case APPCAP_TOKEN:
		{
			switch (m_token)
			{
			case '=':
				aExpression.append(" == ");
				break;
			case '>':
				aExpression.append(" > ");
				break;
			case '<':
				aExpression.append(" < ");
				break;
			case GE_TOKEN:
				aExpression.append(" >= ");
				break;
			case LE_TOKEN:
				aExpression.append(" <= ");
				break;
			case NE_TOKEN:
				aExpression.append(" != ");
				break;
			case APPCAP_TOKEN:
				aExpression.append(" APPPROP ");
				break;
			}
			GetNextToken ();
			ParseUnary (aExpression);
			break;
		}
	}
}

/**
ParseUnary: Parses the unary expression

@internalComponent
@released
*/
void PkgParser::ParseUnary(String& aExpression)
{
    switch (m_token)
	{
	case NOT_TOKEN:
		aExpression.append(" !");
		GetNextToken ();
		ParseUnary (aExpression);
		break;
	case EXISTS_TOKEN:
	case DEVCAP_TOKEN:
		{	// 1 arg function
			int token=m_token;
			GetNextToken ();
			ExpectToken('(');
			GetNextToken ();
			if (token==EXISTS_TOKEN)
			{
				aExpression.append("EXISTS(\"");
				ExpectToken(QUOTED_STRING_TOKEN);
				GetNextToken ();
				aExpression.append(wstring2string(m_tokenValue.pszString));
				aExpression.append("\")");
			}
			else
			{
				aExpression.append("DEVCAP(");
				ParseUnary (aExpression);
				aExpression.append(")");
			}
			ExpectToken(')');
			GetNextToken ();
			break;
		}
	default:
		ParseFactor (aExpression);
		break;
	}
}

/**
ParseFactor: Parses the expression factor

@internalComponent
@released
*/
void PkgParser::ParseFactor(String& aExpression)
{
    switch (m_token) {
	case '(':
		{
			aExpression.append("(");
			GetNextToken ();
			ParseLogicalOp (aExpression);
			ExpectToken(')');
			aExpression.append(")");
		}
		break;
	case QUOTED_STRING_TOKEN:
	case ALPHA_TOKEN:
	case NUMERIC_TOKEN:
		{
			switch (m_token)
			{
			case QUOTED_STRING_TOKEN:
				aExpression.append("\"");
				aExpression.append(wstring2string(m_tokenValue.pszString));
				aExpression.append("\"");
				break;
			case ALPHA_TOKEN:
                if(!CompareNString(m_tokenValue.pszString,const_cast<wchar_t *>(L"option"),6))
				{
					aExpression.append(" defined(");
					aExpression.append(wstring2string(m_tokenValue.pszString));
					aExpression.append(") ");
				}
				else
				{
					aExpression.append(wstring2string(m_tokenValue.pszString));
				}
				break;
			case NUMERIC_TOKEN:
				{
					std::ostringstream str;

					str << "(0x" << std::setbase(16) << m_tokenValue.dwNumber << ")";
					aExpression.append(str.str());
				}
				break;
			}
		}
		break;
	default:
		ParserError(const_cast<char *>("ErrBadCondFormat"));
	}
	GetNextToken ();
}


/**
ParsePackageL: Parses the embedded package section

@internalComponent
@released
*/
void PkgParser::ParsePackageL()
{
	PCMD_BLOCK pCmdBlock = 0;
	int found = 0;

	ExpectToken(QUOTED_STRING_TOKEN);

	//if the sis file already exists then skip it
	SISFILE_LIST::iterator begin = iEmbedSisFiles.begin();
	SISFILE_LIST::iterator end = iEmbedSisFiles.end();

	while(begin != end)
	{
		if((*begin).compare(wstring2string(m_tokenValue.pszString)) == 0)
		{
			found = 1;
			break;
		}
		++begin;
	}

	if(!found)
	{
		iEmbedSisFiles.push_back(wstring2string(m_tokenValue.pszString));
	}
	
	//add as a command block as well
	{
		pCmdBlock = new CMD_BLOCK;

		pCmdBlock->cmdType = PACKAGE;
		pCmdBlock->iInstallFileList = 0;
		pCmdBlock->cmdExpression = wstring2string(m_tokenValue.pszString);

		iPkgBlock.push_back(pCmdBlock);
	}


	GetNextToken ();

	ExpectToken(',');
	GetNextToken ();
	ExpectToken('(');
	GetNextToken ();
	ExpectToken(NUMERIC_TOKEN);
	GetNextToken ();
	ExpectToken(')');
	GetNextToken ();
}

/**
ParseCommentL: Parses the comment section
  Parses a comment line (Does nothing, just throws the line away)

@internalComponent
@released
*/
void PkgParser::ParseCommentL()
{
	// parse to end of line
	while (m_pkgChar && (m_pkgChar!='\n')) GetNextChar();
	GetNextToken ();
}

/**
ParseOptionsBlockL: Parses the install options section

@internalComponent
@released
*/
void PkgParser::ParseOptionsBlockL()
{
	unsigned short wNumLangs;
	
	ExpectToken('(');
	GetNextToken ();
	
	for (;;)
	{
		ExpectToken('{');
		GetNextToken ();
		
		wNumLangs = 0;
		while (wNumLangs < iLangList.size())
		{
			ExpectToken(QUOTED_STRING_TOKEN);
			iInstallOptions.push_back(wstring2string(m_tokenValue.pszString));
			GetNextToken ();
			if (wNumLangs < iLangList.size() - 1)
			{
				ExpectToken(',');
				GetNextToken ();
			}
			wNumLangs++;
		}
		
		ExpectToken('}');
		GetNextToken ();
		if (m_token!=',') break;
		GetNextToken ();
	}
	
	ExpectToken(')');
	GetNextToken ();	
}

/**
ParsePropertyL: Parses the capability options section

@internalComponent
@released
*/
void PkgParser::ParsePropertyL()
{
	ExpectToken('(');
	do
	{
		GetNextToken ();
		
		ExpectToken(NUMERIC_TOKEN);
		GetNextToken ();
		ExpectToken('=');
		GetNextToken ();
		ExpectToken(NUMERIC_TOKEN);
		GetNextToken ();
	} while (m_token==',');
	ExpectToken(')');
	GetNextToken ();
}

/**
ParseVendorNameL: Parses the vendor options section

@internalComponent
@released
*/
void PkgParser::ParseVendorNameL()
{
	ExpectToken('{');
	for (unsigned short wNumLangs = 0; wNumLangs < iLangList.size(); wNumLangs++)
	{
		GetNextToken ();
		ExpectToken(QUOTED_STRING_TOKEN);
		GetNextToken ();
		if (wNumLangs < iLangList.size() -1 )
		{
			ExpectToken(',');
		}
	}
	ExpectToken('}');
	GetNextToken ();
}

/**
ParseLogoL: Parses the logo options section

@internalComponent
@released
*/
void PkgParser::ParseLogoL()
{
	ExpectToken (QUOTED_STRING_TOKEN);
	GetNextToken ();
	ExpectToken(',');
	GetNextToken ();
	ExpectToken (QUOTED_STRING_TOKEN);
	GetNextToken ();
	if (m_token==',')
	{
		GetNextToken ();
		ExpectToken (QUOTED_STRING_TOKEN);
		GetNextToken ();
	}
}

/**
ParseVersion: Parses the version details

@internalComponent
@released
*/
void PkgParser::ParseVersion()
{
	GetNextToken();
	ExpectToken(NUMERIC_TOKEN);

	GetNextToken();
	ExpectToken(',');
	GetNextToken();
	ExpectToken(NUMERIC_TOKEN);

	GetNextToken();
	ExpectToken(',');
	GetNextToken();
	ExpectToken(NUMERIC_TOKEN);

	GetNextToken();
}

/**
ParseDependencyL: Parses the dependency package section

@internalComponent
@released
*/
void PkgParser::ParseDependencyL()
{
	ExpectToken(NUMERIC_TOKEN);
	GetNextToken ();
	ExpectToken(')');
	GetNextToken ();
	ExpectToken(',');

	ParseVersion();
	if (m_token == '~')
	{
		ParseVersion();
		ExpectToken(',');
	}
	
	GetNextToken ();
	ExpectToken('{');
	for (TUint numLangs = 0; numLangs < iLangList.size(); ++numLangs)
	{
		GetNextToken ();
		ExpectToken(QUOTED_STRING_TOKEN);
		GetNextToken ();
		if (numLangs < (iLangList.size() - 1))
			ExpectToken(',');
	}
	ExpectToken('}');
	GetNextToken ();
}

/**
ParseVendorUniqueNameL: Parses the vendor unique name section

@internalComponent
@released
*/
void PkgParser::ParseVendorUniqueNameL()
{
	ExpectToken(QUOTED_STRING_TOKEN);
	GetNextToken ();
}

/**
ParseTargetDeviceL: Parses the target device name section

@internalComponent
@released
*/
void PkgParser::ParseTargetDeviceL()
{
	ExpectToken(NUMERIC_TOKEN);
	GetNextToken ();
	ExpectToken(']');
	GetNextToken ();
	ExpectToken(',');
	
	ParseVersion();
	if (m_token == '~')
	{
		ParseVersion();
		ExpectToken(',');
	}
	GetNextToken ();
	ExpectToken('{');
	
	// must do this before adding language strings	
	for (TUint numLangs = 0; numLangs < iLangList.size(); ++numLangs)
	{
		GetNextToken ();
		ExpectToken(QUOTED_STRING_TOKEN);
		GetNextToken ();
		if (numLangs < (iLangList.size() - 1))
			ExpectToken(',');
	}
	ExpectToken('}');
	GetNextToken ();
}


/**
GetNextChar: Reads the next character from the package file

@internalComponent
@released
*/
void PkgParser::GetNextChar()
{
	wint_t wch = std::fgetwc(iPkgHandle);
	m_pkgChar = (wch == WEOF) ? 0 : wch;
}

/**
ExpectToken: Tests the current token value

@internalComponent
@released

@param aToken - expected token value
*/
void PkgParser::ExpectToken(int aToken)
{
	if (m_token!=aToken)
	{
		ParserError(const_cast<char *>("Unexpected Token"));
	}
}

/**
GetNextToken: Reads the next valid token from the package file

@internalComponent
@released
*/
void PkgParser::GetNextToken ()
{
	// skip any white space & newLine's
	while (m_pkgChar == '\n' || isspace(m_pkgChar) || m_pkgChar == 0xA0)
	{
		if (m_pkgChar == '\n') ++m_nLineNo;
		GetNextChar();
	}
	
	if (m_pkgChar == '\0')
		m_token=EOF_TOKEN;
	else if (IsNumericToken())
	{
		GetNumericToken();
		m_token=NUMERIC_TOKEN;
	}
	else if (isalpha(m_pkgChar))
	{ // have some alphanumeric text
		GetAlphaNumericToken();
		m_token=ALPHA_TOKEN;
		// check if it is a keyword
		for(unsigned short wLoop = 0; wLoop < NUMPARSETOKENS; wLoop++)
		{
			if(CompareTwoString(m_tokenValue.pszString,(wchar_t*)KTokens[wLoop].pszOpt) == 0)
			{
				m_token=KTokens[wLoop].dwOpt;
				break;
			}
		}
	}
	else if (m_pkgChar == '\"')
	{ // have a quoted string
		GetStringToken();
		m_token=QUOTED_STRING_TOKEN;
	}
	else if (m_pkgChar == '>')
	{
		GetNextChar();
		if (m_pkgChar == '=')
		{
			m_token=GE_TOKEN;
			GetNextChar();
		}
		else
			m_token='>';
	}
	else if (m_pkgChar == '<')
	{
		// check if start of an escaped string, e.g. <123>"abc"
		if (GetStringToken())
			m_token=QUOTED_STRING_TOKEN;
		else
		{
			GetNextChar();
			if (m_pkgChar == '=')
			{
				m_token=LE_TOKEN;
				GetNextChar();
			}
			else if (m_pkgChar == '>')
			{
				m_token=NE_TOKEN;
				GetNextChar();
			}
			else
				m_token='<';
		}
	}
	else
	{
		m_token=m_pkgChar;
		GetNextChar();
	}
}

/**
GetStringToken: Reads the string token from the package file

@internalComponent
@released
*/
bool PkgParser::GetStringToken()
{
	unsigned wCount = 0;
	bool done=false;
	bool finished=false;
	unsigned escapeChars = 0;
	
	while (!finished)
	{
		if (m_pkgChar == '\"')
		{
			GetNextChar();
			while(m_pkgChar && m_pkgChar != '\"')
			{
				if(wCount < (MAX_STRING - 1))
					m_tokenValue.pszString[wCount++] = m_pkgChar;
				else //We dont want the string with length greater than MAX_STRING to be cut off silently
					ParserError(const_cast<char *>("Bad String"));
				GetNextChar();
			}
			if(m_pkgChar == '\0')
				ParserError(const_cast<char *>("Bad String"));
			GetNextChar();
			done=true;
		}
		if (m_pkgChar == '<')
		{
			m_tokenValue.pszString[wCount] = L'\0';
			escapeChars=ParseEscapeChars();
			if (escapeChars>0)
			{
				done=true;
				wCount+=escapeChars;
				if (wCount>=MAX_STRING) wCount=MAX_STRING-1;
			}
		}
		if (escapeChars==0 || m_pkgChar != '\"')
			finished=true;
	}
	
	m_tokenValue.pszString[wCount] = L'\0';
	return done;
}

/**
ParseEscapeChars: Parses the escape sequence characters

@internalComponent
@released
*/
unsigned short PkgParser::ParseEscapeChars()
{
	unsigned short found=0;
	wchar_t temp[MAX_STRING];

	while (m_pkgChar == '<')
	{
		wcscpy(temp,m_tokenValue.pszString);
		std::fpos_t foff;
		std::fgetpos(iPkgHandle,&foff);
		try
		{
			GetNextChar();
			GetNumericToken();
			if (m_pkgChar=='>')
				found++;
			else
			{
				std::fsetpos(iPkgHandle,&foff);
				break;
			}
		}
		catch (...)
		{
			wcscpy(m_tokenValue.pszString,temp);
			std::fsetpos(iPkgHandle,&foff);
			break;
		}
		unsigned num=m_tokenValue.dwNumber;
		// watch for CP1252 escapes which aren't appropriate for UNICODE
		if (num>=0x80 && num<=0x9F) ParserError(const_cast<char *>("Invalid Escape"));
		unsigned len=wcslen(temp);
		wcscpy(m_tokenValue.pszString,temp);
		if (len+2<=MAX_STRING)
		{
			m_tokenValue.pszString[len]=(wchar_t)num;
			len++;
			m_tokenValue.pszString[len]='\0';
		}
		GetNextChar();
	}
	return found;
}

/**
GetAlphaNumericToken: Parse an alphanumeric string from the input line

@internalComponent
@released
*/
void PkgParser::GetAlphaNumericToken()
{
	unsigned short wCount = 0;
	while(m_pkgChar && (isalnum(m_pkgChar) || ((m_pkgChar) == '_')))
	{
		if(wCount < (MAX_STRING - 1))
			m_tokenValue.pszString[wCount++] = m_pkgChar;
		GetNextChar();
	}
	m_tokenValue.pszString[wCount] = L'\0';
}

/**
IsNumericToken: Determines if the next lexeme is a numeric token

@internalComponent
@released
*/
bool PkgParser::IsNumericToken()
{
	bool lexemeIsNumber = false;
	if (iswdigit(m_pkgChar))
		lexemeIsNumber = true;
	else if (m_pkgChar == '+' || m_pkgChar == '-')
	{
		// we may have a number but we must look ahead one char to be certain
		
		wchar_t oldChar = m_pkgChar;
		std::fpos_t foff;
		std::fgetpos(iPkgHandle,&foff);
		GetNextChar();
		lexemeIsNumber = iswdigit(m_pkgChar) != FALSE;
		m_pkgChar = oldChar;
		std::fsetpos(iPkgHandle,&foff);
	}
	
	return lexemeIsNumber;
}

/**
GetNumericToken: Parse a number from the input line

@internalComponent
@released
*/
void PkgParser::GetNumericToken()
{
	wchar_t temp[MAX_STRING];
	wchar_t * end;
	bool hexString = false;
	std::fpos_t foff;
	std::fgetpos(iPkgHandle,&foff);
	
	temp[0]=m_pkgChar;
	unsigned wchRead = fread(&temp[1],sizeof(wchar_t),MAX_STRING - 2,iPkgHandle);
	if (!wchRead)
	{ 
		ParserError(const_cast<char *>("Read failed"));
	}
	temp[1+wchRead] = 0;
	hexString = (!CompareNString(temp, const_cast<wchar_t *>(L"0x"), 2) ||
		!CompareNString(&temp[1], const_cast<wchar_t *>(L"0x"), 2));
	
	m_tokenValue.dwNumber = wcstoul(temp, &end, (hexString) ? 16 : 10);
	
	if (end==temp)
	{
		ParserError(const_cast<char *>("Read failed"));
	}
	std::fsetpos(iPkgHandle,&foff);
	std::fseek(iPkgHandle,(end-temp-1) * sizeof(wchar_t),SEEK_CUR);		
	GetNextChar();
}

/**
AddLanguage: Updates the language list structure

@internalComponent
@released

@param aLang - Name of the language
@param aCode - Language code
@param aDialect - Language dialect code
*/
void PkgParser::AddLanguage(String aLang, unsigned long aCode, unsigned long aDialect)
{
	PLANG_LIST lc = new LANG_LIST;
	
	lc->langName = aLang;
	lc->langCode = aCode;
	lc->dialectCode = aDialect;

	iLangList.push_back(lc);
}

/**
DeleteAll: Deallocates memory for the data members

@internalComponent
@released
*/
void PkgParser::DeleteAll()
{
	while(iPkgBlock.size() > 0)
	{
		PCMD_BLOCK ptemp = 0;

		ptemp = iPkgBlock.front();
		iPkgBlock.pop_front();

		if(ptemp->cmdType == INSTALLFILE)
		{
			delete ptemp->iInstallFileList;
		}
		delete ptemp;
	}

	{
		LANGUAGE_LIST::iterator begin = iLangList.begin();
		LANGUAGE_LIST::iterator end = iLangList.end();
		while(begin != end)
		{
			PLANG_LIST ptemp = 0;
			ptemp = (*begin);

			if(ptemp)
				delete ptemp;
			++begin;
		}
		iLangList.clear();
	}
}

/**
ParserError: Throws exception with the given error message

@internalComponent
@released

@param msg - error message to be thrown
*/
void PkgParser::ParserError(char* msg)
{
	std::ostringstream str;

	str << (char*)iPkgFile.data() << "(" << m_nLineNo << "): " << msg;

	throw SisUtilsException(const_cast<char *>("PackageFile-Parser Error"), (char*)(str.str()).data());
}

/**
wstring2string: Converts wide string to string

@internalComponent
@released

@param aWide - input wide string
*/
String wstring2string (const std::wstring& aWide)
{
	std::vector<char> buffer((aWide.length() * sizeof(wchar_t)) + 1, '\0');
	size_t nchars = wcstombs(&buffer[0],aWide.c_str(),buffer.size());
	if (nchars == (size_t)-1)
	{
		throw SisUtilsException(const_cast<char *>("ParserError"),
			const_cast<char *>("wstring to string conversion failed"));
	}
	String reply(&buffer[0]);
	return reply;
}

/**
string2wstring: Converts string to wide string

@internalComponent
@released

@param aNarrow - input string
*/
std::wstring string2wstring (const String& aNarrow)
{
	std::vector<char> buffer(mblen(aNarrow.c_str(),aNarrow.length()) + 2, '\0');
	size_t nchars = mbstowcs((wchar_t *)&buffer[0],aNarrow.c_str(),buffer.size());
	if (nchars == (size_t)-1)
	{
		throw SisUtilsException(const_cast<char *>("ParserError"),
			const_cast<char *>("string to wstring conversion failed"));
	}
	std::wstring reply((wchar_t *)&buffer[0]);
	return reply;
}

/**
CompareTwoString: Compares two wide string

@internalComponent
@released

@param string - first string
@param option - second string
*/
int CompareTwoString(wchar_t const * string ,wchar_t const * option)
{
#if defined(WIN32)
	return wcsicmp(string,option);
#elif defined(__LINUX__)
	return wcscasecmp(string,option);
#endif
}

/**
CompareNString: Compares two wide string for n characters

@internalComponent
@released

@param string - first string
@param option - second string
@param len - no of wide characters to be compared
*/
int CompareNString(wchar_t const * string ,wchar_t const * option, int len)
{
	return wmemcmp(string,option,len);
}
