imgtools/romtools/rofsbuild/r_obey.cpp
author kelvzhu
Thu, 02 Sep 2010 15:02:14 +0800
changeset 632 934f9131337b
parent 626 ac03b93ca9c4
child 647 53d1ab72f5bc
permissions -rw-r--r--
Delivery Blocks to SF

/*
* Copyright (c) 1995-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: 
* @internalComponent * @released
* Rofsbuild Obey file class and its reader class.
*
*/
 
#include <strstream>
#include <iomanip>
 
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <assert.h>
#include <errno.h>

#include <e32std.h>
#include <e32std_private.h>
#include <e32rom.h>
#include <u32std.h>
#include <f32file.h>

#include "h_utl.h"
#include "r_obey.h"
#include "r_coreimage.h"
#include "patchdataprocessor.h"
#include "fatimagegenerator.h" 
#include "r_driveimage.h"

#include "uniconv.hpp"
extern TInt gCodePagingOverride;
extern TInt gDataPagingOverride;
extern ECompression gCompress;
extern TBool gEnableStdPathWarning; // Default to not warn if destination path provided for a file is not in standard path.
extern TBool gKeepGoing;
extern TBool gIsOBYUTF8;


#define _P(word)	word, sizeof(word)-1	// match prefix, optionally followed by [HWVD]
#define _K(word)	word, 0					// match whole word
static char* const NullString = "" ;
const ObeyFileKeyword ObeyFileReader::iKeywords[] =
{
	{_K("file"),		2,-2, EKeywordFile, "File to be copied into ROFS"},
	{_K("data"),		2,-2, EKeywordData, "same as file"},
	{_K("dir"),         2,1, EKeywordDir, "Directory to be created into FAT image"},

	{_K("rofsname"),	1, 1, EKeywordRofsName, "output file for ROFS image"},
	{_K("romsize"),		1, 1, EKeywordRomSize, "size of ROM image"}, 
	{_P("hide"),	    2, -1, EKeywordHide, "Exclude named file from ROM directory structure"},
	{_P("alias"),	    2, -2, EKeywordAlias, "Create alias for existing file in ROM directory structure"},
	{_P("rename"),	    2, -2, EKeywordRename, "Change the name of a file in the ROM directory structure"},
	{_K("rofssize"),		1, 1, EKeywordRofsSize, "maximum size of ROFS image"},
	{_K("romchecksum"),	1, 1, EKeywordRofsChecksum, "desired 32-bit checksum value for the whole image"},
	{_K("version"),		1, 1, EKeywordVersion, "ROFS image version number"},
	{_K("time"),	    1,-1, EKeywordTime, "ROFS image timestamp"},
	{_K("extensionrofs"),1+2, 1, EKeywordExtensionRofs, "Start of definition of optional Extension ROFS"},
	{_K("extensionrofsname"),1, 1, EKeywordCoreRofsName, "ROFS image on which extension ROFS is based"},
	{_K("rem"),			0, 0, EKeywordNone, "comment"},
	{_K("stop"),		0, 0, EKeywordNone, "Terminates OBEY file prematurely"},
	{_K("romchecksum"),	1, 1, EKeywordRomChecksum, "desired 32-bit checksum value for the whole ROFS image"},
	{_K("coreimage"),	1, 1, EKeywordCoreImage, "Core image to be used for extension directory structure"},
	{_K("autosize"),	1, 1, EKeywordRofsAutoSize, "Automatically adjust maximum image size to actual used"},
	{_K("pagingoverride"),	1, 1, EKeywordPagingOverride, "Overide the demand paging attributes for every file in ROM, NOPAGING|DEFAULTUNPAGED|DEFAULTPAGED"},
	{_K("codepagingoverride"),	1, 1, EKeywordCodePagingOverride, "Overide the code paging attributes for every file in ROM, NOPAGING|DEFAULTUNPAGED|DEFAULTPAGED"},
	{_K("datapagingoverride"),	1, 1, EKeywordDataPagingOverride, "Overide the data paging attributes for every file in ROM, NOPAGING|DEFAULTUNPAGED|DEFAULTPAGED"},
	{_K("dataimagename"),1, 1,EKeywordDataImageName, "Data Drive image file name"},
	{_K("dataimagefilesystem"),1, 1,EKeywordDataImageFileSystem, "Drive image file system format"},
	{_K("dataimagesize"),1, 1,EKeywordDataImageSize, "Maximum size of Data Drive image"},
	{_K("volume"),1, -1,EKeywordDataImageVolume, "Volume Label of Data Drive image"},
	{_K("sectorsize"),1, 1,EKeywordDataImageSectorSize, "Sector size(in bytes) of Data Drive image"},
	{_K("clustersize"),1, 1,EKeywordDataImageClusterSize, "Cluster size(in bytes) of Data Drive image"},
	{_K("fattable"),1, 1,EKeywordDataImageNoOfFats, "Number of FATs in the Data Drive image"},
	// things we don't normally report in the help information
	{_K("trace"),		1, 1, EKeywordTrace, "(ROMBUILD activity trace flags)"},
	{_K("filecompress"),2, -2,EKeywordFileCompress,"Non-XIP Executable to be loaded into the ROM compressed" },
	{_K("fileuncompress"),2, -2,EKeywordFileUncompress,"Non-XIP Executable to be loaded into the ROM uncompressed" },
	{_K("patchdata"),2, 5,EKeywordPatchDllData, "Patch exported data"},
	{_K("imagename"), 1, 1, EKeywordSmrImageName, "output file for SMR image"},
	{_K("hcrdata"), 1, 1, EKeywordSmrFileData, "file data for HCR SMR image"},
	{_K("smrdata"), 1, 1, EKeywordSmrFileData, "file data for general SMR image"},
	{_K("formatversion"), 1, 1, EKeywordSmrFormatVersion, "format version for HCR SMR image"},
	{_K("payloadflags"), 1, 1, EKeywordSmrFlags, "payload flags for the HCR SMR image"},
	{_K("payloaduid"), 1, 1, EKeywordSmrUID, "payload UID for the HCR SMR image"},
	{0,0,0,0,EKeywordNone,""}
};

void ObeyFileReader::KeywordHelp() { // static

	cout << "Obey file keywords:\n";

	const ObeyFileKeyword* k=0;
	for (k=iKeywords; k->iKeyword!=0; k++)
	{
		if (k->iHelpText == 0)
			continue;
		if (k->iHelpText[0]=='(' && !H.iVerbose)
			continue;	// don't normally report things in (parentheses)

		char buf[32];
		sprintf(buf, "%-20s", k->iKeyword);
		if (k->iKeywordLength)
			memcpy(buf+k->iKeywordLength,"[HWVD]",6);
		if (H.iVerbose)
			sprintf(buf+20,"%2d",k->iNumArgs);
		cout << "    " << buf << " " << k->iHelpText << endl;
	}
	cout << endl;

	cout << "File attributes:\n";

	const FileAttributeKeyword* f=0;
	for (f=iAttributeKeywords; f->iKeyword!=0; f++)
	{
		if (f->iHelpText == 0)
			continue;
		if (f->iHelpText[0]=='(' && !H.iVerbose)
			continue;	// don't normally report things in (parentheses)

		char buf[32];
		sprintf(buf, "%-20s", f->iKeyword);
		if (H.iVerbose)
			sprintf(buf+20,"%2d",k->iNumArgs);
		cout << "    " << buf << " " << f->iHelpText << endl;
	}
	cout << endl;
}

ObeyFileReader::ObeyFileReader(const char* aFileName):iCurrentLine(0),
iFileName(aFileName),iNumWords(0),iLine(0),iMarkLine(0),iCurrentObeyStatement(0)	{  
	for(TUint i = 0 ; i < KNumWords ; i++)
		iWord[i] = NullString;
	*iSuffix = 0 ; 
}

ObeyFileReader::~ObeyFileReader() {	  
	if(iCurrentObeyStatement) {
		delete []iCurrentObeyStatement;
		iCurrentObeyStatement = 0 ;
	}
	if(iLine) {
		delete []iLine; 
		iLine = 0 ;
	}
}

