inifiles.pp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841
  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;
  48. type
  49. TIniFileKey = class
  50. FIdent: string;
  51. FValue: string;
  52. public
  53. constructor Create(AIdent, AValue: string);
  54. property Ident: string read FIdent write FIdent;
  55. property Value: string read FValue write FValue;
  56. end;
  57. TIniFileKeyList = class(TList)
  58. private
  59. function GetItem(Index: integer): TIniFileKey;
  60. function KeyByName(AName: string; CaseSensitive : Boolean): TIniFileKey;
  61. public
  62. destructor Destroy; override;
  63. procedure Clear; override;
  64. property Items[Index: integer]: TIniFileKey read GetItem; default;
  65. end;
  66. TIniFileSection = class
  67. FName: string;
  68. FKeyList: TIniFileKeyList;
  69. public
  70. constructor Create(AName: string);
  71. destructor Destroy; override;
  72. property Name: string read FName;
  73. property KeyList: TIniFileKeyList read FKeyList;
  74. end;
  75. TIniFileSectionList = class(TList)
  76. private
  77. function GetItem(Index: integer): TIniFileSection;
  78. function SectionByName(AName: string; CaseSensitive : Boolean): TIniFileSection;
  79. public
  80. destructor Destroy; override;
  81. procedure Clear;override;
  82. property Items[Index: integer]: TIniFileSection read GetItem; default;
  83. end;
  84. TCustomIniFile = class
  85. Private
  86. FFileName: string;
  87. FSectionList: TIniFileSectionList;
  88. FEscapeLineFeeds: boolean;
  89. FCaseSensitive : Boolean;
  90. public
  91. constructor Create(const AFileName: string; AEscapeLineFeeds : Boolean = False); virtual;
  92. destructor Destroy; override;
  93. function SectionExists(const Section: string): Boolean; virtual;
  94. function ReadString(const Section, Ident, Default: string): string; virtual; abstract;
  95. procedure WriteString(const Section, Ident, Value: String); virtual; abstract;
  96. function ReadInteger(const Section, Ident: string; Default: Longint): Longint; virtual;
  97. procedure WriteInteger(const Section, Ident: string; Value: Longint); virtual;
  98. function ReadBool(const Section, Ident: string; Default: Boolean): Boolean; virtual;
  99. procedure WriteBool(const Section, Ident: string; Value: Boolean); virtual;
  100. function ReadDate(const Section, Ident: string; Default: TDateTime): TDateTime; virtual;
  101. function ReadDateTime(const Section, Ident: string; Default: TDateTime): TDateTime; virtual;
  102. function ReadFloat(const Section, Ident: string; Default: Double): Double; virtual;
  103. function ReadTime(const Section, Ident: string; Default: TDateTime): TDateTime; virtual;
  104. procedure WriteDate(const Section, Ident: string; Value: TDateTime); virtual;
  105. procedure WriteDateTime(const Section, Ident: string; Value: TDateTime); virtual;
  106. procedure WriteFloat(const Section, Ident: string; Value: Double); virtual;
  107. procedure WriteTime(const Section, Ident: string; Value: TDateTime); virtual;
  108. procedure ReadSection(const Section: string; Strings: TStrings); virtual; abstract;
  109. procedure ReadSections(Strings: TStrings); virtual; abstract;
  110. procedure ReadSectionValues(const Section: string; Strings: TStrings); virtual; abstract;
  111. procedure EraseSection(const Section: string); virtual; abstract;
  112. procedure DeleteKey(const Section, Ident: String); virtual; abstract;
  113. procedure UpdateFile; virtual; abstract;
  114. function ValueExists(const Section, Ident: string): Boolean; virtual;
  115. property FileName: string read FFileName;
  116. property EscapeLineFeeds: boolean read FEscapeLineFeeds;
  117. Property CaseSensitive : Boolean Read FCaseSensitive Write FCaseSensitive;
  118. end;
  119. { TIniFile }
  120. TIniFile = class(TCustomIniFile)
  121. Private
  122. FStream: TStream;
  123. FCacheUpdates: Boolean;
  124. FDirty : Boolean;
  125. procedure FillSectionList(AStrings: TStrings);
  126. protected
  127. procedure MaybeUpdateFile;
  128. property Dirty : Boolean Read FDirty;
  129. public
  130. constructor Create(const AFileName: string; AEscapeLineFeeds : Boolean = False); override;
  131. constructor Create(AStream: TStream; AEscapeLineFeeds : Boolean = False);
  132. destructor Destroy; override;
  133. function ReadString(const Section, Ident, Default: string): string; override;
  134. procedure WriteString(const Section, Ident, Value: String); override;
  135. procedure ReadSection(const Section: string; Strings: TStrings); override;
  136. procedure ReadSectionRaw(const Section: string; Strings: TStrings);
  137. procedure ReadSections(Strings: TStrings); override;
  138. procedure ReadSectionValues(const Section: string; Strings: TStrings); override;
  139. procedure EraseSection(const Section: string); override;
  140. procedure DeleteKey(const Section, Ident: String); override;
  141. procedure UpdateFile; override;
  142. property Stream: TStream read FStream;
  143. property CacheUpdates : Boolean Read FCacheUpdates Write FCacheUpdates;
  144. end;
  145. TMemIniFile = class(TIniFile)
  146. public
  147. constructor Create(const AFileName: string; AEscapeLineFeeds : Boolean = False); override;
  148. procedure Clear;
  149. procedure GetStrings(List: TStrings);
  150. procedure Rename(const AFileName: string; Reload: Boolean);
  151. procedure SetStrings(List: TStrings);
  152. end;
  153. implementation
  154. const
  155. Brackets : array[0..1] of Char = ('[', ']');
  156. Separator : Char = '=';
  157. Comment : Char = ';';
  158. LF_Escape : Char = '\';
  159. function CharToBool(AChar: char): boolean;
  160. begin
  161. Result := (Achar = '1');
  162. end;
  163. function BoolToChar(ABool: boolean): char;
  164. begin
  165. if ABool then
  166. Result := '1'
  167. else
  168. Result := '0';
  169. end;
  170. function IsComment(AString: string): boolean;
  171. begin
  172. Result := False;
  173. if AString > '' then
  174. Result := (Copy(AString, 1, 1) = Comment);
  175. end;
  176. { TIniFileKey }
  177. constructor TIniFileKey.Create(AIdent, AValue: string);
  178. begin
  179. FIdent := AIdent;
  180. FValue := AValue;
  181. end;
  182. { TIniFileKeyList }
  183. function TIniFileKeyList.GetItem(Index: integer): TIniFileKey;
  184. begin
  185. Result := nil;
  186. if (Index >= 0) and (Index < Count) then
  187. Result := TIniFileKey(inherited Items[Index]);
  188. end;
  189. function TIniFileKeyList.KeyByName(AName: string; CaseSensitive : Boolean): TIniFileKey;
  190. var
  191. i: integer;
  192. begin
  193. Result := nil;
  194. if (AName > '') and not IsComment(AName) then
  195. If CaseSensitive then
  196. begin
  197. for i := 0 to Count-1 do
  198. if Items[i].Ident=AName then
  199. begin
  200. Result := Items[i];
  201. Break;
  202. end;
  203. end
  204. else
  205. for i := 0 to Count-1 do
  206. if CompareText(Items[i].Ident, AName) = 0 then begin
  207. Result := Items[i];
  208. Break;
  209. end;
  210. end;
  211. destructor TIniFileKeyList.Destroy;
  212. begin
  213. Clear;
  214. inherited Destroy;
  215. end;
  216. procedure TIniFileKeyList.Clear;
  217. var
  218. i: integer;
  219. begin
  220. for i := Count-1 downto 0 do
  221. Items[i].Free;
  222. inherited Clear;
  223. end;
  224. { TIniFileSection }
  225. constructor TIniFileSection.Create(AName: string);
  226. begin
  227. FName := AName;
  228. FKeyList := TIniFileKeyList.Create;
  229. end;
  230. destructor TIniFileSection.Destroy;
  231. begin
  232. FKeyList.Free;
  233. end;
  234. { TIniFileSectionList }
  235. function TIniFileSectionList.GetItem(Index: integer): TIniFileSection;
  236. begin
  237. Result := nil;
  238. if (Index >= 0) and (Index < Count) then
  239. Result := TIniFileSection(inherited Items[Index]);
  240. end;
  241. function TIniFileSectionList.SectionByName(AName: string; CaseSensitive : Boolean): TIniFileSection;
  242. var
  243. i: integer;
  244. begin
  245. Result := nil;
  246. if (AName > '') and not IsComment(AName) then
  247. If CaseSensitive then
  248. begin
  249. for i:=0 to Count-1 do
  250. if (Items[i].Name=AName) then
  251. begin
  252. Result := Items[i];
  253. Break;
  254. end;
  255. end
  256. else
  257. for i := 0 to Count-1 do
  258. if CompareText(Items[i].Name, AName) = 0 then
  259. begin
  260. Result := Items[i];
  261. Break;
  262. end;
  263. end;
  264. destructor TIniFileSectionList.Destroy;
  265. begin
  266. Clear;
  267. inherited Destroy;
  268. end;
  269. procedure TIniFileSectionList.Clear;
  270. var
  271. i: integer;
  272. begin
  273. for i := Count-1 downto 0 do
  274. Items[i].Free;
  275. inherited Clear;
  276. end;
  277. { TCustomIniFile }
  278. constructor TCustomIniFile.Create(const AFileName: string; AEscapeLineFeeds : Boolean = False);
  279. begin
  280. FFileName := AFileName;
  281. FSectionList := TIniFileSectionList.Create;
  282. FEscapeLineFeeds := AEscapeLineFeeds;
  283. end;
  284. destructor TCustomIniFile.Destroy;
  285. begin
  286. FSectionList.Free;
  287. inherited Destroy;
  288. end;
  289. function TCustomIniFile.SectionExists(const Section: string): Boolean;
  290. begin
  291. Result := (FSectionList.SectionByName(Section,CaseSensitive) <> nil);
  292. end;
  293. function TCustomIniFile.ReadInteger(const Section, Ident: string; Default: Longint): Longint;
  294. var
  295. s: string;
  296. begin
  297. Result := Default;
  298. s := ReadString(Section, Ident, '');
  299. if s > '' then try
  300. // convert hex string
  301. if Pos('0X', UpperCase(s)) = 1 then
  302. s := '$' + Copy(s, 3, Length(s) - 2);
  303. Result := StrToInt(s);
  304. except
  305. on EConvertError do
  306. else raise;
  307. end;
  308. end;
  309. procedure TCustomIniFile.WriteInteger(const Section, Ident: string; Value: Longint);
  310. begin
  311. WriteString(Section, Ident, IntToStr(Value));
  312. end;
  313. function TCustomIniFile.ReadBool(const Section, Ident: string; Default: Boolean): Boolean;
  314. var
  315. s: string;
  316. begin
  317. Result := Default;
  318. s := ReadString(Section, Ident, '');
  319. if s > '' then
  320. Result := CharToBool(s[1]);
  321. end;
  322. procedure TCustomIniFile.WriteBool(const Section, Ident: string; Value: Boolean);
  323. begin
  324. WriteString(Section, Ident, BoolToChar(Value));
  325. end;
  326. function TCustomIniFile.ReadDate(const Section, Ident: string; Default: TDateTime): TDateTime;
  327. var
  328. s: string;
  329. begin
  330. Result := Default;
  331. s := ReadString(Section, Ident, '');
  332. if s > '' then try
  333. Result := StrToDate(s);
  334. except
  335. on EConvertError do
  336. else raise;
  337. end;
  338. end;
  339. function TCustomIniFile.ReadDateTime(const Section, Ident: string; Default: TDateTime): TDateTime;
  340. var
  341. s: string;
  342. begin
  343. Result := Default;
  344. s := ReadString(Section, Ident, '');
  345. if s > '' then try
  346. Result := StrToDateTime(s);
  347. except
  348. on EConvertError do
  349. else raise;
  350. end;
  351. end;
  352. function TCustomIniFile.ReadFloat(const Section, Ident: string; Default: Double): Double;
  353. var
  354. s: string;
  355. begin
  356. Result := Default;
  357. s := ReadString(Section, Ident, '');
  358. if s > '' then try
  359. Result := StrToFloat(s);
  360. except
  361. on EConvertError do
  362. else raise;
  363. end;
  364. end;
  365. function TCustomIniFile.ReadTime(const Section, Ident: string; Default: TDateTime): TDateTime;
  366. var
  367. s: string;
  368. begin
  369. Result := Default;
  370. s := ReadString(Section, Ident, '');
  371. if s > '' then try
  372. Result := StrToTime(s);
  373. except
  374. on EConvertError do
  375. else raise;
  376. end;
  377. end;
  378. procedure TCustomIniFile.WriteDate(const Section, Ident: string; Value: TDateTime);
  379. begin
  380. WriteString(Section, Ident, DateToStr(Value));
  381. end;
  382. procedure TCustomIniFile.WriteDateTime(const Section, Ident: string; Value: TDateTime);
  383. begin
  384. WriteString(Section, Ident, DateTimeToStr(Value));
  385. end;
  386. procedure TCustomIniFile.WriteFloat(const Section, Ident: string; Value: Double);
  387. begin
  388. WriteString(Section, Ident, FloatToStr(Value));
  389. end;
  390. procedure TCustomIniFile.WriteTime(const Section, Ident: string; Value: TDateTime);
  391. begin
  392. WriteString(Section, Ident, TimeToStr(Value));
  393. end;
  394. function TCustomIniFile.ValueExists(const Section, Ident: string): Boolean;
  395. var
  396. oSection: TIniFileSection;
  397. begin
  398. Result := False;
  399. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  400. if oSection <> nil then
  401. Result := (oSection.KeyList.KeyByName(Ident,CaseSensitive) <> nil);
  402. end;
  403. { TIniFile }
  404. constructor TIniFile.Create(const AFileName: string; AEscapeLineFeeds : Boolean = False);
  405. var
  406. slLines: TStringList;
  407. begin
  408. inherited Create(AFileName,AEscapeLineFeeds);
  409. FStream := nil;
  410. slLines := TStringList.Create;
  411. try
  412. if FileExists(FFileName) then
  413. begin
  414. // read the ini file values
  415. slLines.LoadFromFile(FFileName);
  416. FillSectionList(slLines);
  417. end
  418. finally
  419. slLines.Free;
  420. end;
  421. end;
  422. constructor TIniFile.Create(AStream: TStream; AEscapeLineFeeds : Boolean = False);
  423. var
  424. slLines: TStringList;
  425. begin
  426. inherited Create('',AEscapeLineFeeds);
  427. FStream := AStream;
  428. slLines := TStringList.Create;
  429. try
  430. // read the ini file values
  431. slLines.LoadFromStream(FStream);
  432. FillSectionList(slLines);
  433. finally
  434. slLines.Free;
  435. end;
  436. end;
  437. destructor TIniFile.destroy;
  438. begin
  439. If FDirty and FCacheUpdates then
  440. UpdateFile;
  441. inherited destroy;
  442. end;
  443. procedure TIniFile.FillSectionList(AStrings: TStrings);
  444. var
  445. i,j: integer;
  446. sLine, sIdent, sValue: string;
  447. oSection: TIniFileSection;
  448. procedure RemoveBackslashes;
  449. var
  450. i,l: integer;
  451. s: string;
  452. bAppendNextLine, bAppended: boolean;
  453. begin
  454. AStrings.BeginUpdate;
  455. try
  456. For I:=AStrings.Count-2 downto 0 do
  457. begin
  458. S:=AStrings[i];
  459. L:=Length(S);
  460. If (I<AStrings.Count-1) and (L>0) and (S[L]=LF_Escape) then
  461. begin
  462. S:=Copy(S,1,L-1)+AStrings[I+1];
  463. AStrings.Delete(I+1);
  464. AStrings[i]:=S;
  465. end;
  466. end;
  467. finally
  468. AStrings.EndUpdate;
  469. end;
  470. end;
  471. begin
  472. oSection := nil;
  473. FSectionList.Clear;
  474. if FEscapeLineFeeds then
  475. RemoveBackslashes;
  476. for i := 0 to AStrings.Count-1 do begin
  477. sLine := Trim(AStrings[i]);
  478. if sLine > '' then
  479. begin
  480. if IsComment(sLine) and (oSection = nil) then begin
  481. // comment at the beginning of the ini file
  482. oSection := TIniFileSection.Create(sLine);
  483. FSectionList.Add(oSection);
  484. continue;
  485. end;
  486. if (Copy(sLine, 1, 1) = Brackets[0]) and (Copy(sLine, length(sLine), 1) = Brackets[1]) then begin
  487. // regular section
  488. oSection := TIniFileSection.Create(Copy(sLine, 2, Length(sLine) - 2));
  489. FSectionList.Add(oSection);
  490. end else if oSection <> nil then begin
  491. if IsComment(sLine) then begin
  492. // comment within a section
  493. sIdent := sLine;
  494. sValue := '';
  495. end else begin
  496. // regular key
  497. j:=Pos(Separator, sLine);
  498. if j=0 then
  499. begin
  500. sIdent:='';
  501. sValue:=sLine
  502. end
  503. else
  504. begin
  505. sIdent:=Trim(Copy(sLine, 1, j - 1));
  506. sValue:=Trim(Copy(sLine, j + 1, Length(sLine) - j));
  507. J:=Length(sValue);
  508. // Joost, 2-jan-2007: The check (J>1) is there for the case that
  509. // the value consist of a single double-quote character. (see
  510. // mantis bug 6555)
  511. If (J>1) and (sValue[1]='"') and (sValue[J]='"') then
  512. sValue:=Copy(sValue,2,J-2);
  513. end;
  514. end;
  515. oSection.KeyList.Add(TIniFileKey.Create(sIdent, sValue));
  516. end;
  517. end;
  518. end;
  519. end;
  520. function TIniFile.ReadString(const Section, Ident, Default: string): string;
  521. var
  522. oSection: TIniFileSection;
  523. oKey: TIniFileKey;
  524. begin
  525. Result := Default;
  526. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  527. if oSection <> nil then begin
  528. oKey := oSection.KeyList.KeyByName(Ident,CaseSensitive);
  529. if oKey <> nil then
  530. Result := oKey.Value;
  531. end;
  532. end;
  533. procedure TIniFile.WriteString(const Section, Ident, Value: String);
  534. var
  535. oSection: TIniFileSection;
  536. oKey: TIniFileKey;
  537. begin
  538. if (Section > '') and (Ident > '') then begin
  539. // update or add key
  540. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  541. if (Value > '') then begin
  542. if oSection = nil then begin
  543. oSection := TIniFileSection.Create(Section);
  544. FSectionList.Add(oSection);
  545. end;
  546. with oSection.KeyList do begin
  547. oKey := KeyByName(Ident,CaseSensitive);
  548. if oKey <> nil then
  549. oKey.Value := Value
  550. else
  551. oSection.KeyList.Add(TIniFileKey.Create(Ident, Value));
  552. end;
  553. end else if oSection <> nil then begin
  554. // remove key
  555. oKey := oSection.KeyList.KeyByName(Ident,CaseSensitive);
  556. if oKey <> nil then begin
  557. oSection.KeyList.Remove(oKey);
  558. end;
  559. end;
  560. end;
  561. MaybeUpdateFile;
  562. end;
  563. procedure TIniFile.ReadSection(const Section: string; Strings: TStrings);
  564. var
  565. oSection: TIniFileSection;
  566. i: integer;
  567. begin
  568. Strings.BeginUpdate;
  569. try
  570. Strings.Clear;
  571. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  572. if oSection <> nil then with oSection.KeyList do
  573. for i := 0 to Count-1 do
  574. if not IsComment(Items[i].Ident) then
  575. Strings.Add(Items[i].Ident);
  576. finally
  577. Strings.EndUpdate;
  578. end;
  579. end;
  580. procedure TIniFile.ReadSectionRaw(const Section: string; Strings: TStrings);
  581. var
  582. oSection: TIniFileSection;
  583. i: integer;
  584. begin
  585. Strings.BeginUpdate;
  586. try
  587. Strings.Clear;
  588. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  589. if oSection <> nil then with oSection.KeyList do
  590. for i := 0 to Count-1 do
  591. if not IsComment(Items[i].Ident) then
  592. begin
  593. if Items[i].Ident<>'' then
  594. Strings.Add(Items[i].Ident + Separator +Items[i].Value)
  595. else
  596. Strings.Add(Items[i].Value);
  597. end;
  598. finally
  599. Strings.EndUpdate;
  600. end;
  601. end;
  602. procedure TIniFile.ReadSections(Strings: TStrings);
  603. var
  604. i: integer;
  605. begin
  606. Strings.BeginUpdate;
  607. try
  608. Strings.Clear;
  609. for i := 0 to FSectionList.Count-1 do
  610. if not IsComment(FSectionList[i].Name) then
  611. Strings.Add(FSectionList[i].Name);
  612. finally
  613. Strings.EndUpdate;
  614. end;
  615. end;
  616. procedure TIniFile.ReadSectionValues(const Section: string; Strings: TStrings);
  617. var
  618. oSection: TIniFileSection;
  619. s: string;
  620. i: integer;
  621. begin
  622. Strings.BeginUpdate;
  623. try
  624. Strings.Clear;
  625. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  626. if oSection <> nil then with oSection.KeyList do
  627. for i := 0 to Count-1 do begin
  628. s := Items[i].Ident+Separator+Items[i].Value;
  629. Strings.Add(s);
  630. end;
  631. finally
  632. Strings.EndUpdate;
  633. end;
  634. end;
  635. procedure TIniFile.EraseSection(const Section: string);
  636. var
  637. oSection: TIniFileSection;
  638. begin
  639. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  640. if oSection <> nil then begin
  641. { It is needed so UpdateFile doesn't find a defunct section }
  642. { and cause the program to crash }
  643. FSectionList.Delete(FSectionList.IndexOf(oSection));
  644. oSection.Free;
  645. MaybeUpdateFile;
  646. end;
  647. end;
  648. procedure TIniFile.DeleteKey(const Section, Ident: String);
  649. var
  650. oSection: TIniFileSection;
  651. oKey: TIniFileKey;
  652. begin
  653. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  654. if oSection <> nil then begin
  655. oKey := oSection.KeyList.KeyByName(Ident,CaseSensitive);
  656. if oKey <> nil then begin
  657. oSection.KeyList.Delete(oSection.KeyList.IndexOf(oKey));
  658. oKey.Free;
  659. MaybeUpdateFile;
  660. end;
  661. end;
  662. end;
  663. procedure TIniFile.UpdateFile;
  664. var
  665. slLines: TStringList;
  666. i, j: integer;
  667. begin
  668. slLines := TStringList.Create;
  669. try
  670. for i := 0 to FSectionList.Count-1 do
  671. with FSectionList[i] do begin
  672. if IsComment(Name) then
  673. // comment
  674. slLines.Add(Name)
  675. else
  676. // regular section
  677. slLines.Add(Brackets[0] + Name + Brackets[1]);
  678. for j := 0 to KeyList.Count-1 do
  679. if IsComment(KeyList[j].Ident) then
  680. // comment
  681. slLines.Add(KeyList[j].Ident)
  682. else
  683. // regular key
  684. slLines.Add(KeyList[j].Ident + Separator + KeyList[j].Value);
  685. if (i < FSectionList.Count-1) and not IsComment(Name) then
  686. slLines.Add('');
  687. end;
  688. if FFileName > '' then
  689. slLines.SaveToFile(FFileName)
  690. else if FStream <> nil then
  691. slLines.SaveToStream(FStream);
  692. FillSectionList(slLines);
  693. finally
  694. slLines.Free;
  695. end;
  696. end;
  697. procedure TIniFile.MaybeUpdateFile;
  698. begin
  699. If FCacheUpdates then
  700. FDirty:=True
  701. else
  702. UpdateFile;
  703. end;
  704. { TMemIniFile }
  705. constructor TMemIniFile.Create(const AFileName: string; AEscapeLineFeeds : Boolean = False);
  706. begin
  707. Inherited;
  708. FCacheUpdates:=True;
  709. end;
  710. procedure TMemIniFile.Clear;
  711. begin
  712. FSectionList.Clear;
  713. end;
  714. procedure TMemIniFile.GetStrings(List: TStrings);
  715. var
  716. i, j: integer;
  717. oSection: TIniFileSection;
  718. begin
  719. List.BeginUpdate;
  720. try
  721. for i := 0 to FSectionList.Count-1 do begin
  722. oSection := FSectionList[i];
  723. with oSection do begin
  724. if IsComment(Name) then
  725. List.Add(Name)
  726. else
  727. List.Add(Brackets[0] + Name + Brackets[1]);
  728. for j := 0 to KeyList.Count-1 do begin
  729. if IsComment(KeyList[j].Ident) then
  730. List.Add(KeyList[j].Ident)
  731. else
  732. List.Add(KeyList[j].Ident + Separator + KeyList[j].Value);
  733. end;
  734. end;
  735. if i < FSectionList.Count-1 then
  736. List.Add('');
  737. end;
  738. finally
  739. List.EndUpdate;
  740. end;
  741. end;
  742. procedure TMemIniFile.Rename(const AFileName: string; Reload: Boolean);
  743. var
  744. slLines: TStringList;
  745. begin
  746. FFileName := AFileName;
  747. FStream := nil;
  748. if Reload then begin
  749. slLines := TStringList.Create;
  750. try
  751. slLines.LoadFromFile(FFileName);
  752. FillSectionList(slLines);
  753. finally
  754. slLines.Free;
  755. end;
  756. end;
  757. end;
  758. procedure TMemIniFile.SetStrings(List: TStrings);
  759. begin
  760. FillSectionList(List);
  761. end;
  762. end.