/*<<<NL>>>*/
#include "stdafx.h"
#include "mpfs2dynVar.h"
#include "mpfs2lib.h"
#include "xbXmlItf.h"

#pragma warning(push)
#pragma warning(disable: 4311)
xbEimpl(eMpfs2,xbEadv);
/*********************************************************************
 *
 *    Dynamic Variable Parser Library
 *
 *********************************************************************
 * FileName:        DynVar.cs
 * Dependencies:    Microsoft .NET Framework 2.0
 * Processor:       x86
 * Complier:        Microsoft Visual C# 2008 Express Edition
 * Company:         Microchip Technology, Inc.
 *
 * Software License Agreement
 *
 * This software is owned by Microchip Technology Inc. ("Microchip") 
 * and is supplied to you for use exclusively as described in the 
 * associated software agreement.  This software is protected by 
 * software and other intellectual property laws.  Any use in 
 * violation of the software license may subject the user to criminal 
 * sanctions as well as civil liability.  Copyright 2008 Microchip
 * Technology Inc.  All rights reserved.
 *
 *
 * THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT 
 * WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT 
 * LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A 
 * PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL 
 * MICROCHIP BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR 
 * CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF 
 * PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS 
 * BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE 
 * THEREOF), ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER 
 * SIMILAR COSTS, WHETHER ASSERTED ON THE BASIS OF CONTRACT, TORT 
 * (INCLUDING NEGLIGENCE), BREACH OF WARRANTY, OR OTHERWISE.
 *
 *
 * Author               Date   		Comment
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * Elliott Wood		4/17/2008	    Original
 * No microchip stuff:
 * Marc Mirti Mancinelli 28/12/2013 c++ version and ...
 * Marc Mirti Mancinelli 21/08/2015 correcs from Abram Dancy: integration in MPLAB for errors output and other
 ********************************************************************/
    // String Constants
const char* DynamicVariableParser::HTTPPRINT_H_HEADER = 
        "/**************************************************************\r\n"
        " * HTTPPrint.h\r\n"
        " * Provides callback headers and resolution for user\'s custom\r\n"
        " * HTTP Application.\r\n"
        " * \r\n"
        " * This file is automatically generated by the MPFS Utility\r\n"
        " * ALL MODIFICATIONS WILL BE OVERWRITTEN BY THE MPFS GENERATOR\r\n"
        " **************************************************************/\r\n\r\n"
        "#ifndef __HTTPPRINT_H\r\n"
        "#define __HTTPPRINT_H\r\n\r\n"
        "#include \"TCPIP Stack/TCPIP.h\"\r\n\r\n"
        "#if defined(STACK_USE_HTTP2_SERVER)\r\n\r\n"
        "extern HTTP_STUB httpStubs[MAX_HTTP_CONNECTIONS];\r\n"
        "extern BYTE curHTTPID;\r\n\r\n"
        "void HTTPPrint(DWORD callbackID);\r\n";
const char* DynamicVariableParser::HTTPPRINT_H_SUBSTSTART =
        "\r\nvoid HTTPPrint(DWORD callbackID)\r\n"
        "{\r\n"
        "	switch(callbackID)\r\n"
        "	{\r\n";
const char* DynamicVariableParser::HTTPPRINT_H_SUBSTEND =
        "		default:\r\n"
        "			// Output notification for undefined values\r\n"
        "			TCPPutROMArray(sktHTTP, (ROM BYTE*)\"!DEF\", 4);\r\n"
        "	}\r\n\r\n"
        "}\r\n";
const char* DynamicVariableParser::HTTPPRINT_H_QUERYIFSTART =
        "\r\nBOOL HTTPQueryIf(DWORD callbackID)\r\n"
        "{\r\n"
        "	switch(callbackID)\r\n"
        "	{\r\n";
const char* DynamicVariableParser::HTTPPRINT_H_QUERYIFEND =
        "		default:\r\n"
        "			break;\r\n"
        "	}\r\n\r\n"
        "	// Output notification for undefined values\r\n"
        "	TCPPutROMArray(sktHTTP, (ROM BYTE*)\"!DEFQI\", 6);\r\n"
        "	return FALSE;\r\n"
        "}\r\n";
const char* DynamicVariableParser::HTTPPRINT_H_QUERYWHILESTART =
        "\r\nBOOL HTTPQueryWhile(DWORD callbackID,BOOL firstCall)\r\n"
        "{\r\n"
        "	switch(callbackID)\r\n"
        "	{\r\n";
const char* DynamicVariableParser::HTTPPRINT_H_QUERYWHILEEND =
        "		default:\r\n"
        "			break;\r\n"
        "	}\r\n\r\n"
        "	// Output notification for undefined values\r\n"
        "	TCPPutROMArray(sktHTTP, (ROM BYTE*)\"!DEFQW\", 6);\r\n"
        "	return FALSE;\r\n"
        "}\r\n";
const char* DynamicVariableParser::HTTPPRINT_H_CONTENTTYPESTART =
        "\r\nROM char *HTTPQueryContentType(DWORD ctId)\r\n"
        "{\r\n"
        "	switch(ctId)\r\n"
        "	{\r\n";
const char* DynamicVariableParser::HTTPPRINT_H_CONTENTTYPEEND =
        "		default:\r\n"
        "			break;\r\n"
        "	}\r\n\r\n"
        "	return NULL;\r\n"
        "}\r\n";
const char* DynamicVariableParser::HTTPPRINT_H_FOOTER =
        "\r\n"
        "#endif\r\n\r\n#endif\r\n";

DynamicVariableParser::DynamicVariableParser()
{
	m_pUI = NULL;
}

void DynamicVariableParser::Init(IDynamicVariableParserUI *pUI,const xbString &_projectDir,const xbString &_sourceDir)
{
	m_pUI = pUI;
    projectDir = _projectDir;
	sourceDir = _sourceDir;
	bRequireMPFS2m = false;
	bool bHttpPrintIdxLoaded = false;

	// Read previous index file if it exists.
    if (xbFileExists(projectDir + "HTTPPrint.idx"))
    {
        xbFileStream fin(projectDir + "HTTPPrint.idx",xbOpen,xbAccessR);
		fin.ReadTextBOM();
		xbString s;
		bool bOk = fin.ReadStringA(&s,NULL) != 0;
        // Ignore old MPFS2.0 HTTPPrint.idx files
        if (bOk && s.IndexOf('|')<0)
        {
			bHttpPrintIdxLoaded = true;
			while (bOk)
            {
				DynamicVariable *dv = xbDnew DynamicVariable(s);
				vars.Add(dv);
				bOk = fin.ReadStringA(&s,NULL) != 0;
				if (dv->varType != MPFSvarType_ForSubst)
					bRequireMPFS2m = true;
            }
        }
    }

	m_pUI->dynaVarStart(bHttpPrintIdxLoaded);
}