//
// Open the file & return a status
//
TBool ObeyFileReader::Open() {     
	ifstream ifs(iFileName.c_str(),ios_base::in + ios_base::binary);
	if (!ifs.is_open()) {
		Print(EError,"Cannot open obey file %s\n",iFileName.c_str());
		return EFalse;
	}
	iLines.clear();
	if(iLine){
		delete []iLine;
		iLine = 0 ;
	}		
	ifs.seekg(0,ios_base::end);
	size_t length = ifs.tellg();
	char* buffer = new char[length + 2];
	if (0 == buffer) {
		Print(EError,"Insufficent Memory to Continue.");	
		ifs.close();
		return EFalse;
	}
	ifs.seekg(0,ios_base::beg);
	ifs.read(buffer,length);
	size_t readcout = ifs.gcount() ;
	if(readcout != length){ 	
		Print(EError,"Cannot Read All of File.");	
		delete []buffer ;
		ifs.close();
		return EFalse;
	}
	buffer[length] = '\n';
	buffer[length + 1] = 0 ;
	ifs.close();
	char* lineStart = buffer ;
	char* end = buffer + length ;
	string line ;
	size_t maxLengthOfLine = 0 ;
	while(lineStart < end){
		while(*lineStart == ' ' || *lineStart == '\t') //trimleft 
			lineStart ++ ;		
		char* lineEnd = lineStart ;	 
		while(*lineEnd != '\r' && *lineEnd != '\n')
			lineEnd ++ ;
		if(strnicmp(lineStart,"REM",3) == 0){
			line = "" ; // REMOVE "REM ... "
		}
		else {
			TInt lastIndex = lineEnd - lineStart - 1;
			while(lastIndex >= 0 &&  // trimright
				(lineStart[lastIndex] == ' ' || lineStart[lastIndex] == '\t'))
				lastIndex -- ;			
			if(lastIndex >= 0)
				line.assign(lineStart,lastIndex + 1);
			else
				line = "";
		}
		if(line.length() > maxLengthOfLine)
			maxLengthOfLine = line.length();
		iLines.push_back(line);
		if(*lineEnd == '\r') {
			if(lineEnd[1] == '\n')
				lineStart = lineEnd + 2 ;
			else
				lineStart = lineEnd + 1 ;
		}
		else // '\n'
			lineStart = lineEnd + 1 ;
	}	
	delete []buffer ;
	iLine = new char[maxLengthOfLine + 1];
	iCurrentObeyStatement = new char[maxLengthOfLine + 1];
	*iCurrentObeyStatement = 0 ;
	*iLine = 0 ;
	iCurrentLine = 0 ;
	iMarkLine = 0 ;
	return ETrue;
}

void ObeyFileReader::Mark()	{ 
	iMarkLine = iCurrentLine - 1;
}

void ObeyFileReader::MarkNext() { 
	iMarkLine = iCurrentLine;
}

void ObeyFileReader::Rewind() {
	iCurrentLine = iMarkLine;
}

char* ObeyFileReader::DupWord(TInt aIndex) const {
	char* retVal = 0 ;
	if(aIndex >= 0 && aIndex < (TInt)KNumWords){
		size_t len = strlen(iWord[aIndex]) + 1;
		retVal = new char[len];
		if(retVal)
			memcpy(retVal,iWord[aIndex],len);
	} 
	return retVal ;
}

TInt ObeyFileReader::ReadAndParseLine() {
	if (iCurrentLine >= (TInt)iLines.size())
		return KErrEof;
	iCurrentLine++; 	
	iNumWords = Parse();
	return KErrNone;
}

TInt ObeyFileReader::NextLine(TInt aPass, enum EKeyword& aKeyword) {

NextLine:
	TInt err = ReadAndParseLine();
	if (err == KErrEof)
		return KErrEof;
	if(iNumWords == 0)
		goto NextLine;
	if (stricmp(iWord[0], "stop")==0)
		return KErrEof;

	const ObeyFileKeyword* k=0;
	for (k=iKeywords; k->iKeyword!=0; k++) {
		if (k->iKeywordLength == 0) {
			// Exact case-insensitive match on keyword
			if (stricmp(iWord[0], k->iKeyword) != 0)
				continue;
			*iSuffix = 0;
		}
		else {
			// Prefix match
			if (strnicmp(iWord[0], k->iKeyword, k->iKeywordLength) != 0)
				continue;
			// Suffix must be empty, or a variant number in []
			strncpy(iSuffix,iWord[0] + k->iKeywordLength,80);
			if (*iSuffix != '\0' && *iSuffix != '[')
				continue;
		}
		// found a match
		if ((k->iPass & aPass) == 0)
			goto NextLine;
		if (k->iNumArgs>=0 && (1+k->iNumArgs != iNumWords)) {
			Print(EError, "Incorrect number of arguments for keyword %s on line %d.\n",
				iWord[0], iCurrentLine);
			goto NextLine;
		}
		if (k->iNumArgs<0 && (1-k->iNumArgs > iNumWords)) {
			Print(EError, "Too few arguments for keyword %s on line %d.\n",
				iWord[0], iCurrentLine);
			goto NextLine;
		}

		aKeyword = k->iKeywordEnum;
		return KErrNone;
	}
	if (aPass == 1)
		Print(EWarning, "Unknown keyword '%s'.  Line %d ignored\n", iWord[0], iCurrentLine);
	goto NextLine;
}

//
// splits a line into words, and returns the number of words found
// 
TInt ObeyFileReader::Parse() {

	for (TUint i = 0; i < KNumWords; i++)
		iWord[i] = NullString;

	enum TState {EInWord, EInQuotedWord, EInGap};
	TState state = EInGap;
	const string& line = iLines[iCurrentLine - 1]; 	
	size_t len = line.length();	
	memcpy(iLine,line.c_str(),len);
	memcpy(iCurrentObeyStatement,iLine,len);
	iLine[len] = 0 ; 
	iCurrentObeyStatement[len] = 0 ;
	TUint i = 0;
	char* linestr = iLine;
	while (i < KNumWords && *linestr != 0) {	 
		switch (state)
		{
		case EInGap:
			if (*linestr =='\"') {
				if (linestr[1] != 0 && linestr[1]!='\"')
					iWord[i++] = linestr + 1;
				state = EInQuotedWord;
			}
			else if (!IsGap(*linestr)) {
				iWord[i++] = linestr;
				state=EInWord;
			}
			else
				*linestr=0;
			break;
		case EInWord:
			if (*linestr == '\"') {
				*linestr = 0;
				if (linestr[1] != 0 && linestr[1] != '\"')
					iWord[i++] = linestr+1;
				state=EInQuotedWord;
			}
			else if (IsGap(*linestr)) {
				*linestr=0;
				state=EInGap;
			}
			break;
		case EInQuotedWord:
			if (*linestr == '\"'){
				*linestr = 0;
				state = EInGap;
			}
			break;
		}
		linestr++;
	}
	return i;
}

//
// Process the timestamp
//
void ObeyFileReader::ProcessTime(TInt64& aTime) {
	char timebuf[256];
	if (iNumWords>2)
		sprintf(timebuf, "%s_%s", iWord[1], iWord[2]);
	else
		strncpy(timebuf, iWord[1],256);

	TInt r = StringToTime(aTime, timebuf);
	if (r==KErrGeneral) {
		Print(EError, "incorrect format for time keyword on line %d\n", iCurrentLine);
		exit(0x670);
	}
	if (r==KErrArgument){
		Print(EError, "Time out of range on line %d\n", iCurrentLine);
		exit(0x670);
	}
}

TInt64 ObeyFileReader::iTimeNow = 0;
void ObeyFileReader::TimeNow(TInt64& aTime) {
	if (iTimeNow==0) {
		TInt sysTime = time(0);					// seconds since midnight Jan 1st, 1970
		sysTime -= (30*365*24*60*60+7*24*60*60);	// seconds since midnight Jan 1st, 2000
		TInt64 daysTo2000AD=730497;
		TInt64 t=daysTo2000AD*24*3600+sysTime;	// seconds since 0000
		t = t+3600;								// BST (?)
		iTimeNow=t*1000000;						// milliseconds
	}
	aTime=iTimeNow;
}

 

// File attributes.


const FileAttributeKeyword ObeyFileReader::iAttributeKeywords[] =
{
	{"attrib",3			,0,1,EAttributeAtt, "File attributes in ROM file system"},
	{"exattrib",3		,0,1,EAttributeAttExtra, "File extra attributes in ROM file system"}, 
	{"stack",3			,1,1,EAttributeStack, "?"},
	{"fixed",3			,1,0,EAttributeFixed, "Relocate to a fixed address space"},
	{"priority",3		,1,1,EAttributePriority, "Override process priority"},
	{_K("uid1")			,1,1,EAttributeUid1, "Override first UID"},
	{_K("uid2")			,1,1,EAttributeUid2, "Override second UID"},
	{_K("uid3")			,1,1,EAttributeUid3, "Override third UID"},
	{_K("heapmin")		,1,1,EAttributeHeapMin, "Override initial heap size"},
	{_K("heapmax")		,1,1,EAttributeHeapMax, "Override maximum heap size"},
	{_K("capability")	,1,1,EAttributeCapability, "Override capabilities"},
	{_K("unpaged")		,1,0,EAttributeUnpaged, "Don't page code or data for this file"},
	{_K("paged")		,1,0,EAttributePaged, "Page code and data for this file"},
	{_K("unpagedcode")	,1,0,EAttributeUnpagedCode, "Don't page code for this file"},
	{_K("pagedcode")	,1,0,EAttributePagedCode, "Page code for this file"},
	{_K("unpageddata")	,1,0,EAttributeUnpagedData, "Don't page data for this file"},
	{_K("pageddata")	,1,0,EAttributePagedData, "Page data for this file"},
	{0,0,0,0,EAttributeAtt,0}
};

