// Copyright (c) 2004-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:
// Message Implementation Operations for elf2e32 tool
// @internalComponent
// @released
// 
//


#include "messageimplementation.h"
#include "errorhandler.h"
#include<iostream>
#include<stdarg.h>
#include<string>
#include <stdio.h>

using std::endl;
using std::cout;
typedef std::string String;

char *errorMssgPrefix="elf2e32 : Error: E";
char *warnMssgPrefix="elf2e32 : Warning: W";
char *infoMssgPrefix="elf2e32 : Information: I";
char *colSpace=": ";

enum MessageArraySize{MAX=66};

//Messages stored required for the program
struct EnglishMessage MessageArray[MAX]=
{
	{FILEOPENERROR,"Could not open file : %s."},
	{FILEREADERROR,"Could not read file : %s."},
	{FILEWRITEERROR,"Could not write file : %s."},
	{ELFMAGICERROR,"Invalid ELF magic in file : %s."},
	{ELFCLASSERROR,"ELF file %s is not 32 bit."},
	{ELFABIVERSIONERROR,"ELF file %s is not BPABI conformant."},
	{ELFLEERROR,"ELF file %s is not Little Endian."},
	{ELFARMERROR,"ELF file %s does not target ARM."},
	{ELFEXECUTABLEERROR,"ELF file %s is neither executable (ET_EXEC) or shared (ET_DYN)."},
	{ELFSHSTRINDEXERROR,"Error in ELF Section Header String Index : %s."},
	{NAMELIBRARYNOTCORRECT,"Name or Library not supplied correctly : %s[Line No=%d][%s]"},
	{ORDINALSEQUENCEERROR,"Ordinal number is not in sequence : %s[Line No=%d][%s]."},
	{ARGUMENTNAMEERROR,"Argument %s is not correct."},
	{OPTIONNAMEERROR,"Option %s is Unrecognized."},
	{NOARGUMENTERROR,"Missing arguments for option : %s."},
	{OPTIONPREFIXERROR,"Option %s is neither preceedded by '-' nor '--'."},
	{NOREQUIREDOPTIONERROR,"Missing options : %s."},
	{NOFILENAMEERROR,"Missing argument for option : %s."},
	{INVALIDARGUMENTERROR,"Argument '%s' not permitted for option %s."},
	{HUFFMANBUFFEROVERFLOWERROR,"Huffman buffer overflow during E32Image compression."},
	{HUFFMANTOOMANYCODESERROR,"Too many Huffman codes during E32Image compression."},
	{HUFFMANINVALIDCODINGERROR,"Invalid Huffman coding during E32Image compression."},
	{CAPABILITYALLINVERSIONERROR,"-ALL not a valid capability."},
	{CAPABILITYNONEINVERSIONERROR,"+NONE not a valid capability."},
	{UNRECOGNISEDCAPABILITYERROR,"Unrecognized capability : %s."},
	{NOSTATICSYMBOLSERROR,"ELF File %s contains no static symbols."},
	{DLLHASINITIALISEDDATAERROR,"ELF File %s contains initialized writable data."},
	{DLLHASUNINITIALISEDDATAERROR,"ELF File %s contains uninitialized writable data."},
	{ENTRYPOINTCORRUPTERROR,"ELF File %s has corrupt entry point."},
	{ENTRYPOINTNOTSUPPORTEDERROR,"ELF File %s has unsupported entry point type."},
	{EXCEPTIONDESCRIPTOROUTSIDEROERROR,"ELF File %s has invalid exception descriptor."},
	{NOEXCEPTIONDESCRIPTORERROR,"ELF File %s has no exception descriptor."},
	{NEEDSECTIONVIEWERROR,"ELF File %s has no section headers."},
	{DSONOTFOUNDERROR,"DSO %s not found."},
	{UNDEFINEDSYMBOLERROR,"Undefined Symbol %s found in ELF File %s."},
	{SYMBOLMISSINGFROMELFERROR,"Symbol %s Missing from ELF File : %s."},
	{MEMORYALLOCATIONERROR,"Memory allocation failure : %s."},
	{E32IMAGEERROR,"Not able to write E32 Image file."},
	{INVALIDINVOCATIONERROR,"Invalid invocation of elf2e32."},
	{TARGETTYPENOTSPECIFIEDERROR,"Target Type Not Specified."},
	{UNSUPPORTEDTARGETTYPEERROR,"Unsupported Target Type '%s'."},
	{INDEXNOMESSAGEERROR,"There is no message for the message index[%d]."},
	{INDEXNOTREQUIREDERROR,"Message index[%d] not required in message file."},
	{INDEXNOTFOUNDERROR,"Message index [%d] not found in message file"},
	{NOMESSAGEFILEERROR,"There is no message file."},
	{ENTRYPOINTNOTSETERROR,"Entry point is not set for %s."},
	{UNDEFINEDENTRYPOINTERROR,"Entry point and Text segment base both 0, can't tell if entry point set for %s."},
	{ORDINALNOTANUMBER,"Ordinal not a Number : %s[Line No=%d][%s]."},
	{UNRECOGNIZEDTOKEN,"Unrecognized Token : %s[Line No=%d][%s]."},
	{NONAMEMISSING,"NONAME Missing : %s[Line No=%d][%s]."},
	{EXPORTSEXPECTED,"EXPORTS expected before first export entry : %s[Line No=%d][%s]."},
	{ATRATEMISSING,"@ Missing : %s[Line No=%d][%s]."},
	{SYSDEFSMISMATCHERROR,"Symbol %s passed through '--sysdef' option is not at the specified ordinal in the DEF file %s."},
	{SYSDEFERROR,"Ordinal number is not provided as input to the option: %s"},
	{INVALIDE32IMAGEERROR,"%s is not a valid E32Image file."},
	{HUFFMANBUFFERUNDERFLOWERROR,"Huffman buffer underflow on deflate."},
	{HUFFMANINCONSISTENTSIZEERROR,"Inconsistent sizes discovered during uncompression."},
	{MULTIPLESYSDEFERROR, "Multiple system definitions passed to %s should be separated by ';'"},
	{SYSDEFNOSYMBOLERROR, "Symbol Name is not provided as input to the option: %s"},
	{VALUEIGNOREDWARNING, "Value passed to '%s' is ignored"},
	{ELFFILEERROR,"Error while processing the ELF file %s."},
	{SYMBOLCOUNTMISMATCHERROR, "Symbol count provided by DT_ARM_SYMTABSZ is not same as that in the Hash Table in %s"},
	{POSTLINKERERROR,"Fatal Error in Postlinker"},
	{BYTEPAIRINCONSISTENTSIZEERROR, "Inconsistent sizes discovered during Byte pair uncompression." },
	{ILLEGALEXPORTFROMDATASEGMENT,"'%s' : '%s' Import relocation does not refer to code segment."},
	{VALIDATIONERROR,"Image failed validation"}
};