MPFSFileRecord *DynamicVariableParser::MPFSparse(MPFSFileRecord &file)
{
	if (bRequireMPFS2m)
		eMpfs2::Throw("MPFS2m is required for this project, HTTPPrint.idx contains query vars.\n"
			"If you want to use MPFS2 file format, delete HTTPPrint.idx");

	xbData idxData;
	FileLoc fileloc(file.origFileName,1,0);
	FileLoc tokenloc;
	xbString val;
	const byte *pDataBegin = file.data.Begin();
	const byte *pData = pDataBegin;
	const byte *pDataEnd = file.data.End();
	const byte *pDataTokenStart = NULL;
	eCtxBegin();
	while(MPFStokenParse(&pData,pDataEnd,&pDataTokenStart,&val,&fileloc,&tokenloc,MPFStokenVersion_2))
	{
        int i = UseVar(val,tokenloc,MPFSvarType_ForSubst);
		if (i < 0)
			continue; //Changes rejected by user
		
		uint32 fpos = (uint32)(xbNptr(pDataTokenStart) - xbNptr(pDataBegin));
		int idxlg = idxData.Length;
		byte *pIdxData = (byte*)idxData.GetWritePtr(idxlg+8)+idxlg;
		*(pIdxData++) = (byte)fpos;
        *(pIdxData++) = (byte)(fpos >> 8);
        *(pIdxData++) = (byte)(fpos >> 16);
        *(pIdxData++) = (byte)(fpos >> 24);
        *(pIdxData++) = (byte)i;
        *(pIdxData++) = (byte)(i >> 8);
        *(pIdxData++) = (byte)(i >> 16);
        *(pIdxData++) = (byte)(i >> 24);
    }
	//21/08/2015:correcs from Abram Dancy: integration in MPLAB for errors output
	eCtxEndNoLoc(fileloc.locationStr());		

	// Determine if any matches were made
	if (idxData.IsEmpty())
        return NULL;

	// Set up new file record
    MPFSFileRecord *idxFile = xbDnew MPFSFileRecord();
    idxFile->FileName = "";
    idxFile->fileDate = file.fileDate;
    idxFile->isIndex = true;
    idxFile->data = idxData;
    return idxFile;
}

#define MPFS2m_VARSUBST			0x80
#define MPFS2m_DIE				0x81
#define MPFS2m_TILDE			0x82
#define MPFS2m_VOID				0x83
#define MPFS2m_IF				0x84
#define MPFS2m_WHILEFIRST		0x85
#define MPFS2m_WHILENEXT		0x86
#define MPFS2m_JUMPFWD			0x87
//#define MPFS2m_JUMPBCK			0x88
void DynamicVariableParser::MPFS2mParse(MPFSFileRecord &file)
{
	xbData outData;
	FileLoc fileloc(file.origFileName,1,0);
	FileLoc tokenloc;
	xbString val;
	const byte *pDataBegin = file.data.Begin();
	const byte *pData = pDataBegin;
	const byte *pDataEnd = file.data.End();
	const byte *pDataTokenStart = NULL;
	const byte *pDataWr = pDataBegin;
	eCtxBegin();
	int idxlg;
	byte *pIdxData;
	while(MPFStokenParse(&pData,pDataEnd,&pDataTokenStart,&val,&fileloc,&tokenloc,MPFStokenVersion_2m))
	{
		if (pDataTokenStart > pDataWr)
		{
			MPFS2mOutDataWr(&outData,pDataWr,(uint32)(xbNptr(pDataTokenStart) - xbNptr(pDataWr)));
			pDataWr = pDataTokenStart;
		}

		if (val.IsEmpty())
		{
			idxlg = outData.Length;
			pIdxData = (byte*)outData.GetWritePtr(idxlg+2)+idxlg;
			*(pIdxData++) = (byte)0;
			*(pIdxData++) = (byte)MPFS2m_TILDE;
			pDataWr = pData;
			continue;
		}
		if (val == "void")
		{
			idxlg = outData.Length;
			pIdxData = (byte*)outData.GetWritePtr(idxlg+2)+idxlg;
			*(pIdxData++) = (byte)0;
			*(pIdxData++) = (byte)MPFS2m_VOID;
			pDataWr = pData;
			continue;
		}
		if (val == "die")
		{
			idxlg = outData.Length;
			pIdxData = (byte*)outData.GetWritePtr(idxlg+2)+idxlg;
			*(pIdxData++) = (byte)0;
			*(pIdxData++) = (byte)MPFS2m_DIE;
			pDataWr = pData;
			continue;
		}
		if (val == "endif" || val == "wend")
		{
			condPop(val,tokenloc,&outData);
			pDataWr = pData;
			continue;
		}
		if (val == "else")
		{
			condElse(tokenloc,&outData);
			pDataWr = pData;
			continue;
		}
		if (val.StartWith("if "))
		{
			val = val.Mid(3);
			condPush("if",val,tokenloc,&outData);
			int i = UseVar(val,tokenloc,MPFSvarType_ForQueryIf);
			if (i < 0)
				continue; //Changes rejected by user

			pDataWr = pData;
			idxlg = outData.Length;
			pIdxData = (byte*)outData.GetWritePtr(idxlg+8)+idxlg;
			*(pIdxData++) = (byte)i;
			*(pIdxData++) = (byte)MPFS2m_IF;
			*(pIdxData++) = (byte)(i >> 8);
			*(pIdxData++) = (byte)(i >> 16);
			*(pIdxData++) = (byte)0xFF;
			*(pIdxData++) = (byte)0xEE;
			*(pIdxData++) = (byte)0xDD;
			*(pIdxData++) = (byte)0xCC;
			condIfNotJumpToSolveAt(idxlg+4);
			continue;
		}
		if (val.StartWith("while "))
		{
			val = val.Mid(6);
			condPush("while",val,tokenloc,&outData);
			int i = UseVar(val,tokenloc,MPFSvarType_ForQueryWhile);
			if (i < 0)
				continue; //Changes rejected by user

			pDataWr = pData;
			idxlg = outData.Length;
			pIdxData = (byte*)outData.GetWritePtr(idxlg+8)+idxlg;
			*(pIdxData++) = (byte)i;
			*(pIdxData++) = (byte)MPFS2m_WHILEFIRST;
			*(pIdxData++) = (byte)(i >> 8);
			*(pIdxData++) = (byte)(i >> 16);
			*(pIdxData++) = (byte)0xFF;
			*(pIdxData++) = (byte)0xEE;
			*(pIdxData++) = (byte)0xDD;
			*(pIdxData++) = (byte)0xCC;
			condIfNotJumpToSolveAt(idxlg+4);
			continue;
		}
		if (val.StartWith("elseif "))
		{
			val = val.Mid(7);
			condElseIf(val,tokenloc,&outData);
			int i = UseVar(val,tokenloc,MPFSvarType_ForQueryIf);
			if (i < 0)
				continue; //Changes rejected by user

			pDataWr = pData;
			idxlg = outData.Length;
			pIdxData = (byte*)outData.GetWritePtr(idxlg+8)+idxlg;
			*(pIdxData++) = (byte)i;
			*(pIdxData++) = (byte)MPFS2m_IF;
			*(pIdxData++) = (byte)(i >> 8);
			*(pIdxData++) = (byte)(i >> 16);
			*(pIdxData++) = (byte)0xFF;
			*(pIdxData++) = (byte)0xEE;
			*(pIdxData++) = (byte)0xDD;
			*(pIdxData++) = (byte)0xCC;
			condIfNotJumpToSolveAt(idxlg+4);
			continue;
		}

        int i = UseVar(val,tokenloc,MPFSvarType_ForSubst);
		if (i < 0)
			continue; //Changes rejected by user

		if (i > 0xFFFFFF)
			eMpfs2::Throw("Too many dynamic variables");

		pDataWr = pData;
		idxlg = outData.Length;
		pIdxData = (byte*)outData.GetWritePtr(idxlg+4)+idxlg;
        *(pIdxData++) = (byte)i;
        *(pIdxData++) = (byte)MPFS2m_VARSUBST;
        *(pIdxData++) = (byte)(i >> 8);
        *(pIdxData++) = (byte)(i >> 16);
        //*(pIdxData++) = (byte)(i >> 24);
	
    }
	//21/08/2015:correcs from Abram Dancy: integration in MPLAB for errors output
	eCtxEndNoLoc(fileloc.locationStr());
	condEnd(&outData);
	

	// Determine if any matches were made
	if (outData.IsEmpty())
        return;

	if (pDataEnd > pDataWr)
		MPFS2mOutDataWr(&outData,pDataWr,(uint32)(xbNptr(pDataEnd) - xbNptr(pDataWr)));

	file.data = outData;
	file.hasIndex = true;
}

