123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014 |
- {
- 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 ([email protected])
- *
- * 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;
- 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;
- 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);
- 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;
- 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));
- 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;
- 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.
|