* Fix bug ID #29414

git-svn-id: trunk@32961 -
This commit is contained in:
michael 2016-01-17 12:04:09 +00:00
parent 6576c8cd04
commit 2658637a01
4 changed files with 252 additions and 57 deletions

1
.gitattributes vendored
View File

@ -1988,6 +1988,7 @@ packages/fcl-base/examples/testcont.pp svneol=native#text/plain
packages/fcl-base/examples/testexprpars.pp svneol=native#text/plain packages/fcl-base/examples/testexprpars.pp svneol=native#text/plain
packages/fcl-base/examples/testez.pp svneol=native#text/plain packages/fcl-base/examples/testez.pp svneol=native#text/plain
packages/fcl-base/examples/testhres.pp svneol=native#text/plain packages/fcl-base/examples/testhres.pp svneol=native#text/plain
packages/fcl-base/examples/testini.pp svneol=native#text/plain
packages/fcl-base/examples/testipc_client.pp svneol=native#text/plain packages/fcl-base/examples/testipc_client.pp svneol=native#text/plain
packages/fcl-base/examples/testipc_server.pp svneol=native#text/plain packages/fcl-base/examples/testipc_server.pp svneol=native#text/plain
packages/fcl-base/examples/testmime.pp svneol=native#text/plain packages/fcl-base/examples/testmime.pp svneol=native#text/plain

View File

@ -73,3 +73,4 @@ poolmm2.pp Test for pooledmm (nonfree) (VS)
testweb.pp Test for fpcgi (MVC) testweb.pp Test for fpcgi (MVC)
daemon.pp Test for daemonapp (MVC) daemon.pp Test for daemonapp (MVC)
testtimer.pp Test for TFPTimer (MVC) testtimer.pp Test for TFPTimer (MVC)
testini.pp Test/Demo for inifiles, ReadSectionValues.

View File

@ -0,0 +1,61 @@
program testini;
{$mode objfpc}{$H+}
uses
inifiles, classes;
var
i: Integer;
ini: TMemIniFile;
lines: TStrings;
begin
lines:=TStringList.Create();
try
lines.Add('[main]');
lines.Add('key_a=1');
lines.Add(';comment');
lines.Add('key_b =2');
lines.Add('not_valid');
lines.Add('key_c= 3');
lines.Add('key_d="3"');
WriteLn('ini file source:');
for i:=0 to lines.Count-1 do
WriteLn(' ', lines[i]);
ini:=TMemIniFile.Create('');
try
ini.options:=ini.options+[ifoStripQuotes];
ini.SetStrings(lines);
lines.Clear();
ini.ReadSectionValues('main', lines,[]);
WriteLn('ReadSectionValues (no options):');
for i:=0 to lines.Count-1 do
WriteLn(' ', lines[i]);
lines.Clear();
ini.ReadSectionValues('main', lines,[svoIncludeComments]);
WriteLn('ReadSectionValues (with comments, no invalid):');
for i:=0 to lines.Count-1 do
WriteLn(' ', lines[i]);
lines.Clear();
ini.ReadSectionValues('main', lines,[svoIncludeInvalid]);
WriteLn('ReadSectionValues (without comments, with invalid):');
for i:=0 to lines.Count-1 do
WriteLn(' ', lines[i]);
lines.Clear();
ini.ReadSectionValues('main', lines,[svoIncludeComments,svoIncludeInvalid]);
WriteLn('ReadSectionValues (with comments, with invalid):');
for i:=0 to lines.Count-1 do
WriteLn(' ', lines[i]);
Lines.Clear;
ini.ReadSectionValues('main', lines,[svoIncludeQuotes]);
WriteLn('ReadSectionValues (with quotes):');
for i:=0 to lines.Count-1 do
WriteLn(' ', lines[i]);
finally
ini.Free();
end;
finally
lines.Free();
end
end.

View File