void DynamicVariableParser::MPFS2mOutDataWr(xbData *poutData,const byte *pDataWr,uint32 lg)
{
	while(lg>0)
	{
		uint32 wrLg = min(lg,0x7FFF);

		int idxlg = poutData->Length;
		byte *pIdxData = (byte*)poutData->GetWritePtr(idxlg+2+wrLg)+idxlg;
        *(pIdxData++) = (byte)wrLg;
        *(pIdxData++) = (byte)(wrLg >> 8);
		memcpy(pIdxData,pDataWr,wrLg);
		
		pDataWr += wrLg;
		lg -= wrLg;
	}
}

bool DynamicVariableParser::MPFStokenParse(const byte **ppData,const byte *pDataEnd,
	const byte **pDataTokenStart,xbString *pToken,FileLoc *pFileLoc,FileLoc *pTokenLoc,MPFStokenVersion tokVer)
{
	//21/08/2015:correcs from Abram Dancy: support of macros with parenthesis in tokens arguments
	const byte *pData = *ppData;
	bool bInToken = false, bInArgStr = false, bIncludeCmd = false, bArgStart = false;
	int parenthesisLevel = 0;
	xbString tok;
	while(pData < pDataEnd)
	{
		byte ch = *(pData++);
		if (ch == '\r')
			continue;
		if (ch == '\t')
			pFileLoc->m_col = (pFileLoc->m_col + 1 + m_pUI->getTabSize() - 1)/m_pUI->getTabSize()*m_pUI->getTabSize();
		else
			pFileLoc->m_col++;
		if (!bInToken)
		{
			if (ch == '\n')
			{
				pFileLoc->m_line++;
				pFileLoc->m_col=0;
				continue;
			}
			if (ch != '~')
				continue;
			bInToken = true;
			*pDataTokenStart = pData-1;
			*pTokenLoc = *pFileLoc;
			pTokenLoc->m_col++;//To point after ~
			continue;
		}
		if (bInArgStr)
		{
			if (ch == '\n')
				eMpfs2::Throw("Unexpected line feed in string argument of a token ~...~, string or token is probably not closed (end ~ or \" missing)");
			tok.Add((char)ch);
			if (ch == '\\')
			{
				if (pData >= pDataEnd)
					eMpfs2::Throw("Unexpected \\ in string argument of a token ~...~");
				ch = *(pData++);
				if (ch<=32)
					eMpfs2::Throw("Unexpected \\ in string argument of a token ~...~");
				tok.Add((char)ch);
				continue;
			}
			if (ch != '"')
			{
				if (ch<32)
					eMpfs2::ThrowV("Unexpected character(%d) in string argument of a token ~...~",ch);
				continue;
			}
			bInArgStr = false;
			bArgStart = false;
			continue;
		}
		if (ch == '~')
		{
			if (parenthesisLevel>0)
				eMpfs2::Throw("In token ~...~, sub call with missing end parenthesis ')'");
			*ppData = pData;
			if (bIncludeCmd)
				tok.Trim();
			else 
				tok.TrimRight();
			*pToken = tok;
			MPFStokenVerify(tok,tokVer);
			return true;
		}

		if (parenthesisLevel>0)
		{
			if (ch == ')')
			{
				tok.TrimRight();
				tok.Add((char)ch);
				if ((--parenthesisLevel) == 0)
					parenthesisLevel=-1;//It's the end of token arguments, token must be terminated, only spaces allowed at this step
				bArgStart = true;
				continue;
			}
			if (ch == '"')
			{
				tok.Add((char)ch);
				bInArgStr = true;
				continue;
			}
			if (ch == ',')
			{
				tok.TrimRight();
				tok.Add((char)ch);
				bArgStart = true;
				continue;
			}

			if (bArgStart)
			{
				if (ch == '\n')
				{
					pFileLoc->m_line++;
					pFileLoc->m_col=0;
					continue;
				}
				if (::xbIsSpace(ch))
					continue;
				bArgStart = false;
			}
			else
			{
				if (ch == '\n')
					eMpfs2::Throw("Unexpected line feed in token ~...~, it's probably not closed (end ~ missing)");
			}
		}
		else
		{
			if (ch == '\n')
				eMpfs2::Throw("Unexpected line feed in token ~...~, it's probably not closed (end ~ missing)");

			if (bIncludeCmd)
			{
				if (tok.Length <= 4 && ::xbIsSpace(ch))
					continue;
				tok.Add((char)ch);
				continue;
			}

			if (tok.IsEmpty()
				&& ::xbIsSpace(ch))
				continue;
		}
		if (parenthesisLevel<0)
		{
			//It's the end of token arguments, token must be terminated, only spaces allowed at this step
			if (::xbIsSpace(ch))
				continue;
			if (ch<32)
				eMpfs2::ThrowV("Unexpected character(%d) in string argument of a token ~...~",ch);
			eMpfs2::ThrowV("Unexpected character('%c'/%d) in string argument of a token ~...~",ch,ch);
		}
		if (ch == '(')
		{
			tok.Add((char)ch);
			parenthesisLevel++;
			bArgStart = true;
			continue;
		}
		if (parenthesisLevel)
		{
			if (ch<32)
				eMpfs2::ThrowV("Unexpected character(%d) in string argument of a token ~...~",ch);
			tok.Add((char)ch);
			continue;
		}

		if ((ch<'a' || ch>'z') && (ch<'A' || ch>'Z') && (ch<'0' || ch>'9') && ch != '_')
		{
			if (ch == ':' && tok == "inc")
			{
				tok.Add((char)ch);
				bIncludeCmd = true;
				continue;
			}
			if (tokVer == MPFStokenVersion_2m
				&& ch == ' ' && (tok == "if" || tok == "while" || tok == "elseif"))
			{
				tok.Add((char)ch);
				continue;
			}
			if (ch<32)
				eMpfs2::ThrowV("Unexpected character(%d) in string argument of a token ~...~",ch);
			eMpfs2::ThrowV("Unexpected character('%c'/%d) in string argument of a token ~...~",ch,ch);
		}
		tok.Add((char)ch);
	}

	if (bInToken)
		eMpfs2::Throw("Token ~...~ not closed, end ~ missing");
	return false;
}

