inifiles.pp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 1999-2000 Erik WachtMeester.
  4. File which provides TIniFile and friends.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {* Original disclaimer:
  12. * FCL inifiles.pp rewrite by Erik Wachtmeester ([email protected])
  13. *
  14. * Proposed replacement for inifiles.pp v 1.8
  15. *
  16. * This version is Borland Delphi 5 compatible, implementing the classes
  17. * TCustomIniFile, TIniFile and TMemIniFile, with all the public
  18. * properties and methods that Delphi 5 implements.
  19. *
  20. * (inifiles.pp v 1.8 only implements TIniFile with some properties and
  21. * methods missing, and some functionality added)
  22. *
  23. * In order to stay compatible with v 1.8, I added:
  24. * - TIniFile can be created and loaded from, and saved to a stream.
  25. * - ReadSectionRaw method (although it doesn't add empty lines to the
  26. * TStrings recipient like v 1.8, since empty lines aren't stored in
  27. * the SectionList object structure)
  28. * - ReadInteger supports '0x' type hex formats
  29. * - Comment support (this isn't standard in ini files)
  30. * - EscapeLineFeeds creation parameter
  31. *
  32. * Since the SectionList object structure is very different from the
  33. * way Delphi 5 accesses ini files (Delphi mostly uses Windows calls
  34. * like GetPrivateProfileString, etc.) it's completely platform
  35. * independant, and probably faster.
  36. * The only drawback is memory consumption: all sections, keys and
  37. * values are kept in memory. But same goes for inifiles.pp v 1.8
  38. * (the FFileBuffer member) and for Delphi's TMemIniFile.
  39. * Anyway, Windows restricts ini files to 64K max, so this shouldn't be
  40. * too much of a problem.
  41. *
  42. *}
  43. unit IniFiles;
  44. {$mode objfpc}
  45. {$H+}
  46. interface
  47. uses classes, sysutils, contnrs;
  48. type
  49. { THashedStringList }
  50. THashedStringList = class(TStringList)
  51. private
  52. FValueHash: TFPHashList;
  53. FNameHash: TFPHashList;
  54. FValueHashValid: Boolean;
  55. FNameHashValid: Boolean;
  56. procedure UpdateValueHash;
  57. procedure UpdateNameHash;
  58. protected
  59. procedure Changed; override;
  60. public
  61. constructor Create;
  62. destructor Destroy; override;
  63. function IndexOf(const S: String): Integer; override;
  64. function IndexOfName(const Name: String): Integer; override;
  65. end;
  66. TIniFileKey = class
  67. Private
  68. FIdent: string;
  69. FValue: string;
  70. public
  71. constructor Create(AIdent, AValue: string);
  72. property Ident: string read FIdent write FIdent;
  73. property Value: string read FValue write FValue;
  74. end;
  75. TIniFileKeyList = class(TList)
  76. private
  77. function GetItem(Index: integer): TIniFileKey;
  78. function KeyByName(AName: string; CaseSensitive : Boolean): TIniFileKey;
  79. public
  80. destructor Destroy; override;
  81. procedure Clear; override;
  82. property Items[Index: integer]: TIniFileKey read GetItem; default;
  83. end;
  84. TIniFileSection = class
  85. private
  86. FName: string;
  87. FKeyList: TIniFileKeyList;
  88. public
  89. Function Empty : Boolean;
  90. constructor Create(AName: string);
  91. destructor Destroy; override;
  92. property Name: string read FName;
  93. property KeyList: TIniFileKeyList read FKeyList;
  94. end;
  95. TIniFileSectionList = class(TList)
  96. private
  97. function GetItem(Index: integer): TIniFileSection;
  98. function SectionByName(AName: string; CaseSensitive : Boolean): TIniFileSection;
  99. public
  100. destructor Destroy; override;
  101. procedure Clear;override;
  102. property Items[Index: integer]: TIniFileSection read GetItem; default;
  103. end;
  104. TCustomIniFile = class
  105. Private
  106. FFileName: string;
  107. FSectionList: TIniFileSectionList;
  108. FEscapeLineFeeds: boolean;
  109. FCaseSensitive : Boolean;
  110. public
  111. constructor Create(const AFileName: string; AEscapeLineFeeds : Boolean = False); virtual;
  112. destructor Destroy; override;
  113. function SectionExists(const Section: string): Boolean; virtual;
  114. function ReadString(const Section, Ident, Default: string): string; virtual; abstract;
  115. procedure WriteString(const Section, Ident, Value: String); virtual; abstract;
  116. function ReadInteger(const Section, Ident: string; Default: Longint): Longint; virtual;
  117. procedure WriteInteger(const Section, Ident: string; Value: Longint); virtual;
  118. function ReadBool(const Section, Ident: string; Default: Boolean): Boolean; virtual;
  119. procedure WriteBool(const Section, Ident: string; Value: Boolean); virtual;
  120. function ReadDate(const Section, Ident: string; Default: TDateTime): TDateTime; virtual;
  121. function ReadDateTime(const Section, Ident: string; Default: TDateTime): TDateTime; virtual;
  122. function ReadFloat(const Section, Ident: string; Default: Double): Double; virtual;
  123. function ReadTime(const Section, Ident: string; Default: TDateTime): TDateTime; virtual;
  124. function ReadBinaryStream(const Section, Name: string; Value: TStream): Integer;
  125. procedure WriteDate(const Section, Ident: string; Value: TDateTime); virtual;
  126. procedure WriteDateTime(const Section, Ident: string; Value: TDateTime); virtual;
  127. procedure WriteFloat(const Section, Ident: string; Value: Double); virtual;
  128. procedure WriteTime(const Section, Ident: string; Value: TDateTime); virtual;
  129. procedure WriteBinaryStream(const Section, Name: string; Value: TStream);
  130. procedure ReadSection(const Section: string; Strings: TStrings); virtual; abstract;
  131. procedure ReadSections(Strings: TStrings); virtual; abstract;
  132. procedure ReadSectionValues(const Section: string; Strings: TStrings); virtual; abstract;
  133. procedure EraseSection(const Section: string); virtual; abstract;
  134. procedure DeleteKey(const Section, Ident: String); virtual; abstract;
  135. procedure UpdateFile; virtual; abstract;
  136. function ValueExists(const Section, Ident: string): Boolean; virtual;
  137. property FileName: string read FFileName;
  138. property EscapeLineFeeds: boolean read FEscapeLineFeeds;
  139. Property CaseSensitive : Boolean Read FCaseSensitive Write FCaseSensitive;
  140. end;
  141. { TIniFile }
  142. TIniFile = class(TCustomIniFile)
  143. Private
  144. FStream: TStream;
  145. FCacheUpdates: Boolean;
  146. FDirty : Boolean;
  147. procedure FillSectionList(AStrings: TStrings);
  148. Procedure DeleteSection(ASection : TIniFileSection);
  149. Procedure MaybeDeleteSection(ASection : TIniFileSection);
  150. protected
  151. procedure MaybeUpdateFile;
  152. property Dirty : Boolean Read FDirty;
  153. public
  154. constructor Create(const AFileName: string; AEscapeLineFeeds : Boolean = False); override;
  155. constructor Create(AStream: TStream; AEscapeLineFeeds : Boolean = False);
  156. destructor Destroy; override;
  157. function ReadString(const Section, Ident, Default: string): string; override;
  158. procedure WriteString(const Section, Ident, Value: String); override;
  159. procedure ReadSection(const Section: string; Strings: TStrings); override;
  160. procedure ReadSectionRaw(const Section: string; Strings: TStrings);
  161. procedure ReadSections(Strings: TStrings); override;
  162. procedure ReadSectionValues(const Section: string; Strings: TStrings); override;
  163. procedure EraseSection(const Section: string); override;
  164. procedure DeleteKey(const Section, Ident: String); override;
  165. procedure UpdateFile; override;
  166. property Stream: TStream read FStream;
  167. property CacheUpdates : Boolean Read FCacheUpdates Write FCacheUpdates;
  168. end;
  169. TMemIniFile = class(TIniFile)
  170. public
  171. constructor Create(const AFileName: string; AEscapeLineFeeds : Boolean = False); override;
  172. procedure Clear;
  173. procedure GetStrings(List: TStrings);
  174. procedure Rename(const AFileName: string; Reload: Boolean);
  175. procedure SetStrings(List: TStrings);
  176. end;
  177. implementation
  178. const
  179. Brackets : array[0..1] of Char = ('[', ']');
  180. Separator : Char = '=';
  181. Comment : Char = ';';
  182. LF_Escape : Char = '\';
  183. function CharToBool(AChar: char): boolean;
  184. begin
  185. Result := (Achar = '1');
  186. end;
  187. function BoolToChar(ABool: boolean): char;
  188. begin
  189. if ABool then
  190. Result := '1'
  191. else
  192. Result := '0';
  193. end;
  194. function IsComment(AString: string): boolean;
  195. begin
  196. Result := False;
  197. if AString > '' then
  198. Result := (Copy(AString, 1, 1) = Comment);
  199. end;
  200. { THashedStringList }
  201. constructor THashedStringList.Create;
  202. begin
  203. inherited;
  204. FValueHash := nil;
  205. FNameHash := nil;
  206. FValueHashValid := False;
  207. FNameHashValid := False;
  208. end;
  209. destructor THashedStringList.Destroy;
  210. begin
  211. if Assigned(FValueHash) then
  212. FValueHash.Free;
  213. if Assigned(FNameHash) then
  214. FNameHash.Free;
  215. inherited Destroy;
  216. end;
  217. function THashedStringList.IndexOf(const S: String): Integer;
  218. var
  219. I: Integer;
  220. begin
  221. if not FValueHashValid then
  222. UpdateValueHash;
  223. I := FValueHash.FindIndexOf(S);
  224. if I >= 0 then
  225. Result := Integer(FValueHash[I])
  226. else
  227. Result := -1;
  228. end;
  229. function THashedStringList.IndexOfName(const Name: String): Integer;
  230. var
  231. I: Integer;
  232. begin
  233. if not FNameHashValid then
  234. UpdateNameHash;
  235. I := FNameHash.FindIndexOf(Name);
  236. if I >= 0 then
  237. Result := Integer(FNameHash[I])
  238. else
  239. Result := -1;
  240. end;
  241. procedure THashedStringList.Changed;
  242. begin
  243. FValueHashValid := False;
  244. FNameHashValid := False;
  245. inherited Changed;
  246. end;
  247. procedure THashedStringList.UpdateValueHash;
  248. var
  249. I: Integer;
  250. begin
  251. if not Assigned(FValueHash) then
  252. FValueHash := TFPHashList.Create
  253. else
  254. FValueHash.Clear;
  255. for I := 0 to Count - 1 do
  256. FValueHash.Add(Strings[I], Pointer(I));
  257. FValueHashValid := True;
  258. end;
  259. procedure THashedStringList.UpdateNameHash;
  260. var
  261. I: Integer;
  262. begin
  263. if not Assigned(FNameHash) then
  264. FNameHash := TFPHashList.Create
  265. else
  266. FNameHash.Clear;
  267. for I := 0 to Count - 1 do
  268. FNameHash.Add(Names[I], Pointer(I));
  269. FNameHashValid := True;
  270. end;
  271. { TIniFileKey }
  272. constructor TIniFileKey.Create(AIdent, AValue: string);
  273. begin
  274. FIdent := AIdent;
  275. FValue := AValue;
  276. end;
  277. { TIniFileKeyList }
  278. function TIniFileKeyList.GetItem(Index: integer): TIniFileKey;
  279. begin
  280. Result := nil;
  281. if (Index >= 0) and (Index < Count) then
  282. Result := TIniFileKey(inherited Items[Index]);
  283. end;
  284. function TIniFileKeyList.KeyByName(AName: string; CaseSensitive : Boolean): TIniFileKey;
  285. var
  286. i: integer;
  287. begin
  288. Result := nil;
  289. if (AName > '') and not IsComment(AName) then
  290. If CaseSensitive then
  291. begin
  292. for i := 0 to Count-1 do
  293. if Items[i].Ident=AName then
  294. begin
  295. Result := Items[i];
  296. Break;
  297. end;
  298. end
  299. else
  300. for i := 0 to Count-1 do
  301. if CompareText(Items[i].Ident, AName) = 0 then begin
  302. Result := Items[i];
  303. Break;
  304. end;
  305. end;
  306. destructor TIniFileKeyList.Destroy;
  307. begin
  308. Clear;
  309. inherited Destroy;
  310. end;
  311. procedure TIniFileKeyList.Clear;
  312. var
  313. i: integer;
  314. begin
  315. for i := Count-1 downto 0 do
  316. Items[i].Free;
  317. inherited Clear;
  318. end;
  319. Function TIniFileSection.Empty : Boolean;
  320. Var
  321. I : Integer;
  322. begin
  323. Result:=True;
  324. I:=0;
  325. While Result and (I<KeyList.Count) do
  326. begin
  327. result:=IsComment(KeyList[i].Ident);
  328. Inc(i);
  329. end;
  330. end;
  331. { TIniFileSection }
  332. constructor TIniFileSection.Create(AName: string);
  333. begin
  334. FName := AName;
  335. FKeyList := TIniFileKeyList.Create;
  336. end;
  337. destructor TIniFileSection.Destroy;
  338. begin
  339. FKeyList.Free;
  340. end;
  341. { TIniFileSectionList }
  342. function TIniFileSectionList.GetItem(Index: integer): TIniFileSection;
  343. begin
  344. Result := nil;
  345. if (Index >= 0) and (Index < Count) then
  346. Result := TIniFileSection(inherited Items[Index]);
  347. end;
  348. function TIniFileSectionList.SectionByName(AName: string; CaseSensitive : Boolean): TIniFileSection;
  349. var
  350. i: integer;
  351. begin
  352. Result := nil;
  353. if (AName > '') and not IsComment(AName) then
  354. If CaseSensitive then
  355. begin
  356. for i:=0 to Count-1 do
  357. if (Items[i].Name=AName) then
  358. begin
  359. Result := Items[i];
  360. Break;
  361. end;
  362. end
  363. else
  364. for i := 0 to Count-1 do
  365. if CompareText(Items[i].Name, AName) = 0 then
  366. begin
  367. Result := Items[i];
  368. Break;
  369. end;
  370. end;
  371. destructor TIniFileSectionList.Destroy;
  372. begin
  373. Clear;
  374. inherited Destroy;
  375. end;
  376. procedure TIniFileSectionList.Clear;
  377. var
  378. i: integer;
  379. begin
  380. for i := Count-1 downto 0 do
  381. Items[i].Free;
  382. inherited Clear;
  383. end;
  384. { TCustomIniFile }
  385. constructor TCustomIniFile.Create(const AFileName: string; AEscapeLineFeeds : Boolean = False);
  386. begin
  387. FFileName := AFileName;
  388. FSectionList := TIniFileSectionList.Create;
  389. FEscapeLineFeeds := AEscapeLineFeeds;
  390. end;
  391. destructor TCustomIniFile.Destroy;
  392. begin
  393. FSectionList.Free;
  394. inherited Destroy;
  395. end;
  396. function TCustomIniFile.SectionExists(const Section: string): Boolean;
  397. Var
  398. S : TIniFileSection;
  399. begin
  400. S:=FSectionList.SectionByName(Section,CaseSensitive);
  401. Result:=Assigned(S) and Not S.Empty;
  402. end;
  403. function TCustomIniFile.ReadInteger(const Section, Ident: string; Default: Longint): Longint;
  404. begin
  405. // StrToInfDef() supports hex numbers prefixed with '0x' via val()
  406. Result := StrToIntDef(ReadString(Section, Ident, ''), Default);
  407. end;
  408. procedure TCustomIniFile.WriteInteger(const Section, Ident: string; Value: Longint);
  409. begin
  410. WriteString(Section, Ident, IntToStr(Value));
  411. end;
  412. function TCustomIniFile.ReadBool(const Section, Ident: string; Default: Boolean): Boolean;
  413. var
  414. s: string;
  415. begin
  416. Result := Default;
  417. s := ReadString(Section, Ident, '');
  418. if s > '' then
  419. Result := CharToBool(s[1]);
  420. end;
  421. procedure TCustomIniFile.WriteBool(const Section, Ident: string; Value: Boolean);
  422. begin
  423. WriteString(Section, Ident, BoolToChar(Value));
  424. end;
  425. function TCustomIniFile.ReadDate(const Section, Ident: string; Default: TDateTime): TDateTime;
  426. begin
  427. Result := StrToDateDef(ReadString(Section, Ident, ''),Default);
  428. end;
  429. function TCustomIniFile.ReadDateTime(const Section, Ident: string; Default: TDateTime): TDateTime;
  430. begin
  431. Result := StrToDateTimeDef(ReadString(Section, Ident, ''),Default);
  432. end;
  433. function TCustomIniFile.ReadFloat(const Section, Ident: string; Default: Double): Double;
  434. begin
  435. Result:=StrToFloatDef(ReadString(Section, Ident, ''),Default);
  436. end;
  437. function TCustomIniFile.ReadTime(const Section, Ident: string; Default: TDateTime): TDateTime;
  438. begin
  439. Result := StrToTimeDef(ReadString(Section, Ident, ''),Default);
  440. end;
  441. procedure TCustomIniFile.WriteDate(const Section, Ident: string; Value: TDateTime);
  442. begin
  443. WriteString(Section, Ident, DateToStr(Value));
  444. end;
  445. procedure TCustomIniFile.WriteDateTime(const Section, Ident: string; Value: TDateTime);
  446. begin
  447. WriteString(Section, Ident, DateTimeToStr(Value));
  448. end;
  449. procedure TCustomIniFile.WriteFloat(const Section, Ident: string; Value: Double);
  450. begin
  451. WriteString(Section, Ident, FloatToStr(Value));
  452. end;
  453. procedure TCustomIniFile.WriteTime(const Section, Ident: string; Value: TDateTime);
  454. begin
  455. WriteString(Section, Ident, TimeToStr(Value));
  456. end;
  457. function TCustomIniFile.ValueExists(const Section, Ident: string): Boolean;
  458. var
  459. oSection: TIniFileSection;
  460. begin
  461. Result := False;
  462. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  463. if oSection <> nil then
  464. Result := (oSection.KeyList.KeyByName(Ident,CaseSensitive) <> nil);
  465. end;
  466. function TCustomIniFile.ReadBinaryStream(const Section, Name: string; Value: TStream): Integer;
  467. Var
  468. M : TMemoryStream;
  469. S : String;
  470. PB,PR : PByte;
  471. PC : PChar;
  472. H : String[3];
  473. i,l2,code : Integer;
  474. begin
  475. S:=ReadString(Section,Name,'');
  476. Setlength(H,3);
  477. H[1]:='$';
  478. Result:=Length(S) div 2;
  479. If Result>0 then
  480. begin
  481. GetMem(PR,Result);
  482. Try
  483. PC:=PChar(S);
  484. PB:=PR;
  485. For I:=1 to Result do
  486. begin
  487. H[2]:=PC[0];
  488. H[3]:=PC[1];
  489. Val(H,PB^,code);
  490. Inc(PC,2);
  491. Inc(PB);
  492. end;
  493. Value.WriteBuffer(PR^,Result);
  494. finally
  495. FreeMem(PR);
  496. end;
  497. end;
  498. end;
  499. procedure TCustomInifile.WriteBinaryStream(const Section, Name: string; Value: TStream);
  500. Var
  501. M : TMemoryStream;
  502. S : String;
  503. PB : PByte;
  504. PC : PChar;
  505. H : String[2];
  506. i : Integer;
  507. begin
  508. M:=TMemoryStream.Create;
  509. Try
  510. M.CopyFrom(Value,0);
  511. SetLength(S,M.Size*2);
  512. If (length(S)>0) then
  513. begin
  514. PB:=M.Memory;
  515. PC:=PChar(S);
  516. For I:=1 to Length(S) div 2 do
  517. begin
  518. H:=HexStr(PB^,2);
  519. PC[0]:=H[1];
  520. PC[1]:=H[2];
  521. Inc(PC,2);
  522. Inc(PB);
  523. end;
  524. end;
  525. WriteString(Section,Name,S);
  526. Finally
  527. M.Free;
  528. end;
  529. end;
  530. { TIniFile }
  531. constructor TIniFile.Create(const AFileName: string; AEscapeLineFeeds : Boolean = False);
  532. var
  533. slLines: TStringList;
  534. begin
  535. inherited Create(AFileName,AEscapeLineFeeds);
  536. FStream := nil;
  537. slLines := TStringList.Create;
  538. try
  539. if FileExists(FFileName) then
  540. begin
  541. // read the ini file values
  542. slLines.LoadFromFile(FFileName);
  543. FillSectionList(slLines);
  544. end
  545. finally
  546. slLines.Free;
  547. end;
  548. end;
  549. constructor TIniFile.Create(AStream: TStream; AEscapeLineFeeds : Boolean = False);
  550. var
  551. slLines: TStringList;
  552. begin
  553. inherited Create('',AEscapeLineFeeds);
  554. FStream := AStream;
  555. slLines := TStringList.Create;
  556. try
  557. // read the ini file values
  558. slLines.LoadFromStream(FStream);
  559. FillSectionList(slLines);
  560. finally
  561. slLines.Free;
  562. end;
  563. end;
  564. destructor TIniFile.destroy;
  565. begin
  566. If FDirty and FCacheUpdates then
  567. UpdateFile;
  568. inherited destroy;
  569. end;
  570. procedure TIniFile.FillSectionList(AStrings: TStrings);
  571. var
  572. i,j: integer;
  573. sLine, sIdent, sValue: string;
  574. oSection: TIniFileSection;
  575. procedure RemoveBackslashes;
  576. var
  577. i,l: integer;
  578. s: string;
  579. bAppendNextLine, bAppended: boolean;
  580. begin
  581. AStrings.BeginUpdate;
  582. try
  583. For I:=AStrings.Count-2 downto 0 do
  584. begin
  585. S:=AStrings[i];
  586. L:=Length(S);
  587. If (I<AStrings.Count-1) and (L>0) and (S[L]=LF_Escape) then
  588. begin
  589. S:=Copy(S,1,L-1)+AStrings[I+1];
  590. AStrings.Delete(I+1);
  591. AStrings[i]:=S;
  592. end;
  593. end;
  594. finally
  595. AStrings.EndUpdate;
  596. end;
  597. end;
  598. begin
  599. oSection := nil;
  600. FSectionList.Clear;
  601. if FEscapeLineFeeds then
  602. RemoveBackslashes;
  603. for i := 0 to AStrings.Count-1 do begin
  604. sLine := Trim(AStrings[i]);
  605. if sLine > '' then
  606. begin
  607. if IsComment(sLine) and (oSection = nil) then begin
  608. // comment at the beginning of the ini file
  609. oSection := TIniFileSection.Create(sLine);
  610. FSectionList.Add(oSection);
  611. continue;
  612. end;
  613. if (Copy(sLine, 1, 1) = Brackets[0]) and (Copy(sLine, length(sLine), 1) = Brackets[1]) then begin
  614. // regular section
  615. oSection := TIniFileSection.Create(Copy(sLine, 2, Length(sLine) - 2));
  616. FSectionList.Add(oSection);
  617. end else if oSection <> nil then begin
  618. if IsComment(sLine) then begin
  619. // comment within a section
  620. sIdent := sLine;
  621. sValue := '';
  622. end else begin
  623. // regular key
  624. j:=Pos(Separator, sLine);
  625. if j=0 then
  626. begin
  627. sIdent:='';
  628. sValue:=sLine
  629. end
  630. else
  631. begin
  632. sIdent:=Trim(Copy(sLine, 1, j - 1));
  633. sValue:=Trim(Copy(sLine, j + 1, Length(sLine) - j));
  634. J:=Length(sValue);
  635. // Joost, 2-jan-2007: The check (J>1) is there for the case that
  636. // the value consist of a single double-quote character. (see
  637. // mantis bug 6555)
  638. If (J>1) and (sValue[1]='"') and (sValue[J]='"') then
  639. sValue:=Copy(sValue,2,J-2);
  640. end;
  641. end;
  642. oSection.KeyList.Add(TIniFileKey.Create(sIdent, sValue));
  643. end;
  644. end;
  645. end;
  646. end;
  647. function TIniFile.ReadString(const Section, Ident, Default: string): string;
  648. var
  649. oSection: TIniFileSection;
  650. oKey: TIniFileKey;
  651. begin
  652. Result := Default;
  653. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  654. if oSection <> nil then begin
  655. oKey := oSection.KeyList.KeyByName(Ident,CaseSensitive);
  656. if oKey <> nil then
  657. Result := oKey.Value;
  658. end;
  659. end;
  660. procedure TIniFile.WriteString(const Section, Ident, Value: String);
  661. var
  662. oSection: TIniFileSection;
  663. oKey: TIniFileKey;
  664. begin
  665. if (Section > '') and (Ident > '') then begin
  666. // update or add key
  667. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  668. if (Value > '') then begin
  669. if oSection = nil then begin
  670. oSection := TIniFileSection.Create(Section);
  671. FSectionList.Add(oSection);
  672. end;
  673. with oSection.KeyList do begin
  674. oKey := KeyByName(Ident,CaseSensitive);
  675. if oKey <> nil then
  676. oKey.Value := Value
  677. else
  678. oSection.KeyList.Add(TIniFileKey.Create(Ident, Value));
  679. end;
  680. end else if oSection <> nil then begin
  681. // remove key
  682. oKey := oSection.KeyList.KeyByName(Ident,CaseSensitive);
  683. if oKey <> nil then begin
  684. oSection.KeyList.Remove(oKey);
  685. end;
  686. end;
  687. end;
  688. MaybeUpdateFile;
  689. end;
  690. procedure TIniFile.ReadSection(const Section: string; Strings: TStrings);
  691. var
  692. oSection: TIniFileSection;
  693. i: integer;
  694. begin
  695. Strings.BeginUpdate;
  696. try
  697. Strings.Clear;
  698. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  699. if oSection <> nil then with oSection.KeyList do
  700. for i := 0 to Count-1 do
  701. if not IsComment(Items[i].Ident) then
  702. Strings.Add(Items[i].Ident);
  703. finally
  704. Strings.EndUpdate;
  705. end;
  706. end;
  707. procedure TIniFile.ReadSectionRaw(const Section: string; Strings: TStrings);
  708. var
  709. oSection: TIniFileSection;
  710. i: integer;
  711. begin
  712. Strings.BeginUpdate;
  713. try
  714. Strings.Clear;
  715. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  716. if oSection <> nil then with oSection.KeyList do
  717. for i := 0 to Count-1 do
  718. if not IsComment(Items[i].Ident) then
  719. begin
  720. if Items[i].Ident<>'' then
  721. Strings.Add(Items[i].Ident + Separator +Items[i].Value)
  722. else
  723. Strings.Add(Items[i].Value);
  724. end;
  725. finally
  726. Strings.EndUpdate;
  727. end;
  728. end;
  729. procedure TIniFile.ReadSections(Strings: TStrings);
  730. var
  731. i: integer;
  732. begin
  733. Strings.BeginUpdate;
  734. try
  735. Strings.Clear;
  736. for i := 0 to FSectionList.Count-1 do
  737. if not IsComment(FSectionList[i].Name) then
  738. Strings.Add(FSectionList[i].Name);
  739. finally
  740. Strings.EndUpdate;
  741. end;
  742. end;
  743. procedure TIniFile.ReadSectionValues(const Section: string; Strings: TStrings);
  744. var
  745. oSection: TIniFileSection;
  746. s: string;
  747. i: integer;
  748. begin
  749. Strings.BeginUpdate;
  750. try
  751. Strings.Clear;
  752. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  753. if oSection <> nil then with oSection.KeyList do
  754. for i := 0 to Count-1 do begin
  755. s := Items[i].Ident+Separator+Items[i].Value;
  756. Strings.Add(s);
  757. end;
  758. finally
  759. Strings.EndUpdate;
  760. end;
  761. end;
  762. procedure TIniFile.DeleteSection(ASection : TIniFileSection);
  763. begin
  764. FSectionList.Delete(FSectionList.IndexOf(ASection));
  765. ASection.Free;
  766. end;
  767. Procedure TIniFile.MaybeDeleteSection(ASection : TIniFileSection);
  768. begin
  769. If Asection.Empty then
  770. DeleteSection(ASection);
  771. end;
  772. procedure TIniFile.EraseSection(const Section: string);
  773. var
  774. oSection: TIniFileSection;
  775. begin
  776. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  777. if oSection <> nil then begin
  778. { It is needed so UpdateFile doesn't find a defunct section }
  779. { and cause the program to crash }
  780. DeleteSection(OSection);
  781. MaybeUpdateFile;
  782. end;
  783. end;
  784. procedure TIniFile.DeleteKey(const Section, Ident: String);
  785. var
  786. oSection: TIniFileSection;
  787. oKey: TIniFileKey;
  788. begin
  789. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  790. if oSection <> nil then
  791. begin
  792. oKey := oSection.KeyList.KeyByName(Ident,CaseSensitive);
  793. if oKey <> nil then
  794. begin
  795. oSection.KeyList.Delete(oSection.KeyList.IndexOf(oKey));
  796. oKey.Free;
  797. MaybeDeleteSection(oSection);
  798. MaybeUpdateFile;
  799. end;
  800. end;
  801. end;
  802. procedure TIniFile.UpdateFile;
  803. var
  804. slLines: TStringList;
  805. i, j: integer;
  806. begin
  807. slLines := TStringList.Create;
  808. try
  809. for i := 0 to FSectionList.Count-1 do
  810. with FSectionList[i] do begin
  811. if IsComment(Name) then
  812. // comment
  813. slLines.Add(Name)
  814. else
  815. // regular section
  816. slLines.Add(Brackets[0] + Name + Brackets[1]);
  817. for j := 0 to KeyList.Count-1 do
  818. if IsComment(KeyList[j].Ident) then
  819. // comment
  820. slLines.Add(KeyList[j].Ident)
  821. else
  822. // regular key
  823. slLines.Add(KeyList[j].Ident + Separator + KeyList[j].Value);
  824. if (i < FSectionList.Count-1) and not IsComment(Name) then
  825. slLines.Add('');
  826. end;
  827. if FFileName > '' then
  828. slLines.SaveToFile(FFileName)
  829. else if FStream <> nil then
  830. slLines.SaveToStream(FStream);
  831. FillSectionList(slLines);
  832. finally
  833. slLines.Free;
  834. end;
  835. end;
  836. procedure TIniFile.MaybeUpdateFile;
  837. begin
  838. If FCacheUpdates then
  839. FDirty:=True
  840. else
  841. UpdateFile;
  842. end;
  843. { TMemIniFile }
  844. constructor TMemIniFile.Create(const AFileName: string; AEscapeLineFeeds : Boolean = False);
  845. begin
  846. Inherited;
  847. FCacheUpdates:=True;
  848. end;
  849. procedure TMemIniFile.Clear;
  850. begin
  851. FSectionList.Clear;
  852. end;
  853. procedure TMemIniFile.GetStrings(List: TStrings);
  854. var
  855. i, j: integer;
  856. oSection: TIniFileSection;
  857. begin
  858. List.BeginUpdate;
  859. try
  860. for i := 0 to FSectionList.Count-1 do begin
  861. oSection := FSectionList[i];
  862. with oSection do begin
  863. if IsComment(Name) then
  864. List.Add(Name)
  865. else
  866. List.Add(Brackets[0] + Name + Brackets[1]);
  867. for j := 0 to KeyList.Count-1 do begin
  868. if IsComment(KeyList[j].Ident) then
  869. List.Add(KeyList[j].Ident)
  870. else
  871. List.Add(KeyList[j].Ident + Separator + KeyList[j].Value);
  872. end;
  873. end;
  874. if i < FSectionList.Count-1 then
  875. List.Add('');
  876. end;
  877. finally
  878. List.EndUpdate;
  879. end;
  880. end;
  881. procedure TMemIniFile.Rename(const AFileName: string; Reload: Boolean);
  882. var
  883. slLines: TStringList;
  884. begin
  885. FFileName := AFileName;
  886. FStream := nil;
  887. if Reload then begin
  888. slLines := TStringList.Create;
  889. try
  890. slLines.LoadFromFile(FFileName);
  891. FillSectionList(slLines);
  892. finally
  893. slLines.Free;
  894. end;
  895. end;
  896. end;
  897. procedure TMemIniFile.SetStrings(List: TStrings);
  898. begin
  899. FillSectionList(List);
  900. end;
  901. end.