TInt ObeyFileReader::NextAttribute(TInt& aIndex, TInt aHasFile, enum EFileAttribute& aKeyword, char*& aArg)
{
NextAttribute:
	if (aIndex >= iNumWords)
		return KErrEof;
	char* word=iWord[aIndex++];
	const FileAttributeKeyword* k;
	for (k=iAttributeKeywords; k->iKeyword!=0; k++)
	{
		if (k->iKeywordLength == 0)
		{
			// Exact match on keyword
			if (stricmp(word, k->iKeyword) != 0)
				continue;
		}
		else
		{
			// Prefix match
			if (strnicmp(word, k->iKeyword, k->iKeywordLength) != 0)
				continue;
		}
		// found a match
		if (k->iNumArgs>0)
		{
			TInt argIndex = aIndex;
			aIndex += k->iNumArgs;		// interface only really supports 1 argument
			if (aIndex>iNumWords)
			{
				Print(EError, "Missing argument for attribute %s on line %d\n", word, iCurrentLine);
				return KErrArgument;
			}
			aArg=iWord[argIndex];
		}
		if (k->iIsFileAttribute && !aHasFile)
		{
			Print(EError, "File attribute %s applied to non-file on line %d\n", word, iCurrentLine);
			return KErrNotSupported;
		}
		aKeyword=k->iAttributeEnum;
		return KErrNone;
	}
	Print(EWarning, "Unknown attribute '%s' skipped on line %d\n", word, iCurrentLine);
	goto NextAttribute;
}



/**
Constructor:
1.Obey file instance.
2.used by both rofs and datadrive image.

@param aReader - obey file reader object.
*/
CObeyFile::CObeyFile(ObeyFileReader& aReader):
iRomFileName(NULL),
iExtensionRofsName(0),
iKernelRofsName(0),
iRomSize(0),
iVersion(0,0,0),
iCheckSum(0),
iNumberOfFiles(0),
iTime(0),
iRootDirectory(0),
iNumberOfDataFiles(0),
iDriveFileName(0), 
iDriveFileFormat(0), 
iReader(aReader), 
iMissingFiles(0), 
iLastExecutable(0),
iFirstFile(0), 	
iCurrentFile(0),
iAutoSize(EFalse),
iAutoPageSize(4096),
iPagingOverrideParsed(0),
iCodePagingOverrideParsed(0),
iDataPagingOverrideParsed(0),
iPatchData(new CPatchDataProcessor)
{
	iNextFilePtrPtr = &iFirstFile ;
}

/**
Obey file Destructor.
1.Release the tree memory.
2.Release all allocated memory if any.
*/
CObeyFile::~CObeyFile() {
	if(iDriveFileName){
		delete[] iDriveFileName;					
		iDriveFileName = 0 ;
	}
	if(iDriveFileFormat) {
		delete[] iDriveFileFormat;
		iDriveFileFormat = 0 ;
	}
	iRootDirectory->deleteTheFirstNode();                
	iRootDirectory->InitializeCount();

	Release();
	if(iRomFileName){ 
		delete [] iRomFileName;
		iRomFileName = 0 ;
	}
	if (iRootDirectory)
		iRootDirectory->Destroy(); 
	if(iPatchData)
		delete iPatchData;
}

//
// Free resources not needed after building a ROM
//
void CObeyFile::Release() {
	iFirstFile = 0;
	iNextFilePtrPtr = &iFirstFile;
}

TRomBuilderEntry *CObeyFile::FirstFile() {
	iCurrentFile = iFirstFile;
	return iCurrentFile;
}

TRomBuilderEntry *CObeyFile::NextFile() {
	iCurrentFile = iCurrentFile ? iCurrentFile->iNext : 0;
	return iCurrentFile;
}

char* CObeyFile::ProcessCoreImage() const {
	// check for coreimage keyword and return filename	 
	enum EKeyword keyword;
	char* coreImageFileName = 0;
	iReader.Rewind();
	while (iReader.NextLine(1,keyword) != KErrEof) {
		if (keyword == EKeywordCoreImage) {  			  
			coreImageFileName = iReader.DupWord(1);	
			iReader.MarkNext();
			break;
		}
	}
	return coreImageFileName;
}

void CObeyFile::SkipToExtension() {
	iReader.Rewind();
	enum EKeyword keyword;
	while (iReader.NextLine(1,keyword) != KErrEof) {
		if (keyword == EKeywordExtensionRofs) {
			iReader.Mark(); // ready for processing extension
			break;
		}
	}
}

TInt CObeyFile::ProcessRofs() {
	
	//
	// First pass through the obey file to set up key variables
	//

	iReader.Rewind();

	TInt count=0;
	enum EKeyword keyword;
	while (iReader.NextLine(1,keyword) != KErrEof) {
		if (keyword == EKeywordExtensionRofs){
			if (count==0)
				return KErrNotFound;		// no core ROFS, just extension ROFSs.
			break;
		}

		count++;
		if (! ProcessKeyword(keyword))
			return KErrGeneral;
	}

	if (!GotKeyVariables())
		return KErrGeneral;

	//
	// second pass to process the file specifications in the obey file building
	// up the TRomNode directory structure and the TRomBuilderEntry list
	//
	iReader.Rewind();

	iRootDirectory = new TRomNode("");
	iLastExecutable = iRootDirectory;

	TInt align=0;
	while (iReader.NextLine(2,keyword)!=KErrEof) {
		if (keyword == EKeywordExtensionRofs)
			break;

		if (keyword == EKeywordHide)
			keyword = EKeywordHideV2;

		switch (keyword) 
		{
		case EKeywordHide:
		case EKeywordAlias:
		case EKeywordRename:
			if (!ProcessRenaming(keyword))
				return KErrGeneral;
			break;
		case EKeywordPatchDllData:
			{
				// Collect patchdata statements to process at the end
				StringVector patchDataTokens;
				SplitPatchDataStatement(patchDataTokens); 
				iPatchData->AddPatchDataStatement(patchDataTokens);									
				break;
			}
		default:
			if (!ProcessFile(align, keyword))
				return KErrGeneral;
			align=0;
			break;
		}
	}

	if(!ParsePatchDllData())
		return KErrGeneral;
	iReader.Mark();			// ready for processing the extension rom(s)

	if (iMissingFiles!=0) {
		return KErrGeneral;
	}
	if ( 0 == iNumberOfFiles ){
		Print(EError, "No files specified.\n");
		return KErrGeneral;
	}

	return KErrNone;
}

TBool CObeyFile::Process() {
	TBool result = ETrue;
	iReader.Rewind();
	enum EKeyword keyword;
	while(iReader.NextLine(1, keyword) != KErrEof){
		string key = iReader.Word(0);
		string value = iReader.Word(1);
		if(iKeyValues.find(key) != iKeyValues.end()){
			iKeyValues[key].push_back(value);
		}
		else {
			StringVector values;
			values.push_back(value);
			iKeyValues[key]=values;
		}


	}
	return result;
}
StringVector CObeyFile::getValues(const string& aKey) {
	StringVector values;
	if(iKeyValues.find(aKey) != iKeyValues.end()){
		values = iKeyValues[aKey];
	}
	return values;
}

/**
Process drive obey file and construct the tree.

@return - Return the status,
'KErrnone' for Success,
'KErrGeneral' for failure (required keywords not there in obey file or failed
to construct the tree).
*/
TInt CObeyFile::ProcessDataDrive() {
	iReader.Rewind();
	enum EKeyword keyword;

	// First pass through the obey file to set up key variables
	while (iReader.NextLine(1,keyword) != KErrEof) {
		if (!ProcessDriveKeyword(keyword))			
			return KErrGeneral;
	}

	if (!GotKeyDriveVariables())
		return KErrGeneral;

	// Second pass to process the file specifications in the obey file.
	// Build the TRomNode directory structure and the TRomBuilderEntry list
	iReader.Rewind();
	iRootDirectory = new TRomNode("//");					
	iLastExecutable = iRootDirectory;

	while(iReader.NextLine(2,keyword)!=KErrEof) {
		switch (keyword) 
		{
		case EKeywordPatchDllData:
			{	// Collect patchdata statements to process at the end
				StringVector patchDataTokens;
				SplitPatchDataStatement(patchDataTokens); 				
				iPatchData->AddPatchDataStatement(patchDataTokens);									
				break;
			}

		case EKeywordHide:						
		case EKeywordFile:
		case EKeywordDir:
		case EKeywordData:
		case EKeywordFileCompress:
		case EKeywordFileUncompress:
			if (!ProcessDriveFile(keyword))
				return KErrGeneral;
			break;

		default:							
			break;
		}
	}

	if(!ParsePatchDllData())
		return KErrGeneral;
	if (iMissingFiles) {
		Print(EError, "Source Files Missing.\n");
		return KErrGeneral;
	}
	if (!iNumberOfFiles)
		Print(EAlways,"No files specified.\n");

	return KErrNone;
}


