libraries/iosrv/client/text_formatter.cpp
changeset 0 7f656887cf89
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libraries/iosrv/client/text_formatter.cpp	Wed Jun 23 15:52:26 2010 +0100
@@ -0,0 +1,1295 @@
+// text_formatter.cpp
+// 
+// Copyright (c) 2006 - 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 <e32base.h>
+#include "ioutils.h"
+#include "pod_lexer.h"
+#include "pod_formatter.h"
+
+using namespace IoUtils;
+
+
+//
+// Constants.
+//
+
+_LIT(KSpace, " ");
+_LIT(KNewLine, "\r\n");
+
+
+//
+// CTextBuffer.
+//
+
+EXPORT_C CTextBuffer* CTextBuffer::NewL(TInt aExpandSize)
+	{
+	CTextBuffer* self = CTextBuffer::NewLC(aExpandSize);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+EXPORT_C CTextBuffer* CTextBuffer::NewLC(TInt aExpandSize)
+	{
+	CTextBuffer* self = new(ELeave) CTextBuffer();
+	CleanupStack::PushL(self);
+	self->ConstructL(aExpandSize);
+	return self;
+	}
+
+EXPORT_C CTextBuffer::~CTextBuffer()
+	{
+	Cancel();
+	delete iBuf;
+	delete iScratchBuf;
+	delete iScratchBuf8;
+	iAttributes.Close();
+	}
+
+EXPORT_C void CTextBuffer::Zero()
+	{
+	iBuf->Delete(0, iBuf->Size());
+	iAttributes.Reset();
+	}
+
+EXPORT_C void CTextBuffer::Reset()
+	{
+	iBuf->Reset();
+	iAttributes.Reset();
+	}
+
+EXPORT_C void CTextBuffer::ResetText()
+	{
+	iBuf->Reset();
+	}
+
+EXPORT_C void CTextBuffer::SetAttributesL(TUint aAttributes, ConsoleAttributes::TColor aForegroundColor, ConsoleAttributes::TColor aBackgroundColor)
+	{
+	const TInt numAttributes = iAttributes.Count();
+	if (numAttributes == 0)
+		{
+		if (Length() == 0)
+			{
+			TAttributes attributes(0, aAttributes, aForegroundColor, aBackgroundColor);
+			iAttributes.AppendL(attributes);
+			}
+		else
+			{
+			TAttributes attributes(Length(), aAttributes, aForegroundColor, aBackgroundColor);
+			if (!attributes.Matches(ConsoleAttributes::ENone, ConsoleAttributes::EUnchanged, ConsoleAttributes::EUnchanged))
+				{
+				TAttributes defaultAttributes(0, ConsoleAttributes::ENone, ConsoleAttributes::EUnchanged, ConsoleAttributes::EUnchanged);
+				iAttributes.AppendL(defaultAttributes);
+				TInt err = iAttributes.Append(attributes);
+				if (err != KErrNone)
+					{
+					iAttributes.Reset();
+					User::Leave(err);
+					}
+				}
+			}
+		}
+	else if (iAttributes[numAttributes - 1].Matches(aAttributes, aForegroundColor, aBackgroundColor))
+		{
+		// Do nothing.
+		}
+	else if (iAttributes[numAttributes - 1].iPosition == Length())
+		{
+		TAttributes& att = iAttributes[numAttributes - 1];
+		if (aAttributes & ConsoleAttributes::ENone)
+			{
+			att.iAttributes.iAttributes = aAttributes;
+			att.iAttributes.iForegroundColor = aForegroundColor;
+			att.iAttributes.iBackgroundColor = aBackgroundColor;
+			}
+		else
+			{
+			// Merge on top of existing attributes
+			att.iAttributes.iAttributes |= aAttributes;
+			if (aForegroundColor != ConsoleAttributes::EUnchanged) att.iAttributes.iForegroundColor = aForegroundColor;
+			if (aBackgroundColor != ConsoleAttributes::EUnchanged) att.iAttributes.iBackgroundColor = aBackgroundColor;
+			}
+		}
+	else
+		{
+		TAttributes attributes(Length(), aAttributes, aForegroundColor, aBackgroundColor);
+		iAttributes.AppendL(attributes);
+		}
+	}
+
+EXPORT_C void CTextBuffer::SetAttributesL(const ConsoleAttributes::TAttributes& aAttributes)
+	{
+	SetAttributesL(aAttributes.iAttributes, aAttributes.iForegroundColor, aAttributes.iBackgroundColor);
+	}
+
+EXPORT_C void CTextBuffer::GetCurrentAttributes(TUint& aAttributes, ConsoleAttributes::TColor& aForegroundColor, ConsoleAttributes::TColor& aBackgroundColor) const
+	{
+	if (iAttributes.Count() > 0)
+		{
+		const TAttributes& att = iAttributes[iAttributes.Count() - 1];
+		aAttributes = att.iAttributes.iAttributes;
+		aForegroundColor = att.iAttributes.iForegroundColor;
+		aBackgroundColor = att.iAttributes.iBackgroundColor;
+		}
+	else
+		{
+		aAttributes = ConsoleAttributes::ENone;
+		aForegroundColor = ConsoleAttributes::EUnchanged;
+		aBackgroundColor = ConsoleAttributes::EUnchanged;
+		}
+	}
+
+EXPORT_C void CTextBuffer::GetAttributes(TInt aPos, TUint& aAttributes, ConsoleAttributes::TColor& aForegroundColor, ConsoleAttributes::TColor& aBackgroundColor) const
+	{
+	ASSERT(aPos < Length());
+
+	aAttributes = ConsoleAttributes::ENone;
+	aForegroundColor = ConsoleAttributes::EUnchanged;
+	aBackgroundColor = ConsoleAttributes::EUnchanged;
+
+	const TInt numAttributes = iAttributes.Count();
+	for (TInt i = 0; i < numAttributes; ++i)
+		{
+		const TAttributes& att = iAttributes[i];
+		if (aPos < att.iPosition)
+			{
+			return;
+			}
+		else
+			{
+			aAttributes = att.iAttributes.iAttributes;
+			aForegroundColor = att.iAttributes.iForegroundColor;
+			aBackgroundColor = att.iAttributes.iBackgroundColor;
+			}
+		}
+	}
+
+EXPORT_C void CTextBuffer::AppendL(const TChar& aChar)
+	{
+	TUint16 ch = (TUint16)TUint(aChar); // We don't support surrogate pairs
+	iBuf->InsertL(iBuf->Size(), &ch, 2);
+	}
+
+EXPORT_C void CTextBuffer::AppendL(const TDesC& aText)
+	{
+	iBuf->InsertL(iBuf->Size(), aText.Ptr(), aText.Size());
+	}
+
+EXPORT_C void CTextBuffer::AppendL(const TDesC8& aText)
+	{
+	if (!iScratchBuf)
+		{
+		iScratchBuf = HBufC::NewL(Max(aText.Length(), 256));
+		}
+	if (iScratchBuf->Des().MaxLength() < aText.Length())
+		{
+		iScratchBuf = iScratchBuf->ReAllocL(Max(iScratchBuf->Des().MaxLength()*2, aText.Length()));
+		}
+	iScratchBuf->Des().Copy(aText);	
+	iBuf->InsertL(iBuf->Size(), iScratchBuf->Ptr(), iScratchBuf->Size());
+	}
+
+EXPORT_C void CTextBuffer::AppendL(const CTextBuffer& aText)
+	{
+	AppendL(aText, 0, aText.Length());
+	}
+
+EXPORT_C void CTextBuffer::AppendL(const CTextBuffer& aText, TInt aPosition)
+	{
+	AppendL(aText, aPosition, aText.Length() - aPosition);
+	}
+
+EXPORT_C void CTextBuffer::AppendL(const CTextBuffer& aText, TInt aPosition, TInt aLength)
+	{
+	const TInt numAttributes = aText.iAttributes.Count();
+	if (numAttributes > 0)
+		{
+		TInt textPos = 0;
+		TInt appendCount = 0;
+		TInt blockIndex = 0;
+		const TDesC* block = NULL;
+		do
+			{
+			TUint attributes;
+			ConsoleAttributes::TColor foregroundColor;
+			ConsoleAttributes::TColor backgroundColor;
+			aText.NextBlock(blockIndex, block, attributes, foregroundColor, backgroundColor);
+			if (block != NULL)
+				{
+				if ((textPos + block->Length()) > aPosition)
+					{
+					SetAttributesL(attributes, foregroundColor, backgroundColor);
+					TInt blockOffset = aPosition - textPos;
+					if (blockOffset < 0)
+						{
+						blockOffset = 0;
+						}
+					TPtrC appendBlock(block->Mid(blockOffset, Min(aLength - appendCount, block->Length() - blockOffset)));
+					AppendL(appendBlock);
+					appendCount += appendBlock.Length();
+					if (appendCount == aLength)
+						{
+						break;
+						}
+					}
+				textPos += block->Length();
+				}
+			}
+			while (block != NULL);
+		}
+	else
+		{
+		AppendL(aText.Descriptor().Mid(aPosition, aLength));
+		}
+	}
+
+EXPORT_C void CTextBuffer::AppendFormatL(TRefByValue<const TDesC> aFmt, ...)
+	{
+	VA_LIST list;
+	VA_START(list, aFmt);
+	AppendFormatListL(aFmt, list);
+	}
+
+EXPORT_C void CTextBuffer::AppendFormatListL(const TDesC16& aFmt, VA_LIST& aList)
+	{
+	TOverflowLeave overflow;
+	TInt err = KErrNone;
+	do
+		{
+		TPtr ptr(iScratchBuf->Des());
+		ptr.Zero();
+		TRAP(err, ptr.AppendFormatList(aFmt, aList, &overflow));
+		if (err == KErrOverflow)
+			{
+			iScratchBuf = iScratchBuf->ReAllocL(ptr.MaxLength() * 2);
+			}
+		}
+		while (err == KErrOverflow);
+
+	User::LeaveIfError(err);
+	iBuf->InsertL(iBuf->Size(), iScratchBuf->Ptr(), iScratchBuf->Size());
+	}
+	
+EXPORT_C void CTextBuffer::AppendFormatL(TRefByValue<const TDesC8> aFmt, ...)
+	{
+	VA_LIST list;
+	VA_START(list, aFmt);
+	AppendFormatListL(aFmt, list);
+	}
+	
+EXPORT_C void CTextBuffer::AppendFormatListL(const TDesC8& aFmt, VA_LIST& aList)
+	{
+	TOverflowLeave8 overflow;
+	TInt err = KErrNone;
+	if (!iScratchBuf8)
+		{
+		iScratchBuf8 = HBufC8::NewL(0x100);
+		}
+		
+	do
+		{
+		TPtr8 ptr(iScratchBuf8->Des());
+		ptr.Zero();
+		TRAP(err, ptr.AppendFormatList(aFmt, aList, &overflow));
+		if (err == KErrOverflow)
+			{
+			iScratchBuf8 = iScratchBuf8->ReAllocL(ptr.MaxLength() * 2);
+			}
+		}
+		while (err == KErrOverflow);
+
+	User::LeaveIfError(err);
+	
+	TPtr8 ptr(iScratchBuf8->Des());
+	if (iScratchBuf->Des().MaxLength() < ptr.Length())
+		{
+		iScratchBuf = iScratchBuf->ReAllocL(Max(iScratchBuf->Des().MaxLength()*2, ptr.Length()));
+		}
+	iScratchBuf->Des().Copy(*iScratchBuf8);	
+	
+	iBuf->InsertL(iBuf->Size(), iScratchBuf->Ptr(), iScratchBuf->Size());
+	}
+
+EXPORT_C void CTextBuffer::AppendHumanReadableSizeL(TInt aSize)
+	{
+	AppendHumanReadableSizeL(TInt64(aSize), EColumnAlignedRight);
+	}
+
+EXPORT_C void CTextBuffer::AppendHumanReadableSizeL(TInt64 aSize)
+	{
+	AppendHumanReadableSizeL(aSize, EColumnAlignedRight);
+	}
+
+EXPORT_C void CTextBuffer::AppendHumanReadableSizeL(TInt aSize, TAlignment aAlignment)
+	{
+	AppendHumanReadableSizeL(TInt64(aSize), aAlignment);
+	}
+
+EXPORT_C void CTextBuffer::AppendHumanReadableSizeL(TInt64 aSize, TAlignment aAlignment)
+	{
+	const TInt64 KB = 1024;
+	const TInt64 MB = KB * KB;
+	const TInt64 GB = MB * KB;
+	const TInt64 TB = GB * KB;
+	_LIT(KBytes, " B ");
+	_LIT(KKilobytes, " KB");
+	_LIT(KMegabytes, " MB");
+	_LIT(KGigabytes, " GB");
+	_LIT(KTerabytes, " TB");
+
+	_LIT(KLeftWhole, "%- 7.0f");
+	_LIT(KLeftFrac, "%- 7.2f");
+	_LIT(KRightWhole, "%+ 7.0f");
+	_LIT(KRightFrac, "%+ 7.2f");
+	_LIT(KNormalWhole, "%.0f");
+	_LIT(KNormalFrac, "%.2f");
+
+	const TDesC* suff = &KBytes;
+	TReal n = aSize;
+	TInt64 factor = 1;
+
+	TInt64 absSize = aSize;
+	if (absSize < 0) absSize = -absSize;
+
+	if (absSize >= TB)
+		{
+		suff = &KTerabytes;
+		factor = TB;
+		}
+	else if (absSize >= GB)
+		{
+		suff = &KGigabytes;
+		factor = GB;
+		}
+	else if (absSize >= MB)
+		{
+		suff = &KMegabytes;
+		factor = MB;
+		}
+	else if (absSize >= KB)
+		{
+		suff = &KKilobytes;
+		factor = KB;
+		}
+
+	n = n / (TReal)factor;
+	TBool wholeNumUnits = (absSize & (factor-1)) == 0; // ie aSize % factor == 0
+
+	const TDesC* fmt = NULL;
+	if (aAlignment == EColumnAlignedLeft)
+		{
+		fmt = wholeNumUnits ? &KLeftWhole : &KLeftFrac;
+		}
+	else if (aAlignment == EColumnAlignedRight)
+		{
+		fmt = wholeNumUnits ? &KRightWhole : &KRightFrac;
+		}
+	else
+		{
+		fmt = wholeNumUnits ? &KNormalWhole : &KNormalFrac;
+		}
+
+	AppendFormatL(*fmt, n);
+	AppendL(*suff);
+	}
+
+EXPORT_C void CTextBuffer::AppendSpacesL(TInt aCount)
+	{
+	ASSERT(aCount >= 0);
+	while (aCount > 0)
+		{
+		AppendL(KSpace);
+		--aCount;
+		}
+	}
+
+EXPORT_C void CTextBuffer::Delete(TInt aPos, TInt aLength)
+	{
+	//for (TInt i = 0; i < iAttributes.Count(); i++)
+	//	{
+	//	RDebug::Printf("attribute %d: pos %d", i, iAttributes[i].iPosition);
+	//	}
+
+	// Find the first and last attributes that are inside the delete region.
+	TInt firstAttribute = -1;
+	TInt lastAttribute = -1;
+	TInt numAttributes = iAttributes.Count();
+	for (TInt i = (numAttributes - 1); i >= 0; --i)
+		{
+		const TAttributes& thisAttribute = iAttributes[i];
+		if ((lastAttribute == -1) && (thisAttribute.iPosition >= aPos) && (thisAttribute.iPosition < aPos + aLength))
+			{
+			lastAttribute = i;
+			}
+		if (lastAttribute >= 0 && thisAttribute.iPosition >= aPos)
+			{
+			firstAttribute = i;
+			}
+		}
+	//RDebug::Printf("firstAttribute=%d lastAttribute=%d", firstAttribute, lastAttribute);
+
+	if ((lastAttribute > 0))
+		{
+		// Set the position of the last affected attribute to just after the deleted chunk.
+		iAttributes[lastAttribute].iPosition = aPos + aLength;
+
+		// Remove all but the last affected attribute. Need to keep the last affected attribute
+		// because it has an impact on the formatting of text that appears after the deleted chunk.
+		for (TInt i = (lastAttribute - 1); i >= firstAttribute; --i)
+			{
+			iAttributes.Remove(i);
+			}
+		}
+
+	// Update the positions of attributes after the block being deleted.
+	numAttributes = iAttributes.Count();
+	for (TInt i = Max(0, firstAttribute); i < numAttributes; ++i)
+		{
+		TAttributes& thisAttribute = iAttributes[i];
+		if (thisAttribute.iPosition >= aPos + aLength)
+			{
+			thisAttribute.iPosition -= aLength;
+			}
+		}
+	iBuf->Delete(aPos * 2, aLength * 2);
+	}
+
+EXPORT_C TInt CTextBuffer::Length() const
+	{
+	return iBuf->Size() / 2;
+	}
+
+EXPORT_C const TDesC& CTextBuffer::Descriptor() const
+	{
+	TPtr8 narrowPtr(iBuf->Ptr(0));
+	iPtr.Set((const TUint16 *)narrowPtr.Ptr(), narrowPtr.Length() / 2);
+	return iPtr;
+	}
+
+EXPORT_C const TDesC& CTextBuffer::Descriptor(TInt aPos, TInt aLength) const
+	{
+	TPtr8 narrowPtr(iBuf->Ptr(aPos * 2));
+	iPtr.Set((const TUint16 *)narrowPtr.Ptr(), aLength);
+	return iPtr;
+	}
+	
+EXPORT_C TPtrC8 CTextBuffer::Collapse()
+	{
+	TPtr8 narrowPtr(iBuf->Ptr(0));
+	TPtr ptr((TUint16 *)narrowPtr.Ptr(), narrowPtr.Length() / 2, narrowPtr.Length() / 2);
+	return ptr.Collapse();
+	}
+
+EXPORT_C TInt CTextBuffer::Write(RIoWriteHandle& aWriteHandle) const
+	{
+	return Write(aWriteHandle, 0, Length());
+	}
+
+EXPORT_C void CTextBuffer::Write(RIoWriteHandle& aWriteHandle, TRequestStatus& aStatus) const
+	{
+	Write(aWriteHandle, 0, Length(), aStatus);
+	}
+
+EXPORT_C TInt CTextBuffer::Write(RIoWriteHandle& aWriteHandle, TInt aPosition, TInt aLength) const
+	{
+	const TInt numAttributes = iAttributes.Count();
+	if ((aWriteHandle.AttachedToConsole() > 0) && (numAttributes > 0))
+		{
+		// Write the text buffer to the console a block at a time, updating the console's attributes at the beginning of each block.
+		TInt blockIndex = 0;
+		TInt textPos = 0;
+		TInt writeCount = 0;
+		const TDesC* block = NULL;
+		do
+			{
+			TUint attributes;
+			ConsoleAttributes::TColor foregroundColor;
+			ConsoleAttributes::TColor backgroundColor;
+			NextBlock(blockIndex, block, attributes, foregroundColor, backgroundColor);
+			if (block != NULL)
+				{
+				if ((textPos + block->Length()) > aPosition)
+					{
+					RIoConsoleWriteHandle(aWriteHandle).SetAttributes(attributes, foregroundColor, backgroundColor); // Ignore error - may not be supported.
+					TInt blockOffset = aPosition - textPos;
+					if (blockOffset < 0)
+						{
+						blockOffset = 0;
+						}
+					TPtrC toWrite(block->Mid(blockOffset, Min(aLength - writeCount, block->Length() - blockOffset)));
+					TInt err = aWriteHandle.Write(toWrite);
+					if (err)
+						{
+						return err;
+						}
+					writeCount += toWrite.Length();
+					if (writeCount == aLength)
+						{
+						break;
+						}
+					}
+				textPos += block->Length();
+				}
+			}
+			while (block != NULL);
+		}
+	else
+		{
+		return aWriteHandle.Write(Descriptor().Mid(aPosition, aLength));
+		}
+
+	return KErrNone;
+	}
+
+EXPORT_C void CTextBuffer::Write(RIoWriteHandle& aWriteHandle, TInt aPosition, TInt aLength, TRequestStatus& aStatus) const
+	{
+	const TInt numAttributes = iAttributes.Count();
+	if ((aWriteHandle.AttachedToConsole() > 0) && (numAttributes > 0))
+		{
+		// Write the text buffer asynchronously to the console a block at a time, updating the console's attributes at the beginning of each block.
+		if (!IsAdded())
+			{
+			CActiveScheduler::Add(const_cast<CTextBuffer*>(this));
+			}
+		aStatus = KRequestPending;
+		iWriteStatus = &aStatus;
+		iAsyncBlockIndex = 0;
+		iAsyncWritePos = 0;
+		iAsyncWriteStartPos = aPosition;
+		iAsyncWriteLength = aLength;
+		iConsoleWriteHandle = aWriteHandle;
+		AsyncWriteNextBlock();
+		}
+	else
+		{
+		aWriteHandle.Write(Descriptor().Mid(aPosition, aLength), aStatus);
+		}
+	}
+
+CTextBuffer::CTextBuffer()
+	: CActive(CActive::EPriorityStandard), iPtr(NULL, 0)
+	{
+	}
+
+void CTextBuffer::ConstructL(TInt aExpandSize)
+	{
+	iBuf = CBufFlat::NewL(aExpandSize);
+	iScratchBuf = HBufC::NewL(aExpandSize);
+	}
+
+void CTextBuffer::NextBlock(TInt& aBlockIndex, const TDesC*& aText, TUint& aAttributes, ConsoleAttributes::TColor& aForegroundColor, ConsoleAttributes::TColor& aBackgroundColor) const
+	{
+	if (iAttributes.Count() > 0)
+		{
+		if (aBlockIndex < iAttributes.Count())
+			{
+			const TAttributes& thisAttribute = iAttributes[aBlockIndex];
+			aAttributes = thisAttribute.iAttributes.iAttributes;
+			aForegroundColor = thisAttribute.iAttributes.iForegroundColor;
+			aBackgroundColor = thisAttribute.iAttributes.iBackgroundColor;
+			TInt length;
+			if (aBlockIndex < (iAttributes.Count() - 1))
+				{
+				length = iAttributes[aBlockIndex + 1].iPosition - thisAttribute.iPosition;
+				}
+			else
+				{
+				length = Length() - thisAttribute.iPosition;
+				}
+			aText = &Descriptor(thisAttribute.iPosition, length);
+			++aBlockIndex;
+			}
+		else
+			{
+			aText = NULL;
+			}
+		}
+	else
+		{
+		// There are no attributes, so block zero is the whole buffer.
+		ASSERT((aBlockIndex == 0) || (aBlockIndex == 1));
+		if (aBlockIndex == 0)
+			{
+			aAttributes = ConsoleAttributes::ENone;
+			aForegroundColor = ConsoleAttributes::EUnchanged;
+			aBackgroundColor = ConsoleAttributes::EUnchanged;
+			aText = &Descriptor(0, Length());
+			++aBlockIndex;
+			}
+		else
+			{
+			aText = NULL;
+			}
+		}
+	}
+
+void CTextBuffer::AsyncWriteNextBlock() const
+	{
+	const TDesC* block = NULL;
+	TUint attributes;
+	ConsoleAttributes::TColor foregroundColor;
+	ConsoleAttributes::TColor backgroundColor;
+	NextBlock(iAsyncBlockIndex, block, attributes, foregroundColor, backgroundColor);
+	if (block != NULL)
+		{
+		if ((iAsyncWritePos + block->Length()) > iAsyncWriteStartPos)
+			{
+			iConsoleWriteHandle.SetAttributes(attributes, foregroundColor, backgroundColor); // Ignore error - may not be supported.
+			TInt blockOffset = iAsyncWriteStartPos - iAsyncWritePos;
+			if (blockOffset < 0)
+				{
+				blockOffset = 0;
+				}
+			iAsyncWritePtr.Set(block->Mid(blockOffset, Min(iAsyncWriteLength, block->Length() - blockOffset)));
+			iAsyncWriteLength -= iAsyncWritePtr.Length();
+			iConsoleWriteHandle.Write(iAsyncWritePtr, const_cast<TRequestStatus&>(iStatus));
+			const_cast<CTextBuffer*>(this)->SetActive();
+			}
+		iAsyncWritePos += block->Length();
+		}
+	else
+		{
+		User::RequestComplete(iWriteStatus, KErrNone);
+		}
+	}
+
+void CTextBuffer::RunL()
+	{
+	if (iStatus.Int() || (iAsyncWriteLength <= 0))
+		{
+		User::RequestComplete(iWriteStatus, iStatus.Int());
+		}
+	else
+		{
+		AsyncWriteNextBlock();
+		}
+	}
+
+void CTextBuffer::DoCancel()
+	{
+	iConsoleWriteHandle.WriteCancel();
+	}
+
+CTextBuffer::TAttributes::TAttributes(TInt aPosition, TUint aAttributes, ConsoleAttributes::TColor aForegroundColor, ConsoleAttributes::TColor aBackgroundColor)
+	: iPosition(aPosition), iAttributes(aAttributes, aForegroundColor, aBackgroundColor)
+	{
+	}
+
+TBool CTextBuffer::TAttributes::Matches(TUint aAttributes, ConsoleAttributes::TColor aForegroundColor, ConsoleAttributes::TColor aBackgroundColor) const
+	{
+	return ((aAttributes == iAttributes.iAttributes) && (aForegroundColor == iAttributes.iForegroundColor) && (aBackgroundColor == iAttributes.iBackgroundColor));
+	}
+
+
+//
+// CTextFormatter.
+//
+
+EXPORT_C CTextFormatter* CTextFormatter::NewL(TInt aAvailableWidth)
+	{
+	CTextFormatter* self = CTextFormatter::NewLC(aAvailableWidth);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+EXPORT_C CTextFormatter* CTextFormatter::NewLC(TInt aAvailableWidth)
+	{
+	CTextFormatter* self = new(ELeave) CTextFormatter(aAvailableWidth);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	return self;
+	}
+
+EXPORT_C CTextFormatter* CTextFormatter::NewL(RIoConsoleWriteHandle& aConsoleWriteHandle)
+	{
+	CTextFormatter* self = CTextFormatter::NewLC(aConsoleWriteHandle);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+EXPORT_C CTextFormatter* CTextFormatter::NewLC(RIoConsoleWriteHandle& aConsoleWriteHandle)
+	{
+	TSize size;
+	if (aConsoleWriteHandle.AttachedToConsole())
+		{
+		User::LeaveIfError(aConsoleWriteHandle.GetScreenSize(size));
+		}
+	else
+		{
+		size.iWidth = 80;
+		}
+	CTextFormatter* self = CTextFormatter::NewLC(size.iWidth);
+	self->iWriteHandle = &aConsoleWriteHandle;
+	self->iAttributesSupported = (aConsoleWriteHandle.SetAttributes(0) == KErrNone);
+	return self;
+	}
+
+EXPORT_C CTextFormatter::~CTextFormatter()
+	{
+	}
+
+CTextFormatter::CTextFormatter(TInt aAvailableWidth)
+	: iAvailableWidth(aAvailableWidth - 1)
+	{
+	}
+
+void CTextFormatter::ConstructL()
+	{
+	CTextBuffer::ConstructL(0x100);
+	}
+
+EXPORT_C void CTextFormatter::WrapL(TInt aIndent, const TDesC& aText)
+	{
+	AppendSpacesL(aIndent);
+	WrapL(aIndent, aIndent, aText);
+	}
+
+EXPORT_C void CTextFormatter::WrapL(TInt aStartPosition, TInt aIndent, const TDesC& aText)
+	{
+	TUint originalAttributes = ConsoleAttributes::ENone;
+	ConsoleAttributes::TColor foregroundColor = ConsoleAttributes::EUnchanged;
+	ConsoleAttributes::TColor backgroundColor = ConsoleAttributes::EUnchanged;
+	if (iAttributesSupported)
+		{
+		GetCurrentAttributes(originalAttributes, foregroundColor, backgroundColor);
+		}
+
+	CTextBuffer* buffer = CTextBuffer::NewLC(0x100);
+	DecodeInteriorPodSequencesL(aText, *buffer);
+	TLex lex(buffer->Descriptor());
+	TInt linePos = aStartPosition;
+	while (!lex.Eos())
+		{
+		lex.Mark();
+		lex.SkipSpace();
+		TPtrC whiteSpace(lex.MarkedToken());
+		if (whiteSpace.Length() > 0)
+			{
+			TInt newLinePos;
+			do
+				{
+				newLinePos = whiteSpace.Find(KNewLine);
+				if (newLinePos >= 0)
+					{
+					AppendSpacesL(iAvailableWidth - linePos - 1);
+					AppendL(KNewLine);
+					AppendSpacesL(aIndent);
+					linePos = aIndent;
+					whiteSpace.Set(whiteSpace.Mid(newLinePos + KNewLine().Length()));
+					}
+				}
+				while (newLinePos >= 0);
+
+			if (iAvailableWidth < whiteSpace.Length())
+				{
+				AppendSpacesL(iAvailableWidth - linePos - 1);
+				AppendL(KNewLine);
+				AppendSpacesL(aIndent);
+				linePos = aIndent;
+				}
+			else
+				{
+				AppendL(whiteSpace);
+				linePos += whiteSpace.Length();
+				}
+			}
+
+		lex.Mark();
+		lex.SkipCharacters();
+		TPtrC word(lex.MarkedToken());
+		const TInt wordLength = word.Length();
+		if (wordLength > 0)
+			{
+			if ((linePos + wordLength + KNewLine().Length()) > iAvailableWidth)
+				{
+				AppendL(KNewLine);
+				AppendSpacesL(aIndent);
+				linePos = aIndent;
+				}
+
+			if (iAttributesSupported)
+				{
+				for (TInt i = 0; i < wordLength; ++i)
+					{
+					TUint attributes;
+					buffer->GetAttributes(lex.Offset() - wordLength + i, attributes, foregroundColor, backgroundColor);
+					SetAttributesL(originalAttributes | attributes, foregroundColor, backgroundColor);
+					AppendL(word[i]);
+					}
+				}
+			else
+				{
+				AppendL(word);
+				}
+			linePos += wordLength;
+			}
+		}
+
+	CleanupStack::PopAndDestroy(buffer);
+	SetAttributesL(originalAttributes, foregroundColor, backgroundColor);
+	}
+
+EXPORT_C void CTextFormatter::TabulateL(TInt aIndent, TInt aGap, const TDesC& aText)
+	{
+	TabulateL(aIndent, aGap, aText, EWrapLastColumn);
+	}
+
+EXPORT_C void CTextFormatter::TabulateL(TInt aIndent, TInt aGap, const TDesC& aText, TTabMode aMode)
+	{
+	CTextBuffer* scratchBuffer = NULL;
+	RArray<TInt> columnWidths;
+	CleanupClosePushL(columnWidths);
+	TLex lineLexer(aText);
+
+	// Find column widths.
+	TPtrC line;
+	while (NextLine(lineLexer, line))
+		{
+		TLex columnLexer(line);
+		TInt col = 0;
+		TPtrC column;
+		while (NextColumn(columnLexer, column))
+			{
+			if (col >= columnWidths.Count())
+				{
+				User::LeaveIfError(columnWidths.Append(aGap));
+				}
+			TInt thisColumnWidth = ActualLength(column);
+			if (columnWidths[col] < (thisColumnWidth + aGap))
+				{
+				columnWidths[col] = (thisColumnWidth + aGap);
+				}
+			++col;
+			}
+		}
+
+	const TInt numCols = columnWidths.Count();
+
+	if (numCols == 0)
+		{
+		// No columns found, so just copy aText directly.
+		AppendL(aText);
+		CleanupStack::PopAndDestroy(&columnWidths);
+		return;
+		}
+
+	if (aMode == EWrapLastColumn)
+		{
+		// Find the widest of all but the last column.
+		TInt sum = aIndent;
+		for (TInt i = 0; i < (numCols - 1); ++i)
+			{
+			sum += columnWidths[i];
+			}
+		TInt remainingSpace = iAvailableWidth - sum;
+		if (remainingSpace < 0)
+			{
+			// If you hit this, it means that the text cannot be columnised because the columns preceding the last one take up more space than the overall.
+			User::Leave(KErrTooBig); // Let's go for something marginally more descriptive than KErrGeneral
+			}
+		// Assign the remaining space to the last column (which will be wrapped).
+		columnWidths[numCols - 1] = remainingSpace; 
+		}
+	else if (aMode == ETruncateLongestColumn)
+		{
+		// Find the total width of all the columns.
+		TInt sum = aIndent;
+		for (TInt i = 0; i < numCols; ++i)
+			{
+			sum += columnWidths[i];
+			}
+		TInt excess = iAvailableWidth - sum;
+		if (excess < 0)
+			{
+			// Not enough space, so steal from the widest column until there is.
+			while (excess < 0)
+				{
+				TInt widestColumn = -1;
+				TInt widestColumnWidth = 0;
+				for (TInt i = 0; i < numCols; ++i)
+					{
+					if (columnWidths[i] > widestColumnWidth)
+						{
+						widestColumnWidth = columnWidths[i];
+						widestColumn = i;
+						}
+					}
+				if (widestColumn < 0)
+					{
+					User::Leave(KErrGeneral);
+					}
+				--columnWidths[widestColumn];
+				++excess;
+				}
+			}
+		else
+			{
+			// Assign execess to the last column.
+			columnWidths[numCols - 1] += excess; 
+			}
+		}
+	else if (aMode == EIgnoreAvailableWidth)
+		{
+		// just use the column widths we've already worked out, no matter if they're too wide.
+		}
+	else
+		{
+		ASSERT(EFalse);
+		}
+
+	// Write formatted text to the buffer.
+	lineLexer = TLex(aText);
+	TInt linePos = 0;
+	while (NextLine(lineLexer, line))
+		{
+		AppendSpacesL(aIndent);
+		linePos += aIndent;
+		TLex columnLexer(line);
+		TInt col = 0;
+		TPtrC column;
+		while (NextColumn(columnLexer, column))
+			{
+			TBool isLastColumn(col == (numCols - 1));
+			const TInt columnWidth = columnWidths[col];
+			if ((aMode == ETruncateLongestColumn) || !isLastColumn)
+				{
+				if (scratchBuffer == NULL)
+					{
+					scratchBuffer = CTextBuffer::NewLC(0x100);
+					}
+				else
+					{
+					scratchBuffer->Zero();
+					}
+				DecodeInteriorPodSequencesL(column, *scratchBuffer);
+				TInt availableWidth = columnWidth;
+				if (!isLastColumn)
+					{
+					availableWidth -= aGap;
+					}
+				if (scratchBuffer->Descriptor().Length() > availableWidth)
+					{
+					scratchBuffer->Delete(availableWidth, scratchBuffer->Descriptor().Length() - availableWidth);
+					}
+				AppendL(*scratchBuffer);
+				AppendSpacesL(columnWidth - scratchBuffer->Descriptor().Length());
+				linePos += columnWidth;
+				}
+			else if (aMode == EIgnoreAvailableWidth)
+				{
+				AppendL(column);
+				if (!isLastColumn)
+					{
+					AppendSpacesL(columnWidth - column.Length());
+					}
+				linePos += columnWidth;
+				}
+			else
+				{
+				WrapL(linePos, linePos, column);
+				}
+			++col;
+			}
+		AppendL(KNewLine);
+		linePos = 0;
+		}
+
+	if (scratchBuffer)
+		{
+		CleanupStack::PopAndDestroy(scratchBuffer);
+		}
+	CleanupStack::PopAndDestroy(&columnWidths);
+	}
+
+EXPORT_C void CTextFormatter::ColumnizeL(TInt aIndent, TInt aGap, const TDesC& aText)
+	{
+	// Store the tab delimited text as an array of TPtrCs.
+	RArray<TPtrC> items;
+	CleanupClosePushL(items);
+	TLex lex(aText);
+	TPtrC column;
+	while (NextColumn(lex, column))
+		{
+		User::LeaveIfError(items.Append(column));
+		}
+	ColumnizeL(aIndent, aGap, items.Array());
+	CleanupStack::PopAndDestroy(1, &items);
+	}
+
+EXPORT_C void CTextFormatter::ColumnizeL(TInt aIndent, TInt aGap, const TArray<TPtrC>& aItems)
+	{
+	// Lays out tab delimited data as a set of columns that read like a news paper
+	// (i.e. starting with the left most column, you read down to the bottom of it
+	// and then start reading the next column from the top, etc.).
+
+	// Determine the smallest number of rows that allows all the data to fit
+	// the available horizontal space. The algorithm works by iteratively increasing
+	// the number of rows from one until a fit is found.
+	const TInt numItems = aItems.Count();
+	RArray<TInt> columnWidths;
+	CleanupClosePushL(columnWidths);
+	TInt numRows = 0;
+	TBool fits(EFalse);
+	while (!fits)
+		{
+		++numRows;
+		columnWidths.Reset();
+		TInt requiredWidth = aIndent;
+		TInt col = 0;
+		TInt i = 0;
+		while (i < numItems)
+			{
+			for (TInt row = 0; row < numRows; ++row)
+				{
+				if (columnWidths.Count() <= col)
+					{
+					User::LeaveIfError(columnWidths.Append(0));
+					}
+				TInt thisItemWidth = ActualLength(aItems[i]);
+				if (thisItemWidth > columnWidths[col])
+					{
+					columnWidths[col] = thisItemWidth;
+					}
+				if (++i >= numItems)
+					{
+					break;
+					}
+				}
+			requiredWidth += columnWidths[col];
+			if ((requiredWidth + aGap) > iAvailableWidth)
+				{
+				if ((requiredWidth + KNewLine().Length()) > iAvailableWidth)
+					{
+					if (col > 0)
+						{
+						// Not enough space with this number of rows, so try another row.
+						i = 0;
+						break;
+						}
+					else
+						{
+						// Not enough space even with just one column. Lay out as a single column anyway and accept ugly wrapping by the console.
+						CleanupStack::PopAndDestroy(1, &columnWidths);
+						for (TInt i = 0; i < numItems; ++i)
+							{
+							AppendSpacesL(aIndent);
+							AppendL(aItems[i]);
+							AppendL(KNewLine);
+							}
+						return;
+						}
+					}
+				else
+					{
+					requiredWidth += KNewLine().Length();
+					}
+				}
+			else
+				{
+				requiredWidth += aGap;
+				}
+			++col;
+			}
+		if (i >= numItems)
+			{
+			fits = ETrue;
+			}
+		}
+
+	// Layout the columns.
+	const TInt numCols = columnWidths.Count();
+	for (TInt row = 0; row < numRows; ++row)
+		{
+		AppendSpacesL(aIndent);
+		for (TInt col = 0; col < numCols; ++col)
+			{
+			TInt index = (numRows * col) + row;
+			if (index < numItems)
+				{
+				TPtrC item(aItems[index]);
+				DoAppendPodL(item);
+				TInt gap = (col == (numCols - 1)) ? 0 : aGap;
+				AppendSpacesL((columnWidths[col] + gap) - ActualLength(item));
+				}
+			}
+		AppendL(KNewLine);
+		}
+
+	CleanupStack::PopAndDestroy(1, &columnWidths);
+	}
+
+EXPORT_C void CTextFormatter::AppendPodL(const TDesC& aPod)
+	{
+	TPodFormatter podFormatter(*this);
+	podFormatter.FormatL(aPod);
+	}
+
+EXPORT_C TInt CTextFormatter::Write()
+	{
+	return CTextBuffer::Write(*iWriteHandle);
+	}
+
+void CTextFormatter::DoAppendPodL(const TDesC& aPod)
+	{
+	DecodeInteriorPodSequencesL(aPod, *this);
+	}
+
+void CTextFormatter::DecodeInteriorPodSequencesL(const TDesC& aPod, CTextBuffer& aBuffer) const
+	{
+	TPodLexer lexer(aPod);
+	TBool eop(EFalse);
+	TBool eos(EFalse);
+
+	TUint originalAttributes = ConsoleAttributes::ENone;
+	ConsoleAttributes::TColor foregroundColor = ConsoleAttributes::EUnchanged;
+	ConsoleAttributes::TColor backgroundColor = ConsoleAttributes::EUnchanged;
+	if (iAttributesSupported)
+		{
+		aBuffer.GetCurrentAttributes(originalAttributes, foregroundColor, backgroundColor);
+		}
+	TUint currentAttributes = originalAttributes;
+
+	while (!eos)
+		{
+		TPtrC token;
+		TPodLexer::TTokenType tokenType;
+		TUint attributes;
+		lexer.NextTokenL(token, tokenType, attributes, eop, eos);
+
+		switch (tokenType)
+			{
+			case TPodLexer::EAttributePush:
+			case TPodLexer::EAttributePop:
+				if (!iAttributesSupported && (attributes & TPodLexer::EBold))
+					{
+					aBuffer.AppendL('*');
+					}
+				else if (!iAttributesSupported && (attributes & TPodLexer::EItalic))
+					{
+					aBuffer.AppendL('\'');
+					}
+				else if ((!iAttributesSupported && (attributes & TPodLexer::EFileName)) || (attributes & TPodLexer::TPodLexer::ECode))
+					{
+					aBuffer.AppendL('"');
+					}
+				break;
+			case TPodLexer::ELink:
+				{
+				TInt pos = token.Locate('|');
+				if (pos >= 0)
+					{
+					token.Set(token.Left(pos));
+					}
+				}
+				// Deliberate fall through.
+			case TPodLexer::ETextBlock:
+			case TPodLexer::ECodeBlock:
+			case TPodLexer::EIndexEntry:
+				if (iAttributesSupported)
+					{
+					if ((attributes == 0) || (attributes & (TPodLexer::ENull | TPodLexer::ECode)))
+						{
+						currentAttributes = originalAttributes;
+						}
+					if (attributes & (TPodLexer::EBold | TPodLexer::EItalic | TPodLexer::EFileName))
+						{
+						currentAttributes |= ConsoleAttributes::EBold;
+						}
+					aBuffer.SetAttributesL(currentAttributes);
+					}
+				aBuffer.AppendL(token);
+				break;
+			default:
+				ASSERT(EFalse);
+			}
+		}
+
+	if (iAttributesSupported && (currentAttributes != originalAttributes))
+		{
+		aBuffer.SetAttributesL(originalAttributes);
+		}
+	}
+
+EXPORT_C void CTextFormatter::Zero()
+	{
+	CTextBuffer::Zero();
+	}
+
+EXPORT_C void CTextFormatter::Reset()
+	{
+	CTextBuffer::Reset();
+	}
+
+EXPORT_C void CTextFormatter::ResetText()
+	{
+	CTextBuffer::ResetText();
+	}
+
+TBool CTextFormatter::NextColumn(TLex& aLex, TPtrC& aPtr) const
+	{
+	aLex.Mark();
+	while (!aLex.Eos())
+		{
+		if (aLex.Get() == '\t')
+			{
+			aLex.UnGet();
+			aPtr.Set(aLex.MarkedToken());
+			aLex.Get();
+			return ETrue;
+			}
+		}
+	if (aLex.TokenLength() > 0)
+		{
+		aPtr.Set(aLex.MarkedToken());
+		return ETrue;
+		}
+	return EFalse;
+	}
+
+TBool CTextFormatter::NextLine(TLex& aLex, TPtrC& aPtr) const
+	{
+	aLex.Mark();
+	while (!aLex.Eos())
+		{
+		TChar ch = aLex.Get();
+		if (ch == '\r')
+			{
+			if (aLex.Get() == '\n')
+				{
+				aLex.UnGet();
+				aLex.UnGet();
+				aPtr.Set(aLex.MarkedToken());
+				aLex.Get();
+				aLex.Get();
+				return ETrue;
+				}
+			else
+				{
+				aLex.UnGet();
+				}
+			}
+		}
+	if (aLex.TokenLength() > 0)
+		{
+		aPtr.Set(aLex.MarkedToken());
+		return ETrue;
+		}
+	return EFalse;
+	}
+
+TInt CTextFormatter::ActualLength(const TDesC& aPod) const
+	{
+	CTextBuffer* buffer = CTextBuffer::NewLC(0x100);
+	DecodeInteriorPodSequencesL(aPod, *buffer);
+	TInt length = buffer->Descriptor().Length();
+	CleanupStack::PopAndDestroy(buffer);
+	return length;
+	}