inifiles.pp 21 KB

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