/**
Process and stores the keyword information.

@param aKeyword - keyword to update its value to variables.
@return - Return the status i.e Success,
*/
TBool CObeyFile::ProcessDriveKeyword(enum EKeyword aKeyword) {

	TBool success = ETrue;
	switch (aKeyword)
	{
	case EKeywordDataImageName:
		iDriveFileName = iReader.DupWord(1);
		break;
	case EKeywordDataImageFileSystem:
		iDriveFileFormat = iReader.DupWord(1);
		break;
	case EKeywordDataImageSize:
		{
			const char* bigString = iReader.Word(1);
			TInt64 imagesize = 0;
			Val(imagesize,bigString); 
			if(imagesize <= 0)
			{
				Print(EWarning,"Not a valid Image Size. Default size is considered\n");		
			}else
			{
				iConfigurableFatAttributes.iImageSize = imagesize;
			} 
		}
		break;
	case EKeywordDataImageVolume:
		{				
			// Get the volume label provided by using "volume" keyword.
			// e.g. vlolume = NO NAME
			string volumeLabel = iReader.GetCurrentObeyStatement();
			string volumeLabelKeyword = "volume";

			TUint position = volumeLabel.find(volumeLabelKeyword.c_str(),0,volumeLabelKeyword.size());
			position += volumeLabelKeyword.size();
			if (volumeLabel.find('=',position) != string::npos) {
				position=volumeLabel.find('=',position);
				++position;
			}								

			position = volumeLabel.find_first_not_of(' ',position);
			if (position != string::npos) {
				volumeLabel = volumeLabel.substr(position);

				// Remove the new line character from the end
				position = volumeLabel.find_first_of("\r\n");
				if (position != string::npos)
					volumeLabel = volumeLabel.substr(0,position);
				size_t length = volumeLabel.length() ;
				if(length > 11) 
						length = 11 ;
				memcpy(iConfigurableFatAttributes.iDriveVolumeLabel,volumeLabel.c_str(),length) ;
				while(length != 11)
					iConfigurableFatAttributes.iDriveVolumeLabel[length++] = ' ';
				iConfigurableFatAttributes.iDriveVolumeLabel[length] = 0;
			}
			else {
				Print(EWarning,"Value for Volume Label is not provided. Default value is considered.\n");
			}
			break;
		}
	case EKeywordDataImageSectorSize:
		{
			const char* bigString = iReader.Word(1);
			TInt sectorSize = 0;
			Val(sectorSize,bigString); 
			if(sectorSize <= 0)	{
				Print(EWarning,"Invalid Sector Size value. Default value is considered.\n");
			}
			else {
				iConfigurableFatAttributes.iDriveSectorSize = sectorSize;
			}
		}			
		break;
	case EKeywordDataImageClusterSize:
		{
			const char* bigString = iReader.Word(1);
			TInt clusterSize = 0;
			Val(clusterSize,bigString); 
			if(clusterSize <= 0)	{
				Print(EWarning,"Invalid Cluster Size value. Default value is considered.\n");
			}
			else {
				iConfigurableFatAttributes.iDriveClusterSize = clusterSize;
			}
		}			
		break;
	case EKeywordDataImageNoOfFats:
		{
			const char* bigString = iReader.Word(1);
			TInt noOfFats = 0;
			Val(noOfFats,bigString); 
			if (noOfFats <=0)
				Print(EWarning,"Invalid No of FATs specified. Default value is considered.\n");
			else
				iConfigurableFatAttributes.iDriveNoOfFATs = noOfFats;			
		}			
		break;			
	default:
		// unexpected keyword iReader.Word(0), keep going.
		break;
	}
	return success;
}


/**
Checks whether obeyfile has supplied enough variables to continue.

@return - Return the status 
ETrue - Supplied valid values,
EFalse- Not valied values.
*/
TBool CObeyFile::GotKeyDriveVariables() {

	TBool retVal=ETrue;

	// Mandatory keywords
	if (iDriveFileName==0) {                                                  
		Print(EError,"The name of the image file has not been supplied.\n");
		Print(EError,"Use the keyword \"dataimagename\".\n");
		retVal = EFalse;
	}
	// Check for '-'ve entered value.
	if(iConfigurableFatAttributes.iImageSize <= 0){
		Print(EWarning,"Image Size should be positive. Default size is Considered.\n");
	}

	// File system format.
	if(iDriveFileFormat==0) {
		Print(EError,"The name of the file system not been supplied.\n");
		Print(EError,"Use the keyword \"dataimagefilesystem\".\n");
		retVal = EFalse;
	}

	// Checking the validity of file system format.
	if(iDriveFileFormat){		 
		if(stricmp(iDriveFileFormat,"FAT16") && stricmp(iDriveFileFormat,"FAT32")) {
			Print(EError,"The name of the file system not supported : %s\n",iDriveFileFormat);
			retVal = EFalse;
		}
	}
	if(retVal)
		Print(ELog,"\nCreating Data Drive image : %s\n", iDriveFileName);

	return retVal;
}

/**
Process a parsed line to set up one or more new TRomBuilder entry objects.

@param  - obey file keyword.
// iWord[0] = the keyword (file,)      
// iWord[1] = the PC pathname
// iWord[2] = the EPOC pathname
// iWord[3] = start of the file attributes

@return - Return the status 
ETrue - Successful generation of tree.
EFalse- Fail to generate the tree.
*/
TBool CObeyFile::ProcessDriveFile(enum EKeyword aKeyword) {

	TBool isPeFile = ETrue;
	TBool aFileCompressOption, aFileUncompressOption;

	TInt epocPathStart=2;
	aFileCompressOption = aFileUncompressOption = EFalse;
	// do some validation of the keyword
	TInt currentLine = iReader.CurrentLine();

	switch (aKeyword)
	{
	case EKeywordData:
	case EKeywordDir:
	case EKeywordHide:
		isPeFile = EFalse;
		break;

	case EKeywordFile:
		break;

	case EKeywordFileCompress:
		aFileCompressOption = ETrue;
		break;

	case EKeywordFileUncompress:
		aFileUncompressOption = ETrue;
		break;

	default:
		return EFalse;
	}

	if (aKeyword!=EKeywordHide && aKeyword!=EKeywordDir) {
		// check the PC file exists
		char* nname = NormaliseFileName(iReader.Word(1));		  
		if(gIsOBYUTF8 && !UniConv::IsPureASCIITextStream(nname))
		{
			char* tempnname = strdup(nname);
			unsigned int namelen = 0;
			if(UniConv::UTF82DefaultCodePage(tempnname, strlen(tempnname), &nname, &namelen) < 0)
			{
				Print(EError, "Invalid filename encoding: %s\n", tempnname);
				free(tempnname);
				delete[] nname;
				iMissingFiles++;
				return EFalse;
			}
			free(tempnname);
		}
		ifstream test(nname);
		if(!test.is_open()){
			Print(EError,"Cannot open file %s for input.\n",iReader.Word(1));
			iMissingFiles++;
		}
		test.close();
		delete []nname ;												
		 
	}
	else
		epocPathStart=1;   

	if(aKeyword != EKeywordDir)
		iNumberOfFiles++;

	TBool endOfName=EFalse;
	const char *epocStartPtr;
	if(aKeyword != EKeywordDir)
		epocStartPtr = IsValidFilePath(iReader.Word(epocPathStart));
	else
		epocStartPtr = IsValidDirPath(iReader.Word(epocPathStart));
	char *epocEndPtr = const_cast<char*>(epocStartPtr);

	if (epocStartPtr == NULL) {
		Print(EError, "Invalid destination path on line %d\n",currentLine);
		return EFalse;
	}

	TRomNode* dir=iRootDirectory;
	TRomNode* subDir=0;
	TRomBuilderEntry *file=0;      

	while (!endOfName) {
		endOfName = GetNextBitOfFileName(epocEndPtr);      
		if (endOfName && (aKeyword!=EKeywordDir)) { // file
			TRomNode* alreadyExists=dir->FindInDirectory(epocStartPtr);
			if ((aKeyword != EKeywordHide) && alreadyExists) { // duplicate file		
				if (gKeepGoing) {
					Print(EWarning, "Duplicate file for %s on line %d, will be ignored\n",iReader.Word(1),iReader.CurrentLine());
					iNumberOfFiles--;
					return ETrue;
				}
				else {	
					Print(EError, "Duplicate file for %s on line %d\n",iReader.Word(1),iReader.CurrentLine());
					return EFalse;
				}
			}
			else if((aKeyword == EKeywordHide) && (alreadyExists)) { 
				alreadyExists->iEntry->iHidden = ETrue;
				alreadyExists->iHidden = ETrue;
				return ETrue;
			}
			else if((aKeyword == EKeywordHide) && (!alreadyExists)) {
				Print(EWarning, "Hiding non-existent file %s on line %d\n",iReader.Word(1),iReader.CurrentLine());
				return ETrue;
			}

			file = new TRomBuilderEntry(iReader.Word(1), epocStartPtr);                   
			file->iExecutable=isPeFile;
			if( aFileCompressOption ) {
				file->iCompressEnabled = ECompressionCompress;
			}
			else if(aFileUncompressOption )	{
				file->iCompressEnabled = ECompressionUncompress;
			}

			TRomNode* node=new TRomNode(epocStartPtr, file);
			if (node==0)
				return EFalse;

			TInt r=ParseFileAttributes(node, file, aKeyword);         
			if (r!=KErrNone)
				return EFalse;

			if(gCompress != ECompressionUnknown) {
				node->iFileUpdate = ETrue;
			}

			if((node->iOverride) || (aFileCompressOption) || (aFileUncompressOption)) {
				node->iFileUpdate = ETrue;
			}

			dir->AddFile(node);	// to drive directory structure.
		}		 
		else {
			// directory
			//for directory creation, given /sys/bin/, it's possible to reach 0 at the end, just ignore that...
			if(!*epocStartPtr)
				break;

			subDir = dir->FindInDirectory(epocStartPtr);      
			if (!subDir){ // sub directory does not exist			
				if(aKeyword==EKeywordHide) {
					Print(EWarning, "Hiding non-existent file %s on line %d\n",
						iReader.Word(1),iReader.CurrentLine());
					return ETrue;
				}
				subDir = dir->NewSubDir(epocStartPtr);
				if (!subDir)
					return EFalse;
			}
			dir=subDir;
			epocStartPtr = epocEndPtr;
		}  // end of else.
	}
	return ETrue;
}