void DynamicVariableParser::MPFStokenVerify(const xbString &tok,MPFStokenVersion tokVer)
{
	if(tok.StartWith("inc:"))
	{
		xbString sfile(tok.Mid(4));
		if (!xbFileExists(sourceDir+"\\"+sfile))
			eMpfs2::Throw("Include "+sfile+" not found !");
		return;
	}
}

enum DynamicSubArgType
{
	DynamicSubArg_WORD,
	DynamicSubArg_STR
};

class DynamicSubProto:public xbBase
{
public:
	DynamicSubProto()
	{
	}
	void Parse(MPFSvarType vtype,const xbString &subCall,const xbString &fileLocs)
	{
		varType = vtype;
		subName.Empty();
		argType.Empty();
		fileLocations = fileLocs;
		if (varType == MPFSvarType_ForContentType)
		{
			subName = subCall;
			return;
		}
		int pos = subCall.IndexOf('(');
		if (pos < 0)
		{
			subName = subCall;
			return;
		}
		bool bArgStart = true,bInStr = false;
		subName = subCall.Left(pos);
		//21/08/2015:correcs from Abram Dancy: support of macros with parenthesis in tokens arguments
		int parenthesisLevel = 0;
		const char *ptr = subCall.Begin() + pos+1;
		const char *pend = subCall.End();
		while(ptr < pend)
		{
			char ch = *(ptr++);
			if (bInStr)
			{
				if (ch != '"')
					continue;

				if (ch == '\\')
				{
					ptr++;
					continue;
				}

				bInStr = false;
				continue;
			}
			if (ch == '(')
			{
				parenthesisLevel++;
				continue;
			}
			if (parenthesisLevel>0)
			{
				if (ch == ')')
				{
					parenthesisLevel--;
					continue;
				}
				continue;
			}

			if (bArgStart)
			{
				bArgStart = false;
				if (ch == '"')
				{
					argType.Add(DynamicSubArg_STR);
					bInStr = true;
					continue;
				}

				argType.Add(DynamicSubArg_WORD);
				if (ch == ',' || ch == ')')
					eMpfs2::Throw("Token ~"+subCall+"~ invalid: call with missing arguments at following locations:\n"+fileLocs);
				continue;
			}

			if (ch == ',')
			{
				bArgStart = true;
				continue;
			}
		}
	}

	bool FindIn(IDynamicVariableParserUI *pUI,const xbPtrArray<DynamicSubProto> &arrSubProto)
	{
		DynamicSubProto *const *ppDv = arrSubProto.Begin();
		DynamicSubProto *const *ppDvEnd = arrSubProto.End();
		while(ppDv < ppDvEnd)
		{
			if ((*ppDv)->Match(pUI,this))
				return true;
			ppDv++;
		}
		return false;
	}
	bool Match(IDynamicVariableParserUI *pUI,DynamicSubProto *pProto)
	{
		if (varType != pProto->varType || subName != pProto->subName)
			return false;

		if (argType.Length != pProto->argType.Length)
			eMpfs2::ThrowV("Token ~...~ invalid: call made to '%s' with different arguments count:\n"
			"Last call found with prototype: %s at following locations:\n%s\n"
			"First call found with prototype: %s at following locations:\n%s",
			subName.Ptr(),
			pProto->GetLabel(pUI).Ptr(),pProto->fileLocations.Ptr(),
			GetLabel(pUI).Ptr(),fileLocations.Ptr());

		for(int i = 0; i < argType.Length;i++)
		{
			if (argType.GetAt(i) != pProto->argType.GetAt(i))
				eMpfs2::ThrowV("Token ~...~ invalid: call made to '%s' with different arguments types:\n"
				"Last call found with prototype: %s at following locations:\n%s\n"
				"First call found with prototype: %s at following locations:\n%s",
				subName.Ptr(),
				pProto->GetLabel(pUI).Ptr(),pProto->fileLocations.Ptr(),
				GetLabel(pUI).Ptr(),fileLocations.Ptr());
		}

		fileLocations += "\n"+pProto->fileLocations;

		return true;
	}

