libraries/ltkutils/src/settings.cpp
changeset 0 7f656887cf89
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libraries/ltkutils/src/settings.cpp	Wed Jun 23 15:52:26 2010 +0100
@@ -0,0 +1,892 @@
+// settings.cpp
+// 
+// Copyright (c) 2009 - 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 <fshell/ioutils.h>
+#include <fshell/settings.h>
+
+using namespace LtkUtils;
+	
+void Panic(TSettingPanic aReason)
+	{
+	User::Panic(KSettingPanic, aReason);
+	}
+
+void RemoveSpaces(TDes& aDes)
+	{
+	for (TInt i=aDes.Length()-1; i>=0; --i)
+		{
+		if (TChar(aDes[i]).IsSpace()) aDes.Delete(i, 1);
+		}
+	}
+	
+/*
+List of valid values for a boolean. Values interpreted as false, true, false, true, etc.
+i.e. even poitions are false, odd true.
+*/
+_LIT(KBoolValues, "0,1,off,on,no,yes,false,true");
+	
+
+void HandleLineL(const TDesC& aLine, MValueHandler& aValueHandler, TErrorContext aErrorContext)
+	{
+	if (aLine.Length() == 0) return;
+	if (aLine[0] == '#') // comment
+		{
+		aValueHandler.HandleCommentL(aLine.Mid(1), aErrorContext);
+		}
+	else
+		{
+		TLex lex(aLine);
+		lex.SkipSpaceAndMark();
+		lex.SkipCharacters();
+		TPtrC id(lex.MarkedToken());
+		lex.SkipSpace();
+		TPtrC value(lex.Remainder());
+		if (id.Length())
+			{
+			aValueHandler.HandleValueL(id, value, aErrorContext, EFalse);
+			}
+		}
+	}
+	
+
+	
+_LIT(KNewLine, "\n");
+
+EXPORT_C void LtkUtils::ReadIniFileL(const TDesC& aFilename, MValueHandler& aValueHandler, TErrorContext aErrorContext, TFileNotFoundAction aNotFoundAction)
+	{
+	RFs fs;
+	User::LeaveIfError(fs.Connect());
+	CleanupClosePushL(fs);
+	
+	IoUtils::TFileName2 fn(aFilename);
+	TInt err = fn.FindFile(fs);
+	if ((err == KErrNotFound) && (aNotFoundAction == ESucceedIfFileNotFound))
+		{
+		CleanupStack::PopAndDestroy(&fs);
+		return;
+		}
+	StaticLeaveIfErr(err, _L("%SCannot find file '%S'"), &aErrorContext, &fn);
+	
+	RFile file;
+	StaticLeaveIfErr(file.Open(fs, fn, EFileRead | EFileShareReadersOnly), _L("%SCannot open file '%S'"), &aErrorContext, &fn);
+	CleanupClosePushL(file);
+	
+	IoUtils::CTextBuffer* buf = IoUtils::CTextBuffer::NewLC(0x40);
+	
+	TErrorContext context(fn);
+	TBuf8<0x40> readBuf;
+	do
+		{
+		TInt lineEnd;
+		do 
+			{
+			const TDesC& des(buf->Descriptor());
+			lineEnd = des.Find(KNewLine);
+			if (lineEnd != KErrNotFound)
+				{
+				TPtrC line(des.Left(lineEnd));
+				if (line.Length() > 0)
+					{
+					if (line[line.Length()-1] == '\r') line.Set(line.Left(line.Length()-1));
+					HandleLineL(line, aValueHandler, context);					
+					}
+				buf->Delete(0, lineEnd+1);
+				context.NextLine();
+				}
+			} while (lineEnd != KErrNotFound);
+		
+		
+		User::LeaveIfError(file.Read(readBuf));
+		buf->AppendL(readBuf);
+		}
+	while (readBuf.Length() > 0);
+	
+	
+	CleanupStack::PopAndDestroy(3, &fs); // buf, file, fs
+	}
+	
+EXPORT_C void LtkUtils::ReadIniFileL(RIoReadHandle& aReadHandle, MValueHandler& aValueHandler)
+	{
+	RBuf lineBuf;
+	lineBuf.CreateL(0x100);
+	CleanupClosePushL(lineBuf);
+	aReadHandle.SetReadModeL(RIoReadHandle::ELine);
+	TName name;
+	aReadHandle.ObjectNameL(name);
+	
+	TInt err;
+	TErrorContext context(name);
+	while ((err = aReadHandle.Read(lineBuf))==KErrNone)
+		{
+		HandleLineL(lineBuf, aValueHandler, context);
+		context.NextLine();
+		}
+	if (err!=KErrEof) User::LeaveIfError(err);
+	
+	CleanupStack::PopAndDestroy(&lineBuf);
+	}
+	
+TInt ValueLineNumberOrder(const CValue& aRodger, const CValue& aMinnie)
+	{
+	return aRodger.LineNumber() - aMinnie.LineNumber();
+	}
+	
+EXPORT_C void LtkUtils::WriteIniFileL(const TDesC& aFilename, CIniReader& aValues)
+	{
+	RFs fs;
+	User::LeaveIfError(fs.Connect());
+	CleanupClosePushL(fs);
+
+	RFile file;
+	StaticLeaveIfErr(file.Replace(fs, aFilename, EFileWrite | EFileShareExclusive), _L("Cannot open '%S' for writing"), &aFilename);
+	CleanupClosePushL(file);
+	
+
+	IoUtils::CTextFormatter* formatter = IoUtils::CTextFormatter::NewLC(KMaxTInt);
+	aValues.AppendFirstLineL(formatter);
+
+	RPointerArray<CValue> values;
+	CleanupClosePushL(values);
+	aValues.GetValuesL(values);
+	values.Sort(TLinearOrder<CValue>(ValueLineNumberOrder));
+	
+	IoUtils::CTextBuffer* buf = IoUtils::CTextBuffer::NewLC(0x20);	
+	for (TInt i=0; i<values.Count(); ++i)
+		{
+		buf->AppendFormatL(_L("%S\t%S\r\n"), &values[i]->Id(), &values[i]->Value());
+		}
+
+	formatter->TabulateL(0, 4, buf->Descriptor(), IoUtils::EIgnoreAvailableWidth);
+	
+	User::LeaveIfError(file.Write(formatter->Collapse()));
+	
+	CleanupStack::PopAndDestroy(5, &fs); // fs, file, values, buf, formatter
+	}
+	
+//______________________________________________________________________________
+//						TErrorContext
+EXPORT_C TErrorContext::TErrorContext()
+	: iFilename(KNullDesC), iLineNumber(0)
+	{
+	}
+
+EXPORT_C TErrorContext::TErrorContext(const TDesC& aFilename)
+	: iFilename(aFilename), iLineNumber(1)
+	{
+	}
+
+EXPORT_C void TErrorContext::NextLine()
+	{
+	iLineNumber++;
+	}
+
+EXPORT_C TInt TErrorContext::LineNumber()
+	{
+	return iLineNumber;
+	}
+
+EXPORT_C const TDesC& TErrorContext::StringLC()
+	{
+	_LIT(KErrorContextFmt, "%S:%d: ");
+	if (iFilename.Length() && iLineNumber>0)
+		{
+		HBufC* string = HBufC::NewLC(iFilename.Length()+0x10);
+		string->Des().AppendFormat(KErrorContextFmt, &iFilename, iLineNumber);
+		return *string;
+		}
+	else
+		{
+		CleanupStack::PushL((void*)NULL);
+		return KNullDesC;
+		}
+	
+	}
+
+	
+//______________________________________________________________________________
+//						CValue
+EXPORT_C CValue* CValue::NewL(const TDesC& aId, const TDesC& aValue, TErrorContext aErrorContext)
+	{
+	CValue* self = CValue::NewLC(aId, aValue, aErrorContext);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+EXPORT_C CValue* CValue::NewLC(const TDesC& aId, const TDesC& aValue, TErrorContext aErrorContext)
+	{
+	CValue* self = new(ELeave)CValue(aErrorContext);
+	CleanupStack::PushL(self);
+	self->ConstructL(aId, aValue);
+	return self;
+	}
+
+EXPORT_C CValue::~CValue()
+	{
+	iId.Close();
+	iValue.Close();
+	}
+
+EXPORT_C const TDesC& CValue::Id() const
+	{
+	return iId;
+	}
+
+EXPORT_C const TDesC& CValue::Value() const
+	{
+	return iValue;
+	}
+
+EXPORT_C void CValue::SetL(const TDesC& aNewValue, TErrorContext aErrorContext)
+	{
+	if (iValue.MaxLength() < aNewValue.Length())
+		{
+		RBuf newValue;
+		newValue.CreateL(aNewValue.Length());
+		newValue.Swap(iValue);
+		newValue.Close();
+		}
+	if (aErrorContext.LineNumber() > 0) iLineNumber = aErrorContext.LineNumber();
+	iValue.Copy(aNewValue);
+	}
+	
+EXPORT_C TInt CValue::LineNumber() const
+	{
+	return iLineNumber;
+	}
+	
+EXPORT_C TInt CValue::AsInt(TInt& aInt) const
+	{
+	TLex lex(iValue);
+	TInt err = lex.Val(aInt);
+	if (err == KErrNone)
+		{
+		if (lex.Remainder().Length()) err = KErrArgument;
+		}
+	return err;
+	}
+
+EXPORT_C TInt CValue::AsBool(TBool& aBool) const
+	{
+	IoUtils::TEnum truth(KBoolValues);
+	TInt i;
+	TInt err = truth.Parse(iValue, i);
+	if (err==KErrNone)
+		{
+		aBool = i & 0x01;
+		}
+	return err;
+	}
+	
+EXPORT_C TInt CValue::AsIntL() const
+	{
+	TInt i;
+	StaticLeaveIfErr(AsInt(i), _L("'%S' value '%S' is not a valid integer"), &iId ,&iValue);
+	return i;
+	}
+	
+EXPORT_C TBool CValue::AsBoolL() const
+	{
+	TBool b;
+	StaticLeaveIfErr(AsBool(b), _L("'%S' value '%S' is not a valid boolean"), &iId, &iValue);
+	return b;
+	}
+
+
+CValue::CValue(TErrorContext aErrorContext)
+	: iLineNumber(aErrorContext.LineNumber())
+	{
+	}
+
+void CValue::ConstructL(const TDesC& aId, const TDesC& aValue)
+	{
+	iId.CreateL(aId);
+	iValue.CreateL(aValue);
+	}
+	
+//______________________________________________________________________________
+//						CIniReader
+EXPORT_C CIniReader* CIniReader::NewL(const TDesC& aFilename, TErrorContext aErrorContext)
+	{
+	CIniReader* self = new(ELeave)CIniReader();
+	CleanupStack::PushL(self);
+	ReadIniFileL(aFilename, *self, aErrorContext, EFailIfFileNotFound);
+	CleanupStack::Pop(self);
+	return self;
+	}
+	
+CIniReader::CIniReader()
+	{
+	}
+
+EXPORT_C CIniReader::~CIniReader()
+	{
+	TPtrHashMapIter<TDesC, CValue> iter(iValues);
+	while (iter.NextValue())
+		{
+		const CValue* val = iter.CurrentValue();
+		iter.RemoveCurrent();
+		delete val;
+		}
+	iValues.Close();
+	iFirstLineComment.Close();
+	}
+
+EXPORT_C const TDesC* CIniReader::GetValue(const TDesC& aId) const
+	{
+	const CValue* value = iValues.Find(aId);
+	if (value)
+		{
+		return &value->Value();
+		}
+	else
+		{
+		return NULL;
+		}
+	}
+
+
+EXPORT_C void CIniReader::GetValuesL(RPointerArray<CValue>& aValues)
+	{
+	TPtrHashMapIter<TDesC, CValue> iter(iValues);
+	while (iter.NextValue())
+		{
+		if (IncludeValue(iter.CurrentValue())) aValues.AppendL(iter.CurrentValue());
+		}
+	}
+	
+EXPORT_C void CIniReader::GetValuesL(RPointerArray<const CValue>& aValues) const
+	{
+	TPtrHashMapIter<TDesC, CValue> iter(iValues);
+	while (iter.NextValue())
+		{
+		if (IncludeValue(iter.CurrentValue())) aValues.AppendL(iter.CurrentValue());
+		}
+	}
+
+	
+EXPORT_C void CIniReader::GetIdsL(RArray<const TPtrC>& aIds) const
+	{
+	TPtrHashMapIter<TDesC, CValue> iter(iValues);
+	while (iter.NextValue())
+		{
+		aIds.AppendL(*iter.CurrentKey());
+		}
+	}
+	
+EXPORT_C void CIniReader::SetValueL(const TDesC& aId, const TDesC& aValue)
+	{
+	CValue* value = iValues.Find(aId);
+	if (value)
+		{
+		value->SetL(aValue, TErrorContext());
+		}
+	else
+		{
+		HandleValueL(aId, aValue, TErrorContext(), ETrue);
+		}
+	}
+	
+EXPORT_C void CIniReader::RemoveValueL(const TDesC& aId)
+	{
+	CValue* value = iValues.Find(aId);
+	if (value)
+		{
+		DoRemoveL(value);
+		}
+	}
+	
+void CIniReader::DoRemoveL(CValue* aValue)
+	{
+	iValues.Remove(&aValue->Id());
+	delete aValue;
+	}
+	
+void CIniReader::AppendFirstLineL(IoUtils::CTextFormatter* aFormatter)
+	{
+	if (iFirstLineComment.Length())
+		{
+		aFormatter->AppendFormatL(_L("#%S\r\n"), &iFirstLineComment);
+		}
+	}
+	
+void CIniReader::HandleValueL(const TDesC& aId, const TDesC& aValue, TErrorContext aErrorContext, TBool aOverwrite)
+	{
+	CValue* value = iValues.Find(aId);
+	if (!value)
+		{
+		value = CreateValueLC(aId, aValue, aErrorContext);
+		iValues.InsertL(&value->Id(), value);
+		CleanupStack::Pop(value);
+		}
+	else
+		{
+		if (aOverwrite)
+			{
+			value->SetL(aValue, aErrorContext);
+			}
+		// else ignore the duplicate value
+		}
+	}
+	
+void CIniReader::HandleCommentL(const TDesC& aComment, TErrorContext aErrorContext)
+	{
+	if (aErrorContext.LineNumber() == 1)
+		{
+		iFirstLineComment.Close();
+		iFirstLineComment.CreateL(aComment);
+		HandleFirstLineCommentL(iFirstLineComment, aErrorContext);
+		}
+	}
+	
+CValue* CIniReader::CreateValueLC(const TDesC& aId, const TDesC& aValue, TErrorContext aErrorContext)
+	{
+	return CValue::NewLC(aId, aValue, aErrorContext);
+	}
+
+//______________________________________________________________________________
+//						CSetting
+EXPORT_C CSetting* CSetting::NewL(TSettingType aType, const TDesC& aId, const TDesC& aName, const TDesC& aDescription, const TDesC& aDefault, const TDesC& aEnumValues, TErrorContext aErrorContext)
+	{
+	CSetting* self = CSetting::NewLC(aType, aId, aName, aDescription, aDefault, aEnumValues, aErrorContext);
+	CleanupStack::Pop(self);
+	return self;
+	}
+	
+EXPORT_C CSetting* CSetting::NewLC(TSettingType aType, const TDesC& aId, const TDesC& aName, const TDesC& aDescription, const TDesC& aDefault, const TDesC& aEnumValues, TErrorContext aErrorContext)
+	{
+	CSetting* self = new(ELeave)CSetting(aType);
+	CleanupStack::PushL(self);
+	self->ConstructL(aId, aName, aDescription, aDefault, aEnumValues, aErrorContext);
+	return self;
+	}
+	
+EXPORT_C CSetting::~CSetting()
+	{
+	iName.Close();
+	iDescription.Close();
+	iEnumValues.Close();
+	iDefault.Close();
+	}
+
+EXPORT_C void CSetting::SetL(const TDesC& aNewValue, TErrorContext aErrorContext)
+	{
+	TInt value = ParseL(aNewValue, aErrorContext);
+	switch (iType)
+		{
+	case ETypeEnum:
+			{
+			CValue::SetL(iEnum.GetString(value), aErrorContext);
+			iIntValue = value;
+			}
+		break;
+	case ETypeFilename:
+	case ETypeString:
+		CValue::SetL(aNewValue, aErrorContext);
+		break;
+	case ETypeInteger:
+			{
+			CValue::SetL(aNewValue, aErrorContext);
+			iIntValue = value;
+			}
+		break;
+	case ETypeBoolean:
+			{
+			IoUtils::TEnum truth(KBoolValues);
+			TInt value = truth.ParseL(aNewValue); // even false, odd true
+			CValue::SetL(truth.GetString(value), aErrorContext);
+			iIntValue = (value & 0x01) ? (TBool)ETrue : EFalse;
+			}
+		break;
+		}
+	iIsSet = ETrue;
+	}
+	
+TInt CSetting::ParseL(const TDesC& aValue, TErrorContext aErrorContext)
+	{
+	switch (iType)
+		{
+	case ETypeEnum:
+			return iEnum.ParseL(aValue);
+	case ETypeFilename:
+	case ETypeString:
+		return 0;
+	case ETypeInteger:
+			{
+			TLex lex(aValue);
+			TInt value;
+			lex.SkipSpace();
+			StaticLeaveIfErr(lex.Val(value), _L("%S'%S' value '%S' is not a valid integer"), &aErrorContext.StringLC(), &Id(), &aValue);
+			lex.SkipSpace();
+			if (lex.Remainder().Length()) StaticLeaveIfErr(KErrArgument, _L("%S'%S' value '%S' is not a valid integer"), &aErrorContext.StringLC(), &Id(), &aValue);
+			return value;
+			}
+	case ETypeBoolean:
+			{
+			TInt value = iEnum.ParseL(aValue); // even false, odd true
+			return (value & 0x01) ? (TBool)ETrue : EFalse;
+			}
+		}
+	return 0;
+	}
+	
+_LIT(KIntFormat, "%d");
+	
+EXPORT_C void CSetting::SetL(TInt aNewValue)
+	{
+	switch (iType)
+		{
+	case ETypeEnum:
+			{
+			IoUtils::TEnum _enum(iEnumValues);
+			CValue::SetL(_enum.GetString(aNewValue), TErrorContext());
+			iIntValue = aNewValue;
+			}
+		break;
+	case ETypeFilename:
+	case ETypeString:
+	case ETypeInteger:
+			{
+			TBuf<0x10> buf;
+			buf.AppendFormat(KIntFormat, aNewValue);
+			CValue::SetL(buf, TErrorContext());
+			if (iType == ETypeInteger)
+				{
+				iIntValue = aNewValue;
+				}
+			}
+		break;
+	case ETypeBoolean:
+			{
+			CValue::SetL(iEnum.GetString(aNewValue ? 1 : 0), TErrorContext());
+			iIntValue = aNewValue;
+			}
+		break;
+		}
+	iIsSet = ETrue;
+	}
+	
+EXPORT_C TInt CSetting::AsInt(TInt& aInt) const
+	{
+	if (iType == ETypeEnum || iType == ETypeInteger)
+		{
+		aInt = AsInt();
+		return KErrNone;
+		}
+	else return CValue::AsInt(aInt);
+	}
+	
+EXPORT_C TInt CSetting::AsBool(TBool& aBool) const
+	{
+	if (iType == ETypeBoolean)
+		{
+		aBool = AsBool();
+		return KErrNone;
+		}
+	else return CValue::AsBool(aBool);
+	}
+
+EXPORT_C TInt CSetting::AsInt() const
+	{
+	__ASSERT_ALWAYS(iType == ETypeEnum || iType == ETypeInteger, Panic(EInvalidType));
+	return iIntValue;
+	}
+	
+EXPORT_C TBool CSetting::AsBool() const
+	{
+	__ASSERT_ALWAYS(iType == ETypeBoolean, Panic(EInvalidType));
+	return iIntValue;
+	}
+	
+EXPORT_C TBool CSetting::IsSet() const
+	{
+	return iIsSet;
+	}
+	
+EXPORT_C void CSetting::ClearL()
+	{
+	if (IsSet())
+		{
+		SetL(iDefault);
+		iIsSet = EFalse;
+		}
+	}
+	
+EXPORT_C const TDesC& CSetting::Name()
+	{
+	return iName;
+	}
+	
+EXPORT_C const TDesC& CSetting::Description()
+	{
+	return iDescription;
+	}
+
+CSetting::CSetting(TSettingType aType)
+	: CValue(TErrorContext()), iType(aType), iEnum(KNullDesC)
+	{
+	}
+
+void CSetting::ConstructL(const TDesC& aId, const TDesC& aName, const TDesC& aDescription, const TDesC& aDefault, const TDesC& aEnumValues, TErrorContext aErrorContext)
+	{
+	CValue::ConstructL(aId, KNullDesC);
+	iName.CreateL(aName);
+	iDescription.CreateL(aDescription);
+	
+	if (iType == ETypeEnum)
+		{
+		iEnumValues.CreateL(aEnumValues);
+		RemoveSpaces(iEnumValues);
+		new(&iEnum)IoUtils::TEnum(iEnumValues); // in-place construct
+		}
+	else if (iType == ETypeBoolean)
+		{
+		new(&iEnum)IoUtils::TEnum(KBoolValues);
+		}
+
+	iDefaultInt = ParseL(aDefault, aErrorContext);
+	iDefault.CreateL(aDefault);
+	
+	iIntValue = iDefaultInt;
+	CValue::SetL(iDefault, TErrorContext());
+	}
+
+	
+//______________________________________________________________________________
+//						CIniFile
+EXPORT_C CIniFile* CIniFile::NewL(const TDesC& aIniFile, const TDesC& aInfoFile)
+	{
+	CIniFile* self = new(ELeave)CIniFile();
+	CleanupStack::PushL(self);
+	self->ConstructL(aIniFile, aInfoFile);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+EXPORT_C CIniFile::~CIniFile()
+	{
+	delete iInfoFile;
+	iFilename.Close();
+	}
+
+EXPORT_C CSetting* CIniFile::GetSetting(const TDesC& aId)
+	{
+	return (CSetting*)iValues.Find(aId);
+	}
+	
+EXPORT_C const TDesC& CIniFile::GetString(const TDesC& aId)
+	{
+	CSetting* setting = GetSetting(aId);
+	__ASSERT_ALWAYS(setting, Panic(ENotDefined));
+	return setting->Value();
+	}
+	
+EXPORT_C TInt CIniFile::GetInt(const TDesC& aId)
+	{
+	CSetting* setting = GetSetting(aId);
+	__ASSERT_ALWAYS(setting, Panic(ENotDefined));
+	return setting->AsInt();
+	}
+	
+EXPORT_C TBool CIniFile::GetBool(const TDesC& aId)
+	{
+	CSetting* setting = GetSetting(aId);
+	__ASSERT_ALWAYS(setting, Panic(ENotDefined));
+	return setting->AsBool();
+	}
+	
+EXPORT_C void CIniFile::SetL(const TDesC& aId, const TDesC& aString)
+	{
+	CSetting* setting = GetSetting(aId);
+	__ASSERT_ALWAYS(setting, Panic(ENotDefined));
+	setting->SetL(aString, TErrorContext());
+	}
+	
+EXPORT_C void CIniFile::SetL(const TDesC& aId, TInt aInt)
+	{
+	CSetting* setting = GetSetting(aId);
+	__ASSERT_ALWAYS(setting, Panic(ENotDefined));
+	setting->SetL(aInt);
+	}
+
+CValue* CIniFile::CreateValueLC(const TDesC& aId, const TDesC& aValue, TErrorContext aErrorContext)
+	{
+	ASSERT(0);
+	CSetting* setting = CSetting::NewLC(ETypeString, aId, aId, KNullDesC, KNullDesC, KNullDesC, aErrorContext);
+	setting->SetL(aValue, aErrorContext);
+	return setting;
+	}
+
+void CIniFile::HandleValueL(const TDesC& aId, const TDesC& aValue, TErrorContext aErrorContext, TBool aOverwrite)
+	{
+	if (!iInfoFile) StaticLeaveIfErr(KErrCorrupt, _L("%SNo description file has been specified"), &aErrorContext.StringLC());
+	CSetting* setting = GetSetting(aId);
+	if (setting)
+		{
+		if (setting->IsSet() && (!aOverwrite))
+			{
+			StaticLeaveIfErr(KErrAlreadyExists, _L("%Sid '%S' already defined on line %d"), &aErrorContext.StringLC(), &aId, setting->LineNumber());
+			}
+		setting->SetL(aValue, aErrorContext);
+		}
+	else
+		{
+		StaticLeaveIfErr(KErrCorrupt, _L("%Sid '%S' not recognised"), &aErrorContext.StringLC(), &aId);
+		}
+	}
+	
+_LIT(KFirstLineCommentMatch, "!iniedit ");
+_LIT(KOptionInfoFile, "-i");
+
+void CIniFile::HandleFirstLineCommentL(const TDesC& aComment, TErrorContext aErrorContext)
+	{
+	if (!iInfoFile)
+		{
+		if (aComment.Left(KFirstLineCommentMatch().Length()).CompareF(KFirstLineCommentMatch)==0)
+			{
+			//TODO see if we can do something better here, maybe reusing CCommandBase's option parsing?
+					// remove the matched string, without the wildcard
+			TLex lex(aComment.Mid(KFirstLineCommentMatch().Length()));
+			lex.SkipSpace();
+			while (!lex.Eos())
+				{
+				TPtrC next(lex.NextToken());
+				lex.SkipSpace();
+				if (next.Compare(KOptionInfoFile)==0 && !lex.Eos())
+					{
+					TPtrC infoFile(lex.NextToken());
+					lex.SkipSpace();
+					
+					ReadInfoFileL(infoFile, aErrorContext);					
+					}
+				}
+			}
+		}
+	}
+	
+TBool CIniFile::IncludeValue(const CValue* aValue) const
+	{
+	return ((const CSetting*)aValue)->IsSet();
+	}
+	
+void CIniFile::DoRemoveL(CValue* aValue)
+	{
+	static_cast<CSetting*>(aValue)->ClearL();
+	}
+
+CIniFile::CIniFile()
+	{
+	}
+	
+_LIT(KSpace, " ");
+
+void CIniFile::ConstructL(const TDesC& aIniFile, const TDesC& aInfoFile)
+	{
+	if (aInfoFile.Length()!=0)
+		{
+		ReadInfoFileL(aInfoFile, TErrorContext());
+		
+		}
+	ReadIniFileL(aIniFile, *this, TErrorContext(), aIniFile.Length() ? ESucceedIfFileNotFound : EFailIfFileNotFound);
+	iFilename.CreateL(aIniFile);
+	
+	if ((iFirstLineComment.Length()==0) && (aInfoFile.Length()!=0))
+		{
+		// construct the first line comment if it doesn't exist in the file, and we have an IDF file
+		iFirstLineComment.Close();
+		iFirstLineComment.CreateL(aInfoFile.Length() + KFirstLineCommentMatch().Length() + KOptionInfoFile().Length() + 1);
+		iFirstLineComment.Append(KFirstLineCommentMatch);
+		iFirstLineComment.Append(KOptionInfoFile);
+		iFirstLineComment.Append(KSpace);
+		iFirstLineComment.Append(aInfoFile);
+		}
+	}
+
+_LIT(KDot, ".");
+_LIT(KNameFmt, "%S.name");
+_LIT(KDescriptionFmt, "%S.description");
+_LIT(KEnumValuesFmt, "%S.values");
+_LIT(KDefaultFmt, "%S.default");
+_LIT(KTypeEnum, "enum,filename,string,integer,boolean");
+
+void CIniFile::ReadInfoFileL(const TDesC& aFilename, TErrorContext aErrorContext)
+	{
+	if (iInfoFile) return;
+	iInfoFile = CIniReader::NewL(aFilename, aErrorContext);
+	IoUtils::TEnum typeEnum(KTypeEnum);
+	IoUtils::CTextBuffer* idBuf = IoUtils::CTextBuffer::NewLC(8);
+	
+	RArray<const TPtrC> ids;
+	CleanupClosePushL(ids);
+	iInfoFile->GetIdsL(ids);
+	
+	for (TInt i=0; i<ids.Count(); ++i)
+		{
+		if (ids[i].Find(KDot) == KErrNotFound)
+			{
+			const TDesC& id = ids[i];
+			
+			TSettingType type = (TSettingType)typeEnum.ParseL(*iInfoFile->GetValue(id));
+			
+			idBuf->Reset();
+			idBuf->AppendFormatL(KNameFmt, &id);
+			const TDesC* name = iInfoFile->GetValue(idBuf->Descriptor());
+			
+			idBuf->Reset();
+			idBuf->AppendFormatL(KDescriptionFmt, &id);
+			const TDesC* description = iInfoFile->GetValue(idBuf->Descriptor());
+			
+			idBuf->Reset();
+			idBuf->AppendFormatL(KDefaultFmt, &id);
+			const TDesC* defaultValue = iInfoFile->GetValue(idBuf->Descriptor());
+			
+			const TDesC* enumValues = NULL;
+			if (type == ETypeEnum)
+				{
+				idBuf->Reset();
+				idBuf->AppendFormatL(KEnumValuesFmt, &id);
+				enumValues = iInfoFile->GetValue(idBuf->Descriptor());
+				if (!enumValues) StaticLeaveIfErr(KErrCorrupt, _L("%S: enum '%S' doesn't have any values specified (%S.values not defined)"), &aFilename, &id, &id);
+				}
+				
+			CSetting* setting = CSetting::NewLC(type, id, name ? *name : id, description ? *description : KNullDesC, defaultValue ? *defaultValue : KNullDesC, enumValues ? *enumValues : KNullDesC, aErrorContext);
+			iValues.InsertL(&setting->Id(), setting);
+			CleanupStack::Pop();			
+			}
+		}
+	
+	CleanupStack::PopAndDestroy(2, idBuf);// idBuf, ids
+	}
+	
+EXPORT_C void CIniFile::WriteL()
+	{
+	IoUtils::TFileName2 iniFile(iFilename);
+	
+	RFs fs; User::LeaveIfError(fs.Connect());
+	TInt err = iniFile.FindFile(fs);
+	
+	if (err==KErrNotFound)
+		{
+		if (iniFile.Length()==0) User::Leave(KErrBadName);
+		if (iniFile[0] != '\\') iniFile.Insert(0, _L("\\"));
+		iniFile.Insert(0, _L("C:"));
+		
+		err = fs.MkDirAll(iniFile);
+		if (err== KErrAlreadyExists) err=KErrNone;		
+		}
+
+	fs.Close();
+	User::LeaveIfError(err);
+	
+	WriteIniFileL(iniFile, *this);
+	}
+
+