TInt CObeyFile::SetStackSize(TRomNode *aNode, const char* aStr) {
	if (EFalse == IsValidNumber(aStr))
		return Print(EError, "Number required as argument for keyword 'stack'.\n"); 
	TInt size ;
	TInt err = Val(size,aStr);
	if(KErrNone == err){
		aNode->SetStackSize(size );
	}
	return err;
}

TInt CObeyFile::SetHeapSizeMin(TRomNode *aNode, const char* aStr) {
	if (EFalse == IsValidNumber(aStr))
		return Print(EError, "Number required as argument for keyword 'heapmin'.\n");
	TInt size ;
	TInt err = Val(size,aStr);
	if(KErrNone == err){
		aNode->SetHeapSizeMin(size );
	}
	return err;	 
}

TInt CObeyFile::SetHeapSizeMax(TRomNode *aNode, const char* aStr) {
	if (EFalse == IsValidNumber(aStr))
		return Print(EError, "Number required as argument for keyword 'heapmax'.\n");
	TInt size ;
	TInt err = Val(size,aStr);
	if(KErrNone == err){
		aNode->SetHeapSizeMax(size );
	}
	return err;	
	 
}

TInt CObeyFile::SetCapability(TRomNode *aNode, const char* aStr) {
	if ( IsValidNumber(aStr)){
		Print(EDiagnostic,"Old style numeric CAPABILTY specification ignored.\n");
		return KErrNone;
	}
	SCapabilitySet cap;
	TInt r = ParseCapabilitiesArg(cap, (char*)aStr);
	if( KErrNone == r ) {
		aNode->SetCapability( cap );
	}
	return r;
}

TInt CObeyFile::SetPriority(TRomNode *aNode, const char* aStr) {
	TProcessPriority priority;	
	
	if ( IsValidNumber(aStr)) {
		TUint32 temp = 0;
		Val(temp,aStr) ;
		priority = (TProcessPriority)temp ; 
	}
	else {	 
		if (stricmp(aStr, "low")==0)
			priority=EPriorityLow;
		else if (strnicmp(aStr, "background", 4)==0)
			priority=EPriorityBackground;
		else if (strnicmp(aStr, "foreground", 4)==0)
			priority=EPriorityForeground;
		else if (stricmp(aStr, "high")==0)
			priority=EPriorityHigh;
		else if (strnicmp(aStr, "windowserver",3)==0)
			priority=EPriorityWindowServer;
		else if (strnicmp(aStr, "fileserver",4)==0)
			priority=EPriorityFileServer;
		else if (strnicmp(aStr, "realtimeserver",4)==0)
			priority=EPriorityRealTimeServer;
		else if (strnicmp(aStr, "supervisor",3)==0)
			priority=EPrioritySupervisor;
		else
			return Print(EError, "Unrecognised priority keyword.\n");
	}
	if (priority<EPriorityLow || priority>EPrioritySupervisor)
		return Print(EError, "Priority out of range.\n");

	aNode->SetPriority( priority );
	return KErrNone;
}

TInt CObeyFile::SetUid1(TRomNode *aNode, const char* aStr){
	if (EFalse == IsValidNumber(aStr))
		return Print(EError, "Number required as argument for keyword 'uid1'.\n");
	TInt size ;
	TInt err = Val(size,aStr);
	if(KErrNone == err){
		aNode->SetUid1(size );
	}
	return err;		 
}
TInt CObeyFile::SetUid2(TRomNode *aNode, const char* aStr) {
	if (EFalse == IsValidNumber(aStr))
		return Print(EError, "Number required as argument for keyword 'uid2'.\n");
	TInt size ;
	TInt err = Val(size,aStr);
	if(KErrNone == err){
		aNode->SetUid2(size );
	}
	return err;	
}
TInt CObeyFile::SetUid3(TRomNode *aNode, const char* aStr) {
	if (EFalse == IsValidNumber(aStr))
		return Print(EError, "Number required as argument for keyword 'uid3'.\n");
	TInt size ;
	TInt err = Val(size,aStr);
	if(KErrNone == err){
		aNode->SetUid3(size );
	}
	return err;	
}

//
// Process any inline keywords
//
TInt CObeyFile::ParseFileAttributes(TRomNode *aNode, TRomBuilderEntry* aFile, enum EKeyword aKeyword) {
	TInt currentLine = iReader.CurrentLine();
	enum EFileAttribute attribute;
	TInt r=KErrNone;
	TInt index=3;
	char* arg=0;

	while(r==KErrNone) {
		r=iReader.NextAttribute(index,(aFile!=0),attribute,arg);
		if (r!=KErrNone)
			break;
		switch(attribute)
		{
		case EAttributeAtt:
			r=aNode->SetAtt(arg);
			break;
		case EAttributeAttExtra:
			r=aNode->SetAttExtra(arg, aFile, aKeyword);
			break;
		case EAttributeStack:
			r=SetStackSize(aNode, arg);
			break;
		case EAttributeFixed:
			aNode->SetFixed();
			r = KErrNone;
			break;
		case EAttributeUid1:
			r=SetUid1(aNode, arg);
			break;
		case EAttributeUid2:
			r=SetUid2(aNode, arg);
			break;
		case EAttributeUid3:
			r=SetUid3(aNode, arg);
			break;
		case EAttributeHeapMin:
			r=SetHeapSizeMin(aNode, arg);
			break;
		case EAttributeHeapMax:
			r=SetHeapSizeMax(aNode, arg);
			break;
		case EAttributePriority:
			r=SetPriority(aNode, arg);
			break;
		case EAttributeCapability:
			r=SetCapability(aNode, arg);
			break;
		case EAttributeUnpaged:
			aNode->iOverride |= KOverrideCodeUnpaged|KOverrideDataUnpaged;
			aNode->iOverride &= ~(KOverrideCodePaged|KOverrideDataPaged);
			break;
		case EAttributePaged:
			aNode->iOverride |= KOverrideCodePaged;
			aNode->iOverride &= ~(KOverrideCodeUnpaged);
			break;
		case EAttributeUnpagedCode:
			aNode->iOverride |= KOverrideCodeUnpaged;
			aNode->iOverride &= ~KOverrideCodePaged;
			break;
		case EAttributePagedCode:
			aNode->iOverride |= KOverrideCodePaged;
			aNode->iOverride &= ~KOverrideCodeUnpaged;
			break;
		case EAttributeUnpagedData:
			aNode->iOverride |= KOverrideDataUnpaged;
			aNode->iOverride &= ~KOverrideDataPaged;
			break;
		case EAttributePagedData:
			aNode->iOverride |= KOverrideDataPaged;
			aNode->iOverride &= ~KOverrideDataUnpaged;
			break;
		default:
			return Print(EError, "Unrecognised keyword in file attributes on line %d.\n",currentLine);
		}
	}

	if (r==KErrEof)
		return KErrNone;
	return r;
}

