--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/imgtools/romtools/rofsbuild/r_obey.cpp Tue Oct 27 16:36:35 2009 +0000
@@ -0,0 +1,2274 @@
+/*
+* 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 <string.h>
+
+#ifdef __VC32__
+ #ifdef __MSVCDOTNET__
+ #include <strstream>
+ #include <iomanip>
+ #else //__MSVCDOTNET__
+ #include <strstrea.h>
+ #include <iomanip.h>
+ #endif //__MSVCDOTNET__
+#else // !__VC32__
+#ifdef __TOOLS2__
+ #include <sstream>
+ #include <iomanip>
+ #include <sys/stat.h>
+ using namespace std;
+#else
+ #include <strstrea.h>
+ #include <iomanip.h>
+#endif
+
+#endif //__VC32__
+
+
+#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 "filesysteminterface.h"
+#include "r_driveimage.h"
+
+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.
+
+
+
+#define _P(word) word, sizeof(word)-1 // match prefix, optionally followed by [HWVD]
+#define _K(word) word, 0 // match whole word
+
+const ObeyFileKeyword ObeyFileReader::iKeywords[] =
+{
+ {_K("file"), 2,-2, EKeywordFile, "File to be copied into ROFS"},
+ {_K("data"), 2,-2, EKeywordData, "same as file"},
+
+ {_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("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("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,""}
+};
+
+extern TInt isNumber(TText *aString);
+extern TInt getNumber(TText *aStr);
+
+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(TText* aFileName):
+iObeyFile(0),iMark(0), iMarkLine(0), iCurrentMark(0), iCurrentLine(0), imaxLength(0)
+ {
+
+ iFileName = new TText[strlen((const char *)aFileName)+1];
+ strcpy((char *)iFileName,(const char *)aFileName);
+ iNumWords = 0 ;
+ for(unsigned int i = 0 ; i < KNumWords ; i++)
+ iWord[i] = 0 ;
+ iSuffix = new TText();
+ iLine = 0 ;
+ iCurrentObeyStatement = 0 ;
+ }
+
+
+ObeyFileReader::~ObeyFileReader()
+ {
+ if (iObeyFile)
+ fclose(iObeyFile);
+ iObeyFile=0;
+ delete [] iFileName;
+ delete [] iLine;
+ delete [] iCurrentObeyStatement;
+ }
+
+TBool ObeyFileReader::Open()
+//
+// Open the file & return a status
+//
+{
+ if (!iFileName)
+ {
+ return EFalse;
+ }
+
+ iObeyFile = fopen((const char *)iFileName,"r");
+ if (!iObeyFile)
+ {
+ Print(EError,"Cannot open obey file %s\n",iFileName);
+ return EFalse;
+ }
+ if (SetLineLengthBuffer() != KErrNone)
+ {
+ Print(EError,"Insufficent Memory to Continue.");
+ return EFalse;
+ }
+ return ETrue;
+}
+
+ TInt ObeyFileReader::SetLineLengthBuffer()
+ // Get the Max Line length for the given obey file and allocate the buffer.
+ {
+ char ch = '\0';
+ TInt length = 0;
+
+ Rewind();
+ while ((ch = (char)fgetc(iObeyFile)) != EOF)
+ {
+ length++;
+ if (ch == '\n')
+ {
+ if (length > imaxLength)
+ imaxLength = length;
+ length = 0;
+ }
+ }
+
+ if (length > imaxLength)
+ imaxLength = length;
+
+ if (0 == imaxLength)
+ {
+ Print(EError,"Empty obey file passed as input.");
+ exit(-1);
+ }
+ else if (imaxLength < 2)
+ {
+ Print(EError,"Invalid obey file passed as input.");
+ exit(-1);
+ }
+
+ Rewind();
+ iLine = new TText[imaxLength+1];
+
+ if(!iLine)
+ return KErrNoMemory;
+
+ return KErrNone;
+ }
+
+void ObeyFileReader::Mark()
+ {
+
+ iMark = iCurrentMark;
+ iMarkLine = iCurrentLine-1;
+ }
+
+void ObeyFileReader::MarkNext()
+ {
+
+ iMark = ftell(iObeyFile);
+ iMarkLine = iCurrentLine;
+ }
+
+void ObeyFileReader::Rewind()
+ {
+
+ fseek(iObeyFile,iMark,SEEK_SET);
+ iCurrentMark = iMark;
+ iCurrentLine = iMarkLine;
+ }
+
+void ObeyFileReader::CopyWord(TInt aIndex, TText*& aString)
+ {
+ aString = new TText[strlen((const char *)iWord[aIndex])+1];
+ strcpy((char *)aString, (const char *)iWord[aIndex]);
+ }
+
+TInt ObeyFileReader::ReadAndParseLine()
+ {
+ if (feof(iObeyFile))
+ return KErrEof;
+ iCurrentLine++;
+ iCurrentMark = ftell(iObeyFile);
+ iLine[0]='\0';
+ fgets((char*)iLine,imaxLength+1,iObeyFile);
+ iCurrentObeyStatement = new TText[imaxLength+1];
+ strcpy((char*)iCurrentObeyStatement,(char*)iLine);
+ iNumWords = Parse();
+ return KErrNone;
+ }
+
+TInt ObeyFileReader::NextLine(TInt aPass, enum EKeyword& aKeyword)
+ {
+
+NextLine:
+ TInt err = ReadAndParseLine();
+ if (err == KErrEof)
+ return KErrEof;
+ if (iNumWords == 0 || stricmp((const char*)iWord[0], "rem")==0)
+ goto NextLine;
+ if (stricmp((const char*)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((const char*)iWord[0], k->iKeyword) != 0)
+ continue;
+ iSuffix = 0;
+ }
+ else
+ {
+ // Prefix match
+ if (strnicmp((const char*)iWord[0], k->iKeyword, k->iKeywordLength) != 0)
+ continue;
+ // Suffix must be empty, or a variant number in []
+ iSuffix = iWord[0]+k->iKeywordLength;
+ 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;
+ }
+
+inline TBool ObeyFileReader::IsGap(char ch)
+ {
+ return (ch==' ' || ch=='=' || ch=='\t');
+ }
+
+TInt ObeyFileReader::Parse()
+//
+// splits a line into words, and returns the number of words found
+//
+ {
+
+ TInt i;
+ TText *letter=iLine;
+ TText *end=iLine+strlen((char *)iLine);
+ for (i=0; (TUint)i<KNumWords; i++)
+ iWord[i]=end;
+
+ enum TState {EInWord, EInQuotedWord, EInGap};
+ TState state=EInGap;
+
+ i=0;
+ while ((TUint)i<KNumWords && letter<end)
+ {
+ char ch=*letter;
+ if (ch==0)
+ break;
+ if (ch=='\n')
+ {
+ *letter='\0'; // remove trailing newline left by fgets
+ break;
+ }
+ switch (state)
+ {
+ case EInGap:
+ if (ch=='\"')
+ {
+ if (letter[1]!=0 && letter[1]!='\"')
+ iWord[i++]=letter+1;
+ state=EInQuotedWord;
+ }
+ else if (!IsGap(ch))
+ {
+ iWord[i++]=letter;
+ state=EInWord;
+ }
+ else
+ *letter=0;
+ break;
+ case EInWord:
+ if (ch=='\"')
+ {
+ *letter=0;
+ if (letter[1]!=0 && letter[1]!='\"')
+ iWord[i++]=letter+1;
+ state=EInQuotedWord;
+ }
+ else if (IsGap(ch))
+ {
+ *letter=0;
+ state=EInGap;
+ }
+ break;
+ case EInQuotedWord:
+ if (ch=='\"')
+ {
+ *letter=0;
+ state=EInGap;
+ }
+ break;
+ }
+ letter++;
+ }
+ return i;
+ }
+
+
+void ObeyFileReader::ProcessTime(TInt64& aTime)
+//
+// Process the timestamp
+//
+ {
+ char timebuf[256];
+ if (iNumWords>2)
+ sprintf(timebuf, "%s_%s", iWord[1], iWord[2]);
+ else
+ strcpy(timebuf, (char*)iWord[1]);
+
+ 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;
+ }
+
+/**
+Funtion to get the current oby file line
+*/
+TText* ObeyFileReader::GetCurrentObeyStatement() const
+{
+ return iCurrentObeyStatement;
+}
+
+// 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"},
+// {_K("compress") ,1,1,EAttributeCompress, "Compress file"},
+ {"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, TText*& aArg)
+ {
+NextAttribute:
+ if (aIndex >= iNumWords)
+ return KErrEof;
+ TText* word=iWord[aIndex++];
+ const FileAttributeKeyword* k;
+ for (k=iAttributeKeywords; k->iKeyword!=0; k++)
+ {
+ if (k->iKeywordLength == 0)
+ {
+ // Exact match on keyword
+ if (stricmp((const char*)word, k->iKeyword) != 0)
+ continue;
+ }
+ else
+ {
+ // Prefix match
+ if (strnicmp((const char*)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),
+ iDataSize(0),
+ iDriveFileFormat(0),
+ iConfigurableFatAttributes(new ConfigurableFatAttributes),
+ 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()
+//
+// Destructor
+//
+ {
+ if(iDriveFileName)
+ delete[] iDriveFileName;
+ if(iDriveFileFormat)
+ delete[] iDriveFileFormat;
+ iRootDirectory->deleteTheFirstNode();
+ iRootDirectory->InitializeCount();
+
+ Release();
+ delete [] iRomFileName;
+ if (iRootDirectory)
+ iRootDirectory->Destroy();
+
+ delete iConfigurableFatAttributes;
+ delete iPatchData;
+ }
+
+TBool CObeyFile::AutoSize()
+{
+ return iAutoSize;
+}
+
+TUint32 CObeyFile::AutoPageSize()
+{
+ return iAutoPageSize;
+}
+
+void CObeyFile::Release()
+//
+// Free resources not needed after building a ROM
+//
+ {
+ iFirstFile = 0;
+ iNextFilePtrPtr = &iFirstFile;
+ }
+
+TRomBuilderEntry *CObeyFile::FirstFile()
+ {
+ iCurrentFile = iFirstFile;
+ return iCurrentFile;
+ }
+
+TRomBuilderEntry *CObeyFile::NextFile()
+ {
+ iCurrentFile = iCurrentFile ? iCurrentFile->iNext : 0;
+ return iCurrentFile;
+ }
+
+TText* CObeyFile::ProcessCoreImage()
+ {
+ // check for coreimage keyword and return filename
+ iReader.Rewind();
+ enum EKeyword keyword;
+ TText* coreImageFileName = 0;
+ while (iReader.NextLine(1,keyword) != KErrEof)
+ {
+ if (keyword == EKeywordCoreImage)
+ {
+ #if defined(__TOOLS2__) && defined (_STLP_THREADS)
+ istringstream val(iReader.Word(1),(ios_base::in+ios_base::out));
+ #elif __TOOLS2__
+ istringstream val(iReader.Word(1),(std::_Ios_Openmode)(ios_base::in+ios_base::out));
+ #else
+ istrstream val(iReader.Word(1),strlen(iReader.Word(1)));
+ #endif
+ iReader.CopyWord(1, coreImageFileName);
+ iReader.MarkNext(); // ready for processing extension
+ 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((TText*)"");
+ 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((TText*)"//");
+ 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 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(EWarning,"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:
+ iReader.CopyWord(1, iDriveFileName);
+ break;
+ case EKeywordDataImageFileSystem:
+ iReader.CopyWord(1, iDriveFileFormat);
+ break;
+ case EKeywordDataImageSize:
+ {
+ char* bigString = iReader.Word(1);
+ if(*bigString == '\0')
+ {
+ Print(EWarning,"Not a valid Image Size. Default size is considered\n");
+ break;
+ }
+#ifdef __LINUX__
+ errno = 0;
+ iDataSize = strtoll(bigString,NULL,10);
+ if((iDataSize == LONG_MAX) || (iDataSize == LONG_MIN) ||(errno == ERANGE))
+ {
+ Print(EWarning,"Invalid Range. Default size is considered\n");
+ }
+#else
+ iDataSize = _atoi64(bigString);
+#endif
+ }
+ break;
+ case EKeywordDataImageVolume:
+ {
+ // Get the volume label provided by using "volume" keyword.
+ // e.g. vlolume = NO NAME
+ String volumeLabel = (char*)iReader.GetCurrentObeyStatement();
+ String volumeLabelKeyword = "volume";
+
+ TUint position = volumeLabel.find(volumeLabelKeyword.c_str(),0,volumeLabelKeyword.size());
+ position += volumeLabelKeyword.size();
+ if (volumeLabel.find('=',position) != std::string::npos)
+ {
+ position=volumeLabel.find('=',position);
+ ++position;
+ }
+
+ position = volumeLabel.find_first_not_of(' ',position);
+ if (position != std::string::npos)
+ {
+ volumeLabel = volumeLabel.substr(position);
+
+ // Remove the new line character from the end
+ position = volumeLabel.find_first_of("\r\n");
+ if (position != std::string::npos)
+ volumeLabel = volumeLabel.substr(0,position);
+
+ iConfigurableFatAttributes->iDriveVolumeLabel = volumeLabel.data();
+ }
+ else
+ {
+ Print(EWarning,"Value for Volume Label is not provided. Default value is considered.\n");
+ }
+ break;
+ }
+ case EKeywordDataImageSectorSize:
+ {
+ char* bigString = iReader.Word(1);
+ TInt sectorSize = atoi(bigString);
+ if(sectorSize <= 0)
+ {
+ Print(EWarning,"Invalid Sector Size value. Default value is considered.\n");
+ }
+ else
+ {
+ iConfigurableFatAttributes->iDriveSectorSize = atoi(bigString);
+ }
+ }
+ break;
+ case EKeywordDataImageNoOfFats:
+ {
+ char* bigString = iReader.Word(1);
+ TInt noOfFats = atoi(bigString);
+ if (noOfFats <=0)
+ {
+ Print(EWarning,"Invalid No of FATs specified. Default value is considered.\n");
+ }
+ else
+ {
+ iConfigurableFatAttributes->iDriveNoOfFATs = atoi(bigString);
+ }
+ }
+ 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(iDataSize <= 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)
+ {
+ strupr((char *)iDriveFileFormat);
+ enum TFileSystem check = (TFileSystem)0;
+ if(!(CDriveImage::FormatTranslation(iDriveFileFormat,check)))
+ {
+ 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 EKeywordHide:
+ isPeFile = EFalse;
+ break;
+
+ case EKeywordFile:
+ break;
+
+ case EKeywordFileCompress:
+ aFileCompressOption = ETrue;
+ break;
+
+ case EKeywordFileUncompress:
+ aFileUncompressOption = ETrue;
+ break;
+
+ default:
+ return EFalse;
+ }
+
+ if (aKeyword!=EKeywordHide)
+ {
+ // check the PC file exists
+ char* nname = NormaliseFileName(iReader.Word(1));
+
+#if defined(__MSVCDOTNET__) || defined(__TOOLS2__)
+ ifstream test(nname);
+#else //!__MSVCDOTNET__
+ ifstream test(nname, ios::nocreate);
+#endif //__MSVCDOTNET__
+
+ if (!test)
+ {
+ Print(EError,"Cannot open file %s for input.\n",iReader.Word(1));
+ iMissingFiles++;
+ }
+
+ test.close();
+ if(nname)
+ free(nname);
+ nname = 0;
+ }
+ else
+ epocPathStart=1;
+
+ iNumberOfFiles++;
+
+ TBool endOfName=EFalse;
+ TText *epocStartPtr=IsValidFilePath(iReader.Text(epocPathStart));
+ TText *epocEndPtr=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) // file
+ {
+ TRomNode* alreadyExists=dir->FindInDirectory(epocStartPtr);
+
+ if ((aKeyword != EKeywordHide) && alreadyExists) // duplicate file
+ {
+ 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
+ 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, TText *aStr)
+ {
+ if (isNumber(aStr)==0)
+ return Print(EError, "Number required as argument for keyword 'stack'.\n");
+ aNode->SetStackSize( getNumber(aStr) );
+ return KErrNone;
+ }
+
+TInt CObeyFile::SetHeapSizeMin(TRomNode *aNode, TText *aStr)
+ {
+ if (isNumber(aStr)==0)
+ return Print(EError, "Number required as argument for keyword 'heapmin'.\n");
+ aNode->SetHeapSizeMin( getNumber(aStr) );
+ return KErrNone;
+ }
+
+TInt CObeyFile::SetHeapSizeMax(TRomNode *aNode, TText *aStr)
+ {
+ if (isNumber(aStr)==0)
+ return Print(EError, "Number required as argument for keyword 'heapmax'.\n");
+ aNode->SetHeapSizeMax( getNumber(aStr) );
+ return KErrNone;
+ }
+
+TInt CObeyFile::SetCapability(TRomNode *aNode, TText *aStr)
+ {
+ if (isNumber(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, TText *aStr)
+ {
+ TProcessPriority priority;
+ if (isNumber(aStr))
+ {
+ priority = (TProcessPriority)getNumber(aStr);
+ }
+ else
+ {
+ char *str=(char *)aStr;
+ if (stricmp(str, "low")==0)
+ priority=EPriorityLow;
+ else if (strnicmp(str, "background", 4)==0)
+ priority=EPriorityBackground;
+ else if (strnicmp(str, "foreground", 4)==0)
+ priority=EPriorityForeground;
+ else if (stricmp(str, "high")==0)
+ priority=EPriorityHigh;
+ else if (strnicmp(str, "windowserver",3)==0)
+ priority=EPriorityWindowServer;
+ else if (strnicmp(str, "fileserver",4)==0)
+ priority=EPriorityFileServer;
+ else if (strnicmp(str, "realtimeserver",4)==0)
+ priority=EPriorityRealTimeServer;
+ else if (strnicmp(str, "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, TText *aStr)
+ {
+ if (isNumber(aStr)==0)
+ return Print(EError, "Number required as argument for keyword 'uid1'.\n");
+ aNode->SetUid1( getNumber(aStr) );
+ return KErrNone;
+ }
+TInt CObeyFile::SetUid2(TRomNode *aNode, TText *aStr)
+ {
+ if (isNumber(aStr)==0)
+ return Print(EError, "Number required as argument for keyword 'uid2'.\n");
+ aNode->SetUid2( getNumber(aStr) );
+ return KErrNone;
+ }
+TInt CObeyFile::SetUid3(TRomNode *aNode, TText *aStr)
+ {
+ if (isNumber(aStr)==0)
+ return Print(EError, "Number required as argument for keyword 'uid3'.\n");
+ aNode->SetUid3( getNumber(aStr) );
+ return KErrNone;
+ }
+
+
+TInt CObeyFile::ParseFileAttributes(TRomNode *aNode, TRomBuilderEntry* aFile, enum EKeyword aKeyword)
+//
+// Process any inline keywords
+//
+ {
+ TInt currentLine = iReader.CurrentLine();
+ enum EFileAttribute attribute;
+ TInt r=KErrNone;
+ TInt index=3;
+ TText* 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|KOverrideDataPaged;
+ aNode->iOverride &= ~(KOverrideCodeUnpaged|KOverrideDataUnpaged);
+ 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;
+ }
+
+
+TBool CObeyFile::ProcessFile(TInt /*aAlign*/, enum EKeyword aKeyword)
+//
+// 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 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 defined(__MSVCDOTNET__) || defined(__TOOLS2__)
+ ifstream test(nname);
+#else //!__MSVCDOTNET__
+ ifstream test(nname, ios::nocreate);
+#endif //__MSVCDOTNET__
+
+ if (!test)
+ {
+ Print(EError,"Cannot open file %s for input.\n",iReader.Word(1));
+ iMissingFiles++;
+ }
+ test.close();
+ free(nname);
+ }
+ else
+ epocPathStart=1;
+
+ iNumberOfFiles++;
+
+
+ TBool endOfName=EFalse;
+ TText *epocStartPtr=IsValidFilePath(iReader.Text(epocPathStart));
+ TText *epocEndPtr=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,(const char*)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
+ {
+ 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;
+ TText *epocStartPtr=IsValidFilePath(iReader.Text(1));
+
+ // Store the current name and new name to maintain renamed file map
+ String currentName=iReader.Word(1);
+ String newName=iReader.Word(2);
+
+ TText *epocEndPtr=epocStartPtr;
+ if (epocStartPtr==NULL)
+ {
+ Print(EError, "Invalid source path on line %d\n",iReader.CurrentLine());
+ return EFalse;
+ }
+
+ char saved_srcname[257];
+ strcpy(saved_srcname, iReader.Word(1));
+
+ 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.Text(2));
+ epocEndPtr=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
+ {
+ 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)
+ {
+ #ifdef __TOOLS2__
+ istringstream val(iReader.Word(1));
+ #else
+ istrstream val(iReader.Word(1),strlen(iReader.Word(1)));
+ #endif
+
+#if defined(__MSVCDOTNET__) || defined(__TOOLS2__)
+ val >> setbase(0);
+#endif //__MSVCDOTNET__
+
+ TBool success = ETrue;
+
+ switch (aKeyword)
+ {
+ case EKeywordRofsName:
+ iReader.CopyWord(1, iRomFileName);
+ break;
+ case EKeywordRofsSize:
+ val >> iRomSize;
+ break;
+ case EKeywordVersion:
+ val >> iVersion;
+ break;
+ case EKeywordRofsChecksum:
+ val >> iCheckSum;
+ 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");
+ if(iDataPagingOverrideParsed)
+ Print(EWarning, "PagingOverride defined - previous DataPagingOverride values lost\n");
+ iPagingOverrideParsed = true;
+ TInt policy = ParsePagingPolicy(iReader.Word(1));
+ if(policy<0)
+ {
+ Print(EError,"Unrecognised option for PAGINGOVERRIDE keyword\n");
+ success = false;
+ }
+ else
+ {
+ gCodePagingOverride = policy;
+ 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,"Unrecognised 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,"Unrecognised option for DATAPAGINGOVERRIDE keyword\n");
+ success = false;
+ }
+ else
+ gDataPagingOverride = policy;
+ }
+ break;
+ case EKeywordRofsAutoSize:
+ iAutoSize = ETrue;
+ val >> iAutoPageSize;
+ break;
+ default:
+ // unexpected keyword iReader.Word(0)
+ break;
+ }
+
+ return success;
+ }
+
+TBool CObeyFile::GotKeyVariables()
+//
+// Checks that the obeyfile has supplied enough variables to continue
+//
+ {
+
+ 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;
+ }
+
+
+TText *CObeyFile::IsValidFilePath(TText *aPath)
+//
+// Check the path is valid
+//
+ {
+ // skip leading "\"
+ if (*aPath=='\\')
+ aPath++;
+ if (*aPath==0)
+ return NULL; // file ends in a backslash
+
+ TText *p=aPath;
+ TInt len=0;
+ FOREVER
+ {
+ if (*p==0)
+ return (len ? aPath : NULL);
+ if (*p=='\\')
+ {
+ if (len==0)
+ return NULL;
+ len=0;
+ }
+ len++;
+ p++;
+ }
+ }
+
+TBool CObeyFile::GetNextBitOfFileName(TText **epocEndPtr)
+//
+// Move the end pointer past the next directory separator, replacing it with 0
+//
+ {
+ while (**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);
+ }
+
+
+
+TInt CObeyFile::ProcessExtensionRofs(MRofsImage* aKernelRom)
+ {
+ //
+ // First pass through the obey file to set up key variables
+ //
+
+
+ iReader.Rewind();
+
+ enum EKeyword keyword;
+
+ // Deal with the "extensionrofs" keyword, which should be first
+
+ if (iReader.NextLine(1,keyword) != KErrNone)
+ return KErrEof;
+ if (keyword != EKeywordExtensionRofs)
+ return Print(EError, "Unexpected keyword '%s' at start of extension rom - line %d\n",
+ iReader.Word(0), iReader.CurrentLine());
+
+ iReader.CopyWord(1, iRomFileName);
+ 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((TText*)"");
+
+ 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.Mark(); // 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)
+ {
+ #ifdef __TOOLS2__
+ istringstream val(iReader.Word(1));
+ #else
+ istrstream val(iReader.Word(1),strlen(iReader.Word(1)));
+ #endif
+
+#if defined(__MSVCDOTNET__) || defined(__TOOLS2__)
+ val >> setbase(0);
+#endif //__MSVCDOTNET__
+
+ switch (aKeyword)
+ {
+ case EKeywordCoreRofsName:
+ iReader.CopyWord(1, iKernelRofsName);
+ return;
+ case EKeywordRofsSize:
+ val >> iRomSize;
+ return;
+ case EKeywordVersion:
+ val >> iVersion;
+ return;
+ case EKeywordRomChecksum:
+ val >> iCheckSum;
+ return;
+ case EKeywordTime:
+ iReader.ProcessTime(iTime);
+ return;
+ case EKeywordRofsAutoSize:
+ iAutoSize = ETrue;
+ val >> iAutoPageSize;
+ return;
+ default:
+ Print(EError,"Keyword '%s' not valid in extension ROFS - line %d\n", iReader.Word(0), iReader.CurrentLine());
+ break;
+ }
+ return;
+ }
+
+TBool CObeyFile::GotExtensionVariables(MRofsImage* aRom)
+//
+// Checks that the obeyfile has supplied enough variables to continue
+//
+ {
+
+ TBool retVal=ETrue;
+ TText* 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
+ TText newname[256];
+ TText* p=newname;
+ TText* q=iRomFileName;
+ TText c;
+
+ while ((c=*q++)!='\0')
+ {
+ if (c!='*')
+ {
+ *p++=c;
+ continue;
+ }
+ TText *r=kernelRofsName;
+ while ((c=*r++)!='\0')
+ *p++=c;
+ }
+ *p = '\0';
+ free(iRomFileName);
+ iRomFileName = (TText*)strdup((char*)newname);
+
+ 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.
+ OutputStringStream 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=getNumber(((TText*)lineNoStr.c_str()));
+ TRomNode* existingFile = NULL;
+
+ do
+ {
+ TRomNode* dir=iRootDirectory;
+ TBool endOfName=EFalse;
+
+ TText *epocStartPtr=IsValidFilePath((TText*)filename.c_str());
+ if (epocStartPtr==NULL)
+ {
+ Print(EError, "Invalid source path on line %d\n",lineNo);
+ return EFalse;
+ }
+ epocStartPtr = (TText*)NormaliseFileName((const char*)epocStartPtr);
+ TText *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;
+ }
+ }
+
+ 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);
+ aSize = getNumber((TText*)symbolSize.c_str());
+ String aValue=strVector.at(4);
+ aNewValue = getNumber((TText*)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 != std::string::npos)
+ {
+ /* Get the offset that we found after the + sign */
+ String offset = keywordValue.substr(plus+1);
+ aOffset = getNumber((TText*)offset.c_str());
+
+ keywordValue.resize(plus);
+ }
+ if(stricmp (keyword.c_str(), "addr") == 0)
+ aDataAddr = getNumber((TText*)keywordValue.c_str());
+
+ else
+ aOrdinal = getNumber((TText*)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;
+}
+
+
+
+
+
+
+
+
+