/**
Constructor to reset the logging option flag.

@internalComponent
@released
*/
MessageImplementation::MessageImplementation()
{
    iLogging = false;
}

/**
Destructor to close log file if logging is enabled and to clear the messaged.
@internalComponent
@released
*/
MessageImplementation::~MessageImplementation()
{
    if(iLogging)
    {
		fclose(iLogPtr);
    }
	iMessage.clear();
}

/**
Function to Get Message stored in map.

@internalComponent
@released

@param aMessageIndex
Index of the Message to be displayed
@return Message string to be displayed
*/
char * MessageImplementation::GetMessageString(int aMessageIndex)
{
	Map::iterator p;

	if(iMessage.empty())
	{
		if(aMessageIndex <= MAX)
		{
			return MessageArray[aMessageIndex-1].message;
		}
		else
		{
			return NULL;
		}
	}
	else
	{
		p=iMessage.find(aMessageIndex);
		if(p == iMessage.end())
		{
			if(aMessageIndex <= MAX)
			{
				return MessageArray[aMessageIndex-1].message;
			}
			else
			{
				return NULL;
			}
		}

		if(aMessageIndex <= MAX)
		{
			return p->second;
		}
		else
		{
			return NULL;
		}
	}
}

/**
Function to display output and log message in log file if logging is enable.

@internalComponent
@released

@param aString
Message to be displayed
*/
void MessageImplementation::Output(const char *aString)
{

    if (iLogging)
    {
		fputs(aString,iLogPtr);
		fputs("\n",iLogPtr);
    }
	cout << aString << endl;
}