//
// Process a parsed line to set up one or more new TRomBuilder entry objects.
// iWord[0] = the keyword (file, primary or secondary)
// iWord[1] = the PC pathname
// iWord[2] = the EPOC pathname
// iWord[3] = start of the file attributes
//
TBool CObeyFile::ProcessFile(TInt /*aAlign*/, enum EKeyword aKeyword){

	TBool isPeFile = ETrue;
	TBool aFileCompressOption, aFileUncompressOption;
	TInt epocPathStart=2;
	aFileCompressOption = aFileUncompressOption = EFalse;
	TBool warnFlag = EFalse;
	static const char aStdPath[] = "SYS\\BIN\\";
	static const int sysBinLength = sizeof(aStdPath)-1;

	// do some validation of the keyword
	TInt currentLine = iReader.CurrentLine();
	switch (aKeyword)
	{
	case EKeywordData:
	case EKeywordHideV2:
		iNumberOfDataFiles++;
		isPeFile = EFalse;
		break;

	case EKeywordFile:
		warnFlag = gEnableStdPathWarning;
		break;
	case EKeywordFileCompress:
		aFileCompressOption = ETrue;
		warnFlag = gEnableStdPathWarning;
		break;
	case EKeywordFileUncompress:
		aFileUncompressOption = ETrue;
		warnFlag = gEnableStdPathWarning;
		break;

	default:
		Print(EError,"Unexpected keyword '%s' on line %d.\n",iReader.Word(0),currentLine);
		return EFalse;
	}

	if (aKeyword!=EKeywordHideV2) {

		// check the PC file exists
		char* nname = NormaliseFileName(iReader.Word(1)); 
		if(gIsOBYUTF8 && !UniConv::IsPureASCIITextStream(nname))
		{
			char* tempnname = strdup(nname);
			unsigned int namelen = 0;
			if(UniConv::UTF82DefaultCodePage(tempnname, strlen(tempnname), &nname, &namelen) < 0)
			{
				Print(EError, "Invalid filename encoding: %s\n", tempnname);
				free(tempnname);
				delete[] nname;
				return EFalse;
			}
			free(tempnname);
		}
		ifstream test(nname);
		if (!test) {
			Print(EError,"Cannot open file %s for input.\n",iReader.Word(1));
			iMissingFiles++;
		}
		test.close();
		delete []nname;
	}
	else
		epocPathStart=1;

	iNumberOfFiles++;


	TBool endOfName=EFalse;
	const char *epocStartPtr=IsValidFilePath(iReader.Word(epocPathStart));
	char *epocEndPtr=const_cast<char*>(epocStartPtr);
	if (epocStartPtr==NULL) {
		Print(EError, "Invalid destination path on line %d\n",currentLine);
		return EFalse;
	}
	if(warnFlag){	// Check for the std destination path(for executables) as per platsec.	
		if(strnicmp(aStdPath,epocStartPtr,sysBinLength) != 0) {
			Print(EWarning,"Invalid destination path on line %d. \"%s\" \n",currentLine,epocStartPtr);
		}
	}

	TRomNode* dir=iRootDirectory;
	TRomNode* subDir=0;
	TRomBuilderEntry *file=0;
	while (!endOfName) {
		endOfName = GetNextBitOfFileName(epocEndPtr);
		if (endOfName) {// file		
			TRomNode* alreadyExists=dir->FindInDirectory(epocStartPtr);
			/*
			* The EKeywordHideV2 keyword is used to indicate that:
			*	1. if the file exists in the same image and then hidden, mark it hidden
			*	2. if the file exists in another image, but in this (ROFS) image, it is
			*		required to hide that file, create a 0 length file entry setting the 'hide'
			*		flag so that at runtime, file gets hidden in the composite filesystem.
			*/
			if ((aKeyword != EKeywordHideV2) && alreadyExists){ // duplicate file		
				if(gKeepGoing){	
					Print(EWarning, "Duplicate file for %s on line %d, will be ignored\n",iReader.Word(1),iReader.CurrentLine());
					switch (aKeyword)
					{
					case EKeywordData:
					case EKeywordHideV2:
						iNumberOfDataFiles--;
					default:
						break;
					}
					iNumberOfFiles--;	 	
					return ETrue;
				}
				else {					
					Print(EError, "Duplicate file for %s on line %d\n",iReader.Word(1),iReader.CurrentLine());
					return EFalse;
				}
			}

			TBool aHidden = aKeyword==EKeywordHideV2;
			/* The file is only marked hidden and hence the source file name isn't known 
			* here as hide statement says :
			*	hide <filename as in ROM>
			* Therefore, create TRomBuilderEntry with iFileName as 0 for hidden file when
			* the file doesn't exist in the same ROM image. Otherwise, the src file name
			* is known because of alreadyExists (which comes from the 'file'/'data' statement).
			*/
			if(aHidden)
				file = new TRomBuilderEntry(0, epocStartPtr);
			else
				file = new TRomBuilderEntry(iReader.Word(1), epocStartPtr);
			file->iExecutable=isPeFile;
			file->iHidden= aHidden;
			if( aFileCompressOption ){
				file->iCompressEnabled = ECompressionCompress;
			}
			else if(aFileUncompressOption )	{
				file->iCompressEnabled = ECompressionUncompress;
			}
			TRomNode* node=new TRomNode(epocStartPtr, file);
			if (node==0)
				return EFalse;
			TInt r=ParseFileAttributes(node, file, aKeyword);
			if (r!=KErrNone)
				return EFalse;

			dir->AddFile(node);	// to ROFS directory structure
			AddFile(file);		// to our list of files
		}		 
		else { // directory		
			subDir = dir->FindInDirectory(epocStartPtr);
			if (!subDir) { // sub directory does not exist			
				subDir = dir->NewSubDir(epocStartPtr);
				if (!subDir)
					return EFalse;
			}
			dir=subDir;
			epocStartPtr = epocEndPtr;
		}
	}
	return ETrue;
}


TBool CObeyFile::ProcessRenaming(enum EKeyword aKeyword) {

	// find existing file
	TBool endOfName=EFalse;
	const char *epocStartPtr=IsValidFilePath(iReader.Word(1));

	// Store the current name and new name to maintain renamed file map
	string currentName=iReader.Word(1);
	string newName=iReader.Word(2);

	char *epocEndPtr= const_cast<char*>(epocStartPtr);
	if (epocStartPtr == NULL) {
		Print(EError, "Invalid source path on line %d\n",iReader.CurrentLine());
		return EFalse;
	}

	char saved_srcname[257];
	strncpy(saved_srcname, iReader.Word(1),257);

	TRomNode* dir=iRootDirectory;
	TRomNode* existingFile=0;
	while (!endOfName){
		endOfName = GetNextBitOfFileName(epocEndPtr);
		if (endOfName) { // file
			existingFile=dir->FindInDirectory(epocStartPtr);
			if (existingFile) {
				TInt fileCount=0;
				TInt dirCount=0;
				existingFile->CountDirectory(fileCount, dirCount);
				if (dirCount != 0 || fileCount != 0) {
					Print(EError, "Keyword %s not applicable to directories - line %d\n",
						iReader.Word(0),iReader.CurrentLine());
					return EFalse;
				}
			}
		}
		else { // directory		
			TRomNode* subDir = dir->FindInDirectory(epocStartPtr);
			if (!subDir) // sub directory does not exist
				break;
			dir=subDir;
			epocStartPtr = epocEndPtr;
		}
	}
	if (aKeyword == EKeywordHide) {
		/*
		* The EKeywordHide keyword is used to indicate that if the file exists in 
		* the primary ROFS image and then hidden in extension ROFS, mark it hidden.
		*/
		if (!existingFile) {
			Print(EWarning, "Hiding non-existent file %s on line %d\n", 
				saved_srcname, iReader.CurrentLine());
			// Just a warning, as we've achieved the right overall effect.
		}
		else if (existingFile->iFileStartOffset==(TUint)KFileHidden){
			Print(EWarning, "Hiding already hidden file %s on line %d\n", 
				saved_srcname, iReader.CurrentLine());
			// We will igrore this request, otherwise it will "undelete" it.
		}
		else {
			//hidden files will not be placed to the image
			existingFile->iHidden = ETrue;
		}
		return ETrue;
	}

	if (!existingFile) {
		Print(EError, "Can't %s non-existent source file %s on line %d\n",
			iReader.Word(0), saved_srcname, iReader.CurrentLine());
		return EFalse;
	}

	epocStartPtr = IsValidFilePath(iReader.Word(2));
	epocEndPtr = const_cast<char*>(epocStartPtr);
	endOfName = EFalse;
	if (epocStartPtr == NULL) {
		Print(EError, "Invalid destination path on line %d\n",iReader.CurrentLine());
		return EFalse;
	}

	TRomNode* newdir=iRootDirectory;
	while (!endOfName) {
		endOfName = GetNextBitOfFileName(epocEndPtr);
		if (endOfName) {// file		
			TRomNode* alreadyExists=newdir->FindInDirectory(epocStartPtr);
			if (alreadyExists && !(alreadyExists->iHidden)) {// duplicate file	
				if(gKeepGoing){
					Print(EWarning, "Duplicate file for %s on line %d, renaming will be skipped\n",saved_srcname,iReader.CurrentLine());
					return ETrue;
				}
				else {							
					Print(EError, "Duplicate file for %s on line %d\n",saved_srcname,iReader.CurrentLine());
					return EFalse;
				}
			}
		}
		else { // directory		
			TRomNode* subDir = newdir->FindInDirectory(epocStartPtr);
			if (!subDir) {// sub directory does not exist			
				subDir = newdir->NewSubDir(epocStartPtr);
				if (!subDir)
					return EFalse;
			}
			newdir=subDir;
			epocStartPtr = epocEndPtr;
		}
	}

	if (aKeyword == EKeywordRename) {
		// rename => remove existingFile and insert into tree at new place
		// has no effect on the iNextExecutable or iNextNodeForSameFile links
		TInt r=ParseFileAttributes(existingFile, existingFile->iEntry, aKeyword);
		if (r!=KErrNone)
			return EFalse;
		existingFile->Rename(dir, newdir, epocStartPtr);
		// Store the current and new name of file in the renamed file map.
		iPatchData->AddToRenamedFileMap(currentName, newName);
		return ETrue;
	}

	// alias => create new TRomNode entry and insert into tree
	TRomNode* node = new TRomNode(epocStartPtr, 0);
	if (node == 0) {
		Print(EError, "Out of memory\n");
		return EFalse;
	}
	node->Alias(existingFile);
	TInt r=ParseFileAttributes(node, 0, aKeyword);
	if (r!=KErrNone)
		return EFalse;
	newdir->AddFile(node);	// to ROFS directory structure, though possibly hidden
	return ETrue;
}

