commands/tail/tail.cpp
changeset 0 7f656887cf89
equal deleted inserted replaced
-1:000000000000 0:7f656887cf89
       
     1 // tail.cpp
       
     2 // 
       
     3 // Copyright (c) 2005 - 2010 Accenture. All rights reserved.
       
     4 // This component and the accompanying materials are made available
       
     5 // under the terms of the "Eclipse Public License v1.0"
       
     6 // which accompanies this distribution, and is available
       
     7 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 // 
       
     9 // Initial Contributors:
       
    10 // Accenture - Initial contribution
       
    11 //
       
    12 
       
    13 #include "character_converter.h"
       
    14 #include "file_reader.h"
       
    15 #include "tail.h"
       
    16 
       
    17 const TInt KBlockSize = 512;
       
    18 const TText KUnicodeParagraphSeparator = 0x2029;
       
    19 const TText KUnicodeLineSeparator = 0x2028;
       
    20 const TText KCarriageReturn = 0x000d;
       
    21 const TText KLineFeed = 0x000a;
       
    22 
       
    23 
       
    24 //
       
    25 // CTrailingLineFinder.
       
    26 //
       
    27 
       
    28 class CTrailingLineFinder : public CBase, public MFileReaderObserver
       
    29 	{
       
    30 public:
       
    31 	static CTrailingLineFinder* NewL(TInt aBlockSize, CCharacterConverter& aCharacterConverter);
       
    32 	~CTrailingLineFinder();
       
    33 	void Find(CFileReader& aFileReader, const TDesC& aFileName, TInt aNumLines, MTrailingLineFinderObserver& aObserver);
       
    34 	void Cancel();
       
    35 	const TDesC& operator[](TInt aIndex) const;
       
    36 private:
       
    37 	CTrailingLineFinder(CCharacterConverter& aCharacterConverter);
       
    38 	void ConstructL(TInt aBlockSize);
       
    39 	void HandleFileDataL(const TDesC8& aData, TReadType aType, TBool& aContinue);
       
    40 	void FindNextLine(const TDesC& aData, TInt aOffset, TPtrC& aLine, TBool& aComplete);
       
    41 	void AddLineL(const TDesC& aLine, TBool aComplete);
       
    42 private:	// From MFileReaderObserver.
       
    43 	virtual void HandleFileData(const TDesC8& aData, TReadType aType, TBool& aContinue);
       
    44 	virtual void HandleFileReadError(TInt aError);
       
    45 private:
       
    46 	CFileReader* iFileReader;
       
    47 	CCharacterConverter& iCharacterConverter;
       
    48 	MTrailingLineFinderObserver* iObserver;
       
    49 	HBufC8* iBuf;
       
    50 	TPtr8 iPtr;
       
    51 	RPointerArray<HBufC> iTrailingLines;
       
    52 	TInt iCurrentLineIndex;
       
    53 	TInt iNumLinesToFind;
       
    54 	TInt iNumCompleteLinesFound;
       
    55 	TInt iFilePos;
       
    56 	TBool iPossiblySplitCrLf;
       
    57 	TBool iLastLineNotComplete;
       
    58 	};
       
    59 
       
    60 CTrailingLineFinder* CTrailingLineFinder::NewL(TInt aBlockSize, CCharacterConverter& aCharacterConverter)
       
    61 	{
       
    62 	CTrailingLineFinder* self = new(ELeave) CTrailingLineFinder(aCharacterConverter);
       
    63 	CleanupStack::PushL(self);
       
    64 	self->ConstructL(aBlockSize);
       
    65 	CleanupStack::Pop(self);
       
    66 	return self;
       
    67 	}
       
    68 
       
    69 CTrailingLineFinder::~CTrailingLineFinder()
       
    70 	{
       
    71 	delete iBuf;
       
    72 	iTrailingLines.ResetAndDestroy();
       
    73 	}
       
    74 
       
    75 void CTrailingLineFinder::Find(CFileReader& aFileReader, const TDesC& aFileName, TInt aNumLines, MTrailingLineFinderObserver& aObserver)
       
    76 	{
       
    77 	ASSERT(!iFileReader);
       
    78 	iTrailingLines.ResetAndDestroy();
       
    79 	iFileReader = &aFileReader;
       
    80 	iObserver = &aObserver;
       
    81 	iNumLinesToFind = aNumLines;
       
    82 	iNumCompleteLinesFound = 0;
       
    83 	iCurrentLineIndex = -1;
       
    84 	iPossiblySplitCrLf = EFalse;
       
    85 	iFileReader->Read(aFileName, *this);
       
    86 	}
       
    87 
       
    88 void CTrailingLineFinder::Cancel()
       
    89 	{
       
    90 	if (iFileReader)
       
    91 		{
       
    92 		iFileReader->Cancel();
       
    93 		}
       
    94 	}
       
    95 
       
    96 const TDesC& CTrailingLineFinder::operator[](TInt aIndex) const
       
    97 	{
       
    98 	return *iTrailingLines[aIndex];
       
    99 	}
       
   100 
       
   101 void CTrailingLineFinder::HandleFileDataL(const TDesC8& aData, TReadType aType, TBool& aContinue)
       
   102 	{
       
   103 	iFilePos += aData.Length();
       
   104 	aContinue = ETrue;
       
   105 	const TDesC* convertedData = NULL;
       
   106 
       
   107 	switch (aType)
       
   108 		{
       
   109 		case MFileReaderObserver::EFirst:
       
   110 			{
       
   111 			convertedData = &iCharacterConverter.ConvertChunkL(aData, CCharacterConverter::EFirst);
       
   112 			break;
       
   113 			}
       
   114 		case MFileReaderObserver::EMiddle:
       
   115 			{
       
   116 			convertedData = &iCharacterConverter.ConvertChunkL(aData, CCharacterConverter::EMiddle);
       
   117 			break;
       
   118 			}
       
   119 		case MFileReaderObserver::ELast:
       
   120 			{
       
   121 			convertedData = &iCharacterConverter.ConvertChunkL(aData, CCharacterConverter::ELast);
       
   122 			break;
       
   123 			}
       
   124 		default:
       
   125 			{
       
   126 			ASSERT(FALSE);
       
   127 			}
       
   128 		}
       
   129 
       
   130 	if (convertedData->Length() > 0)
       
   131 		{
       
   132 		TInt offset = 0;
       
   133 		FOREVER
       
   134 			{
       
   135 			TPtrC linePtr;
       
   136 			TBool completeLine;
       
   137 			FindNextLine(*convertedData, offset, linePtr, completeLine);
       
   138 			AddLineL(linePtr, completeLine);
       
   139 			if (!completeLine)
       
   140 				{
       
   141 				break;
       
   142 				}
       
   143 			offset += linePtr.Length();
       
   144 			}
       
   145 		}
       
   146 
       
   147 	if (aType == MFileReaderObserver::ELast)
       
   148 		{
       
   149 		iFileReader = NULL;
       
   150 		iObserver->HandleTrailingLines(iCurrentLineIndex + 1, iFilePos);
       
   151 		}
       
   152 	}
       
   153 
       
   154 void CTrailingLineFinder::AddLineL(const TDesC& aLine, TBool aComplete)
       
   155 	{
       
   156 	if (iLastLineNotComplete)
       
   157 		{
       
   158 		HBufC*& buf = iTrailingLines[iCurrentLineIndex];
       
   159 		buf = buf->ReAllocL(buf->Length() + aLine.Length());
       
   160 		buf->Des() += aLine;
       
   161 		}
       
   162 	else
       
   163 		{
       
   164 		HBufC* line = aLine.AllocLC();
       
   165 		if (iNumCompleteLinesFound <= iNumLinesToFind)
       
   166 			{
       
   167 			++iCurrentLineIndex;
       
   168 			User::LeaveIfError(iTrailingLines.Append(line));
       
   169 			}
       
   170 		else
       
   171 			{
       
   172 			delete iTrailingLines[0];
       
   173 			iTrailingLines.Remove(0);
       
   174 			iTrailingLines.Append(line); // Will always succeed due to the above removal.
       
   175 			}
       
   176 		CleanupStack::Pop(line);
       
   177 		}
       
   178 
       
   179 	iLastLineNotComplete = !aComplete;
       
   180 	if (aComplete)
       
   181 		{
       
   182 		++iNumCompleteLinesFound;
       
   183 		}
       
   184 	}
       
   185 
       
   186 CTrailingLineFinder::CTrailingLineFinder(CCharacterConverter& aCharacterConverter)
       
   187 	: iCharacterConverter(aCharacterConverter), iPtr(NULL, 0)
       
   188 	{
       
   189 	}
       
   190 
       
   191 void CTrailingLineFinder::ConstructL(TInt aBlockSize)
       
   192 	{
       
   193 	iBuf = HBufC8::NewL(aBlockSize);
       
   194 	iPtr.Set(iBuf->Des());
       
   195 	}
       
   196 
       
   197 void CTrailingLineFinder::FindNextLine(const TDesC& aData, TInt aOffset, TPtrC& aLine, TBool& aComplete)
       
   198 	{
       
   199 	const TText* first = aData.Ptr() + aOffset;
       
   200 	const TText* p = first;
       
   201 	const TText* last = first + aData.Length() - aOffset - 1;
       
   202 	aComplete = EFalse;
       
   203 	TBool foundLineBreak = EFalse;
       
   204 	while ((p <= last) && !foundLineBreak)
       
   205 		{
       
   206 		if (iPossiblySplitCrLf)
       
   207 			{
       
   208 			iPossiblySplitCrLf = EFalse;
       
   209 			if (*p == KLineFeed)
       
   210 				{
       
   211 				aComplete = ETrue;
       
   212 				foundLineBreak = ETrue;
       
   213 				}
       
   214 			}
       
   215 		else if ((*p == KUnicodeParagraphSeparator) || (*p == KUnicodeLineSeparator) || (*p == KLineFeed))
       
   216 			{
       
   217 			aComplete = ETrue;
       
   218 			foundLineBreak = ETrue;
       
   219 			}
       
   220 		else if (*p == KCarriageReturn)
       
   221 			{
       
   222 			if (p == last)
       
   223 				{
       
   224 				iPossiblySplitCrLf = ETrue;
       
   225 				}
       
   226 			else if (*(p + 1) == KLineFeed)
       
   227 				{
       
   228 				aComplete = ETrue;
       
   229 				foundLineBreak = ETrue;
       
   230 				++p;
       
   231 				}
       
   232 			else
       
   233 				{
       
   234 				aComplete = ETrue;
       
   235 				foundLineBreak = ETrue;
       
   236 				}
       
   237 			}
       
   238 		++p;
       
   239 		}
       
   240 
       
   241 	aLine.Set(first, p - first);
       
   242 	}
       
   243 
       
   244 void CTrailingLineFinder::HandleFileData(const TDesC8& aData, TReadType aType, TBool& aContinue)
       
   245 	{
       
   246 	TRAPD(err, HandleFileDataL(aData, aType, aContinue));
       
   247 	if (err)
       
   248 		{
       
   249 		iObserver->HandleTrailingLineError(err);
       
   250 		}
       
   251 	}
       
   252 
       
   253 void CTrailingLineFinder::HandleFileReadError(TInt aError)
       
   254 	{
       
   255 	iObserver->HandleTrailingLineError(aError);
       
   256 	}
       
   257 
       
   258 
       
   259 //
       
   260 // CFileWatcher.
       
   261 //
       
   262 
       
   263 class CFileWatcher : public CActive
       
   264 	{
       
   265 public:
       
   266 	static CFileWatcher* NewL(RFs& aFs);
       
   267 	~CFileWatcher();
       
   268 	void Start(const TDesC& aFileName, MFileChangeObserver& aObserver);
       
   269 private:
       
   270 	CFileWatcher(RFs& aFs);
       
   271 	void Queue();
       
   272 private:	// From CActive.
       
   273 	virtual void DoCancel();
       
   274 	virtual void RunL();
       
   275 private:
       
   276 	RFs& iFs;
       
   277 	TFileName iFileName;
       
   278 	MFileChangeObserver* iObserver;
       
   279 	};
       
   280 
       
   281 
       
   282 CFileWatcher* CFileWatcher::NewL(RFs& aFs)
       
   283 	{
       
   284 	return new(ELeave) CFileWatcher(aFs);
       
   285 	}
       
   286 
       
   287 CFileWatcher::~CFileWatcher()
       
   288 	{
       
   289 	Cancel();
       
   290 	}
       
   291 
       
   292 void CFileWatcher::Start(const TDesC& aFileName, MFileChangeObserver& aObserver)
       
   293 	{
       
   294 	ASSERT(!IsActive());
       
   295 	iFileName = aFileName;
       
   296 	iObserver = &aObserver;
       
   297 	Queue();
       
   298 	}
       
   299 
       
   300 CFileWatcher::CFileWatcher(RFs& aFs)
       
   301 	: CActive(CActive::EPriorityStandard), iFs(aFs)
       
   302 	{
       
   303 	CActiveScheduler::Add(this);
       
   304 	}
       
   305 
       
   306 void CFileWatcher::Queue()
       
   307 	{
       
   308 	ASSERT(!IsActive());
       
   309 	iFs.NotifyChange(ENotifyWrite, iStatus, iFileName);
       
   310 	SetActive();
       
   311 	}
       
   312 
       
   313 void CFileWatcher::DoCancel()
       
   314 	{
       
   315 	iFs.NotifyChangeCancel(iStatus);
       
   316 	}
       
   317 
       
   318 void CFileWatcher::RunL()
       
   319 	{
       
   320 	Queue();
       
   321 	iObserver->HandleFileChange(iFileName);
       
   322 	}
       
   323 
       
   324 
       
   325 //
       
   326 // CCmdTail.
       
   327 //
       
   328 
       
   329 CCommandBase* CCmdTail::NewLC()
       
   330 	{
       
   331 	CCmdTail* self = new(ELeave) CCmdTail();
       
   332 	CleanupStack::PushL(self);
       
   333 	self->ConstructL();
       
   334 	return self;
       
   335 	}
       
   336 
       
   337 CCmdTail::~CCmdTail()
       
   338 	{
       
   339 	delete iCharacterConverter;
       
   340 	delete iTrailingLineFinder;
       
   341 	delete iFileWatcher;
       
   342 	delete iFileReader;
       
   343 	}
       
   344 
       
   345 CCmdTail::CCmdTail()
       
   346 	: CCommandBase(EManualComplete), iNumLines(10)
       
   347 	{
       
   348 	}
       
   349 
       
   350 void CCmdTail::ConstructL()
       
   351 	{
       
   352 	BaseConstructL();
       
   353 	}
       
   354 
       
   355 void CCmdTail::WriteChunkToConsoleL(const TDesC8& aData, TReadType aType)
       
   356 	{
       
   357 	switch (aType)
       
   358 		{
       
   359 		case MFileReaderObserver::EFirst:
       
   360 			{
       
   361 			Write(iCharacterConverter->ConvertChunkL(aData, CCharacterConverter::EFirst));
       
   362 			break;
       
   363 			}
       
   364 		case MFileReaderObserver::EMiddle: // Treat "middle" and "last" to same as with tailing it's not possible to know if "last" is really last.
       
   365 		case MFileReaderObserver::ELast:
       
   366 			{
       
   367 			Write(iCharacterConverter->ConvertChunkL(aData, CCharacterConverter::EMiddle));
       
   368 			break;
       
   369 			}
       
   370 		default:
       
   371 			{
       
   372 			ASSERT(FALSE);
       
   373 			}
       
   374 		}
       
   375 	}
       
   376 
       
   377 const TDesC& CCmdTail::Name() const
       
   378 	{
       
   379 	_LIT(KName, "tail");
       
   380 	return KName;
       
   381 	}
       
   382 
       
   383 void CCmdTail::DoRunL()
       
   384 	{
       
   385 	iCharacterConverter = CCharacterConverter::NewL(KBlockSize, FsL());
       
   386 	iTrailingLineFinder = CTrailingLineFinder::NewL(KBlockSize, *iCharacterConverter);
       
   387 	iFileWatcher = CFileWatcher::NewL(Fs());
       
   388 	iFileReader = CFileReader::NewL(KBlockSize, Fs(), ETrue);
       
   389 	iTrailingLineFinder->Find(*iFileReader, iFileName, iNumLines, *this);
       
   390 	}
       
   391 
       
   392 void CCmdTail::OptionsL(RCommandOptionList& aOptions)
       
   393 	{
       
   394 	_LIT(KOptFollow, "follow");
       
   395 	_LIT(KOptNumLines, "lines");
       
   396 	aOptions.AppendBoolL(iFollow, KOptFollow);
       
   397 	aOptions.AppendIntL(iNumLines, KOptNumLines);
       
   398 	}
       
   399 
       
   400 void CCmdTail::ArgumentsL(RCommandArgumentList& aArguments)
       
   401 	{
       
   402 	_LIT(KArgFileName, "file_name");
       
   403 	aArguments.AppendFileNameL(iFileName, KArgFileName);
       
   404 	}
       
   405 
       
   406 void CCmdTail::HandleTrailingLines(TInt aNumLinesFound, TInt aEndFilePos)
       
   407 	{
       
   408 	if (iFollow)
       
   409 		{
       
   410 		iFilePos = aEndFilePos;
       
   411 		iFileWatcher->Start(iFileName, *this);
       
   412 		}
       
   413 	for (TInt i = 0; i < aNumLinesFound; ++i)
       
   414 		{
       
   415 		Write((*iTrailingLineFinder)[i]);
       
   416 		}
       
   417 	if (!iFollow)
       
   418 		{
       
   419 		Complete();
       
   420 		}
       
   421 	}
       
   422 
       
   423 void CCmdTail::HandleTrailingLineError(TInt aError)
       
   424 	{
       
   425 	PrintError(aError, _L("Problem finding trailing %d lines in %S: %d"), iNumLines, &iFileName, aError);
       
   426 	Complete(aError);
       
   427 	}
       
   428 
       
   429 void CCmdTail::HandleFileChange(const TDesC& /*aFileName*/)
       
   430 	{
       
   431 	if (!iFileReader->IsActive())
       
   432 		{
       
   433 		iFileReader->Read(iFileName, iFilePos, *this);
       
   434 		}
       
   435 	}
       
   436 
       
   437 void CCmdTail::HandleFileData(const TDesC8& aData, TReadType aType, TBool& aContinue)
       
   438 	{
       
   439 	aContinue = ETrue;
       
   440 	iFilePos += aData.Length();
       
   441 	TRAPD(err, WriteChunkToConsoleL(aData, aType));
       
   442 	if (err)
       
   443 		{
       
   444 		aContinue = EFalse;
       
   445 		PrintError(err, _L("Problem writing chunk of %S to console: %d"), &iFileName, err);
       
   446 		}
       
   447 	}
       
   448 
       
   449 void CCmdTail::HandleFileReadError(TInt aError)
       
   450 	{
       
   451 	PrintWarning(_L("Problem reading %S: %d"), &iFileName, aError);
       
   452 	}
       
   453 
       
   454 
       
   455 #ifdef EXE_BUILD
       
   456 EXE_BOILER_PLATE(CCmdTail)
       
   457 #endif
       
   458