@ -134,19 +134,31 @@ type
property Items[Index: integer]: TIniFileSection read GetItem; default; property Items[Index: integer]: TIniFileSection read GetItem; default;
end; end;
TIniFileOption = (ifoStripComments, // Strip comments when reading file
ifoStripInvalid, // Strip invalid lines when reading file.
ifoEscapeLineFeeds, // Escape linefeeds when reading file.
ifoCaseSensitive, // Use Case sensitive section/key names
ifoStripQuotes, // Strip quotes when reading string values.
ifoFormatSettingsActive); // Use format settings when writing date/float etc.
TIniFileOptions = Set of TIniFileOption;
TSectionValuesOption = (svoIncludeComments,svoIncludeInvalid, svoIncludeQuotes);
TSectionValuesOptions = set of TSectionValuesOption;
{ TCustomIniFile } { TCustomIniFile }
TCustomIniFile = class TCustomIniFile = class
Private Private
FFileName: string; FFileName: string;
FOptions: TIniFileOptions;
FSectionList: TIniFileSectionList; FSectionList: TIniFileSectionList;
FEscapeLineFeeds: boolean; function GetOption(AIndex: TIniFileOption): Boolean;
FCaseSensitive : Boolean; procedure SetOption(AIndex: TIniFileOption; AValue: Boolean);
FStripQuotes : Boolean; procedure SetOptions(AValue: TIniFileOptions);
FFormatSettingsActive: Boolean;
public public
FormatSettings: TFormatSettings; FormatSettings: TFormatSettings;
constructor Create(const AFileName: string; AEscapeLineFeeds : Boolean = False); virtual; constructor Create(const AFileName: string; AOptions : TIniFileOptions = []); virtual;
constructor Create(const AFileName: string; AEscapeLineFeeds : Boolean); virtual;
destructor Destroy; override; destructor Destroy; override;
function SectionExists(const Section: string): Boolean; virtual; function SectionExists(const Section: string): Boolean; virtual;
function ReadString(const Section, Ident, Default: string): string; virtual; abstract; function ReadString(const Section, Ident, Default: string): string; virtual; abstract;
@ -169,16 +181,18 @@ type
procedure WriteBinaryStream(const Section, Name: string; Value: TStream); virtual; procedure WriteBinaryStream(const Section, Name: string; Value: TStream); virtual;
procedure ReadSection(const Section: string; Strings: TStrings); virtual; abstract; procedure ReadSection(const Section: string; Strings: TStrings); virtual; abstract;
procedure ReadSections(Strings: TStrings); virtual; abstract; procedure ReadSections(Strings: TStrings); virtual; abstract;
procedure ReadSectionValues(const Section: string; Strings: TStrings); virtual; abstract; procedure ReadSectionValues(const Section: string; Strings: TStrings; Options : TSectionValuesOptions); virtual; overload;
procedure ReadSectionValues(const Section: string; Strings: TStrings); virtual;overload;
procedure EraseSection(const Section: string); virtual; abstract; procedure EraseSection(const Section: string); virtual; abstract;
procedure DeleteKey(const Section, Ident: String); virtual; abstract; procedure DeleteKey(const Section, Ident: String); virtual; abstract;
procedure UpdateFile; virtual; abstract; procedure UpdateFile; virtual; abstract;
function ValueExists(const Section, Ident: string): Boolean; virtual; function ValueExists(const Section, Ident: string): Boolean; virtual;
property FileName: string read FFileName; property FileName: string read FFileName;
property EscapeLineFeeds: boolean read FEscapeLineFeeds; Property Options : TIniFileOptions Read FOptions Write SetOptions;
Property CaseSensitive : Boolean Read FCaseSensitive Write FCaseSensitive; property EscapeLineFeeds: boolean index ifoEscapeLineFeeds Read GetOption ;deprecated 'Use options instead';
Property StripQuotes : Boolean Read FStripQuotes Write FStripQuotes; Property CaseSensitive : Boolean index ifoCaseSensitive Read GetOption Write SetOption; deprecated 'Use options instead';
Property FormatSettingsActive: Boolean Read FFormatSettingsActive write FFormatSettingsActive; Property StripQuotes : Boolean index ifoStripQuotes Read GetOption Write SetOption; deprecated 'Use options instead';
Property FormatSettingsActive : Boolean index ifoFormatSettingsActive Read GetOption Write SetOption;deprecated 'Use options instead';
end; end;
{ TIniFile } { TIniFile }
@ -197,15 +211,16 @@ type
procedure MaybeUpdateFile; procedure MaybeUpdateFile;
property Dirty : Boolean Read FDirty; property Dirty : Boolean Read FDirty;
public public
constructor Create(const AFileName: string; AEscapeLineFeeds : Boolean = False); overload; override; constructor Create(const AFileName: string; AOptions : TIniFileoptions = []); overload; override;
constructor Create(AStream: TStream; AEscapeLineFeeds : Boolean = False); overload; constructor Create(AStream: TStream; AOptions : TIniFileoptions = []); overload;
constructor Create(AStream: TStream; AEscapeLineFeeds : Boolean); overload; deprecated 'Use Options argument instead';
destructor Destroy; override; destructor Destroy; override;
function ReadString(const Section, Ident, Default: string): string; override; function ReadString(const Section, Ident, Default: string): string; override;
procedure WriteString(const Section, Ident, Value: String); override; procedure WriteString(const Section, Ident, Value: String); override;
procedure ReadSection(const Section: string; Strings: TStrings); override; procedure ReadSection(const Section: string; Strings: TStrings); override;
procedure ReadSectionRaw(const Section: string; Strings: TStrings); procedure ReadSectionRaw(const Section: string; Strings: TStrings);
procedure ReadSections(Strings: TStrings); override; procedure ReadSections(Strings: TStrings); override;
procedure ReadSectionValues(const Section: string; Strings: TStrings); override; procedure ReadSectionValues(const Section: string; Strings: TStrings; AOptions : TSectionValuesOptions = []); overload; override;
procedure EraseSection(const Section: string); override; procedure EraseSection(const Section: string); override;
procedure DeleteKey(const Section, Ident: String); override; procedure DeleteKey(const Section, Ident: String); override;
procedure UpdateFile; override; procedure UpdateFile; override;
@ -510,11 +525,34 @@ end;
{ TCustomIniFile } { TCustomIniFile }
constructor TCustomIniFile.Create(const AFileName: string; AEscapeLineFeeds : Boolean = False);
function TCustomIniFile.GetOption(AIndex: TIniFileOption): Boolean;
begin
Result:=AIndex in FOptions;
end;
procedure TCustomIniFile.SetOption(AIndex: TIniFileOption; AValue: Boolean);
begin
if AIndex in [ifoStripComments,ifoStripInvalid] then
Raise Exception.Create('Flags ifoStripComments or ifoStripInvalid must be set/unset in the constructor');
if AValue then
Include(FOptions,AIndex)
else
Exclude(FOptions,AIndex)
end;
procedure TCustomIniFile.SetOptions(AValue: TIniFileOptions);
begin
if FOptions=AValue then Exit;
FOptions:=AValue;
end;
constructor TCustomIniFile.Create(const AFileName: string; AOptions : TIniFileOptions = []);
begin begin
FFileName := AFileName; FFileName := AFileName;
FSectionList := TIniFileSectionList.Create; FSectionList := TIniFileSectionList.Create;
FEscapeLineFeeds := AEscapeLineFeeds; FOptions:=AOptions;
FormatSettings := DefaultFormatSettings; FormatSettings := DefaultFormatSettings;
with FormatSettings do begin with FormatSettings do begin
DecimalSeparator := '.'; DecimalSeparator := '.';
@ -528,6 +566,15 @@ begin
end; end;
end; end;
constructor TCustomIniFile.Create(const AFileName: string;
AEscapeLineFeeds: Boolean);
begin
if AEscapeLineFeeds then
Create(AFileName,[ifoEscapeLineFeeds])
else
Create(AFileName,[])
end;
destructor TCustomIniFile.Destroy; destructor TCustomIniFile.Destroy;
begin begin
FSectionList.Free; FSectionList.Free;
@ -584,7 +631,7 @@ end;
function TCustomIniFile.ReadDate(const Section, Ident: string; Default: TDateTime): TDateTime; function TCustomIniFile.ReadDate(const Section, Ident: string; Default: TDateTime): TDateTime;
begin begin
if FFormatSettingsActive then begin if FormatSettingsActive then begin
if not TryStrToDate(ReadString(Section, Ident, ''), Result, FormatSettings) then if not TryStrToDate(ReadString(Section, Ident, ''), Result, FormatSettings) then
Result := Default; Result := Default;
end else end else
@ -594,7 +641,7 @@ end;
function TCustomIniFile.ReadDateTime(const Section, Ident: string; Default: TDateTime): TDateTime; function TCustomIniFile.ReadDateTime(const Section, Ident: string; Default: TDateTime): TDateTime;
begin begin
if FFormatSettingsActive then begin if FormatSettingsActive then begin
if not TryStrToDateTime(ReadString(Section, Ident, ''), Result, FormatSettings) then if not TryStrToDateTime(ReadString(Section, Ident, ''), Result, FormatSettings) then
Result := Default; Result := Default;
end else end else
@ -604,7 +651,7 @@ end;
function TCustomIniFile.ReadFloat(const Section, Ident: string; Default: Double): Double; function TCustomIniFile.ReadFloat(const Section, Ident: string; Default: Double): Double;
begin begin
if FFormatSettingsActive then if FormatSettingsActive then
Result:=StrToFloatDef(ReadString(Section, Ident, ''),Default, FormatSettings) Result:=StrToFloatDef(ReadString(Section, Ident, ''),Default, FormatSettings)
else else
Result:=StrToFloatDef(ReadString(Section, Ident, ''),Default); Result:=StrToFloatDef(ReadString(Section, Ident, ''),Default);
@ -613,7 +660,7 @@ end;
function TCustomIniFile.ReadTime(const Section, Ident: string; Default: TDateTime): TDateTime; function TCustomIniFile.ReadTime(const Section, Ident: string; Default: TDateTime): TDateTime;
begin begin
if FFormatSettingsActive then if FormatSettingsActive then
Result := StrToTimeDef(ReadString(Section, Ident, ''),Default, FormatSettings.TimeSeparator) Result := StrToTimeDef(ReadString(Section, Ident, ''),Default, FormatSettings.TimeSeparator)
else else
Result := StrToTimeDef(ReadString(Section, Ident, ''),Default); Result := StrToTimeDef(ReadString(Section, Ident, ''),Default);
@ -621,7 +668,7 @@ end;
procedure TCustomIniFile.WriteDate(const Section, Ident: string; Value: TDateTime); procedure TCustomIniFile.WriteDate(const Section, Ident: string; Value: TDateTime);
begin begin
if FFormatSettingsActive then if FormatSettingsActive then
WriteString(Section, Ident, DateToStr(Value, FormatSettings)) WriteString(Section, Ident, DateToStr(Value, FormatSettings))
else else
WriteString(Section, Ident, DateToStr(Value)); WriteString(Section, Ident, DateToStr(Value));
@ -629,7 +676,7 @@ end;
procedure TCustomIniFile.WriteDateTime(const Section, Ident: string; Value: TDateTime); procedure TCustomIniFile.WriteDateTime(const Section, Ident: string; Value: TDateTime);
begin begin
if FFormatSettingsActive then if FormatSettingsActive then
WriteString(Section, Ident, DateTimeToStr(Value, FormatSettings)) WriteString(Section, Ident, DateTimeToStr(Value, FormatSettings))
else else
WriteString(Section, Ident, DateTimeToStr(Value)); WriteString(Section, Ident, DateTimeToStr(Value));
@ -637,7 +684,7 @@ end;
procedure TCustomIniFile.WriteFloat(const Section, Ident: string; Value: Double); procedure TCustomIniFile.WriteFloat(const Section, Ident: string; Value: Double);
begin begin
if FFormatSettingsActive then if FormatSettingsActive then
WriteString(Section, Ident, FloatToStr(Value, FormatSettings)) WriteString(Section, Ident, FloatToStr(Value, FormatSettings))
else else
WriteString(Section, Ident, FloatToStr(Value)); WriteString(Section, Ident, FloatToStr(Value));
@ -645,7 +692,7 @@ end;
procedure TCustomIniFile.WriteTime(const Section, Ident: string; Value: TDateTime); procedure TCustomIniFile.WriteTime(const Section, Ident: string; Value: TDateTime);
begin begin
if FFormatSettingsActive then if FormatSettingsActive then
WriteString(Section, Ident, TimeToStr(Value, FormatSettings)) WriteString(Section, Ident, TimeToStr(Value, FormatSettings))
else else
WriteString(Section, Ident, TimeToStr(Value)); WriteString(Section, Ident, TimeToStr(Value));
@ -698,7 +745,8 @@ begin
end; end;
end; end;
procedure TCustomInifile.WriteBinaryStream(const Section, Name: string; Value: TStream); procedure TCustomIniFile.WriteBinaryStream(const Section, Name: string;
Value: TStream);
Var Var
@ -733,16 +781,54 @@ begin
end; end;
end; end;
procedure TCustomIniFile.ReadSectionValues(const Section: string; Strings: TStrings; Options: TSectionValuesOptions);
type
TOldSectionValues = Procedure (const Section: string; Strings: TStrings) of object;
var
CurrSV,
TCustomSV: TOldSectionValues;
CurrClass : TClass;
begin
if (Options<>[]) then
Raise Exception.Create('Options not supported, options must be empty');
// Redirect calls to old implementation, if it is overridden.
CurrSV:=nil;
CurrClass:=Classtype;
while (CurrClass<>nil) and (CurrClass<>TCustomIniFile) do
CurrClass:=CurrClass.Classparent;
if CurrClass<>nil then
begin
CurrSV:=@Self.ReadSectionValues;
TCustomSV:=@TCustomIniFile(@CurrClass).ReadSectionValues;
if TMethod(TCustomSV).Code=TMethod(CurrSV).Code then
CurrSV:=nil;
end;
if Assigned(CurrSV) then
ReadSectionValues(Section,Strings)
else
Raise Exception.Create('ReadSectionValues not overridden');
end;
procedure TCustomIniFile.ReadSectionValues(const Section: string;
Strings: TStrings);
begin
ReadSectionValues(Section,Strings,[]);
end;
{ TIniFile } { TIniFile }
constructor TIniFile.Create(const AFileName: string; AEscapeLineFeeds : Boolean = False);
constructor TIniFile.Create(const AFileName: string; AOptions : TIniFileOptions = []);
var var
slLines: TStringList; slLines: TStringList;
begin begin
FBOM := ''; FBOM := '';
If Not (self is TMemIniFile) then If Not (self is TMemIniFile) then
StripQuotes:=True; StripQuotes:=True;
inherited Create(AFileName,AEscapeLineFeeds); inherited Create(AFileName,AOptions);
FStream := nil; FStream := nil;
slLines := TStringList.Create; slLines := TStringList.Create;
try try
@ -757,12 +843,23 @@ begin
end; end;
end; end;
constructor TIniFile.Create(AStream: TStream; AEscapeLineFeeds : Boolean = False); constructor TIniFile.Create(AStream: TStream; AEscapeLineFeeds : Boolean);
begin
if AEscapeLineFeeds then
Create(AStream,[ifoEscapeLineFeeds])
else
Create(AStream,[]);
end;
constructor TIniFile.Create(AStream: TStream; AOptions : TIniFileOptions = []);
var var
slLines: TStringList; slLines: TStringList;
begin begin
FBOM := ''; FBOM := '';
inherited Create('',AEscapeLineFeeds); inherited Create('',AOptions);
FStream := AStream; FStream := AStream;
slLines := TStringList.Create; slLines := TStringList.Create;
try try
@ -818,10 +915,13 @@ var
end; end;
end; end;
Var
addKey : Boolean;
begin begin
oSection := nil; oSection := nil;
FSectionList.Clear; FSectionList.Clear;
if FEscapeLineFeeds then if EscapeLineFeeds then
RemoveBackslashes; RemoveBackslashes;
if (AStrings.Count > 0) and (copy(AStrings.Strings[0],1,Length(Utf8Bom)) = Utf8Bom) then if (AStrings.Count > 0) and (copy(AStrings.Strings[0],1,Length(Utf8Bom)) = Utf8Bom) then
begin begin
@ -832,37 +932,51 @@ begin
sLine := Trim(AStrings[i]); sLine := Trim(AStrings[i]);
if sLine > '' then if sLine > '' then
begin begin
if IsComment(sLine) and (oSection = nil) then begin if IsComment(sLine) and (oSection = nil) then
begin
// comment at the beginning of the ini file // comment at the beginning of the ini file
oSection := TIniFileSection.Create(sLine); if Not (ifoStripComments in Options) then
FSectionList.Add(oSection); begin
oSection := TIniFileSection.Create(sLine);
FSectionList.Add(oSection);
end;
continue; continue;
end; end;
if (Copy(sLine, 1, 1) = Brackets[0]) and (Copy(sLine, length(sLine), 1) = Brackets[1]) then begin if (Copy(sLine, 1, 1) = Brackets[0]) and (Copy(sLine, length(sLine), 1) = Brackets[1]) then
begin
// regular section // regular section
oSection := TIniFileSection.Create(Copy(sLine, 2, Length(sLine) - 2)); oSection := TIniFileSection.Create(Copy(sLine, 2, Length(sLine) - 2));
FSectionList.Add(oSection); FSectionList.Add(oSection);
end else if oSection <> nil then begin end
if IsComment(sLine) then begin else if oSection <> nil then
begin
if IsComment(sLine) then
begin
AddKey:=Not (ifoStripComments in Options);
// comment within a section // comment within a section
sIdent := sLine; sIdent := sLine;
sValue := ''; sValue := '';
end else begin end
else
begin
// regular key // regular key
j:=Pos(Separator, sLine); j:=Pos(Separator, sLine);
if j=0 then if j=0 then
begin begin
sIdent:=''; AddKey:=Not (ifoStripInvalid in Options);
sValue:=sLine sIdent:='';
sValue:=sLine
end end
else else
begin begin
sIdent:=Trim(Copy(sLine, 1, j - 1)); AddKey:=True;
sValue:=Trim(Copy(sLine, j + 1, Length(sLine) - j)); sIdent:=Trim(Copy(sLine, 1, j - 1));
sValue:=Trim(Copy(sLine, j + 1, Length(sLine) - j));
end; end;
end; end;
oSection.KeyList.Add(TIniFileKey.Create(sIdent, sValue)); if AddKey then
end; oSection.KeyList.Add(TIniFileKey.Create(sIdent, sValue));
end;
end; end;
end; end;
end; end;
@ -982,31 +1096,49 @@ begin
end; end;
end; end;
procedure TIniFile.ReadSectionValues(const Section: string; Strings: TStrings); procedure TIniFile.ReadSectionValues(const Section: string; Strings: TStrings; AOptions : TSectionValuesOptions = []);
var var
oSection: TIniFileSection; oSection: TIniFileSection;
s: string; s: string;
i,J: integer; i,J: integer;
KeyIsComment,IncludeComments,IncludeInvalid,DoStripQuotes : boolean;
K : TIniFileKey;
begin begin
IncludeComments:=(svoIncludeComments in AOptions) Or (ifoStripComments in Options);
IncludeInvalid:=(svoIncludeInvalid in AOptions) Or (ifoStripInvalid in Options);
DoStripQuotes:=StripQuotes and Not (svoIncludeQuotes in AOptions);
Strings.BeginUpdate; Strings.BeginUpdate;
try try
Strings.Clear; Strings.Clear;
oSection := FSectionList.SectionByName(Section,CaseSensitive); oSection := FSectionList.SectionByName(Section,CaseSensitive);
if oSection <> nil then with oSection.KeyList do if oSection = nil then
for i := 0 to Count-1 do begin Exit;
s := Items[i].Value; for i := 0 to oSection.KeyList.Count-1 do
If StripQuotes then begin
K:=oSection.KeyList.Items[i];
if IncludeInvalid or (K.Ident<>'') then
begin begin
J:=Length(s); s := K.Value;
// Joost, 2-jan-2007: The check (J>1) is there for the case that KeyIsComment:=IsComment(K.Ident);
// the value consist of a single double-quote character. (see if IncludeComments Or Not KeyIsComment then
// mantis bug 6555) begin
If (J>1) and ((s[1] in ['"','''']) and (s[J]=s[1])) then If DoStripQuotes then
s:=Copy(s,2,J-2); begin
J:=Length(s);
// 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 ((s[1] in ['"','''']) and (s[J]=s[1])) then
s:=Copy(s,2,J-2);
end;
if KeyIsComment then
S:=K.Ident
else if k.ident<>'' then
s:=K.Ident+Separator+s;
Strings.Add(s);
end;
end; end;
if Items[i].Ident<>'' then
s:=Items[i].Ident+Separator+s;
Strings.Add(s);
end; end;
finally finally
Strings.EndUpdate; Strings.EndUpdate;