TInt ParsePagingPolicy(const char* policy){
	if(stricmp(policy,"NOPAGING") == 0)
		return EKernelConfigPagingPolicyNoPaging;
	else if (stricmp(policy,"ALWAYSPAGE") == 0)
		return EKernelConfigPagingPolicyAlwaysPage;
	else if(stricmp(policy,"DEFAULTUNPAGED") == 0)
		return EKernelConfigPagingPolicyDefaultUnpaged;
	else if(stricmp(policy,"DEFAULTPAGED") == 0)
		return EKernelConfigPagingPolicyDefaultPaged;
	return KErrArgument;
}

TBool CObeyFile::ProcessKeyword(enum EKeyword aKeyword) { 

	TBool success = ETrue;
	switch (aKeyword)
	{
	case EKeywordRofsName:
		iRomFileName = iReader.DupWord(1);
		break;
	case EKeywordRofsSize:
		Val(iRomSize,iReader.Word(1));
		break;
	case EKeywordVersion:
		{
			istringstream val(iReader.Word(1)); 
			val >> iVersion;
		}
		break;
	case EKeywordRofsChecksum:
		Val(iCheckSum,iReader.Word(1));
		break;
	case EKeywordTime:
		iReader.ProcessTime(iTime);
		break;
	case EKeywordPagingOverride:
		{
			if(iPagingOverrideParsed)
				Print(EWarning, "PagingOverride redefined - previous PagingOverride values lost\n");
			if(iCodePagingOverrideParsed)
				Print(EWarning, "PagingOverride defined - previous CodePagingOverride values lost\n");
			iPagingOverrideParsed = true;
			TInt policy = ParsePagingPolicy(iReader.Word(1));
			if(policy < 0) {
				Print(EError,"Unrecognized option for PAGINGOVERRIDE keyword\n");
				success = false;
			}
			else {
				gCodePagingOverride = policy;
				if((policy == EKernelConfigPagingPolicyNoPaging) || (policy == EKernelConfigPagingPolicyDefaultUnpaged))
					gDataPagingOverride = policy;
			}
		}
		break;
	case EKeywordCodePagingOverride:
		{
			if(iCodePagingOverrideParsed)
				Print(EWarning, "CodePagingOverride redefined - previous CodePagingOverride values lost\n");
			if(iPagingOverrideParsed)
				Print(EWarning, "CodePagingOverride defined - previous PagingOverride values lost\n");
			iCodePagingOverrideParsed = true;
			TInt policy = ParsePagingPolicy(iReader.Word(1));
			if(policy<0)
			{
				Print(EError,"Unrecognized option for CODEPAGINGOVERRIDE keyword\n");
				success = false;
			}
			else
				gCodePagingOverride = policy;
		}
		break;
	case EKeywordDataPagingOverride:
		{
			if(iDataPagingOverrideParsed)
				Print(EWarning, "DataPagingOverride redefined - previous DataPagingOverride values lost\n");
/*			if(iPagingOverrideParsed){
				Print(EError, "DataPagingOverride defined - previous PagingOverride values lost\n");
				success = false;
				break;
			}
*/
			iDataPagingOverrideParsed = true;
			TInt policy = ParsePagingPolicy(iReader.Word(1));
			if(policy < 0) {
				Print(EError,"Unrecognized option for DATAPAGINGOVERRIDE keyword\n");
				success = false;
			}
			else
				gDataPagingOverride = policy;
		}
		break;
	case EKeywordRofsAutoSize:
		iAutoSize = ETrue;
		Val(iAutoPageSize,iReader.Word(1));
		break;
	default:
		// unexpected keyword iReader.Word(0)
		Print(EWarning, "Unexpected keyword '%s' on line %d.\n",iReader.Word(0),iReader.CurrentLine());
		break;
	}

	return success;
}
//
// Checks that the obeyfile has supplied enough variables to continue
//
TBool CObeyFile::GotKeyVariables() {

	TBool retVal=ETrue;
	// Mandatory keywords
	if (iRomFileName == 0) {
		Print(EAlways,"The name of the image file has not been supplied.\n");
		Print(EAlways,"Use the keyword \"rofsname\".\n");
		retVal = EFalse;
	}
	if (iRomSize == 0) {
		Print(EAlways,"The size of the image has not been supplied.\n");
		Print(EAlways,"Use the keyword \"rofssize\".\n");
		retVal = EFalse;
	}
	// Apply defaults as necessary
	if (iTime == 0)	{
		Print(ELog, "No timestamp specified. Using current time...\n");
		ObeyFileReader::TimeNow(iTime);
	}
	Print(ELog, "\nCreating Rofs image %s\n", iRomFileName);
	return retVal;
}

//
// Check the path is valid
//
const char* CObeyFile::IsValidFilePath(const char* aPath) {
	// skip leading "\"
	if (*aPath == '/' || *aPath == '\\')
		aPath++;
	if (*aPath == 0)
		return NULL; // file ends in a backslash

	const char *p = aPath;
	TInt len=0;
	while(*p) {			
		if (*p == '/' || *p == '\\') {
			if (len == 0)
				return NULL;
			len=0;
			p++;
			continue;

		}
		len++;
		p++;
	}
	return (len ? aPath : NULL);
}

const char* CObeyFile::IsValidDirPath(const char* aPath)
{
	const char* walker = aPath;

	//validate path...
	while(*walker)
	{
		if(((*walker=='/') || (*walker=='\\')) && ((*(walker+1)=='/') || (*(walker+1)=='\\')))
			return (const char*)0;
		walker++;
	}

	if((*aPath=='/') || (*aPath=='\\'))
		aPath++;

	return aPath;
}

//
// Move the end pointer past the next directory separator, replacing it with 0
//
TBool CObeyFile::GetNextBitOfFileName(char*& epocEndPtr) {
	while (*epocEndPtr != '/' && *epocEndPtr != '\\'){ // until reach the directory separator		
		if (*epocEndPtr == 0) // if reach end of string, return TRUE, it's the filename
			return ETrue;
		epocEndPtr++;
	}
	*epocEndPtr = 0; // overwrite the directory separator with a 0
	epocEndPtr++; // point past the 0 ready for the next one
	return EFalse;
}

void CObeyFile::AddFile(TRomBuilderEntry* aFile) {
	*iNextFilePtrPtr = aFile;
	iNextFilePtrPtr = &(aFile->iNext);
}