	xbString GetLabel(IDynamicVariableParserUI *pUI) const
	{
		if (varType == MPFSvarType_ForContentType)
			return subName;
		xbString ret(subName + "(");
		if(argType.Length > 0)
		{
			for (int i = 0; i < argType.Length; i++)
			{
				if (i>0)
					ret += ',';
				switch(argType.GetAt(i))
				{
				case DynamicSubArg_STR:
					if (pUI)
						ret+=pUI->HTTPPrint_TokenArgType_String();
					else
						ret+="S";
					break;
				case DynamicSubArg_WORD:
				default:
					if (pUI)
						ret+=pUI->HTTPPrint_TokenArgType_Number();
					else
						ret+="W";
					break;
				}
			}
			if (varType == MPFSvarType_ForQueryWhile)
				ret+=",BOOL firstCall";
		}
		else
		{
			if (varType == MPFSvarType_ForQueryWhile)
				ret+="BOOL firstCall";
			else
				ret+="void";
		}
		ret +=")";
		return ret;
	}
	MPFSvarType					varType;
	xbString					subName;
	xbArray<DynamicSubArgType>	argType;
	xbString					fileLocations;
};

bool DynamicVariableParser::WriteIndices(MPFStokenVersion tokVer)
{
    // Determine if an update is necessary
    bool isChanged = false;
	DynamicVariable **ppDv = vars.Begin();
	DynamicVariable **ppDvEnd = vars.End();
	while(ppDv < ppDvEnd)
	{
		const DynamicVariable *pDv = *(ppDv++);
		if (pDv->varType != MPFSvarType_ForSubst)
		{
			if (tokVer != MPFStokenVersion_2m)
				eMpfs2::Throw("MPFS2m is required for this project, HTTPPrint.idx contains query vars.\n"
					"If you want to use MPFS2 file format, delete HTTPPrint.idx");
			if (pDv->varType != MPFSvarType_ForQueryIf 
				&& pDv->varType != MPFSvarType_ForQueryWhile
				&& pDv->varType != MPFSvarType_ForContentType)
				eMpfs2::Throw("Unsupported variable type");
		}
		if ((pDv->WasUsed && pDv->Count == 0)
			|| (!pDv->WasUsed && pDv->Count != 0))
		{
			//But ask before:
			if (pDv->WasUsed && pDv->Count == 0
				&& !m_pUI->confirmDynaVarDel(pDv->Name,pDv->varType))
				continue;

			isChanged = true;
			break;
		}
	}

	if (!isChanged)
        return false;

    // Begin writing HTTPPrint.h
	{
		xbFileStream fout(projectDir + "HTTPPrint.h",xbOpenAlways,xbAccessW);
		fout.WriteTextBOM(xbBOM_none);
		fout.WriteString(HTTPPRINT_H_HEADER);
		xbString hdrAdd(m_pUI->HTTPPrint_getHeaderAdd());
		if (hdrAdd.IsValid())
			fout.WriteString(hdrAdd);

		// Write the prototypes
		ppDv = vars.Begin();
		DynamicSubProto *pSubProto = xbDnew DynamicSubProto();
		xbPtrArray<DynamicSubProto> arrSubProto;
		try
		{
			while(ppDv < ppDvEnd)
			{
				const DynamicVariable *pDv = *(ppDv++);
				if (pDv->Name.StartWith("inc:") || pDv->Count == 0)
					continue;

				//eCtxBegin();
				pSubProto->Parse(pDv->varType,pDv->Name,pDv->fileLocations);

				if (pSubProto->FindIn(m_pUI,arrSubProto))
					continue;

				// Add the prototype
				switch(pDv->varType)
				{
				case MPFSvarType_ForQueryIf:
					fout.WriteString("BOOL HTTPQueryIf_" + pSubProto->GetLabel(m_pUI)+";\r\n");
					break;
				case MPFSvarType_ForQueryWhile:
					fout.WriteString("BOOL HTTPQueryWhile_" + pSubProto->GetLabel(m_pUI)+";\r\n");
					break;
				case MPFSvarType_ForContentType:
					break;
				default:
					fout.WriteString("void HTTPPrint_" + pSubProto->GetLabel(m_pUI)+";\r\n");
					break;
				}
				//eCtxEndNoLoc("Token "+pDv->Name+" used in file(s):\n"+pDv->fileLocations);
				arrSubProto.Add(pSubProto);
				pSubProto = xbDnew DynamicSubProto();
			}
		}
		catch(...)
		{
			delete pSubProto;
			throw;
		}
		delete pSubProto;

		// Write the function itself (the big switch)
		fout.WriteString(HTTPPRINT_H_SUBSTSTART);
		ppDv = vars.Begin();
		while(ppDv < ppDvEnd)
		{
			const DynamicVariable *pDv = *(ppDv++);
			if (pDv->Count == 0 || pDv->varType != MPFSvarType_ForSubst)
				continue;

			if (pDv->Index < 0)
				eMpfs2::Throw("DynamicVariableParser::WriteIndices: internal error, var index < 0");

			fout.WriteString("\t\tcase 0x" + xbString(pDv->Index,xbStringNum_hexa).RetPadLeft(8, '0') + ":\r\n");
			        
			// Write the actual case statement
			if(pDv->Name.IsEmpty())
			{ //For the case ~~ (empty token -> replace ~~ by ~)
				fout.WriteString("\t\t\tTCPPutROMString(sktHTTP, (ROM BYTE *)\"~\");\r\n"
					"\t\t\tbreak;\r\n");
				continue;
			}
			if(pDv->Name.StartWith("inc:"))
			{
				xbString sfile(pDv->Name.Mid(4));
				if (!xbFileExists(sourceDir+"\\"+sfile))
					eMpfs2::Throw("Include "+sfile+" not found ! It is used in:\n"+pDv->fileLocations);

				fout.WriteString("\t\t\tHTTPIncFile((ROM BYTE*)\"" + sfile + "\");\r\n"
					"\t\t\tbreak;\r\n");
				continue;
			}
			fout.WriteString("\t\t\tHTTPPrint_" + pDv->Name);
			if(!pDv->Name.EndWith(")"))
				fout.WriteString("()");
			fout.WriteString(";\r\n"
				"\t\t\tbreak;\r\n");
		}
		fout.WriteString(HTTPPRINT_H_SUBSTEND);
		if (tokVer == MPFStokenVersion_2m)
		{
			// Write the big switch for if queries
			fout.WriteString(HTTPPRINT_H_QUERYIFSTART);
			ppDv = vars.Begin();
			while(ppDv < ppDvEnd)
			{
				const DynamicVariable *pDv = *(ppDv++);
				if (pDv->Count == 0 || pDv->varType != MPFSvarType_ForQueryIf)
					continue;

				if (pDv->Index < 0)
					eMpfs2::Throw("DynamicVariableParser::WriteIndices: internal error, var index < 0");

				fout.WriteString("\t\tcase 0x" + xbString(pDv->Index,xbStringNum_hexa).RetPadLeft(8, '0') + ":\r\n");
				
				// Write the actual case statement
				fout.WriteString("\t\t\treturn HTTPQueryIf_" + pDv->Name);
				if(!pDv->Name.EndWith(")"))
					fout.WriteString("()");
				fout.WriteString(";\r\n");
			}
			fout.WriteString(HTTPPRINT_H_QUERYIFEND);

			// Write the big switch for while queries
			fout.WriteString(HTTPPRINT_H_QUERYWHILESTART);
			ppDv = vars.Begin();
			while(ppDv < ppDvEnd)
			{
				const DynamicVariable *pDv = *(ppDv++);
				if (pDv->Count == 0 || pDv->varType != MPFSvarType_ForQueryWhile)
					continue;

				if (pDv->Index < 0)
					eMpfs2::Throw("DynamicVariableParser::WriteIndices: internal error, var index < 0");

				fout.WriteString("\t\tcase 0x" + xbString(pDv->Index,xbStringNum_hexa).RetPadLeft(8, '0') + ":\r\n");
				
				// Write the actual case statement
				xbString scall(pDv->Name);
				if(!scall.EndWith(")"))
					scall += "(firstCall)";
				else
					scall = scall.Left(scall.Length-1)+",firstCall)";
				fout.WriteString("\t\t\treturn HTTPQueryWhile_" + scall+";\r\n");
			}
			fout.WriteString(HTTPPRINT_H_QUERYWHILEEND);

			// Write the big switch for content types queries
			fout.WriteString(HTTPPRINT_H_CONTENTTYPESTART);
			ppDv = vars.Begin();
			while(ppDv < ppDvEnd)
			{
				const DynamicVariable *pDv = *(ppDv++);
				if (pDv->Count == 0 || pDv->varType != MPFSvarType_ForContentType)
					continue;

				if (pDv->Index < 0)
					eMpfs2::Throw("DynamicVariableParser::WriteIndices: internal error, var index < 0");

				fout.WriteString("\t\tcase 0x" + xbString(pDv->Index,xbStringNum_hexa).RetPadLeft(8, '0') + ":\r\n");
				
				// Write the actual case statement
				fout.WriteString("\t\t\treturn (ROM char *)\"" + xbEscape(pDv->Name) +"\";\r\n");
			}
			fout.WriteString(HTTPPRINT_H_CONTENTTYPEEND);
		}

		// Write the footer part
		fout.WriteString(HTTPPRINT_H_FOOTER);

		fout.SetEof();
	}
    
    // Write out HTTPPrint.idx
	{
		xbFileStream fout(projectDir + "HTTPPrint.idx",xbOpenAlways,xbAccessW);
		fout.WriteTextBOM(xbBOM_none);
		ppDv = vars.Begin();
		while(ppDv < ppDvEnd)
		{
			const DynamicVariable *pDv = *(ppDv++);
			switch(pDv->varType)
			{
			case MPFSvarType_ForQueryIf:
				fout.WriteString("?I?");
				break;
			case MPFSvarType_ForQueryWhile:
				fout.WriteString("?W?");
				break;
			case MPFSvarType_ForContentType:
				fout.WriteString("?C?");
				break;
			}
			if (pDv->Count != 0)
				fout.WriteChar('+');
			fout.WriteString(pDv->Name,true);
		}
		fout.SetEof();
	}

    return true;
}

