inifiles.pp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  1. {
  2. $Id$
  3. This file is part of the Free Component Library (FCL)
  4. Copyright (c) 1999-2000 Erik WachtMeester.
  5. File which provides TIniFile and friends.
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {* Original disclaimer:
  13. * FCL inifiles.pp rewrite by Erik Wachtmeester ([email protected])
  14. *
  15. * Proposed replacement for inifiles.pp v 1.8
  16. *
  17. * This version is Borland Delphi 5 compatible, implementing the classes
  18. * TCustomIniFile, TIniFile and TMemIniFile, with all the public
  19. * properties and methods that Delphi 5 implements.
  20. *
  21. * (inifiles.pp v 1.8 only implements TIniFile with some properties and
  22. * methods missing, and some functionality added)
  23. *
  24. * In order to stay compatible with v 1.8, I added:
  25. * - TIniFile can be created and loaded from, and saved to a stream.
  26. * - ReadSectionRaw method (although it doesn't add empty lines to the
  27. * TStrings recipient like v 1.8, since empty lines aren't stored in
  28. * the SectionList object structure)
  29. * - ReadInteger supports '0x' type hex formats
  30. * - Comment support (this isn't standard in ini files)
  31. * - EscapeLineFeeds property
  32. *
  33. * Since the SectionList object structure is very different from the
  34. * way Delphi 5 accesses ini files (Delphi mostly uses Windows calls
  35. * like GetPrivateProfileString, etc.) it's completely platform
  36. * independant, and probably faster.
  37. * The only drawback is memory consumption: all sections, keys and
  38. * values are kept in memory. But same goes for inifiles.pp v 1.8
  39. * (the FFileBuffer member) and for Delphi's TMemIniFile.
  40. * Anyway, Windows restricts ini files to 64K max, so this shouldn't be
  41. * too much of a problem.
  42. *
  43. *}
  44. unit IniFiles;
  45. {$mode objfpc}
  46. {$H+}
  47. interface
  48. uses classes, sysutils;
  49. type
  50. TIniFileKey = class
  51. FIdent: string;
  52. FValue: string;
  53. public
  54. constructor Create(AIdent, AValue: string);
  55. property Ident: string read FIdent write FIdent;
  56. property Value: string read FValue write FValue;
  57. end;
  58. TIniFileKeyList = class(TList)
  59. private
  60. function GetItem(Index: integer): TIniFileKey;
  61. function KeyByName(AName: string): TIniFileKey;
  62. public
  63. destructor Destroy; override;
  64. procedure Clear;
  65. override;
  66. property Items[Index: integer]: TIniFileKey read GetItem; default;
  67. end;
  68. TIniFileSection = class
  69. FName: string;
  70. FKeyList: TIniFileKeyList;
  71. public
  72. constructor Create(AName: string);
  73. destructor Destroy; override;
  74. property Name: string read FName;
  75. property KeyList: TIniFileKeyList read FKeyList;
  76. end;
  77. TIniFileSectionList = class(TList)
  78. private
  79. function GetItem(Index: integer): TIniFileSection;
  80. function SectionByName(AName: string): TIniFileSection;
  81. public
  82. destructor Destroy; override;
  83. procedure Clear;override;
  84. property Items[Index: integer]: TIniFileSection read GetItem; default;
  85. end;
  86. TCustomIniFile = class
  87. FFileName: string;
  88. FSectionList: TIniFileSectionList;
  89. FEscapeLineFeeds: boolean;
  90. public
  91. constructor Create(const AFileName: string);
  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 write FEscapeLineFeeds;
  117. end;
  118. TIniFile = class(TCustomIniFile)
  119. FStream: TStream;
  120. private
  121. procedure FillSectionList(AStrings: TStrings);
  122. public
  123. constructor Create(const AFileName: string);
  124. constructor Create(AStream: TStream);
  125. function ReadString(const Section, Ident, Default: string): string; override;
  126. procedure WriteString(const Section, Ident, Value: String); override;
  127. procedure ReadSection(const Section: string; Strings: TStrings); override;
  128. procedure ReadSectionRaw(const Section: string; Strings: TStrings);
  129. procedure ReadSections(Strings: TStrings); override;
  130. procedure ReadSectionValues(const Section: string; Strings: TStrings); override;
  131. procedure EraseSection(const Section: string); override;
  132. procedure DeleteKey(const Section, Ident: String); override;
  133. procedure UpdateFile; override;
  134. property Stream: TStream read FStream;
  135. end;
  136. TMemIniFile = class(TIniFile)
  137. public
  138. procedure Clear;
  139. procedure GetStrings(List: TStrings);
  140. procedure Rename(const AFileName: string; Reload: Boolean);
  141. procedure SetStrings(List: TStrings);
  142. end;
  143. implementation
  144. const
  145. Brackets : array[0..1] of Char = ('[', ']');
  146. Separator : Char = '=';
  147. Comment : Char = ';';
  148. LF_Escape : Char = '\';
  149. function CharToBool(AChar: char): boolean;
  150. begin
  151. Result := (Achar = '1');
  152. end;
  153. function BoolToChar(ABool: boolean): char;
  154. begin
  155. if ABool then
  156. Result := '1'
  157. else
  158. Result := '0';
  159. end;
  160. function IsComment(AString: string): boolean;
  161. begin
  162. Result := False;
  163. if AString > '' then
  164. Result := (Copy(AString, 1, 1) = Comment);
  165. end;
  166. { TIniFileKey }
  167. constructor TIniFileKey.Create(AIdent, AValue: string);
  168. begin
  169. FIdent := AIdent;
  170. FValue := AValue;
  171. end;
  172. { TIniFileKeyList }
  173. function TIniFileKeyList.GetItem(Index: integer): TIniFileKey;
  174. begin
  175. Result := nil;
  176. if (Index >= 0) and (Index < Count) then
  177. Result := TIniFileKey(inherited Items[Index]);
  178. end;
  179. function TIniFileKeyList.KeyByName(AName: string): TIniFileKey;
  180. var
  181. i: integer;
  182. begin
  183. Result := nil;
  184. if (AName > '') and not IsComment(AName) then
  185. for i := 0 to Count-1 do
  186. if CompareText(Items[i].Ident, AName) = 0 then begin
  187. Result := Items[i];
  188. Break;
  189. end;
  190. end;
  191. destructor TIniFileKeyList.Destroy;
  192. begin
  193. Clear;
  194. inherited Destroy;
  195. end;
  196. procedure TIniFileKeyList.Clear;
  197. var
  198. i: integer;
  199. begin
  200. for i := Count-1 downto 0 do
  201. Items[i].Free;
  202. inherited Clear;
  203. end;
  204. { TIniFileSection }
  205. constructor TIniFileSection.Create(AName: string);
  206. begin
  207. FName := AName;
  208. FKeyList := TIniFileKeyList.Create;
  209. end;
  210. destructor TIniFileSection.Destroy;
  211. begin
  212. FKeyList.Free;
  213. end;
  214. { TIniFileSectionList }
  215. function TIniFileSectionList.GetItem(Index: integer): TIniFileSection;
  216. begin
  217. Result := nil;
  218. if (Index >= 0) and (Index < Count) then
  219. Result := TIniFileSection(inherited Items[Index]);
  220. end;
  221. function TIniFileSectionList.SectionByName(AName: string): TIniFileSection;
  222. var
  223. i: integer;
  224. begin
  225. Result := nil;
  226. if (AName > '') and not IsComment(AName) then
  227. for i := 0 to Count-1 do
  228. if CompareText(Items[i].Name, AName) = 0 then begin
  229. Result := Items[i];
  230. Break;
  231. end;
  232. end;
  233. destructor TIniFileSectionList.Destroy;
  234. begin
  235. Clear;
  236. inherited Destroy;
  237. end;
  238. procedure TIniFileSectionList.Clear;
  239. var
  240. i: integer;
  241. begin
  242. for i := Count-1 downto 0 do
  243. Items[i].Free;
  244. inherited Clear;
  245. end;
  246. { TCustomIniFile }
  247. constructor TCustomIniFile.Create(const AFileName: string);
  248. begin
  249. FFileName := AFileName;
  250. FSectionList := TIniFileSectionList.Create;
  251. FEscapeLineFeeds := False;
  252. end;
  253. destructor TCustomIniFile.Destroy;
  254. begin
  255. FSectionList.Free;
  256. inherited Destroy;
  257. end;
  258. function TCustomIniFile.SectionExists(const Section: string): Boolean;
  259. begin
  260. Result := (FSectionList.SectionByName(Section) <> nil);
  261. end;
  262. function TCustomIniFile.ReadInteger(const Section, Ident: string; Default: Longint): Longint;
  263. var
  264. s: string;
  265. begin
  266. Result := Default;
  267. s := ReadString(Section, Ident, '');
  268. if s > '' then try
  269. // convert hex string
  270. if Pos('0X', UpperCase(s)) = 1 then
  271. s := '$' + Copy(s, 3, Length(s) - 2);
  272. Result := StrToInt(s);
  273. except
  274. on EConvertError do
  275. else raise;
  276. end;
  277. end;
  278. procedure TCustomIniFile.WriteInteger(const Section, Ident: string; Value: Longint);
  279. begin
  280. WriteString(Section, Ident, IntToStr(Value));
  281. end;
  282. function TCustomIniFile.ReadBool(const Section, Ident: string; Default: Boolean): Boolean;
  283. var
  284. s: string;
  285. begin
  286. Result := Default;
  287. s := ReadString(Section, Ident, '');
  288. if s > '' then
  289. Result := CharToBool(s[1]);
  290. end;
  291. procedure TCustomIniFile.WriteBool(const Section, Ident: string; Value: Boolean);
  292. begin
  293. WriteString(Section, Ident, BoolToChar(Value));
  294. end;
  295. function TCustomIniFile.ReadDate(const Section, Ident: string; Default: TDateTime): TDateTime;
  296. var
  297. s: string;
  298. begin
  299. Result := Default;
  300. s := ReadString(Section, Ident, '');
  301. if s > '' then try
  302. Result := StrToDate(s);
  303. except
  304. on EConvertError do
  305. else raise;
  306. end;
  307. end;
  308. function TCustomIniFile.ReadDateTime(const Section, Ident: string; Default: TDateTime): TDateTime;
  309. var
  310. s: string;
  311. begin
  312. Result := Default;
  313. s := ReadString(Section, Ident, '');
  314. if s > '' then try
  315. Result := StrToDateTime(s);
  316. except
  317. on EConvertError do
  318. else raise;
  319. end;
  320. end;
  321. function TCustomIniFile.ReadFloat(const Section, Ident: string; Default: Double): Double;
  322. var
  323. s: string;
  324. begin
  325. Result := Default;
  326. s := ReadString(Section, Ident, '');
  327. if s > '' then try
  328. Result := StrToFloat(s);
  329. except
  330. on EConvertError do
  331. else raise;
  332. end;
  333. end;
  334. function TCustomIniFile.ReadTime(const Section, Ident: string; Default: TDateTime): TDateTime;
  335. var
  336. s: string;
  337. begin
  338. Result := Default;
  339. s := ReadString(Section, Ident, '');
  340. if s > '' then try
  341. Result := StrToTime(s);
  342. except
  343. on EConvertError do
  344. else raise;
  345. end;
  346. end;
  347. procedure TCustomIniFile.WriteDate(const Section, Ident: string; Value: TDateTime);
  348. begin
  349. WriteString(Section, Ident, DateToStr(Value));
  350. end;
  351. procedure TCustomIniFile.WriteDateTime(const Section, Ident: string; Value: TDateTime);
  352. begin
  353. WriteString(Section, Ident, DateTimeToStr(Value));
  354. end;
  355. procedure TCustomIniFile.WriteFloat(const Section, Ident: string; Value: Double);
  356. begin
  357. WriteString(Section, Ident, FloatToStr(Value));
  358. end;
  359. procedure TCustomIniFile.WriteTime(const Section, Ident: string; Value: TDateTime);
  360. begin
  361. WriteString(Section, Ident, TimeToStr(Value));
  362. end;
  363. function TCustomIniFile.ValueExists(const Section, Ident: string): Boolean;
  364. var
  365. oSection: TIniFileSection;
  366. begin
  367. Result := False;
  368. oSection := FSectionList.SectionByName(Section);
  369. if oSection <> nil then
  370. Result := (oSection.KeyList.KeyByName(Ident) <> nil);
  371. end;
  372. { TIniFile }
  373. constructor TIniFile.Create(const AFileName: string);
  374. var
  375. slLines: TStringList;
  376. begin
  377. inherited Create(AFileName);
  378. FStream := nil;
  379. slLines := TStringList.Create;
  380. try
  381. if FileExists(FFileName) then begin
  382. // read the ini file values
  383. slLines.LoadFromFile(FFileName);
  384. FillSectionList(slLines);
  385. end else
  386. // create a new ini file
  387. slLines.SaveToFile(FFileName);
  388. finally
  389. slLines.Free;
  390. end;
  391. end;
  392. constructor TIniFile.Create(AStream: TStream);
  393. var
  394. slLines: TStringList;
  395. begin
  396. inherited Create('');
  397. FStream := AStream;
  398. slLines := TStringList.Create;
  399. try
  400. // read the ini file values
  401. slLines.LoadFromStream(FStream);
  402. FillSectionList(slLines);
  403. finally
  404. slLines.Free;
  405. end;
  406. end;
  407. procedure TIniFile.FillSectionList(AStrings: TStrings);
  408. var
  409. i,j: integer;
  410. sLine, sIdent, sValue: string;
  411. oSection: TIniFileSection;
  412. procedure RemoveBackslashes;
  413. var
  414. i: integer;
  415. s: string;
  416. bAppendNextLine, bAppended: boolean;
  417. begin
  418. AStrings.BeginUpdate;
  419. try
  420. i := 0;
  421. bAppendNextLine := False;
  422. while i < AStrings.Count do begin
  423. s := AStrings[i];
  424. bAppended := False;
  425. if bAppendNextLine then begin
  426. // add line to previous line
  427. AStrings[i-1] := AStrings[i-1] + Trim(s);
  428. AStrings.Delete(i);
  429. s := AStrings[i-1];
  430. bAppended := True;
  431. end;
  432. bAppendNextLine := (Copy(s, Length(s), 1) = LF_Escape);
  433. if bAppendNextLine then
  434. // remove backslash
  435. AStrings[i] := Copy(s, 1, Length(s) - 1);
  436. if not bAppended then
  437. Inc(i);
  438. end;
  439. finally
  440. AStrings.EndUpdate;
  441. end;
  442. end;
  443. begin
  444. oSection := nil;
  445. FSectionList.Clear;
  446. if FEscapeLineFeeds then
  447. RemoveBackslashes;
  448. for i := 0 to AStrings.Count-1 do begin
  449. sLine := Trim(AStrings[i]);
  450. if sLine > '' then
  451. begin
  452. if IsComment(sLine) and (oSection = nil) then begin
  453. // comment at the beginning of the ini file
  454. oSection := TIniFileSection.Create(sLine);
  455. FSectionList.Add(oSection);
  456. continue;
  457. end;
  458. if (Copy(sLine, 1, 1) = Brackets[0]) and (Copy(sLine, length(sLine), 1) = Brackets[1]) then begin
  459. // regular section
  460. oSection := TIniFileSection.Create(Copy(sLine, 2, Length(sLine) - 2));
  461. FSectionList.Add(oSection);
  462. end else if oSection <> nil then begin
  463. if IsComment(sLine) then begin
  464. // comment within a section
  465. sIdent := sLine;
  466. sValue := '';
  467. end else begin
  468. // regular key
  469. j:=Pos(Separator, sLine);
  470. if j=0 then
  471. begin
  472. sIdent:='';
  473. sValue:=sLine
  474. end
  475. else
  476. begin
  477. sIdent:=Trim(Copy(sLine, 1, j - 1));
  478. sValue:=Trim(Copy(sLine, j + 1, Length(sLine) - j));
  479. end;
  480. end;
  481. oSection.KeyList.Add(TIniFileKey.Create(sIdent, sValue));
  482. end;
  483. end;
  484. end;
  485. end;
  486. function TIniFile.ReadString(const Section, Ident, Default: string): string;
  487. var
  488. oSection: TIniFileSection;
  489. oKey: TIniFileKey;
  490. begin
  491. Result := Default;
  492. oSection := FSectionList.SectionByName(Section);
  493. if oSection <> nil then begin
  494. oKey := oSection.KeyList.KeyByName(Ident);
  495. if oKey <> nil then
  496. Result := oKey.Value;
  497. end;
  498. end;
  499. procedure TIniFile.WriteString(const Section, Ident, Value: String);
  500. var
  501. oSection: TIniFileSection;
  502. oKey: TIniFileKey;
  503. begin
  504. if (Section > '') and (Ident > '') then begin
  505. // update or add key
  506. oSection := FSectionList.SectionByName(Section);
  507. if (Value > '') then begin
  508. if oSection = nil then begin
  509. oSection := TIniFileSection.Create(Section);
  510. FSectionList.Add(oSection);
  511. end;
  512. with oSection.KeyList do begin
  513. oKey := KeyByName(Ident);
  514. if oKey <> nil then
  515. oKey.Value := Value
  516. else
  517. oSection.KeyList.Add(TIniFileKey.Create(Ident, Value));
  518. end;
  519. end else if oSection <> nil then begin
  520. // remove key
  521. oKey := oSection.KeyList.KeyByName(Ident);
  522. if oKey <> nil then begin
  523. oSection.KeyList.Remove(oKey);
  524. end;
  525. end;
  526. UpdateFile;
  527. end;
  528. end;
  529. procedure TIniFile.ReadSection(const Section: string; Strings: TStrings);
  530. var
  531. oSection: TIniFileSection;
  532. i: integer;
  533. begin
  534. Strings.BeginUpdate;
  535. try
  536. Strings.Clear;
  537. oSection := FSectionList.SectionByName(Section);
  538. if oSection <> nil then with oSection.KeyList do
  539. for i := 0 to Count-1 do
  540. if not IsComment(Items[i].Ident) then
  541. Strings.Add(Items[i].Ident);
  542. finally
  543. Strings.EndUpdate;
  544. end;
  545. end;
  546. procedure TIniFile.ReadSectionRaw(const Section: string; Strings: TStrings);
  547. var
  548. oSection: TIniFileSection;
  549. i: integer;
  550. begin
  551. Strings.BeginUpdate;
  552. try
  553. Strings.Clear;
  554. oSection := FSectionList.SectionByName(Section);
  555. if oSection <> nil then with oSection.KeyList do
  556. for i := 0 to Count-1 do
  557. if not IsComment(Items[i].Ident) then
  558. begin
  559. if Items[i].Ident<>'' then
  560. Strings.Add(Items[i].Ident + Separator +Items[i].Value)
  561. else
  562. Strings.Add(Items[i].Value);
  563. end;
  564. finally
  565. Strings.EndUpdate;
  566. end;
  567. end;
  568. procedure TIniFile.ReadSections(Strings: TStrings);
  569. var
  570. i: integer;
  571. begin
  572. Strings.BeginUpdate;
  573. try
  574. Strings.Clear;
  575. for i := 0 to FSectionList.Count-1 do
  576. if not IsComment(FSectionList[i].Name) then
  577. Strings.Add(FSectionList[i].Name);
  578. finally
  579. Strings.EndUpdate;
  580. end;
  581. end;
  582. procedure TIniFile.ReadSectionValues(const Section: string; Strings: TStrings);
  583. var
  584. oSection: TIniFileSection;
  585. s: string;
  586. i: integer;
  587. begin
  588. Strings.BeginUpdate;
  589. try
  590. Strings.Clear;
  591. oSection := FSectionList.SectionByName(Section);
  592. if oSection <> nil then with oSection.KeyList do
  593. for i := 0 to Count-1 do begin
  594. s := Items[i].Value;
  595. if s > '' then
  596. Strings.Add(s);
  597. end;
  598. finally
  599. Strings.EndUpdate;
  600. end;
  601. end;
  602. procedure TIniFile.EraseSection(const Section: string);
  603. var
  604. oSection: TIniFileSection;
  605. begin
  606. oSection := FSectionList.SectionByName(Section);
  607. if oSection <> nil then begin
  608. { It is needed so UpdateFile doesn't find a defunct section }
  609. { and cause the program to crash }
  610. FSectionList.Delete(FSectionList.IndexOf(oSection));
  611. oSection.Free;
  612. UpdateFile;
  613. end;
  614. end;
  615. procedure TIniFile.DeleteKey(const Section, Ident: String);
  616. var
  617. oSection: TIniFileSection;
  618. oKey: TIniFileKey;
  619. begin
  620. oSection := FSectionList.SectionByName(Section);
  621. if oSection <> nil then begin
  622. oKey := oSection.KeyList.KeyByName(Ident);
  623. if oKey <> nil then begin
  624. oKey.Free;
  625. UpdateFile;
  626. end;
  627. end;
  628. end;
  629. procedure TIniFile.UpdateFile;
  630. var
  631. slLines: TStringList;
  632. i, j: integer;
  633. begin
  634. slLines := TStringList.Create;
  635. try
  636. for i := 0 to FSectionList.Count-1 do
  637. with FSectionList[i] do begin
  638. if IsComment(Name) then
  639. // comment
  640. slLines.Add(Name)
  641. else
  642. // regular section
  643. slLines.Add(Brackets[0] + Name + Brackets[1]);
  644. for j := 0 to KeyList.Count-1 do
  645. if IsComment(KeyList[j].Ident) then
  646. // comment
  647. slLines.Add(KeyList[j].Ident)
  648. else
  649. // regular key
  650. slLines.Add(KeyList[j].Ident + Separator + KeyList[j].Value);
  651. if (i < FSectionList.Count-1) and not IsComment(Name) then
  652. slLines.Add('');
  653. end;
  654. if FFileName > '' then
  655. slLines.SaveToFile(FFileName)
  656. else if FStream <> nil then
  657. slLines.SaveToStream(FStream);
  658. FillSectionList(slLines);
  659. finally
  660. slLines.Free;
  661. end;
  662. end;
  663. { TMemIniFile }
  664. procedure TMemIniFile.Clear;
  665. begin
  666. FSectionList.Clear;
  667. end;
  668. procedure TMemIniFile.GetStrings(List: TStrings);
  669. var
  670. i, j: integer;
  671. oSection: TIniFileSection;
  672. begin
  673. List.BeginUpdate;
  674. try
  675. for i := 0 to FSectionList.Count-1 do begin
  676. oSection := FSectionList[i];
  677. with oSection do begin
  678. if IsComment(Name) then
  679. List.Add(Name)
  680. else
  681. List.Add(Brackets[0] + Name + Brackets[1]);
  682. for j := 0 to KeyList.Count-1 do begin
  683. if IsComment(KeyList[j].Ident) then
  684. List.Add(KeyList[j].Ident)
  685. else
  686. List.Add(KeyList[j].Ident + Separator + KeyList[j].Value);
  687. end;
  688. end;
  689. if i < FSectionList.Count-1 then
  690. List.Add('');
  691. end;
  692. finally
  693. List.EndUpdate;
  694. end;
  695. end;
  696. procedure TMemIniFile.Rename(const AFileName: string; Reload: Boolean);
  697. var
  698. slLines: TStringList;
  699. begin
  700. FFileName := AFileName;
  701. FStream := nil;
  702. if Reload then begin
  703. slLines := TStringList.Create;
  704. try
  705. slLines.LoadFromFile(FFileName);
  706. FillSectionList(slLines);
  707. finally
  708. slLines.Free;
  709. end;
  710. end;
  711. end;
  712. procedure TMemIniFile.SetStrings(List: TStrings);
  713. begin
  714. FillSectionList(List);
  715. end;
  716. end.
  717. {
  718. $Log$
  719. Revision 1.9 2005-04-05 21:08:08 peter
  720. * fix memory leaks
  721. Revision 1.8 2005/02/14 17:13:15 peter
  722. * truncate log
  723. }