mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-28 18:53:40 +02:00
792 lines
21 KiB
ObjectPascal
792 lines
21 KiB
ObjectPascal
{
|
|
This file is part of the Free Component Library (FCL)
|
|
Copyright (c) 1999-2000 Erik WachtMeester.
|
|
|
|
File which provides TIniFile and friends.
|
|
|
|
See the file COPYING.FPC, included in this distribution,
|
|
for details about the copyright.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
**********************************************************************}
|
|
|
|
{* Original disclaimer:
|
|
* FCL inifiles.pp rewrite by Erik Wachtmeester (erikw@hotelconcepts.com)
|
|
*
|
|
* Proposed replacement for inifiles.pp v 1.8
|
|
*
|
|
* This version is Borland Delphi 5 compatible, implementing the classes
|
|
* TCustomIniFile, TIniFile and TMemIniFile, with all the public
|
|
* properties and methods that Delphi 5 implements.
|
|
*
|
|
* (inifiles.pp v 1.8 only implements TIniFile with some properties and
|
|
* methods missing, and some functionality added)
|
|
*
|
|
* In order to stay compatible with v 1.8, I added:
|
|
* - TIniFile can be created and loaded from, and saved to a stream.
|
|
* - ReadSectionRaw method (although it doesn't add empty lines to the
|
|
* TStrings recipient like v 1.8, since empty lines aren't stored in
|
|
* the SectionList object structure)
|
|
* - ReadInteger supports '0x' type hex formats
|
|
* - Comment support (this isn't standard in ini files)
|
|
* - EscapeLineFeeds property
|
|
*
|
|
* Since the SectionList object structure is very different from the
|
|
* way Delphi 5 accesses ini files (Delphi mostly uses Windows calls
|
|
* like GetPrivateProfileString, etc.) it's completely platform
|
|
* independant, and probably faster.
|
|
* The only drawback is memory consumption: all sections, keys and
|
|
* values are kept in memory. But same goes for inifiles.pp v 1.8
|
|
* (the FFileBuffer member) and for Delphi's TMemIniFile.
|
|
* Anyway, Windows restricts ini files to 64K max, so this shouldn't be
|
|
* too much of a problem.
|
|
*
|
|
*}
|
|
|
|
unit IniFiles;
|
|
|
|
{$mode objfpc}
|
|
{$H+}
|
|
|
|
interface
|
|
|
|
uses classes, sysutils;
|
|
|
|
type
|
|
TIniFileKey = class
|
|
FIdent: string;
|
|
FValue: string;
|
|
public
|
|
constructor Create(AIdent, AValue: string);
|
|
property Ident: string read FIdent write FIdent;
|
|
property Value: string read FValue write FValue;
|
|
end;
|
|
|
|
TIniFileKeyList = class(TList)
|
|
private
|
|
function GetItem(Index: integer): TIniFileKey;
|
|
function KeyByName(AName: string): TIniFileKey;
|
|
public
|
|
destructor Destroy; override;
|
|
procedure Clear;
|
|
override;
|
|
property Items[Index: integer]: TIniFileKey read GetItem; default;
|
|
end;
|
|
|
|
TIniFileSection = class
|
|
FName: string;
|
|
FKeyList: TIniFileKeyList;
|
|
public
|
|
constructor Create(AName: string);
|
|
destructor Destroy; override;
|
|
property Name: string read FName;
|
|
property KeyList: TIniFileKeyList read FKeyList;
|
|
end;
|
|
|
|
TIniFileSectionList = class(TList)
|
|
private
|
|
function GetItem(Index: integer): TIniFileSection;
|
|
function SectionByName(AName: string): TIniFileSection;
|
|
public
|
|
destructor Destroy; override;
|
|
procedure Clear;override;
|
|
property Items[Index: integer]: TIniFileSection read GetItem; default;
|
|
end;
|
|
|
|
TCustomIniFile = class
|
|
FFileName: string;
|
|
FSectionList: TIniFileSectionList;
|
|
FEscapeLineFeeds: boolean;
|
|
public
|
|
constructor Create(const AFileName: string);
|
|
destructor Destroy; override;
|
|
function SectionExists(const Section: string): Boolean; virtual;
|
|
function ReadString(const Section, Ident, Default: string): string; virtual; abstract;
|
|
procedure WriteString(const Section, Ident, Value: String); virtual; abstract;
|
|
function ReadInteger(const Section, Ident: string; Default: Longint): Longint; virtual;
|
|
procedure WriteInteger(const Section, Ident: string; Value: Longint); virtual;
|
|
function ReadBool(const Section, Ident: string; Default: Boolean): Boolean; virtual;
|
|
procedure WriteBool(const Section, Ident: string; Value: Boolean); virtual;
|
|
function ReadDate(const Section, Ident: string; Default: TDateTime): TDateTime; virtual;
|
|
function ReadDateTime(const Section, Ident: string; Default: TDateTime): TDateTime; virtual;
|
|
function ReadFloat(const Section, Ident: string; Default: Double): Double; virtual;
|
|
function ReadTime(const Section, Ident: string; Default: TDateTime): TDateTime; virtual;
|
|
procedure WriteDate(const Section, Ident: string; Value: TDateTime); virtual;
|
|
procedure WriteDateTime(const Section, Ident: string; Value: TDateTime); virtual;
|
|
procedure WriteFloat(const Section, Ident: string; Value: Double); virtual;
|
|
procedure WriteTime(const Section, Ident: string; Value: TDateTime); virtual;
|
|
procedure ReadSection(const Section: string; Strings: TStrings); virtual; abstract;
|
|
procedure ReadSections(Strings: TStrings); virtual; abstract;
|
|
procedure ReadSectionValues(const Section: string; Strings: TStrings); virtual; abstract;
|
|
procedure EraseSection(const Section: string); virtual; abstract;
|
|
procedure DeleteKey(const Section, Ident: String); virtual; abstract;
|
|
procedure UpdateFile; virtual; abstract;
|
|
function ValueExists(const Section, Ident: string): Boolean; virtual;
|
|
property FileName: string read FFileName;
|
|
property EscapeLineFeeds: boolean read FEscapeLineFeeds write FEscapeLineFeeds;
|
|
end;
|
|
|
|
TIniFile = class(TCustomIniFile)
|
|
FStream: TStream;
|
|
private
|
|
procedure FillSectionList(AStrings: TStrings);
|
|
public
|
|
constructor Create(const AFileName: string);
|
|
constructor Create(AStream: TStream);
|
|
function ReadString(const Section, Ident, Default: string): string; override;
|
|
procedure WriteString(const Section, Ident, Value: String); override;
|
|
procedure ReadSection(const Section: string; Strings: TStrings); override;
|
|
procedure ReadSectionRaw(const Section: string; Strings: TStrings);
|
|
procedure ReadSections(Strings: TStrings); override;
|
|
procedure ReadSectionValues(const Section: string; Strings: TStrings); override;
|
|
procedure EraseSection(const Section: string); override;
|
|
procedure DeleteKey(const Section, Ident: String); override;
|
|
procedure UpdateFile; override;
|
|
property Stream: TStream read FStream;
|
|
end;
|
|
|
|
TMemIniFile = class(TIniFile)
|
|
public
|
|
procedure Clear;
|
|
procedure GetStrings(List: TStrings);
|
|
procedure Rename(const AFileName: string; Reload: Boolean);
|
|
procedure SetStrings(List: TStrings);
|
|
end;
|
|
|
|
implementation
|
|
|
|
const
|
|
Brackets : array[0..1] of Char = ('[', ']');
|
|
Separator : Char = '=';
|
|
Comment : Char = ';';
|
|
LF_Escape : Char = '\';
|
|
|
|
function CharToBool(AChar: char): boolean;
|
|
begin
|
|
Result := (Achar = '1');
|
|
end;
|
|
|
|
function BoolToChar(ABool: boolean): char;
|
|
begin
|
|
if ABool then
|
|
Result := '1'
|
|
else
|
|
Result := '0';
|
|
end;
|
|
|
|
function IsComment(AString: string): boolean;
|
|
begin
|
|
Result := False;
|
|
if AString > '' then
|
|
Result := (Copy(AString, 1, 1) = Comment);
|
|
end;
|
|
|
|
{ TIniFileKey }
|
|
|
|
constructor TIniFileKey.Create(AIdent, AValue: string);
|
|
begin
|
|
FIdent := AIdent;
|
|
FValue := AValue;
|
|
end;
|
|
|
|
{ TIniFileKeyList }
|
|
|
|
function TIniFileKeyList.GetItem(Index: integer): TIniFileKey;
|
|
begin
|
|
Result := nil;
|
|
if (Index >= 0) and (Index < Count) then
|
|
Result := TIniFileKey(inherited Items[Index]);
|
|
end;
|
|
|
|
function TIniFileKeyList.KeyByName(AName: string): TIniFileKey;
|
|
var
|
|
i: integer;
|
|
begin
|
|
Result := nil;
|
|
if (AName > '') and not IsComment(AName) then
|
|
for i := 0 to Count-1 do
|
|
if CompareText(Items[i].Ident, AName) = 0 then begin
|
|
Result := Items[i];
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
destructor TIniFileKeyList.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TIniFileKeyList.Clear;
|
|
var
|
|
i: integer;
|
|
begin
|
|
for i := Count-1 downto 0 do
|
|
Items[i].Free;
|
|
inherited Clear;
|
|
end;
|
|
|
|
{ TIniFileSection }
|
|
|
|
constructor TIniFileSection.Create(AName: string);
|
|
begin
|
|
FName := AName;
|
|
FKeyList := TIniFileKeyList.Create;
|
|
end;
|
|
|
|
destructor TIniFileSection.Destroy;
|
|
begin
|
|
FKeyList.Free;
|
|
end;
|
|
|
|
{ TIniFileSectionList }
|
|
|
|
function TIniFileSectionList.GetItem(Index: integer): TIniFileSection;
|
|
begin
|
|
Result := nil;
|
|
if (Index >= 0) and (Index < Count) then
|
|
Result := TIniFileSection(inherited Items[Index]);
|
|
end;
|
|
|
|
function TIniFileSectionList.SectionByName(AName: string): TIniFileSection;
|
|
var
|
|
i: integer;
|
|
begin
|
|
Result := nil;
|
|
if (AName > '') and not IsComment(AName) then
|
|
for i := 0 to Count-1 do
|
|
if CompareText(Items[i].Name, AName) = 0 then begin
|
|
Result := Items[i];
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
destructor TIniFileSectionList.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TIniFileSectionList.Clear;
|
|
var
|
|
i: integer;
|
|
begin
|
|
for i := Count-1 downto 0 do
|
|
Items[i].Free;
|
|
inherited Clear;
|
|
end;
|
|
|
|
{ TCustomIniFile }
|
|
|
|
constructor TCustomIniFile.Create(const AFileName: string);
|
|
begin
|
|
FFileName := AFileName;
|
|
FSectionList := TIniFileSectionList.Create;
|
|
FEscapeLineFeeds := False;
|
|
end;
|
|
|
|
destructor TCustomIniFile.Destroy;
|
|
begin
|
|
FSectionList.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TCustomIniFile.SectionExists(const Section: string): Boolean;
|
|
begin
|
|
Result := (FSectionList.SectionByName(Section) <> nil);
|
|
end;
|
|
|
|
function TCustomIniFile.ReadInteger(const Section, Ident: string; Default: Longint): Longint;
|
|
var
|
|
s: string;
|
|
begin
|
|
Result := Default;
|
|
s := ReadString(Section, Ident, '');
|
|
if s > '' then try
|
|
// convert hex string
|
|
if Pos('0X', UpperCase(s)) = 1 then
|
|
s := '$' + Copy(s, 3, Length(s) - 2);
|
|
Result := StrToInt(s);
|
|
except
|
|
on EConvertError do
|
|
else raise;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomIniFile.WriteInteger(const Section, Ident: string; Value: Longint);
|
|
begin
|
|
WriteString(Section, Ident, IntToStr(Value));
|
|
end;
|
|
|
|
function TCustomIniFile.ReadBool(const Section, Ident: string; Default: Boolean): Boolean;
|
|
var
|
|
s: string;
|
|
begin
|
|
Result := Default;
|
|
s := ReadString(Section, Ident, '');
|
|
if s > '' then
|
|
Result := CharToBool(s[1]);
|
|
end;
|
|
|
|
procedure TCustomIniFile.WriteBool(const Section, Ident: string; Value: Boolean);
|
|
begin
|
|
WriteString(Section, Ident, BoolToChar(Value));
|
|
end;
|
|
|
|
function TCustomIniFile.ReadDate(const Section, Ident: string; Default: TDateTime): TDateTime;
|
|
var
|
|
s: string;
|
|
begin
|
|
Result := Default;
|
|
s := ReadString(Section, Ident, '');
|
|
if s > '' then try
|
|
Result := StrToDate(s);
|
|
except
|
|
on EConvertError do
|
|
else raise;
|
|
end;
|
|
end;
|
|
|
|
function TCustomIniFile.ReadDateTime(const Section, Ident: string; Default: TDateTime): TDateTime;
|
|
var
|
|
s: string;
|
|
begin
|
|
Result := Default;
|
|
s := ReadString(Section, Ident, '');
|
|
if s > '' then try
|
|
Result := StrToDateTime(s);
|
|
except
|
|
on EConvertError do
|
|
else raise;
|
|
end;
|
|
end;
|
|
|
|
function TCustomIniFile.ReadFloat(const Section, Ident: string; Default: Double): Double;
|
|
var
|
|
s: string;
|
|
begin
|
|
Result := Default;
|
|
s := ReadString(Section, Ident, '');
|
|
if s > '' then try
|
|
Result := StrToFloat(s);
|
|
except
|
|
on EConvertError do
|
|
else raise;
|
|
end;
|
|
end;
|
|
|
|
function TCustomIniFile.ReadTime(const Section, Ident: string; Default: TDateTime): TDateTime;
|
|
var
|
|
s: string;
|
|
begin
|
|
Result := Default;
|
|
s := ReadString(Section, Ident, '');
|
|
if s > '' then try
|
|
Result := StrToTime(s);
|
|
except
|
|
on EConvertError do
|
|
else raise;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomIniFile.WriteDate(const Section, Ident: string; Value: TDateTime);
|
|
begin
|
|
WriteString(Section, Ident, DateToStr(Value));
|
|
end;
|
|
|
|
procedure TCustomIniFile.WriteDateTime(const Section, Ident: string; Value: TDateTime);
|
|
begin
|
|
WriteString(Section, Ident, DateTimeToStr(Value));
|
|
end;
|
|
|
|
procedure TCustomIniFile.WriteFloat(const Section, Ident: string; Value: Double);
|
|
begin
|
|
WriteString(Section, Ident, FloatToStr(Value));
|
|
end;
|
|
|
|
procedure TCustomIniFile.WriteTime(const Section, Ident: string; Value: TDateTime);
|
|
begin
|
|
WriteString(Section, Ident, TimeToStr(Value));
|
|
end;
|
|
|
|
function TCustomIniFile.ValueExists(const Section, Ident: string): Boolean;
|
|
var
|
|
oSection: TIniFileSection;
|
|
begin
|
|
Result := False;
|
|
oSection := FSectionList.SectionByName(Section);
|
|
if oSection <> nil then
|
|
Result := (oSection.KeyList.KeyByName(Ident) <> nil);
|
|
end;
|
|
|
|
{ TIniFile }
|
|
|
|
constructor TIniFile.Create(const AFileName: string);
|
|
var
|
|
slLines: TStringList;
|
|
begin
|
|
inherited Create(AFileName);
|
|
FStream := nil;
|
|
slLines := TStringList.Create;
|
|
try
|
|
if FileExists(FFileName) then begin
|
|
// read the ini file values
|
|
slLines.LoadFromFile(FFileName);
|
|
FillSectionList(slLines);
|
|
end else
|
|
// create a new ini file
|
|
slLines.SaveToFile(FFileName);
|
|
finally
|
|
slLines.Free;
|
|
end;
|
|
end;
|
|
|
|
constructor TIniFile.Create(AStream: TStream);
|
|
var
|
|
slLines: TStringList;
|
|
begin
|
|
inherited Create('');
|
|
FStream := AStream;
|
|
slLines := TStringList.Create;
|
|
try
|
|
// read the ini file values
|
|
slLines.LoadFromStream(FStream);
|
|
FillSectionList(slLines);
|
|
finally
|
|
slLines.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TIniFile.FillSectionList(AStrings: TStrings);
|
|
var
|
|
i,j: integer;
|
|
sLine, sIdent, sValue: string;
|
|
oSection: TIniFileSection;
|
|
|
|
procedure RemoveBackslashes;
|
|
var
|
|
i: integer;
|
|
s: string;
|
|
bAppendNextLine, bAppended: boolean;
|
|
begin
|
|
AStrings.BeginUpdate;
|
|
try
|
|
i := 0;
|
|
bAppendNextLine := False;
|
|
while i < AStrings.Count do begin
|
|
s := AStrings[i];
|
|
bAppended := False;
|
|
if bAppendNextLine then begin
|
|
// add line to previous line
|
|
AStrings[i-1] := AStrings[i-1] + Trim(s);
|
|
AStrings.Delete(i);
|
|
s := AStrings[i-1];
|
|
bAppended := True;
|
|
end;
|
|
bAppendNextLine := (Copy(s, Length(s), 1) = LF_Escape);
|
|
if bAppendNextLine then
|
|
// remove backslash
|
|
AStrings[i] := Copy(s, 1, Length(s) - 1);
|
|
if not bAppended then
|
|
Inc(i);
|
|
end;
|
|
finally
|
|
AStrings.EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
oSection := nil;
|
|
FSectionList.Clear;
|
|
if FEscapeLineFeeds then
|
|
RemoveBackslashes;
|
|
for i := 0 to AStrings.Count-1 do begin
|
|
sLine := Trim(AStrings[i]);
|
|
if sLine > '' then
|
|
begin
|
|
if IsComment(sLine) and (oSection = nil) then begin
|
|
// comment at the beginning of the ini file
|
|
oSection := TIniFileSection.Create(sLine);
|
|
FSectionList.Add(oSection);
|
|
continue;
|
|
end;
|
|
if (Copy(sLine, 1, 1) = Brackets[0]) and (Copy(sLine, length(sLine), 1) = Brackets[1]) then begin
|
|
// regular section
|
|
oSection := TIniFileSection.Create(Copy(sLine, 2, Length(sLine) - 2));
|
|
FSectionList.Add(oSection);
|
|
end else if oSection <> nil then begin
|
|
if IsComment(sLine) then begin
|
|
// comment within a section
|
|
sIdent := sLine;
|
|
sValue := '';
|
|
end else begin
|
|
// regular key
|
|
j:=Pos(Separator, sLine);
|
|
if j=0 then
|
|
begin
|
|
sIdent:='';
|
|
sValue:=sLine
|
|
end
|
|
else
|
|
begin
|
|
sIdent:=Trim(Copy(sLine, 1, j - 1));
|
|
sValue:=Trim(Copy(sLine, j + 1, Length(sLine) - j));
|
|
J:=Length(sValue);
|
|
If (J>0) and (sValue[1]='"') and (sValue[J]='"') then
|
|
sValue:=Copy(sValue,2,J-2);
|
|
end;
|
|
end;
|
|
oSection.KeyList.Add(TIniFileKey.Create(sIdent, sValue));
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TIniFile.ReadString(const Section, Ident, Default: string): string;
|
|
var
|
|
oSection: TIniFileSection;
|
|
oKey: TIniFileKey;
|
|
begin
|
|
Result := Default;
|
|
oSection := FSectionList.SectionByName(Section);
|
|
if oSection <> nil then begin
|
|
oKey := oSection.KeyList.KeyByName(Ident);
|
|
if oKey <> nil then
|
|
Result := oKey.Value;
|
|
end;
|
|
end;
|
|
|
|
procedure TIniFile.WriteString(const Section, Ident, Value: String);
|
|
var
|
|
oSection: TIniFileSection;
|
|
oKey: TIniFileKey;
|
|
begin
|
|
if (Section > '') and (Ident > '') then begin
|
|
// update or add key
|
|
oSection := FSectionList.SectionByName(Section);
|
|
if (Value > '') then begin
|
|
if oSection = nil then begin
|
|
oSection := TIniFileSection.Create(Section);
|
|
FSectionList.Add(oSection);
|
|
end;
|
|
with oSection.KeyList do begin
|
|
oKey := KeyByName(Ident);
|
|
if oKey <> nil then
|
|
oKey.Value := Value
|
|
else
|
|
oSection.KeyList.Add(TIniFileKey.Create(Ident, Value));
|
|
end;
|
|
end else if oSection <> nil then begin
|
|
// remove key
|
|
oKey := oSection.KeyList.KeyByName(Ident);
|
|
if oKey <> nil then begin
|
|
oSection.KeyList.Remove(oKey);
|
|
end;
|
|
end;
|
|
UpdateFile;
|
|
end;
|
|
end;
|
|
|
|
procedure TIniFile.ReadSection(const Section: string; Strings: TStrings);
|
|
var
|
|
oSection: TIniFileSection;
|
|
i: integer;
|
|
begin
|
|
Strings.BeginUpdate;
|
|
try
|
|
Strings.Clear;
|
|
oSection := FSectionList.SectionByName(Section);
|
|
if oSection <> nil then with oSection.KeyList do
|
|
for i := 0 to Count-1 do
|
|
if not IsComment(Items[i].Ident) then
|
|
Strings.Add(Items[i].Ident);
|
|
finally
|
|
Strings.EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TIniFile.ReadSectionRaw(const Section: string; Strings: TStrings);
|
|
var
|
|
oSection: TIniFileSection;
|
|
i: integer;
|
|
begin
|
|
Strings.BeginUpdate;
|
|
try
|
|
Strings.Clear;
|
|
oSection := FSectionList.SectionByName(Section);
|
|
if oSection <> nil then with oSection.KeyList do
|
|
for i := 0 to Count-1 do
|
|
if not IsComment(Items[i].Ident) then
|
|
begin
|
|
if Items[i].Ident<>'' then
|
|
Strings.Add(Items[i].Ident + Separator +Items[i].Value)
|
|
else
|
|
Strings.Add(Items[i].Value);
|
|
end;
|
|
finally
|
|
Strings.EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TIniFile.ReadSections(Strings: TStrings);
|
|
var
|
|
i: integer;
|
|
begin
|
|
Strings.BeginUpdate;
|
|
try
|
|
Strings.Clear;
|
|
for i := 0 to FSectionList.Count-1 do
|
|
if not IsComment(FSectionList[i].Name) then
|
|
Strings.Add(FSectionList[i].Name);
|
|
finally
|
|
Strings.EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TIniFile.ReadSectionValues(const Section: string; Strings: TStrings);
|
|
var
|
|
oSection: TIniFileSection;
|
|
s: string;
|
|
i: integer;
|
|
begin
|
|
Strings.BeginUpdate;
|
|
try
|
|
Strings.Clear;
|
|
oSection := FSectionList.SectionByName(Section);
|
|
if oSection <> nil then with oSection.KeyList do
|
|
for i := 0 to Count-1 do begin
|
|
s := Items[i].Ident+Separator+Items[i].Value;
|
|
Strings.Add(s);
|
|
end;
|
|
finally
|
|
Strings.EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TIniFile.EraseSection(const Section: string);
|
|
var
|
|
oSection: TIniFileSection;
|
|
begin
|
|
oSection := FSectionList.SectionByName(Section);
|
|
if oSection <> nil then begin
|
|
{ It is needed so UpdateFile doesn't find a defunct section }
|
|
{ and cause the program to crash }
|
|
FSectionList.Delete(FSectionList.IndexOf(oSection));
|
|
oSection.Free;
|
|
UpdateFile;
|
|
end;
|
|
end;
|
|
|
|
procedure TIniFile.DeleteKey(const Section, Ident: String);
|
|
var
|
|
oSection: TIniFileSection;
|
|
oKey: TIniFileKey;
|
|
begin
|
|
oSection := FSectionList.SectionByName(Section);
|
|
if oSection <> nil then begin
|
|
oKey := oSection.KeyList.KeyByName(Ident);
|
|
if oKey <> nil then begin
|
|
oKey.Free;
|
|
UpdateFile;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TIniFile.UpdateFile;
|
|
var
|
|
slLines: TStringList;
|
|
i, j: integer;
|
|
begin
|
|
slLines := TStringList.Create;
|
|
try
|
|
for i := 0 to FSectionList.Count-1 do
|
|
with FSectionList[i] do begin
|
|
if IsComment(Name) then
|
|
// comment
|
|
slLines.Add(Name)
|
|
else
|
|
// regular section
|
|
slLines.Add(Brackets[0] + Name + Brackets[1]);
|
|
for j := 0 to KeyList.Count-1 do
|
|
if IsComment(KeyList[j].Ident) then
|
|
// comment
|
|
slLines.Add(KeyList[j].Ident)
|
|
else
|
|
// regular key
|
|
slLines.Add(KeyList[j].Ident + Separator + KeyList[j].Value);
|
|
if (i < FSectionList.Count-1) and not IsComment(Name) then
|
|
slLines.Add('');
|
|
end;
|
|
if FFileName > '' then
|
|
slLines.SaveToFile(FFileName)
|
|
else if FStream <> nil then
|
|
slLines.SaveToStream(FStream);
|
|
FillSectionList(slLines);
|
|
finally
|
|
slLines.Free;
|
|
end;
|
|
end;
|
|
|
|
{ TMemIniFile }
|
|
|
|
procedure TMemIniFile.Clear;
|
|
begin
|
|
FSectionList.Clear;
|
|
end;
|
|
|
|
procedure TMemIniFile.GetStrings(List: TStrings);
|
|
var
|
|
i, j: integer;
|
|
oSection: TIniFileSection;
|
|
begin
|
|
List.BeginUpdate;
|
|
try
|
|
for i := 0 to FSectionList.Count-1 do begin
|
|
oSection := FSectionList[i];
|
|
with oSection do begin
|
|
if IsComment(Name) then
|
|
List.Add(Name)
|
|
else
|
|
List.Add(Brackets[0] + Name + Brackets[1]);
|
|
for j := 0 to KeyList.Count-1 do begin
|
|
if IsComment(KeyList[j].Ident) then
|
|
List.Add(KeyList[j].Ident)
|
|
else
|
|
List.Add(KeyList[j].Ident + Separator + KeyList[j].Value);
|
|
end;
|
|
end;
|
|
if i < FSectionList.Count-1 then
|
|
List.Add('');
|
|
end;
|
|
finally
|
|
List.EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TMemIniFile.Rename(const AFileName: string; Reload: Boolean);
|
|
var
|
|
slLines: TStringList;
|
|
begin
|
|
FFileName := AFileName;
|
|
FStream := nil;
|
|
if Reload then begin
|
|
slLines := TStringList.Create;
|
|
try
|
|
slLines.LoadFromFile(FFileName);
|
|
FillSectionList(slLines);
|
|
finally
|
|
slLines.Free;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TMemIniFile.SetStrings(List: TStrings);
|
|
begin
|
|
FillSectionList(List);
|
|
end;
|
|
|
|
end.
|