int DynamicVariableParser::UseVar(const xbString &name,const FileLoc &fileLoc, MPFSvarType varType)
{
	// Search for the dynamic variable
	int idx = -1;
	DynamicVariable **ppDv = vars.Begin();
	DynamicVariable **ppDvEnd = vars.End();
	while(ppDv < ppDvEnd)
	{
		DynamicVariable *pDv = *(ppDv++);
		if (pDv->varType != varType)
			continue;
		idx++;
		if (pDv->Name != name)
			continue;
		if (pDv->Index < 0)
			pDv->Index = idx;
		else
		{
			if (pDv->Index != idx)
				eMpfs2::Throw("DynamicVariableParser::UseVar: internal error, var index invalid");
		}

		// Mark as used and return the index
		pDv->AddFileLocation(fileLoc);
		return idx;
	}

    // If not found, add a new one
	//But ask before:
	if (!m_pUI->confirmDynaVarAdd(name,varType,fileLoc))
		return -1;

	idx++;
	DynamicVariable *pDvNew = xbDnew DynamicVariable(name);
	pDvNew->varType = varType;
	pDvNew->Index = idx;
	vars.Add(pDvNew);

	// Mark as used and return the index
	pDvNew->AddFileLocation(fileLoc);
	return idx;
}

int DynamicVariableParser::GetVarIndex(const xbString &name, MPFSvarType varType)
{
	// Search for the dynamic variable
	int idx = -1;
	DynamicVariable **ppDv = vars.Begin();
	DynamicVariable **ppDvEnd = vars.End();
	while(ppDv < ppDvEnd)
	{
		DynamicVariable *pDv = *(ppDv++);
		if (pDv->varType != varType)
			continue;
		idx++;
		if (pDv->Name != name)
			continue;
		if (pDv->Index < 0)
			eMpfs2::Throw("DynamicVariableParser::GetVarIndex: internal error, var index invalid");
		if (pDv->Index != idx)
			eMpfs2::Throw("DynamicVariableParser::GetVarIndex: internal error, var index invalid");

		return idx;
	}
	
	eMpfs2::Throw("DynamicVariableParser::GetVarIndex: internal error, var not found");
	return -1;
}

void DynamicVariableParser::condPush(const xbString &stype,const xbString &squery,const FileLoc &fileLoc,xbData *pOutData)
{
	int idx = m_condLevel.IncLength();
	condLevel &lvl = m_condLevel.GetAt(idx);
	lvl.m_stype = stype;
	lvl.m_fileLoc = fileLoc;
	lvl.m_squery = squery;
	if (stype == "if")
	{
		lvl.m_bElseIfOk = true;
		lvl.m_bElseOk = true;
	}
	else
	{
		//while
		if (stype != "while")
			eMpfs2::Throw("condPush: Unsupported statement ("+stype+")");
		lvl.m_dataOutStart = pOutData->Length+8;//+8 to point just after WhileFirst opCode
	}
}

void DynamicVariableParser::condElseIf(const xbString &squery,const FileLoc &fileLoc,xbData *pOutData)
{
	if (m_condLevel.IsEmpty())
		eMpfs2::Throw("Unexpected elseif statement !\nIt must be placed after an if or an other elseif");
	condLevel &lvl = m_condLevel.Last();
	if (!lvl.m_bElseIfOk)
		eMpfs2::Throw("Unexpected elseif statement !\nIt must be placed after an if or an other elseif");
	condJumpToEndToSolve(lvl,pOutData);
	lvl.m_fileLoc = fileLoc;
	lvl.m_squery = squery;
	condIfNotJumpSolveHere(lvl,pOutData);
}