/**
Function to Get Message stored in map and to display the Message

@internalComponent
@released

@param
The type of the message, whether it is Error or Warning or Information.
@param
The index of the information and the corresponding arguments.
*/
void MessageImplementation::ReportMessage(int aMessageType, int aMsgIndex,...)
{
	char *reportMessage, *ptr, *tmpMessage;
	char intStr[16];
	char mssgNo[MAXMSSGNOLENGTH];
	int mssgIndex,k;

	va_list ap;
	va_start(ap,aMsgIndex);
	
	reportMessage=GetMessageString(aMsgIndex);
	
	if(reportMessage)
	{
		String message;
		switch (aMessageType)
		{
			case ERROR:
				message = errorMssgPrefix;
				break;
			case WARNING:
				message = warnMssgPrefix;
				break;
			case INFORMATION:
				message = infoMssgPrefix;
				break;
		}
		mssgIndex = BASEMSSGNO + aMsgIndex;
		sprintf(mssgNo,"%d",mssgIndex);
		message += mssgNo;
		message += colSpace;

		ptr = strchr(reportMessage,'%');

		while( ptr != NULL && (ptr[0]) == '%' )
		{
			tmpMessage=new char[ptr - reportMessage + 1];
			strncpy(tmpMessage, reportMessage, ptr - reportMessage+1);
			tmpMessage[ptr - reportMessage]='\0';
			message += tmpMessage;
			delete tmpMessage;
			ptr++;
			switch(ptr[0])
			{
				case 'd':
					k = va_arg(ap, int);
					sprintf(intStr,"%d",k);
					message += intStr;
					ptr++;
					reportMessage = ptr;
					break;
				case 's':
					message += va_arg(ap, char *);
					ptr++;
					reportMessage = ptr;
					break;
				case '%':
					message += ptr[0];
					reportMessage = ptr;
				default:
					break;
			}
			ptr=strchr(reportMessage,'%');
		}
		message += reportMessage;
		Output(message.c_str());
	}
}

/**
Function to start logging.

@internalComponent
@released

@param aFileName
Name of the Log file
*/
void MessageImplementation::StartLogging(char *aFileName)
{
	char logFile[1024];
	FILE *fptr;

	strcpy(logFile,aFileName);

	// open file for log etc.
	if((fptr=fopen(logFile,"w"))==NULL)
	{
		ReportMessage(WARNING, FILEOPENERROR,aFileName);
	}
	else
	{
	    iLogging = true;
		iLogPtr=fptr;
	}
}

/**
Function to Create Messages file.

@internalComponent
@released
@param aFileName
Name of the Message file to be dumped
*/
void MessageImplementation::CreateMessageFile(char *aFileName)
{
	int i;
	FILE *fptr;

	// open file for writing messages.
	if((fptr=fopen(aFileName,"w"))==NULL)
	{
		ReportMessage(WARNING, FILEOPENERROR,aFileName);
	}
	else
	{
		for(i=0;i<MAX;i++)
		{
			fprintf(fptr,"%d,%s\n",i+1,MessageArray[i].message);
		}
			
		fclose(fptr);
	}

}

/**
Function to put Message string in map which is stored in message file.
If file is not available the put message in map from Message Array structure.

@internalComponent
@released

@param aFileName
Name of the Message file passed in
*/
void MessageImplementation::InitializeMessages(char *aFileName)
{
	char index[16];
	char *message, *errStr;
	int i, lineLength;
	int fileSize;
	char *messageEntries, *lineToken;

	FILE *fptr;

	if(aFileName && (fptr=fopen(aFileName,"rb"))!=NULL)
	{

		iMessage.clear();
		//Getting File size
		fseek(fptr, 0, SEEK_END);
		fileSize=ftell(fptr);
		rewind(fptr);

		messageEntries= new char[fileSize+2];

		//Getting whole file in memory
		fread(messageEntries, fileSize, 1, fptr);

		//Adding ENTER at end
		*(messageEntries+fileSize)='\n';
		//Adding '\0' at end
		*(messageEntries+fileSize+1)='\0';

		fclose(fptr);

		lineToken=strtok(messageEntries,"\n");
		while(lineToken != NULL)
		{
			if( lineToken[0] == '\n' || lineToken[0] == '\r' )
			{
				lineToken=strtok(NULL,"\n");
				continue;
			}

			lineLength=strlen(lineToken);

			if( lineToken[strlen(lineToken)-1] == '\r' )
			{
				lineToken[strlen(lineToken)-1]='\0';
			}

			message=strchr(lineToken,',');
			strncpy(index,lineToken,message-lineToken);
			index[message-lineToken]='\0';
			errStr = new char[strlen(message+1) + 1];
			strcpy(errStr, (message+1));
			iMessage.insert(std::pair<int,char*>(atoi(index),errStr));

			lineToken=strtok(lineToken+lineLength+1,"\n");
		}

		delete messageEntries;

	}
	else
	{
		for(i=0;i<MAX;i++)
		{
			errStr = new char[strlen(MessageArray[i].message) + 1];
			strcpy(errStr, MessageArray[i].message);
			iMessage.insert(std::pair<int,char*>(MessageArray[i].index,errStr));
		}
	}
}


