diff -r 000000000000 -r 7f656887cf89 commands/tail/tail.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/commands/tail/tail.cpp Wed Jun 23 15:52:26 2010 +0100 @@ -0,0 +1,458 @@ +// tail.cpp +// +// Copyright (c) 2005 - 2010 Accenture. All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the "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: +// Accenture - Initial contribution +// + +#include "character_converter.h" +#include "file_reader.h" +#include "tail.h" + +const TInt KBlockSize = 512; +const TText KUnicodeParagraphSeparator = 0x2029; +const TText KUnicodeLineSeparator = 0x2028; +const TText KCarriageReturn = 0x000d; +const TText KLineFeed = 0x000a; + + +// +// CTrailingLineFinder. +// + +class CTrailingLineFinder : public CBase, public MFileReaderObserver + { +public: + static CTrailingLineFinder* NewL(TInt aBlockSize, CCharacterConverter& aCharacterConverter); + ~CTrailingLineFinder(); + void Find(CFileReader& aFileReader, const TDesC& aFileName, TInt aNumLines, MTrailingLineFinderObserver& aObserver); + void Cancel(); + const TDesC& operator[](TInt aIndex) const; +private: + CTrailingLineFinder(CCharacterConverter& aCharacterConverter); + void ConstructL(TInt aBlockSize); + void HandleFileDataL(const TDesC8& aData, TReadType aType, TBool& aContinue); + void FindNextLine(const TDesC& aData, TInt aOffset, TPtrC& aLine, TBool& aComplete); + void AddLineL(const TDesC& aLine, TBool aComplete); +private: // From MFileReaderObserver. + virtual void HandleFileData(const TDesC8& aData, TReadType aType, TBool& aContinue); + virtual void HandleFileReadError(TInt aError); +private: + CFileReader* iFileReader; + CCharacterConverter& iCharacterConverter; + MTrailingLineFinderObserver* iObserver; + HBufC8* iBuf; + TPtr8 iPtr; + RPointerArray iTrailingLines; + TInt iCurrentLineIndex; + TInt iNumLinesToFind; + TInt iNumCompleteLinesFound; + TInt iFilePos; + TBool iPossiblySplitCrLf; + TBool iLastLineNotComplete; + }; + +CTrailingLineFinder* CTrailingLineFinder::NewL(TInt aBlockSize, CCharacterConverter& aCharacterConverter) + { + CTrailingLineFinder* self = new(ELeave) CTrailingLineFinder(aCharacterConverter); + CleanupStack::PushL(self); + self->ConstructL(aBlockSize); + CleanupStack::Pop(self); + return self; + } + +CTrailingLineFinder::~CTrailingLineFinder() + { + delete iBuf; + iTrailingLines.ResetAndDestroy(); + } + +void CTrailingLineFinder::Find(CFileReader& aFileReader, const TDesC& aFileName, TInt aNumLines, MTrailingLineFinderObserver& aObserver) + { + ASSERT(!iFileReader); + iTrailingLines.ResetAndDestroy(); + iFileReader = &aFileReader; + iObserver = &aObserver; + iNumLinesToFind = aNumLines; + iNumCompleteLinesFound = 0; + iCurrentLineIndex = -1; + iPossiblySplitCrLf = EFalse; + iFileReader->Read(aFileName, *this); + } + +void CTrailingLineFinder::Cancel() + { + if (iFileReader) + { + iFileReader->Cancel(); + } + } + +const TDesC& CTrailingLineFinder::operator[](TInt aIndex) const + { + return *iTrailingLines[aIndex]; + } + +void CTrailingLineFinder::HandleFileDataL(const TDesC8& aData, TReadType aType, TBool& aContinue) + { + iFilePos += aData.Length(); + aContinue = ETrue; + const TDesC* convertedData = NULL; + + switch (aType) + { + case MFileReaderObserver::EFirst: + { + convertedData = &iCharacterConverter.ConvertChunkL(aData, CCharacterConverter::EFirst); + break; + } + case MFileReaderObserver::EMiddle: + { + convertedData = &iCharacterConverter.ConvertChunkL(aData, CCharacterConverter::EMiddle); + break; + } + case MFileReaderObserver::ELast: + { + convertedData = &iCharacterConverter.ConvertChunkL(aData, CCharacterConverter::ELast); + break; + } + default: + { + ASSERT(FALSE); + } + } + + if (convertedData->Length() > 0) + { + TInt offset = 0; + FOREVER + { + TPtrC linePtr; + TBool completeLine; + FindNextLine(*convertedData, offset, linePtr, completeLine); + AddLineL(linePtr, completeLine); + if (!completeLine) + { + break; + } + offset += linePtr.Length(); + } + } + + if (aType == MFileReaderObserver::ELast) + { + iFileReader = NULL; + iObserver->HandleTrailingLines(iCurrentLineIndex + 1, iFilePos); + } + } + +void CTrailingLineFinder::AddLineL(const TDesC& aLine, TBool aComplete) + { + if (iLastLineNotComplete) + { + HBufC*& buf = iTrailingLines[iCurrentLineIndex]; + buf = buf->ReAllocL(buf->Length() + aLine.Length()); + buf->Des() += aLine; + } + else + { + HBufC* line = aLine.AllocLC(); + if (iNumCompleteLinesFound <= iNumLinesToFind) + { + ++iCurrentLineIndex; + User::LeaveIfError(iTrailingLines.Append(line)); + } + else + { + delete iTrailingLines[0]; + iTrailingLines.Remove(0); + iTrailingLines.Append(line); // Will always succeed due to the above removal. + } + CleanupStack::Pop(line); + } + + iLastLineNotComplete = !aComplete; + if (aComplete) + { + ++iNumCompleteLinesFound; + } + } + +CTrailingLineFinder::CTrailingLineFinder(CCharacterConverter& aCharacterConverter) + : iCharacterConverter(aCharacterConverter), iPtr(NULL, 0) + { + } + +void CTrailingLineFinder::ConstructL(TInt aBlockSize) + { + iBuf = HBufC8::NewL(aBlockSize); + iPtr.Set(iBuf->Des()); + } + +void CTrailingLineFinder::FindNextLine(const TDesC& aData, TInt aOffset, TPtrC& aLine, TBool& aComplete) + { + const TText* first = aData.Ptr() + aOffset; + const TText* p = first; + const TText* last = first + aData.Length() - aOffset - 1; + aComplete = EFalse; + TBool foundLineBreak = EFalse; + while ((p <= last) && !foundLineBreak) + { + if (iPossiblySplitCrLf) + { + iPossiblySplitCrLf = EFalse; + if (*p == KLineFeed) + { + aComplete = ETrue; + foundLineBreak = ETrue; + } + } + else if ((*p == KUnicodeParagraphSeparator) || (*p == KUnicodeLineSeparator) || (*p == KLineFeed)) + { + aComplete = ETrue; + foundLineBreak = ETrue; + } + else if (*p == KCarriageReturn) + { + if (p == last) + { + iPossiblySplitCrLf = ETrue; + } + else if (*(p + 1) == KLineFeed) + { + aComplete = ETrue; + foundLineBreak = ETrue; + ++p; + } + else + { + aComplete = ETrue; + foundLineBreak = ETrue; + } + } + ++p; + } + + aLine.Set(first, p - first); + } + +void CTrailingLineFinder::HandleFileData(const TDesC8& aData, TReadType aType, TBool& aContinue) + { + TRAPD(err, HandleFileDataL(aData, aType, aContinue)); + if (err) + { + iObserver->HandleTrailingLineError(err); + } + } + +void CTrailingLineFinder::HandleFileReadError(TInt aError) + { + iObserver->HandleTrailingLineError(aError); + } + + +// +// CFileWatcher. +// + +class CFileWatcher : public CActive + { +public: + static CFileWatcher* NewL(RFs& aFs); + ~CFileWatcher(); + void Start(const TDesC& aFileName, MFileChangeObserver& aObserver); +private: + CFileWatcher(RFs& aFs); + void Queue(); +private: // From CActive. + virtual void DoCancel(); + virtual void RunL(); +private: + RFs& iFs; + TFileName iFileName; + MFileChangeObserver* iObserver; + }; + + +CFileWatcher* CFileWatcher::NewL(RFs& aFs) + { + return new(ELeave) CFileWatcher(aFs); + } + +CFileWatcher::~CFileWatcher() + { + Cancel(); + } + +void CFileWatcher::Start(const TDesC& aFileName, MFileChangeObserver& aObserver) + { + ASSERT(!IsActive()); + iFileName = aFileName; + iObserver = &aObserver; + Queue(); + } + +CFileWatcher::CFileWatcher(RFs& aFs) + : CActive(CActive::EPriorityStandard), iFs(aFs) + { + CActiveScheduler::Add(this); + } + +void CFileWatcher::Queue() + { + ASSERT(!IsActive()); + iFs.NotifyChange(ENotifyWrite, iStatus, iFileName); + SetActive(); + } + +void CFileWatcher::DoCancel() + { + iFs.NotifyChangeCancel(iStatus); + } + +void CFileWatcher::RunL() + { + Queue(); + iObserver->HandleFileChange(iFileName); + } + + +// +// CCmdTail. +// + +CCommandBase* CCmdTail::NewLC() + { + CCmdTail* self = new(ELeave) CCmdTail(); + CleanupStack::PushL(self); + self->ConstructL(); + return self; + } + +CCmdTail::~CCmdTail() + { + delete iCharacterConverter; + delete iTrailingLineFinder; + delete iFileWatcher; + delete iFileReader; + } + +CCmdTail::CCmdTail() + : CCommandBase(EManualComplete), iNumLines(10) + { + } + +void CCmdTail::ConstructL() + { + BaseConstructL(); + } + +void CCmdTail::WriteChunkToConsoleL(const TDesC8& aData, TReadType aType) + { + switch (aType) + { + case MFileReaderObserver::EFirst: + { + Write(iCharacterConverter->ConvertChunkL(aData, CCharacterConverter::EFirst)); + break; + } + case MFileReaderObserver::EMiddle: // Treat "middle" and "last" to same as with tailing it's not possible to know if "last" is really last. + case MFileReaderObserver::ELast: + { + Write(iCharacterConverter->ConvertChunkL(aData, CCharacterConverter::EMiddle)); + break; + } + default: + { + ASSERT(FALSE); + } + } + } + +const TDesC& CCmdTail::Name() const + { + _LIT(KName, "tail"); + return KName; + } + +void CCmdTail::DoRunL() + { + iCharacterConverter = CCharacterConverter::NewL(KBlockSize, FsL()); + iTrailingLineFinder = CTrailingLineFinder::NewL(KBlockSize, *iCharacterConverter); + iFileWatcher = CFileWatcher::NewL(Fs()); + iFileReader = CFileReader::NewL(KBlockSize, Fs(), ETrue); + iTrailingLineFinder->Find(*iFileReader, iFileName, iNumLines, *this); + } + +void CCmdTail::OptionsL(RCommandOptionList& aOptions) + { + _LIT(KOptFollow, "follow"); + _LIT(KOptNumLines, "lines"); + aOptions.AppendBoolL(iFollow, KOptFollow); + aOptions.AppendIntL(iNumLines, KOptNumLines); + } + +void CCmdTail::ArgumentsL(RCommandArgumentList& aArguments) + { + _LIT(KArgFileName, "file_name"); + aArguments.AppendFileNameL(iFileName, KArgFileName); + } + +void CCmdTail::HandleTrailingLines(TInt aNumLinesFound, TInt aEndFilePos) + { + if (iFollow) + { + iFilePos = aEndFilePos; + iFileWatcher->Start(iFileName, *this); + } + for (TInt i = 0; i < aNumLinesFound; ++i) + { + Write((*iTrailingLineFinder)[i]); + } + if (!iFollow) + { + Complete(); + } + } + +void CCmdTail::HandleTrailingLineError(TInt aError) + { + PrintError(aError, _L("Problem finding trailing %d lines in %S: %d"), iNumLines, &iFileName, aError); + Complete(aError); + } + +void CCmdTail::HandleFileChange(const TDesC& /*aFileName*/) + { + if (!iFileReader->IsActive()) + { + iFileReader->Read(iFileName, iFilePos, *this); + } + } + +void CCmdTail::HandleFileData(const TDesC8& aData, TReadType aType, TBool& aContinue) + { + aContinue = ETrue; + iFilePos += aData.Length(); + TRAPD(err, WriteChunkToConsoleL(aData, aType)); + if (err) + { + aContinue = EFalse; + PrintError(err, _L("Problem writing chunk of %S to console: %d"), &iFileName, err); + } + } + +void CCmdTail::HandleFileReadError(TInt aError) + { + PrintWarning(_L("Problem reading %S: %d"), &iFileName, aError); + } + + +#ifdef EXE_BUILD +EXE_BOILER_PLATE(CCmdTail) +#endif +