void DynamicVariableParser::condElse(const FileLoc &fileLoc,xbData *pOutData)
{
	if (m_condLevel.IsEmpty())
		eMpfs2::Throw("Unexpected else statement !\nIt must be placed after an if or an elseif");
	condLevel &lvl = m_condLevel.Last();
	if (!lvl.m_bElseOk)
		eMpfs2::Throw("Unexpected else statement !\nIt must be placed after an if or an elseif");
	lvl.m_bElseIfOk = false;
	lvl.m_bElseOk = false;
	condJumpToEndToSolve(lvl,pOutData);
	lvl.m_fileLoc = fileLoc;
	condIfNotJumpSolveHere(lvl,pOutData);
}

void DynamicVariableParser::condPop(const xbString &stype,const FileLoc &fileLoc,xbData *pOutData)
{
	if (m_condLevel.IsEmpty())
		eMpfs2::Throw("Unexpected "+stype+" statement !");
	condLevel &lvl = m_condLevel.Last();
	if (lvl.m_stype == "if")
	{
		if (stype != "endif")
			eMpfs2::Throw("Unexpected "+stype+" statement !");
	}
	else
	{
		if (stype != "wend")
			eMpfs2::Throw("Unexpected "+stype+" statement !");
		//condJumpBackFromHere(lvl.m_dataOutStart,pOutData); //loop -> come back to test
		int varIdx = GetVarIndex(lvl.m_squery,MPFSvarType_ForQueryWhile);
		int idxlg = pOutData->Length;
		int step = idxlg - lvl.m_dataOutStart;
		if (step < 0)
			eMpfs2::Throw("Internal error in conditional statements, invalid jump address for while next","DynamicVariableParser::condPop");
		byte *pIdxData = (byte*)pOutData->GetWritePtr(idxlg+8)+idxlg;
		*(pIdxData++) = (byte)varIdx;
		*(pIdxData++) = (byte)MPFS2m_WHILENEXT;
		*(pIdxData++) = (byte)(varIdx >> 8);
		*(pIdxData++) = (byte)(varIdx >> 16);
		*(pIdxData++) = (byte)step;
		*(pIdxData++) = (byte)(step >> 8);
		*(pIdxData++) = (byte)(step >> 16);
		*(pIdxData++) = (byte)(step >> 24);
	}
	condIfNotJumpSolveHere(lvl,pOutData);
	condJumpToEndSolveHere(lvl,pOutData);

	m_condLevel.Length = m_condLevel.Length - 1;
}

void DynamicVariableParser::condEnd(xbData *pOutData)
{
	xbString serr;
	condLevel *pLvl = m_condLevel.End();
	condLevel *pLvlBegin = m_condLevel.Begin();
	while(pLvl > pLvlBegin)
	{
		pLvl--;
		if (serr.IsValid())
			serr += "\n";
//		serr += "- "+pLvl->m_fileName+"("+xbString(pLvl->m_fileLine)+"): "+pLvl->m_stype+" not closed";
		serr += pLvl->m_fileLoc.locationStr(pLvl->m_stype+" not closed");
	}
	if (serr.IsValid())
		eMpfs2::Throw("Conditional statements not closed:\n"+serr);
}

void DynamicVariableParser::condIfNotJumpToSolveAt(int pos)
{
	condLevel &lvl = m_condLevel.Last();
	if (lvl.m_ifNotJumpToSolve >= 0 || pos < 0)
		eMpfs2::Throw("Internal error in conditional statements, invalid jump","DynamicVariableParser::condIfNotJumpToSolveAt(int pos)");

	lvl.m_ifNotJumpToSolve = pos;
}

void DynamicVariableParser::condJumpToEndToSolve(condLevel &lvl,xbData *pOutData)
{
	int idxlg = pOutData->Length;
//	int step = 0;
	byte *pIdxData = (byte*)pOutData->GetWritePtr(idxlg+4)+idxlg;
	*(pIdxData++) = (byte)0xBB;
	*(pIdxData++) = (byte)0xAA;
	*(pIdxData++) = (byte)0x99;
	*(pIdxData++) = (byte)0x88;
	//*(pIdxData++) = (byte)step;
	//*(pIdxData++) = (byte)MPFS2m_JUMPFWD;
	//*(pIdxData++) = (byte)(step >> 8);
	//*(pIdxData++) = (byte)(step >> 16);
	lvl.m_arrEndJumpToSolve.Add(idxlg);
}

/*void DynamicVariableParser::condJumpBackFromHere(int addr,xbData *pOutData)
{
	int idxlg = pOutData->Length;
	int step = idxlg - addr;
	if (step <= 0 || step > 0xFFFFFF)
		eMpfs2::Throw("Internal error in conditional statements, invalid jump address","DynamicVariableParser::condJump(int addr,xbData *pOutData)");
	byte *pIdxData = (byte*)pOutData->GetWritePtr(idxlg+4)+idxlg;
	*(pIdxData++) = (byte)step;
	*(pIdxData++) = (byte)MPFS2m_JUMPBCK;
	*(pIdxData++) = (byte)(step >> 8);
	*(pIdxData++) = (byte)(step >> 16);
}
*/
void DynamicVariableParser::condIfNotJumpSolveHere(condLevel &lvl,xbData *pOutData)
{
	if (lvl.m_ifNotJumpToSolve < 0)
		return;

	byte *pIdxData = (byte*)pOutData->Begin() + lvl.m_ifNotJumpToSolve;
	if (*(pIdxData) != 0xFF || *(pIdxData+1) != 0xEE || *(pIdxData+2) != 0xDD || *(pIdxData+3) != 0xCC)
		eMpfs2::Throw("Internal error in conditional statements, invalid command address","DynamicVariableParser::condIfNotJumpSolveHere(condLevel &lvl,xbData *pOutData)");
	int step = pOutData->Length - lvl.m_ifNotJumpToSolve + 4; //4 bytes to point on command start
	if (step <= 0)
		eMpfs2::Throw("Internal error in conditional statements, invalid jump address","DynamicVariableParser::condIfNotJumpSolveHere(condLevel &lvl,xbData *pOutData)");
	*(pIdxData++) = (byte)step;
	*(pIdxData++) = (byte)(step >> 8);
	*(pIdxData++) = (byte)(step >> 16);
	*(pIdxData++) = (byte)(step >> 24);

	lvl.m_ifNotJumpToSolve = -1;
}

