/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "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: This module contains implementation of CStifParser
* class member functions.
*
*/
// INCLUDE FILES
#include <e32std.h>
#include "StifFileParser.h"
#include "StifTestInterface.h"
#include "ParserTracing.h"
// EXTERNAL DATA STRUCTURES
// None
// EXTERNAL FUNCTION PROTOTYPES
// None
// CONSTANTS
// None
// MACROS
// None
// LOCAL CONSTANTS AND MACROS
// None
// MODULE DATA STRUCTURES
// None
// LOCAL FUNCTION PROTOTYPES
// None
// FORWARD DECLARATIONS
// None
// ==================== LOCAL FUNCTIONS =======================================
// None
// ================= MEMBER FUNCTIONS =========================================
/*
-------------------------------------------------------------------------------
Class: CStifFileParser
Method: CStifFileParser
Description: Default constructor
C++ default constructor can NOT contain any code, that
might leave.
Parameters: TCommentType aCommentType: in: Comment type's indication
Return Values: None
Errors/Exceptions: None
Status: Approved
-------------------------------------------------------------------------------
*/
CStifFileParser::CStifFileParser(CStifParser::TCommentType aCommentType)
{
iCommentType = aCommentType;
}
/*
-------------------------------------------------------------------------------
Class: CStifFileParser
Method: ConstructL
Description: Symbian OS second phase constructor
Symbian OS default constructor can leave.
Sets variables.
Parameters: RFs& aFs: in: Handle to valid file server
RFile& aFile: in: Handle to the source file
TBool aIsUnicode in: Is file in unicode format
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CStifFileParser::ConstructL(RFs& aFs,
RFile& aFile,
TBool aIsUnicode)
{
//Initialization
iFileServer = aFs;
iBaseFile = aFile;
iCurrentFile = &aFile;
iIsUnicode = aIsUnicode;
iBytesPerChar = iIsUnicode ? 2 : 1;
//Create file stack (INCLUDE feature)
iFileStack = new (ELeave) CStackDeprecated<RFile, EFalse>;
//Add base file to file names array
TFileName fn;
iCurrentFile->FullName(fn);
HBufC* newFile = fn.AllocLC();
User::LeaveIfError(iFileNames.Append(newFile));
CleanupStack::Pop(newFile);
}
/*
-------------------------------------------------------------------------------
Class: CStifFileParser
Method: NewL
Description: Two-phased constructor.
Starting creating parser with path and file information.
Parameters: RFs& aFs: in: Handle to file server
RFile& aFile: in: Source path definition
TBool aIsUnicode in: Is file in unicode format
TCommentType aCommentType: in: Comment type's indication
Return Values: CStifFileParser* : pointer to CStifFileParser object
Errors/Exceptions: Leaves if ConstructL leaves
Status: Proposal
-------------------------------------------------------------------------------
*/
CStifFileParser* CStifFileParser::NewL(RFs& aFs,
RFile& aFile,
TBool aIsUnicode,
CStifParser::TCommentType aCommentType)
{
// Create CStifParser object
CStifFileParser* parser = new (ELeave) CStifFileParser(aCommentType);
CleanupStack::PushL(parser);
parser->ConstructL(aFs, aFile, aIsUnicode);
CleanupStack::Pop(parser);
return parser;
}
/*
-------------------------------------------------------------------------------
Class: CStifFileParser
Method: ~CStifFileParser
Description: Destructor
Parameters: None
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
CStifFileParser::~CStifFileParser()
{
//Close file stack
ClearFileStack();
delete iFileStack;
//Close section lines array
ClearSectionLinesArray();
iSectionLines.Close();
//Close fila names array
while(iFileNames.Count() > 0)
{
delete iFileNames[0];
iFileNames.Remove(0);
}
iFileNames.Close();
}
/*
-------------------------------------------------------------------------------
Class: CStifFileParser
Method: ReadLineL
Description: Reads line from source file
Parameters: TPtr& aLineBuffer: in: Descriptor in which line loads
TPtr& aEndOfLineBuffer: in: Descriptor in which end of line sequence is loaded (0x0A or 0x0D 0x0A)
Return Values: TBool: determines whether line was readed or not
Errors/Exceptions: Leaves if seek command leaves
Leaves if buffer is too small
Status: Proposal
-------------------------------------------------------------------------------
*/
TBool CStifFileParser::ReadLineL(TPtr& aLineBuffer,
TPtr& aEndOfLineBuffer)
{
//Variables
TBuf8<128> buf8;
TBuf16<128> buf16;
TPtrC16 buf;
TInt pos;
TInt offset;
TChar char0x0A = 0x000A;
TChar char0x0D = 0x000D;
TBuf<1> b0x0A;
b0x0A.Append(char0x0A);
//Reset buffers
aLineBuffer.Zero();
aEndOfLineBuffer.Zero();
//Read from source
User::LeaveIfError(iCurrentFile->Read(buf8));
buf16.Copy(buf8);
if(iIsUnicode)
buf.Set((TUint16 *)(buf8.Ptr()), buf8.Length() / 2);
else
buf.Set(buf16.Ptr(), buf16.Length());
while(buf.Length())
{
//Search for end of line char
pos = buf.Find(b0x0A);
//If found, append readed data to output descriptor and move back file offset to correct position
if(pos >= 0)
{
offset = -((buf.Length() - pos - 1) * iBytesPerChar);
User::LeaveIfError(iCurrentFile->Seek(ESeekCurrent, offset));
buf.Set(buf.Ptr(), pos + 1);
aLineBuffer.Append(buf);
break;
}
//Otherwise, append whole buffer to output descriptor
else
{
aLineBuffer.Append(buf);
}
//Read next part of data
User::LeaveIfError(iCurrentFile->Read(buf8));
buf16.Copy(buf8);
if(iIsUnicode)
{
buf.Set((TUint16 *)(buf8.Ptr()), buf8.Length() / 2);
}
else
{
buf.Set(buf16.Ptr(), buf16.Length());
}
}
//Set correct end of line buffer
if(buf.Length() > 1)
{
if(buf[buf.Length() - 2] == char0x0D)
{
aEndOfLineBuffer.Append(char0x0D);
}
}
if(buf.Length() > 0)
{
if(buf[buf.Length() - 1] == char0x0A)
{
aEndOfLineBuffer.Append(char0x0A);
}
else
{
aEndOfLineBuffer.Zero();
}
}
if(aEndOfLineBuffer.Length())
{
aLineBuffer.SetLength(aLineBuffer.Length() - aEndOfLineBuffer.Length());
}
//If no data was found, try to get previous file from stack
if(aLineBuffer.Length() + aEndOfLineBuffer.Length() == 0)
{
//Pop file from stack. If stack is empty, then we achieved end of base file
if(!iFileStack->IsEmpty())
{
PopFromFileStack();
aEndOfLineBuffer.Copy(iEolBuf);
return aEndOfLineBuffer.Length();
}
}
//Check if this is include line
else
{
if(aLineBuffer.Find(KIncludeKeyword) == 0)
{
TFileName fn;
TLex lex(aLineBuffer);
fn.Copy(lex.NextToken()); //get INCLUDE keyword
fn.Copy(lex.NextToken()); //get cfg file name
if(fn.Length() > 0)
{
TStifUtil::CorrectFilePathL( fn );
PushFileToStackL(fn);
iEolBuf.Copy(aEndOfLineBuffer);
}
else
{
__TRACE(KError, (_L("No filename was given after INCLUDE. Ignoring")));
}
//Read next line
return ReadLineL(aLineBuffer, aEndOfLineBuffer);
}
}
return aLineBuffer.Length() + aEndOfLineBuffer.Length();
}
/*
-------------------------------------------------------------------------------
Class: CStifFileParser
Method: ReplaceCommentsLineL
Description: Replaces comments with spaces from line and copy it to destination buffer
Parameters: TPtr& aSrcBufPtr: in: Source line with comments
TPtr& aDstBufPtr: out: Destination line without comments
TWhatToFind& aFind: in out: Determines what method currently is looking for
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CStifFileParser::ReplaceCommentsLineL(TPtr& aSrcBufPtr,
TPtr& aDstBufPtr,
TWhatToFind& aFind)
{
//Variables
TLex lex(aSrcBufPtr);
TChar ch;
TChar chSpace(' ');
TChar chSlash('/');
TChar chQuota('\"');
TChar chMulti('*');
TChar chHash('#');
//Clean buffer
aDstBufPtr.Zero();
while(!lex.Eos())
{
ch = lex.Get();
//If the end of line, exit
if(!ch)
{
break;
}
//We're looking for start od quoted text or comment
if(aFind == EStart)
{
//Special character readed
if(ch == chQuota)
{
aFind = EQuota;
aDstBufPtr.Append(chSpace);
}
//Possible start of comment
else if(ch == chSlash)
{
//Comment to the end of line found
if(lex.Peek() == chSlash)
{
break;
}
//Beginning of a comment found
else if(lex.Peek() == chMulti)
{
aFind = EEndOfComment;
ch = lex.Get();
aDstBufPtr.Append(chSpace);
aDstBufPtr.Append(chSpace);
}
//No start of comment - add read slash
else
{
aDstBufPtr.Append(ch);
}
}
//Start of hash comment (to the end of line)
else if(ch == chHash)
{
break;
}
//Append readed character to the destination buffer
else
{
aDstBufPtr.Append(ch);
}
}
//We're looking for the end of quoted text
else if(aFind == EQuota)
{
if(ch == chQuota)
{
aFind = EStart;
aDstBufPtr.Append(chSpace);
}
else
{
aDstBufPtr.Append(chSpace);
}
}
//We're looking for the end of comment
else if(aFind == EEndOfComment)
{
//It may be the end of a comment
if(ch == chMulti)
{
ch = lex.Peek();
//It is the end of a comment
if(ch == chSlash)
{
aFind = EStart;
ch = lex.Get();
aDstBufPtr.Append(chSpace);
aDstBufPtr.Append(chSpace);
}
//It is not the end of a comment, add this character to the destinaton buffer
else
{
aDstBufPtr.Append(chSpace);
}
}
//It is not the end of a comment, add this character to the destinaton buffer
else
{
aDstBufPtr.Append(chSpace);
}
}
}
}
/*
-------------------------------------------------------------------------------
Class: CStifFileParser
Method: ReplaceHashCommentsLineL
Description: Copy line to destination buffer and deletes #-style comments
Parameters: TPtr& aSrcBufPtr: in: Source line with comments
TPtr& aDstBufPtr: out: Destination line without comments
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CStifFileParser::ReplaceHashCommentsLineL(TPtr& aSrcBufPtr,
TPtr& aDstBufPtr)
{
//Variables
TLex lex(aSrcBufPtr);
TChar ch;
TChar chHash('#');
//Prepare destination buffer
aDstBufPtr.Zero();
//Copy source line to destination until # char is found
while(!lex.Eos())
{
ch = lex.Get();
//If the end of line, exit
if(!ch)
{
break;
}
if(ch == chHash)
{
break;
}
else
{
aDstBufPtr.Append(ch);
}
}
}
/*
-------------------------------------------------------------------------------
Class: CStifFileParser
Method: NextSectionL
Description: Finds n-th (aSeeked) section in file starting from m-th (aOffset) position
Parameters: const TDesc& aStartTag: in: Starting tag of a section
const TDesc& aEndTag: in: Ending tag of a section
TInt& aOffset: in out: Current offset in file (*)
TInt aSeeked: in: Which section is to be found
Notification: aOffset has different meaning than before adding INCLUDE functionality.
If it has 0 value, that means that we need to remove files stack and go
back to base file to the beginning. Otherwise we don't change current
file read-handler position.
Return Values: HBufC *: address of a descriptor containing section. Section is returned without tags.
Caller must take care about the freeing descriptor.
Errors/Exceptions: Leaves if aSeeked is less than 1 or aOffset is negative
Leaves if seek command leaves
Leaves if cannot allocate buffers
Leaves if cannot allocate section
Leaves if length of readed line exceeds KMaxLineLength constant
Status: Proposal
-------------------------------------------------------------------------------
*/
HBufC* CStifFileParser::NextSectionL(const TDesC& aStartTag,
const TDesC& aEndTag,
TInt& aOffset,
TInt aSeeked)
{
// Check arguments
if(aSeeked < 1)
{
User::Leave(KErrArgument);
}
if(aOffset < 0)
{
User::Leave(KErrArgument);
}
TInt foundSection = 0;
TInt ret;
// Alloc buffers to read line
const TInt KMaxLineLength = 4096; //If length of readed line exceeds this constant, method will leave
HBufC* buf = HBufC::NewL(KMaxLineLength);
CleanupStack::PushL(buf);
TPtr bufPtr(buf->Des());
HBufC* withoutCommentsBuf = HBufC::NewL(KMaxLineLength);
CleanupStack::PushL(withoutCommentsBuf);
TPtr withoutCommentsBufPtr(withoutCommentsBuf->Des());
HBufC* endOfLine = HBufC::NewL(2); //After reading a line it contains 0D0A or 0A or null (how readed line is ended)
CleanupStack::PushL(endOfLine);
TPtr endOfLinePtr(endOfLine->Des());
//Set valid position in file
//but only if offset shows to 0. Otherwise keep previus position
if(aOffset == 0)
{
User::LeaveIfError(iBaseFile.Seek(ESeekStart, aOffset));
ClearFileStack();
aOffset = 1;
}
//Prepare to read lines
TBool validSectionBeginFound = EFalse;
TBool validSectionEndFound = EFalse;
TSectionFind whatToFindSection = ESectionStart;
//If no start tag is given start to find end tag immediatly
if(aStartTag.Length() == 0)
{
foundSection++;
whatToFindSection = ESectionEnd;
validSectionBeginFound = ETrue;
}
if(aEndTag.Length() == 0)
{
validSectionEndFound = ETrue;
}
TWhatToFind whatToFind = EStart;
//Perform reading file
while(ReadLineL(bufPtr, endOfLinePtr))
{
if(iCommentType == CStifParser::ECStyleComments)
{
ReplaceCommentsLineL(bufPtr, withoutCommentsBufPtr, whatToFind);
}
else
{
ReplaceHashCommentsLineL(bufPtr, withoutCommentsBufPtr);
}
if(whatToFindSection == ESectionStart)
{
//Find in line star tag (if start tag is not given, behave like we've found it)
if(aStartTag.Length() == 0)
{
ret = 0;
}
else
{
ret = withoutCommentsBuf->Find(aStartTag);
}
//If found remember position, move offset of file to actual position
if(ret >= 0)
{
whatToFindSection = ESectionEnd;
TInt offset = -(bufPtr.Length() + endOfLinePtr.Length() - ret - aStartTag.Length()) * iBytesPerChar;
User::LeaveIfError(iCurrentFile->Seek(ESeekCurrent, offset));
whatToFind = EStart; //reset marker, because if we've found tag, we couldn't be in the middle of comment or quota
foundSection++;
//Add this line to section lines array
if(foundSection == aSeeked)
{
validSectionBeginFound = ETrue;
}
continue;
}
}
else if(whatToFindSection == ESectionEnd)
{
//Find in line end tag (if end tag is not given, behave like we've found it)
if(aEndTag.Length() == 0)
{
ret = KErrNotFound;
}
else
{
ret = withoutCommentsBuf->Find(aEndTag);
}
//If found check if this is the one we're looking for
if(ret >= 0)
{
whatToFindSection = ESectionStart;
TInt offset = -(bufPtr.Length() + endOfLinePtr.Length() - ret - aEndTag.Length()) * iBytesPerChar;
User::LeaveIfError(iCurrentFile->Seek(ESeekCurrent, offset));
whatToFind = EStart; //reset marker, because if we've found tag, we couldn't be in the middle of comment or quota
if(foundSection == aSeeked)
{
//Add this line to section lines array
HBufC* line = HBufC::NewLC(bufPtr.Length());
TPtr linePtr(line->Des());
linePtr.Copy(bufPtr.MidTPtr(0, ret));
User::LeaveIfError(iSectionLines.Append(line));
CleanupStack::Pop(line);
validSectionEndFound = ETrue;
break;
}
else
{
continue;
}
}
else
{
//If we're in section we are looking for, add line to array
if(foundSection == aSeeked)
{
HBufC* line = HBufC::NewLC(bufPtr.Length() + endOfLinePtr.Length());
TPtr linePtr(line->Des());
linePtr.Copy(bufPtr);
linePtr.Append(endOfLinePtr);
User::LeaveIfError(iSectionLines.Append(line));
CleanupStack::Pop(line);
}
}
}
}
//Clean data
CleanupStack::PopAndDestroy(endOfLine);
CleanupStack::PopAndDestroy(withoutCommentsBuf);
CleanupStack::PopAndDestroy(buf);
//Load into section if found
HBufC* section = NULL;
if(validSectionBeginFound && validSectionEndFound)
{
//Count amount of memory needed for section
TInt i;
TInt size = 0;
for(i = 0; i < iSectionLines.Count(); i++)
size += iSectionLines[i]->Length();
//Copy section from array to buffer
section = HBufC::NewL(size);
CleanupStack::PushL(section);
TPtr sectionPtr(section->Des());
for(i = 0; i < iSectionLines.Count(); i++)
sectionPtr.Append(*iSectionLines[i]);
ClearSectionLinesArray();
//Clean local data
CleanupStack::Pop(section);
return section;
}
ClearSectionLinesArray();
//If section was not found, then for compability with CSectionParser leave when not first section was seeking, return NULL when first section was seeking
if(foundSection != aSeeked)
{
if( aSeeked - foundSection > 1)
{
User::Leave(KErrNotFound);
}
}
return section;
}
/*
-------------------------------------------------------------------------------
Class: CStifFileParser
Method: ClearFileStack
Description: Closes all files on file stack and clears the stack
Parameters: None
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CStifFileParser::ClearFileStack(void)
{
while(!iFileStack->IsEmpty())
{
PopFromFileStack();
}
}
/*
-------------------------------------------------------------------------------
Class: CStifFileParser
Method: PopFromFileStack
Description: Pops RFile handle from file stack and sets correct current file handle
Parameters: None
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CStifFileParser::PopFromFileStack(void)
{
if(!iFileStack->IsEmpty())
{
//Pop from stack
iCurrentFile = iFileStack->Pop();
TFileName fn;
iCurrentFile->FullName(fn);
//And remove from file names array
for(TInt i = iFileNames.Count() - 1; i >= 0; i--)
{
if(fn.CompareF(iFileNames[i]->Des()) == KErrNone)
{
delete iFileNames[i];
iFileNames.Remove(i);
break;
}
}
__TRACE(KInfo, (_L("Closing file [%S]"), &fn));
//Close file
iCurrentFile->Close();
delete iCurrentFile;
//Set correct current file
if(iFileStack->IsEmpty())
{
iCurrentFile = &iBaseFile; //base file, because stack is empty
}
else
{
iCurrentFile = iFileStack->Last();
}
}
}
/*
-------------------------------------------------------------------------------
Class: CStifFileParser
Method: PushFileToStack
Description: Opens file and pushes it to stack
Parameters: TDesc& aFileName in: name of file to open and add to stack
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CStifFileParser::PushFileToStackL(const TDesC& aFileName)
{
//First check if file is not already included
for(TInt i = 0; i < iFileNames.Count(); i++)
{
if(aFileName.CompareF(iFileNames[i]->Des()) == KErrNone)
{
__TRACE(KError, (_L("File [%S] was already included. Ignoring"), &aFileName));
return;
}
}
//Open and add file to stack
RFile *nf = new RFile();
__TRACE(KInfo, (_L("Including file [%S]"), &aFileName));
TInt r = nf->Open(iFileServer, aFileName, EFileRead | EFileShareAny);
if(r == KErrNone)
{
//Add to stack
iFileStack->PushL(nf);
//And add to file names array
HBufC* newFile = aFileName.AllocLC();
User::LeaveIfError(iFileNames.Append(newFile));
CleanupStack::Pop(newFile);
//Set valid pointer of current file
iCurrentFile = nf;
}
else
{
__TRACE(KError, (_L("Could not open file [%S]. Error %d. Ignoring"), &aFileName, r));
}
}
/*
-------------------------------------------------------------------------------
Class: CStifFileParser
Method: ClearSectionLinesArray
Description: Deletes all descriptors assigned to array and empties array
Parameters: None
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CStifFileParser::ClearSectionLinesArray(void)
{
while(iSectionLines.Count() > 0)
{
delete iSectionLines[0];
iSectionLines.Remove(0);
}
}
// End of File