unit ConfigFile; {$mode objfpc}{$H+} interface uses Classes, SysUtils; type { TConfigFile } TConfigFile = class private FDirty: Boolean; FFileName: string; FSections: TStringList; procedure LoadFromFile; function AddSection(s: string): TStringList; public constructor Create(const AFileName: string); destructor Destroy; override; function ReadString(const Section, Ident, Default: string): string; procedure WriteString(const Section, Ident, Value: String); function ReadBool(const Section, Ident: string; Default: Boolean): Boolean; procedure WriteBool(const Section, Ident: string; Value: Boolean); procedure ReadSection(const Section: string; Strings: TStrings); procedure WriteSection(const Section: string; Strings: TStrings); procedure WriteSectionValues(const Section: string; Strings: TStrings); function FindSection(Section: string): TStringList; function SectionExists(const Section: string): Boolean; procedure Flush; property Dirty : Boolean Read FDirty; property FileName: string read FFileName; property Sections: TStringList read FSections; end; implementation { TConfigFile } constructor TConfigFile.Create(const AFileName: string); begin FSections := TStringList.Create; FSections.OwnsObjects := True; if AFileName = '' then exit; //nothing to do FFileName:=AFileName; LoadFromFile; end; destructor TConfigFile.Destroy; begin Flush; FreeAndNil(FSections); //recursive! inherited Destroy; end; procedure TConfigFile.LoadFromFile; var lst, sec: TStringList; s: string; i: integer; begin if (FFileName = '') or not FileExists(FFileName) then exit; //nothing to load lst := TStringList.Create; lst.LoadFromFile(FFileName); sec := nil; for i := 0 to lst.Count - 1 do begin s := lst[i]; if s = '' then continue; if s[1] = '[' then sec := AddSection(s) else sec.Add(s); end; lst.Free; FDirty:=False; //in case it was set during initialization end; function TConfigFile.FindSection(Section: string): TStringList; var i: integer; begin Result := nil; if Section = '' then exit; if Section[1] = '[' then Section := Copy(Section, 2, Length(Section)-2); //strip [] i := FSections.IndexOf(Section); if i >= 0 then TObject(Result) := FSections.Objects[i]; end; function TConfigFile.SectionExists(const Section: string): Boolean; begin Result := FindSection(Section) <> nil; end; function TConfigFile.AddSection(s: string): TStringList; var i: integer; begin Result := nil; if s = '' then exit; if s[1] = '[' then s := Copy(s, 2, Length(s)-2); i := FSections.IndexOf(s); if i < 0 then begin Result := TStringList.Create; FSections.AddObject(s, Result); FDirty:=True; end else TObject(Result) := FSections.Objects[i]; end; function TConfigFile.ReadString(const Section, Ident, Default: string): string; var sec: TStringList; i: integer; begin sec := FindSection(Section); if sec = nil then exit(Default); i := sec.IndexOfName(Ident); if i < 0 then Result := Default else Result := sec.ValueFromIndex[i]; end; procedure TConfigFile.WriteString(const Section, Ident, Value: String); var sec: TStringList; i: integer; s: string; begin if (Ident = '') {or (Value = '')} then exit; //invalid Ident sec := AddSection(Section); s := Ident + '=' + Value; i := sec.IndexOf(s); if i >= 0 then exit; //already stored i := sec.IndexOfName(ident); if i < 0 then sec.Add(s) else sec.Strings[i] := s; FDirty:=True; end; function TConfigFile.ReadBool(const Section, Ident: string; Default: Boolean ): Boolean; var s: string; begin s := ReadString(Section, Ident, ''); if s = '' then Result := Default else Result := s <> '0'; end; procedure TConfigFile.WriteBool(const Section, Ident: string; Value: Boolean); const aValues: array[boolean] of string = ('0', '1'); begin WriteString(Section, Ident, aValues[Value]); end; procedure TConfigFile.ReadSection(const Section: string; Strings: TStrings); var sec: TStringList; begin Strings.Clear; sec := FindSection(Section); if sec <> nil then Strings.Assign(sec); end; procedure TConfigFile.WriteSection(const Section: string; Strings: TStrings); var sec: TStringList; begin if Strings = nil then exit; if Strings.Count = 0 then exit; //delete section??? sec := AddSection(Section); if not sec.Equals(Strings) then begin sec.Assign(Strings); FDirty:=True; end; end; procedure TConfigFile.WriteSectionValues(const Section: string; Strings: TStrings); begin WriteSection(Section, Strings); end; procedure TConfigFile.Flush; var lst, sec: TStringList; i: integer; s: string; begin if (not FDirty) or (FFileName = '') then exit; lst := TStringList.Create; for i := 0 to FSections.Count - 1 do begin s := FSections[i]; TObject(sec) := FSections.Objects[i]; lst.Add('[' + s + ']'); lst.AddStrings(sec); end; lst.SaveToFile(FFileName); lst.Free; FDirty:=False; end; end.