void DynamicVariableParser::condJumpToEndSolveHere(condLevel &lvl,xbData *pOutData)
{
	int *pJump = lvl.m_arrEndJumpToSolve.Begin();
	int *pJumpEnd = lvl.m_arrEndJumpToSolve.End();
	while(pJump < pJumpEnd)
	{
		int jump = *(pJump++);
		byte *pIdxData = (byte*)pOutData->Begin() + jump;
		if (*(pIdxData) != 0xBB || *(pIdxData+1) != 0xAA || *(pIdxData+2) != 0x99 || *(pIdxData+3) != 0x88)
			eMpfs2::Throw("Internal error in conditional statements, invalid command address","DynamicVariableParser::condJumpToEndSolveHere(condLevel &lvl,xbData *pOutData)");
		int step = pOutData->Length - jump; //To point on command start
		if (step <= 0 || step > 0xFFFFFF)
			eMpfs2::Throw("Internal error in conditional statements, invalid jump address","DynamicVariableParser::condJumpToEndSolveHere(condLevel &lvl,xbData *pOutData)");
		*(pIdxData++) = (byte)step;
		*(pIdxData++) = (byte)MPFS2m_JUMPFWD;
		*(pIdxData++) = (byte)(step >> 8);
		*(pIdxData++) = (byte)(step >> 16);
	}
}


int DynamicVariableParser::MPFS2mContentTypeAdd(const xbString &fileName,const xbString &sct,const FileLoc &fileLoc)
{
	return UseVar(sct,fileLoc,MPFSvarType_ForContentType);
}

int DynamicVariableParser::DynamicSubProtoSortCb(DynamicSubProto* const &pProto1,DynamicSubProto* const &pProto2)
{
	return pProto1->GetLabel(NULL).Compare(pProto2->GetLabel(NULL));
}

void DynamicVariableParser::ListUsedTokens(bool bFull)
{
	DynamicVariable **ppDv = vars.Begin();
	DynamicVariable **ppDvEnd = vars.End();
	xbPtrArray<DynamicSubProto> arrSubProto;
	xbArray<const DynamicVariable*> arrUnused;
	xbArray<const DynamicVariable*> arrInclude;
	xbArray<const DynamicVariable*> arrCT;
	DynamicSubProto *pSubProto = xbDnew DynamicSubProto();
	try
	{
		while(ppDv < ppDvEnd)
		{
			const DynamicVariable *pDv = *(ppDv++);
			if (pDv->Count == 0)
			{
				arrUnused.Add(pDv);
				continue;
			}
			if (pDv->varType == MPFSvarType_ForContentType)
			{
				arrCT.Add(pDv);
				continue;
			}
			if (pDv->Name.StartWith("inc:"))
			{
				arrInclude.Add(pDv);
				continue;
			}
			pSubProto->Parse(pDv->varType,pDv->Name,pDv->fileLocations);

			if (pSubProto->FindIn(m_pUI,arrSubProto))
				continue;

			// Add the prototype
			arrSubProto.Add(pSubProto);
			pSubProto = xbDnew DynamicSubProto();
		}
	}
	catch(...)
	{
		delete pSubProto;
		throw;
	}
	delete pSubProto;

	m_pUI->infoLog(xbFormat(
		"\n"
		"*******************************************************************************\n"
		"Tokens list : %d tokens, %d functions (%d no more used, %d includes, %d CTs)\n"
		"*******************************************************************************",
		vars.Length,arrSubProto.Length,arrUnused.Length,arrInclude.Length,arrCT.Length));
	arrSubProto.Sort(DynamicSubProtoSortCb);
	DynamicSubProto **ppSp = arrSubProto.Begin();
	DynamicSubProto **ppSpEnd = arrSubProto.End();
	while(ppSp < ppSpEnd)
	{
		const DynamicSubProto *pSp = *(ppSp++);
		const char *pPrefix;
		switch(pSp->varType)
		{
		case MPFSvarType_ForQueryIf:
			pPrefix = "BOOL HTTPQueryIf_";
			break;
		case MPFSvarType_ForQueryWhile:
			pPrefix = "BOOL HTTPQueryWhile_";
			break;
		default:
			pPrefix = "void HTTPPrint_";
			break;
		}

		m_pUI->infoLog(xbFormat(
			"- %s%s :\n"
			"%s",pPrefix,pSp->GetLabel(m_pUI).Ptr(),pSp->fileLocations.Ptr()
			));
	}
	if (arrCT.Length > 0)
	{
		m_pUI->infoLog(xbFormat(
			"\n"
			"*******************************************************************************\n"
			"Content types list : %d tokens\n"
			"*******************************************************************************",
			arrCT.Length));
		ppDv = (DynamicVariable**)arrCT.Begin();
		ppDvEnd = (DynamicVariable**)arrCT.End();
		while(ppDv < ppDvEnd)
		{
			const DynamicVariable *pDv = *(ppDv++);
			m_pUI->infoLog(xbFormat(
				"- %s :\n"
				"%s",
				pDv->Name.Ptr(),
				pDv->fileLocations.Ptr()
				));
		}
	}
	if (arrInclude.Length > 0)
	{
		m_pUI->infoLog(xbFormat(
			"\n"
			"*******************************************************************************\n"
			"Includes list : %d tokens\n"
			"*******************************************************************************",
			arrInclude.Length));
		ppDv = (DynamicVariable**)arrInclude.Begin();
		ppDvEnd = (DynamicVariable**)arrInclude.End();
		while(ppDv < ppDvEnd)
		{
			const DynamicVariable *pDv = *(ppDv++);
			m_pUI->infoLog(xbFormat(
				"- %s :\n"
				"%s",
				pDv->Name.Ptr(),
				pDv->fileLocations.Ptr()
				));
		}
	}
	if (bFull && arrUnused.Length > 0)
	{
		m_pUI->infoLog(xbFormat(
			"\n"
			"*******************************************************************************\n"
			"No more used tokens list : %d tokens\n"
			"*******************************************************************************",
			arrUnused.Length));
		ppDv = (DynamicVariable**)arrUnused.Begin();
		ppDvEnd = (DynamicVariable**)arrUnused.End();
		while(ppDv < ppDvEnd)
		{
			const DynamicVariable *pDv = *(ppDv++);
			m_pUI->infoLog(xbFormat(
				"- %s",
				pDv->Name.Ptr()
				));
		}
	}
	m_pUI->infoLog("");
}

xbString FileLoc::locationStr() const
{
	return m_fname+":"+xbString(m_line)+":"+xbString(m_col);
}

xbString FileLoc::locationStr(const xbString &msg) const
{
	return locationStr()+":"+msg;
}

#pragma warning(pop)