//
// First pass through the obey file to set up key variables
//
TInt CObeyFile::ProcessExtensionRofs(MRofsImage* aKernelRom) { 
	
	iReader.Rewind();
	enum EKeyword keyword;

	// Deal with the "extensionrofs" keyword, which should be first
	// however, you may've found "time" before it.
	while(iReader.NextLine(1,keyword) != KErrEof) {
		if(EKeywordExtensionRofs == keyword)
			break ;		
	}
	if(EKeywordExtensionRofs != keyword) return KErrEof;
	iRomFileName = iReader.DupWord(1);
	Print(ELog, "\n========================================================\n");
	Print(ELog, "Extension ROFS %s starting at line %d\n\n", iRomFileName, iReader.CurrentLine());

	iReader.MarkNext();		// so that we rewind to the line after the extensionrom keyword

	while (iReader.NextLine(1,keyword) != KErrEof) {
		if (keyword == EKeywordExtensionRofs)
			break;
		ProcessExtensionKeyword(keyword);
	}

	if (!GotExtensionVariables(aKernelRom))
		return KErrGeneral;

	// second pass to process the file specifications in the obey file building
	// up the TRomNode directory structure and the TRomBuilderEntry list
	//
	iReader.Rewind();

	//
	if (aKernelRom == 0)
		return Print(EError, "Option to extend a kernel ROFS image not yet implemented\n");

	iRootDirectory = new TRomNode("");

	iLastExecutable = 0;

	(aKernelRom->RootDirectory())->deleteTheFirstNode();


	iRootDirectory = aKernelRom->CopyDirectory(iLastExecutable);
	aKernelRom->SetRootDirectory(iRootDirectory);


	TInt align=0;
	while (iReader.NextLine(2,keyword)!=KErrEof) {
		if (keyword == EKeywordExtensionRofs)
			break;

		switch (keyword)
		{
		case EKeywordHide:
		case EKeywordAlias:
		case EKeywordRename:
			if (!ProcessRenaming(keyword))
				return KErrGeneral;
			break;

		case EKeywordPatchDllData:
			{	
				// Collect patchdata statements to process at the end
				StringVector patchDataTokens;
				SplitPatchDataStatement(patchDataTokens); 
				iPatchData->AddPatchDataStatement(patchDataTokens);									
				break;
			}
		default:
			if (!ProcessFile(align, keyword))
				return KErrGeneral;			
			align=0;
			break;
		}
	}
	
	if(!ParsePatchDllData() )
		return KErrGeneral;

	iReader.MarkNext();			// ready for processing the next extension rom(s)

	if (iMissingFiles!=0)
		return KErrGeneral;
	if (iNumberOfFiles == 0) {
		Print(EError, "No files specified.\n");
		return KErrGeneral;
	}
	return KErrNone;
}
void CObeyFile::ProcessExtensionKeyword(enum EKeyword aKeyword)	{ 

	switch (aKeyword)
	{
	case EKeywordCoreRofsName:
		iKernelRofsName = iReader.DupWord(1);
		return;
	case EKeywordRofsSize:
		Val(iRomSize,iReader.Word(1));
		return;
	case EKeywordVersion:
		{
			istringstream val(iReader.Word(1)); 
			val >> iVersion;
		}
		return;
	case EKeywordRomChecksum:
		Val(iCheckSum,iReader.Word(1)); //--
		return;
	case EKeywordTime:
		iReader.ProcessTime(iTime);
		return;
	case EKeywordRofsAutoSize:
		iAutoSize = ETrue;
		Val(iAutoPageSize , iReader.Word(1)); 
		return;
	default:
		Print(EError,"Keyword '%s' not valid in extension ROFS - line %d\n", iReader.Word(0), iReader.CurrentLine());
		break;
	}
	return;
}
//
// Checks that the obeyfile has supplied enough variables to continue
//
TBool CObeyFile::GotExtensionVariables(MRofsImage* aRom) {

	TBool retVal=ETrue;
	const char* kernelRofsName = iKernelRofsName;

	// Mandatory keywords

	if (iRomSize == 0){
		Print(EAlways,"The size of the extension ROFS has not been supplied.\n");
		Print(EAlways,"Use the keyword \"rofssize\".\n");
		retVal = EFalse;
	}

	// keywords we need if we don't already have a ROFS image to work from
	if (aRom == 0) {
		if (iKernelRofsName == 0) {
			Print(EAlways,"The name of the core ROFS has not been supplied.\n");
			Print(EAlways,"Use the keyword \"rofsname\".\n");
			retVal = EFalse;
		}
	}
	else {
		if (iKernelRofsName != 0){
			Print(EWarning,"Keyword \"rofsname\" ignored.\n");
		}
		kernelRofsName = aRom->RomFileName();
	}

	// validation
	// Apply defaults as necessary
	if (iTime == 0)	{
		Print(ELog, "No timestamp specified. Using current time...\n");
		ObeyFileReader::TimeNow(iTime);
	}

	// fix up "*" in rofsname
	char newname[256];
	char* p=newname;
	char* q=iRomFileName;
	char c;

	while ((c=*q++)!='\0'){
		if (c!='*') {
			*p++=c;
			continue;
		}
		const char *r = kernelRofsName;
		while ((c=*r++)!='\0')
			*p++=c;
	}
	*p++ = '\0';
	delete []iRomFileName;
	size_t len = p - newname ;
	iRomFileName = new char[len];
	memcpy(iRomFileName,newname,len);
	Print(ELog, "\nCreating ROFS image %s\n", iRomFileName);
	return retVal;
}

// Fuction to split patchdata statement 
void CObeyFile::SplitPatchDataStatement(StringVector& aPatchDataTokens) {
	// Get the value of symbol size, address/ordinal and new value 
	// to be patched from the patchdata statement.
	// Syntax of patchdata statements is as follows:
	// 1)	patchdata dll_name  ordinal OrdinalNumber size_in_bytes   new_value 
	// 2)   patchdata dll_name  addr    Address       size_in_bytes   new_value
	for(TInt count=1; count<=5; count++) {
		aPatchDataTokens.push_back(iReader.Word(count));
	}

	// Store the the value of current line which will be used
	// when displaying error messages.
	ostringstream outStrStream;
	outStrStream<<iReader.CurrentLine();
	aPatchDataTokens.push_back(outStrStream.str());
}

TBool CObeyFile::ParsePatchDllData() {
	// Get the list of patchdata statements
	VectorOfStringVector patchDataStatements=iPatchData->GetPatchDataStatements();
	// Get the list of renamed file map
	MapOfString RenamedFileMap=iPatchData->GetRenamedFileMap();

	for(TUint count=0; count < patchDataStatements.size(); count++) {
		
		StringVector strVector = patchDataStatements.at(count);
		string filename=strVector.at(0);
		string lineNoStr = strVector.at(5);
		TUint lineNo = 0 ;
		Val(lineNo,lineNoStr.c_str());
		TRomNode* existingFile = NULL;

		do {
			TRomNode* dir=iRootDirectory;			
			TBool endOfName=EFalse; 
			
			if (!IsValidFilePath(filename.c_str())) {
				Print(EError, "Invalid source path on line %d\n",lineNo);
				return EFalse;
			}
			char* epocStartPtr =NormaliseFileName(filename.c_str());
			char* savedPtr = epocStartPtr;
			if(*epocStartPtr == '/' ||*epocStartPtr == '\\')
				epocStartPtr++ ;
			char* epocEndPtr = epocStartPtr;

			while (!endOfName) {
				endOfName = GetNextBitOfFileName(epocEndPtr);
				if (endOfName) {// file				
					existingFile=dir->FindInDirectory(epocStartPtr);
					if (existingFile) {
						TInt fileCount=0;
						TInt dirCount=0;
						existingFile->CountDirectory(fileCount, dirCount);
						if (dirCount != 0 || fileCount != 0) {
							Print(EError, "Keyword %s not applicable to directories - line %d\n","patchdata",lineNo);
							return EFalse;
						}
					}
				}
				else {// directory				
					TRomNode* subDir = dir->FindInDirectory(epocStartPtr);
					if (!subDir) // sub directory does not exist
						break;
					dir=subDir;
					epocStartPtr = epocEndPtr;
				}
			}
			delete []savedPtr;

			if(!existingFile) {
				// If the E32Image file to be patched is not included then check if the
				// file was renamed.
				MapOfStringIterator RenamedFileMapIterator;
				if ((RenamedFileMapIterator=RenamedFileMap.find(filename)) != RenamedFileMap.end())
					filename = (*RenamedFileMapIterator).second; 				
				else {
					Print(EError, "File %s not found - line %d\n", filename.c_str(), lineNo);
					return EFalse;
				}
			}
		}while(!existingFile);

		TUint32 aSize, aOrdinal, aNewValue, aOffset;
		TLinAddr aDataAddr;

		aOrdinal = (TUint32)-1;
		aDataAddr = (TUint32)-1;
		aOffset = 0;

		string symbolSize=strVector.at(3);
		Val(aSize,symbolSize.c_str());
		string aValue=strVector.at(4);
		Val(aNewValue,aValue.c_str());		

		DllDataEntry *dataEntry = new DllDataEntry(aSize, aNewValue);

		// Set the address of the data or the ordinal number specified in OBY statement.
		string keyword=strVector.at(1);
		string keywordValue=strVector.at(2);

		/* Check for +OFFSET at the end of the ordinal number or address */
		TUint plus = keywordValue.find("+",0);
		if (plus != string::npos) {
			/* Get the offset that we found after the + sign */
			string offset = keywordValue.substr(plus+1);
			Val(aOffset,offset.c_str());

			keywordValue.resize(plus);		
		}
		if(stricmp (keyword.c_str(), "addr") == 0)
			Val(aDataAddr,keywordValue.c_str());

		else 
			Val(aOrdinal,keywordValue.c_str());

		dataEntry->iDataAddress = aDataAddr;
		dataEntry->iOrdinal = aOrdinal;
		dataEntry->iOffset = aOffset;

		existingFile->SetDllData();

		DllDataEntry *aDllDataEntry= existingFile->iEntry->GetFirstDllDataEntry();
		if (aDllDataEntry == NULL) {
			// Set the first node of the patchdata linked list
			aDllDataEntry=dataEntry;
			existingFile->iEntry->SetFirstDllDataEntry(aDllDataEntry);
		}
		else {
			// Goto the last node
			while((aDllDataEntry->NextDllDataEntry()) != NULL) {
				aDllDataEntry = aDllDataEntry->NextDllDataEntry();
			}
			// Add the new node at the end of linked list
			aDllDataEntry->AddDllDataEntry(dataEntry);			
		}
	}
	return ETrue;
}