diff -r 000000000000 -r 7f656887cf89 libraries/ltkutils/src/settings.cpp --- /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 +#include + +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 values; + CleanupClosePushL(values); + aValues.GetValuesL(values); + values.Sort(TLinearOrder(ValueLineNumberOrder)); + + IoUtils::CTextBuffer* buf = IoUtils::CTextBuffer::NewLC(0x20); + for (TInt i=0; iAppendFormatL(_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 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& aValues) + { + TPtrHashMapIter iter(iValues); + while (iter.NextValue()) + { + if (IncludeValue(iter.CurrentValue())) aValues.AppendL(iter.CurrentValue()); + } + } + +EXPORT_C void CIniReader::GetValuesL(RPointerArray& aValues) const + { + TPtrHashMapIter iter(iValues); + while (iter.NextValue()) + { + if (IncludeValue(iter.CurrentValue())) aValues.AppendL(iter.CurrentValue()); + } + } + + +EXPORT_C void CIniReader::GetIdsL(RArray& aIds) const + { + TPtrHashMapIter 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(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 ids; + CleanupClosePushL(ids); + iInfoFile->GetIdsL(ids); + + for (TInt i=0; iGetValue(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); + } + +