--- /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<HBufC> 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
+