summaryrefslogtreecommitdiff
path: root/packages/fcl-base/src/inifiles.pp
diff options
context:
space:
mode:
Diffstat (limited to 'packages/fcl-base/src/inifiles.pp')
-rw-r--r--packages/fcl-base/src/inifiles.pp1019
1 files changed, 1019 insertions, 0 deletions
diff --git a/packages/fcl-base/src/inifiles.pp b/packages/fcl-base/src/inifiles.pp
new file mode 100644
index 0000000000..70640b4e8b
--- /dev/null
+++ b/packages/fcl-base/src/inifiles.pp
@@ -0,0 +1,1019 @@
+{
+ 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 creation parameter
+ *
+ * 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, contnrs;
+
+type
+ { THashedStringList }
+
+ THashedStringList = class(TStringList)
+ private
+ FValueHash: TFPHashList;
+ FNameHash: TFPHashList;
+ FValueHashValid: Boolean;
+ FNameHashValid: Boolean;
+ procedure UpdateValueHash;
+ procedure UpdateNameHash;
+ protected
+ procedure Changed; override;
+ public
+ constructor Create;
+ destructor Destroy; override;
+ function IndexOf(const S: String): Integer; override;
+ function IndexOfName(const Name: String): Integer; override;
+ end;
+
+ TIniFileKey = class
+ Private
+ 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; CaseSensitive : Boolean): TIniFileKey;
+ public
+ destructor Destroy; override;
+ procedure Clear; override;
+ property Items[Index: integer]: TIniFileKey read GetItem; default;
+ end;
+
+ TIniFileSection = class
+ private
+ FName: string;
+ FKeyList: TIniFileKeyList;
+ public
+ Function Empty : Boolean;
+ 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; CaseSensitive : Boolean): TIniFileSection;
+ public
+ destructor Destroy; override;
+ procedure Clear;override;
+ property Items[Index: integer]: TIniFileSection read GetItem; default;
+ end;
+
+ TCustomIniFile = class
+ Private
+ FFileName: string;
+ FSectionList: TIniFileSectionList;
+ FEscapeLineFeeds: boolean;
+ FCaseSensitive : Boolean;
+ FStripQuotes : Boolean;
+ public
+ constructor Create(const AFileName: string; AEscapeLineFeeds : Boolean = False); virtual;
+ 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;
+ function ReadBinaryStream(const Section, Name: string; Value: TStream): Integer; 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 WriteBinaryStream(const Section, Name: string; Value: TStream); 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;
+ Property CaseSensitive : Boolean Read FCaseSensitive Write FCaseSensitive;
+ Property StripQuotes : Boolean Read FStripQuotes Write FStripQuotes;
+ end;
+
+ { TIniFile }
+
+ TIniFile = class(TCustomIniFile)
+ Private
+ FStream: TStream;
+ FCacheUpdates: Boolean;
+ FDirty : Boolean;
+ procedure FillSectionList(AStrings: TStrings);
+ Procedure DeleteSection(ASection : TIniFileSection);
+ Procedure MaybeDeleteSection(ASection : TIniFileSection);
+ protected
+ procedure MaybeUpdateFile;
+ property Dirty : Boolean Read FDirty;
+ public
+ constructor Create(const AFileName: string; AEscapeLineFeeds : Boolean = False); override;
+ constructor Create(AStream: TStream; AEscapeLineFeeds : Boolean = False);
+ destructor Destroy; override;
+ 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;
+ property CacheUpdates : Boolean Read FCacheUpdates Write FCacheUpdates;
+ end;
+
+ TMemIniFile = class(TIniFile)
+ public
+ constructor Create(const AFileName: string; AEscapeLineFeeds : Boolean = False); override;
+ 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;
+
+{ THashedStringList }
+
+constructor THashedStringList.Create;
+begin
+ inherited;
+ FValueHash := nil;
+ FNameHash := nil;
+ FValueHashValid := False;
+ FNameHashValid := False;
+end;
+
+destructor THashedStringList.Destroy;
+begin
+ if Assigned(FValueHash) then
+ FValueHash.Free;
+ if Assigned(FNameHash) then
+ FNameHash.Free;
+ inherited Destroy;
+end;
+
+function THashedStringList.IndexOf(const S: String): Integer;
+var
+ I: Integer;
+begin
+ if not FValueHashValid then
+ UpdateValueHash;
+
+ I := FValueHash.FindIndexOf(S);
+ if I >= 0 then
+ Result := Integer(FValueHash[I])
+ else
+ Result := -1;
+end;
+
+function THashedStringList.IndexOfName(const Name: String): Integer;
+var
+ I: Integer;
+begin
+ if not FNameHashValid then
+ UpdateNameHash;
+
+ I := FNameHash.FindIndexOf(Name);
+ if I >= 0 then
+ Result := Integer(FNameHash[I])
+ else
+ Result := -1;
+end;
+
+procedure THashedStringList.Changed;
+begin
+ FValueHashValid := False;
+ FNameHashValid := False;
+ inherited Changed;
+end;
+
+procedure THashedStringList.UpdateValueHash;
+var
+ I: Integer;
+begin
+ if not Assigned(FValueHash) then
+ FValueHash := TFPHashList.Create
+ else
+ FValueHash.Clear;
+ for I := 0 to Count - 1 do
+ FValueHash.Add(Strings[I], Pointer(I));
+ FValueHashValid := True;
+end;
+
+procedure THashedStringList.UpdateNameHash;
+var
+ I: Integer;
+begin
+ if not Assigned(FNameHash) then
+ FNameHash := TFPHashList.Create
+ else
+ FNameHash.Clear;
+ for I := 0 to Count - 1 do
+ FNameHash.Add(Names[I], Pointer(I));
+ FNameHashValid := True;
+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; CaseSensitive : Boolean): TIniFileKey;
+var
+ i: integer;
+begin
+ Result := nil;
+ if (AName > '') and not IsComment(AName) then
+ If CaseSensitive then
+ begin
+ for i := 0 to Count-1 do
+ if Items[i].Ident=AName then
+ begin
+ Result := Items[i];
+ Break;
+ end;
+ end
+ else
+ 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;
+
+Function TIniFileSection.Empty : Boolean;
+
+Var
+ I : Integer;
+
+begin
+ Result:=True;
+ I:=0;
+ While Result and (I<KeyList.Count) do
+ begin
+ result:=IsComment(KeyList[i].Ident);
+ Inc(i);
+ end;
+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; CaseSensitive : Boolean): TIniFileSection;
+var
+ i: integer;
+begin
+ Result := nil;
+ if (AName > '') and not IsComment(AName) then
+ If CaseSensitive then
+ begin
+ for i:=0 to Count-1 do
+ if (Items[i].Name=AName) then
+ begin
+ Result := Items[i];
+ Break;
+ end;
+ end
+ else
+ 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; AEscapeLineFeeds : Boolean = False);
+begin
+ FFileName := AFileName;
+ FSectionList := TIniFileSectionList.Create;
+ FEscapeLineFeeds := AEscapeLineFeeds;
+end;
+
+destructor TCustomIniFile.Destroy;
+begin
+ FSectionList.Free;
+ inherited Destroy;
+end;
+
+function TCustomIniFile.SectionExists(const Section: string): Boolean;
+
+Var
+ S : TIniFileSection;
+
+begin
+ S:=FSectionList.SectionByName(Section,CaseSensitive);
+ Result:=Assigned(S) and Not S.Empty;
+end;
+
+function TCustomIniFile.ReadInteger(const Section, Ident: string; Default: Longint): Longint;
+begin
+ // StrToInfDef() supports hex numbers prefixed with '0x' via val()
+ Result := StrToIntDef(ReadString(Section, Ident, ''), Default);
+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;
+
+begin
+ Result := StrToDateDef(ReadString(Section, Ident, ''),Default);
+end;
+
+function TCustomIniFile.ReadDateTime(const Section, Ident: string; Default: TDateTime): TDateTime;
+
+begin
+ Result := StrToDateTimeDef(ReadString(Section, Ident, ''),Default);
+end;
+
+function TCustomIniFile.ReadFloat(const Section, Ident: string; Default: Double): Double;
+
+begin
+ Result:=StrToFloatDef(ReadString(Section, Ident, ''),Default);
+end;
+
+function TCustomIniFile.ReadTime(const Section, Ident: string; Default: TDateTime): TDateTime;
+
+begin
+ Result := StrToTimeDef(ReadString(Section, Ident, ''),Default);
+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,CaseSensitive);
+ if oSection <> nil then
+ Result := (oSection.KeyList.KeyByName(Ident,CaseSensitive) <> nil);
+end;
+
+function TCustomIniFile.ReadBinaryStream(const Section, Name: string; Value: TStream): Integer;
+
+Var
+ M : TMemoryStream;
+ S : String;
+ PB,PR : PByte;
+ PC : PChar;
+ H : String[3];
+ i,l2,code : Integer;
+
+
+begin
+ S:=ReadString(Section,Name,'');
+ Setlength(H,3);
+ H[1]:='$';
+ Result:=Length(S) div 2;
+ If Result>0 then
+ begin
+ GetMem(PR,Result);
+ Try
+ PC:=PChar(S);
+ PB:=PR;
+ For I:=1 to Result do
+ begin
+ H[2]:=PC[0];
+ H[3]:=PC[1];
+ Val(H,PB^,code);
+ Inc(PC,2);
+ Inc(PB);
+ end;
+ Value.WriteBuffer(PR^,Result);
+ finally
+ FreeMem(PR);
+ end;
+ end;
+end;
+
+procedure TCustomInifile.WriteBinaryStream(const Section, Name: string; Value: TStream);
+
+
+Var
+ M : TMemoryStream;
+ S : String;
+ PB : PByte;
+ PC : PChar;
+ H : String[2];
+ i : Integer;
+
+begin
+ M:=TMemoryStream.Create;
+ Try
+ M.CopyFrom(Value,0);
+ SetLength(S,M.Size*2);
+ If (length(S)>0) then
+ begin
+ PB:=M.Memory;
+ PC:=PChar(S);
+ For I:=1 to Length(S) div 2 do
+ begin
+ H:=HexStr(PB^,2);
+ PC[0]:=H[1];
+ PC[1]:=H[2];
+ Inc(PC,2);
+ Inc(PB);
+ end;
+ end;
+ WriteString(Section,Name,S);
+ Finally
+ M.Free;
+ end;
+end;
+
+{ TIniFile }
+
+constructor TIniFile.Create(const AFileName: string; AEscapeLineFeeds : Boolean = False);
+var
+ slLines: TStringList;
+begin
+ inherited Create(AFileName,AEscapeLineFeeds);
+ FStream := nil;
+ slLines := TStringList.Create;
+ try
+ if FileExists(FFileName) then
+ begin
+ // read the ini file values
+ slLines.LoadFromFile(FFileName);
+ FillSectionList(slLines);
+ end
+ finally
+ slLines.Free;
+ end;
+end;
+
+constructor TIniFile.Create(AStream: TStream; AEscapeLineFeeds : Boolean = False);
+var
+ slLines: TStringList;
+begin
+ inherited Create('',AEscapeLineFeeds);
+ FStream := AStream;
+ slLines := TStringList.Create;
+ try
+ // read the ini file values
+ slLines.LoadFromStream(FStream);
+ FillSectionList(slLines);
+ finally
+ slLines.Free;
+ end;
+end;
+
+destructor TIniFile.destroy;
+begin
+ If FDirty and FCacheUpdates then
+ UpdateFile;
+ inherited destroy;
+end;
+
+procedure TIniFile.FillSectionList(AStrings: TStrings);
+var
+ i,j: integer;
+ sLine, sIdent, sValue: string;
+ oSection: TIniFileSection;
+
+ procedure RemoveBackslashes;
+ var
+ i,l: integer;
+ s: string;
+ bAppendNextLine, bAppended: boolean;
+ begin
+ AStrings.BeginUpdate;
+ try
+ For I:=AStrings.Count-2 downto 0 do
+ begin
+ S:=AStrings[i];
+ L:=Length(S);
+ If (I<AStrings.Count-1) and (L>0) and (S[L]=LF_Escape) then
+ begin
+ S:=Copy(S,1,L-1)+AStrings[I+1];
+ AStrings.Delete(I+1);
+ AStrings[i]:=S;
+ end;
+ 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));
+ If StripQuotes then
+ begin
+ J:=Length(sValue);
+ // Joost, 2-jan-2007: The check (J>1) is there for the case that
+ // the value consist of a single double-quote character. (see
+ // mantis bug 6555)
+ If (J>1) and (sValue[1]='"') and (sValue[J]='"') then
+ sValue:=Copy(sValue,2,J-2);
+ end;
+ 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,CaseSensitive);
+ if oSection <> nil then begin
+ oKey := oSection.KeyList.KeyByName(Ident,CaseSensitive);
+ 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,CaseSensitive);
+ 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,CaseSensitive);
+ 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,CaseSensitive);
+ if oKey <> nil then begin
+ oSection.KeyList.Remove(oKey);
+ end;
+ end;
+ end;
+ MaybeUpdateFile;
+end;
+
+procedure TIniFile.ReadSection(const Section: string; Strings: TStrings);
+var
+ oSection: TIniFileSection;
+ i: integer;
+begin
+ Strings.BeginUpdate;
+ try
+ Strings.Clear;
+ oSection := FSectionList.SectionByName(Section,CaseSensitive);
+ 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,CaseSensitive);
+ 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,CaseSensitive);
+ 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.DeleteSection(ASection : TIniFileSection);
+
+begin
+ FSectionList.Delete(FSectionList.IndexOf(ASection));
+ ASection.Free;
+end;
+
+Procedure TIniFile.MaybeDeleteSection(ASection : TIniFileSection);
+
+begin
+ If Asection.Empty then
+ DeleteSection(ASection);
+end;
+
+procedure TIniFile.EraseSection(const Section: string);
+var
+ oSection: TIniFileSection;
+begin
+ oSection := FSectionList.SectionByName(Section,CaseSensitive);
+ if oSection <> nil then begin
+ { It is needed so UpdateFile doesn't find a defunct section }
+ { and cause the program to crash }
+ DeleteSection(OSection);
+ MaybeUpdateFile;
+ end;
+end;
+
+procedure TIniFile.DeleteKey(const Section, Ident: String);
+var
+ oSection: TIniFileSection;
+ oKey: TIniFileKey;
+begin
+ oSection := FSectionList.SectionByName(Section,CaseSensitive);
+ if oSection <> nil then
+ begin
+ oKey := oSection.KeyList.KeyByName(Ident,CaseSensitive);
+ if oKey <> nil then
+ begin
+ oSection.KeyList.Delete(oSection.KeyList.IndexOf(oKey));
+ oKey.Free;
+ MaybeDeleteSection(oSection);
+ MaybeUpdateFile;
+ 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;
+
+procedure TIniFile.MaybeUpdateFile;
+begin
+ If FCacheUpdates then
+ FDirty:=True
+ else
+ UpdateFile;
+end;
+
+{ TMemIniFile }
+
+constructor TMemIniFile.Create(const AFileName: string; AEscapeLineFeeds : Boolean = False);
+
+begin
+ Inherited;
+ FCacheUpdates:=True;
+end;
+
+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.