inifiles.pp 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490
  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. { TStringHash }
  50. TStringHash = class
  51. private
  52. FAddReplacesExisting: Boolean;
  53. FHashList : TFPDataHashTable;
  54. public
  55. constructor Create(ACapacity : Cardinal = 256);
  56. destructor Destroy;override;
  57. procedure Add(const Key: string; Value: Integer);
  58. procedure Clear;
  59. function Modify(const Key: string; Value: Integer): Boolean;
  60. procedure Remove(const Key: string);
  61. function ValueOf(const Key: string): Integer;
  62. Property AddReplacesExisting : Boolean Read FAddReplacesExisting Write FAddReplacesExisting;
  63. end;
  64. { THashedStringList }
  65. THashedStringList = class(TStringList)
  66. private
  67. FValueHash: TFPHashList;
  68. FNameHash: TFPHashList;
  69. FValueHashValid: Boolean;
  70. FNameHashValid: Boolean;
  71. procedure UpdateValueHash;
  72. procedure UpdateNameHash;
  73. protected
  74. procedure Changed; override;
  75. public
  76. destructor Destroy; override;
  77. function IndexOf(const S: String): Integer; override;
  78. function IndexOfName(const Name: String): Integer; override;
  79. end;
  80. TIniFileKey = class
  81. Private
  82. FIdent: string;
  83. FValue: string;
  84. public
  85. constructor Create(const AIdent, AValue: string);
  86. property Ident: string read FIdent write FIdent;
  87. property Value: string read FValue write FValue;
  88. end;
  89. TIniFileKeyList = class(TList)
  90. private
  91. function GetItem(Index: integer): TIniFileKey;
  92. function KeyByName(const AName: string; CaseSensitive : Boolean): TIniFileKey;
  93. public
  94. destructor Destroy; override;
  95. procedure Clear; override;
  96. property Items[Index: integer]: TIniFileKey read GetItem; default;
  97. end;
  98. TIniFileSection = class
  99. private
  100. FName: string;
  101. FKeyList: TIniFileKeyList;
  102. public
  103. Function Empty : Boolean;
  104. constructor Create(const AName: string);
  105. destructor Destroy; override;
  106. property Name: string read FName;
  107. property KeyList: TIniFileKeyList read FKeyList;
  108. end;
  109. TIniFileSectionList = class(TList)
  110. private
  111. function GetItem(Index: integer): TIniFileSection;
  112. function SectionByName(const AName: string; CaseSensitive : Boolean): TIniFileSection;
  113. public
  114. destructor Destroy; override;
  115. procedure Clear;override;
  116. property Items[Index: integer]: TIniFileSection read GetItem; default;
  117. end;
  118. TIniFileOption = (ifoStripComments, // Strip comments when reading file
  119. ifoStripInvalid, // Strip invalid lines when reading file.
  120. ifoEscapeLineFeeds, // Escape linefeeds when reading file.
  121. ifoCaseSensitive, // Use Case sensitive section/key names
  122. ifoStripQuotes, // Strip quotes when reading string values.
  123. ifoFormatSettingsActive, // Use format settings when writing date/float etc.
  124. ifoWriteStringBoolean // Write booleans as string
  125. );
  126. TIniFileOptions = Set of TIniFileOption;
  127. TSectionValuesOption = (svoIncludeComments,svoIncludeInvalid, svoIncludeQuotes);
  128. TSectionValuesOptions = set of TSectionValuesOption;
  129. { TCustomIniFile }
  130. TCustomIniFile = class
  131. Private
  132. FBoolFalseStrings: TStringArray;
  133. FBoolTrueStrings: TStringArray;
  134. FEncoding: TEncoding;
  135. FFileName: string;
  136. FOptions: TIniFileOptions;
  137. FOwnsEncoding: Boolean;
  138. FSectionList: TIniFileSectionList;
  139. function GetOption(AIndex: TIniFileOption): Boolean;
  140. procedure SetOption(AIndex: TIniFileOption; AValue: Boolean);
  141. procedure SetOptions(AValue: TIniFileOptions);
  142. protected
  143. procedure SetEncoding(const aEncoding: TEncoding); virtual;
  144. public
  145. FormatSettings: TFormatSettings;
  146. constructor Create(const AFileName: string; ADefaultEncoding: TEncoding; AOptions : TIniFileOptions = []);
  147. constructor Create(const AFileName: string; ADefaultEncoding: TEncoding; AOwnsEncoding: Boolean; AOptions : TIniFileOptions = []);
  148. constructor Create(const AFileName: string; AOptions : TIniFileOptions = []); virtual;
  149. constructor Create(const AFileName: string; AEscapeLineFeeds : Boolean); virtual;
  150. destructor Destroy; override;
  151. Procedure SetBoolStringValues(ABoolValue : Boolean; Values : Array of string);
  152. function SectionExists(const Section: string): Boolean; virtual;
  153. function ReadString(const Section, Ident, Default: string): string; virtual; abstract;
  154. procedure WriteString(const Section, Ident, Value: String); virtual; abstract;
  155. function ReadInteger(const Section, Ident: string; Default: Longint): Longint; virtual;
  156. procedure WriteInteger(const Section, Ident: string; Value: Longint); virtual;
  157. function ReadInt64(const Section, Ident: string; Default: Int64): Int64; virtual;
  158. procedure WriteInt64(const Section, Ident: string; Value: Int64); virtual;
  159. function ReadBool(const Section, Ident: string; Default: Boolean): Boolean; virtual;
  160. procedure WriteBool(const Section, Ident: string; Value: Boolean); virtual;
  161. function ReadDate(const Section, Ident: string; Default: TDateTime): TDateTime; virtual;
  162. function ReadDateTime(const Section, Ident: string; Default: TDateTime): TDateTime; virtual;
  163. function ReadFloat(const Section, Ident: string; Default: Double): Double; virtual;
  164. function ReadTime(const Section, Ident: string; Default: TDateTime): TDateTime; virtual;
  165. function ReadBinaryStream(const Section, Name: string; Value: TStream): Integer; virtual;
  166. procedure WriteDate(const Section, Ident: string; Value: TDateTime); virtual;
  167. procedure WriteDateTime(const Section, Ident: string; Value: TDateTime); virtual;
  168. procedure WriteFloat(const Section, Ident: string; Value: Double); virtual;
  169. procedure WriteTime(const Section, Ident: string; Value: TDateTime); virtual;
  170. procedure WriteBinaryStream(const Section, Name: string; Value: TStream); virtual;
  171. procedure ReadSection(const Section: string; Strings: TStrings); virtual; abstract;
  172. procedure ReadSections(Strings: TStrings); virtual; abstract;
  173. procedure ReadSectionValues(const Section: string; Strings: TStrings; Options : TSectionValuesOptions); virtual; overload;
  174. procedure ReadSectionValues(const Section: string; Strings: TStrings); virtual;overload;
  175. procedure EraseSection(const Section: string); virtual; abstract;
  176. procedure DeleteKey(const Section, Ident: String); virtual; abstract;
  177. procedure UpdateFile; virtual; abstract;
  178. function ValueExists(const Section, Ident: string): Boolean; virtual;
  179. property Encoding: TEncoding read FEncoding write SetEncoding;
  180. property FileName: string read FFileName;
  181. Property Options : TIniFileOptions Read FOptions Write SetOptions;
  182. property EscapeLineFeeds: boolean index ifoEscapeLineFeeds Read GetOption ;deprecated 'Use options instead';
  183. Property CaseSensitive : Boolean index ifoCaseSensitive Read GetOption Write SetOption; deprecated 'Use options instead';
  184. Property StripQuotes : Boolean index ifoStripQuotes Read GetOption Write SetOption; deprecated 'Use options instead';
  185. Property FormatSettingsActive : Boolean index ifoFormatSettingsActive Read GetOption Write SetOption;deprecated 'Use options instead';
  186. Property BoolTrueStrings : TStringArray Read FBoolTrueStrings Write FBoolTrueStrings;
  187. Property BoolFalseStrings : TStringArray Read FBoolFalseStrings Write FBoolFalseStrings;
  188. Property OwnsEncoding: Boolean Read FOwnsEncoding;
  189. end;
  190. { TIniFile }
  191. TIniFile = class(TCustomIniFile)
  192. Private
  193. FStream: TStream;
  194. FCacheUpdates: Boolean;
  195. FDirty : Boolean;
  196. FWriteBOM: Boolean;
  197. procedure FillSectionList(AStrings: TStrings);
  198. Procedure DeleteSection(ASection : TIniFileSection);
  199. Procedure MaybeDeleteSection(ASection : TIniFileSection);
  200. procedure SetCacheUpdates(const AValue: Boolean);
  201. procedure SetWriteBOM(const aWriteBOM: Boolean);
  202. protected
  203. procedure ReadIniValues;
  204. procedure MaybeUpdateFile;
  205. property Dirty : Boolean Read FDirty;
  206. procedure SetEncoding(const aEncoding: TEncoding); override;
  207. public
  208. constructor Create(const AFileName: string; AOptions : TIniFileoptions = []); overload; override;
  209. constructor Create(AStream: TStream; AOptions : TIniFileoptions = []); overload;
  210. constructor Create(AStream: TStream; AEscapeLineFeeds : Boolean); overload; deprecated 'Use Options argument instead';
  211. constructor Create(AStream: TStream; ADefaultEncoding: TEncoding; AOptions : TIniFileOptions = []);
  212. constructor Create(AStream: TStream; ADefaultEncoding: TEncoding; AOwnsEncoding: Boolean; AOptions : TIniFileOptions = []);
  213. destructor Destroy; override;
  214. function ReadString(const Section, Ident, Default: string): string; override;
  215. procedure WriteString(const Section, Ident, Value: String); override;
  216. procedure ReadSection(const Section: string; Strings: TStrings); override;
  217. procedure ReadSectionRaw(const Section: string; Strings: TStrings);
  218. procedure ReadSections(Strings: TStrings); override;
  219. procedure ReadSectionValues(const Section: string; Strings: TStrings; AOptions : TSectionValuesOptions = [svoIncludeInvalid]); overload; override;
  220. procedure EraseSection(const Section: string); override;
  221. procedure DeleteKey(const Section, Ident: String); override;
  222. procedure UpdateFile; override;
  223. property Stream: TStream read FStream;
  224. property CacheUpdates : Boolean read FCacheUpdates write SetCacheUpdates;
  225. property WriteBOM: Boolean Read FWriteBOM Write SetWriteBOM;
  226. end;
  227. { TMemIniFile }
  228. TMemIniFile = class(TIniFile)
  229. public
  230. constructor Create(const AFileName: string; AOptions : TIniFileoptions = []); overload; override;
  231. constructor Create(const AFileName: string; AEscapeLineFeeds : Boolean); overload; override;
  232. procedure Clear;
  233. procedure GetStrings(List: TStrings);
  234. procedure Rename(const AFileName: string; Reload: Boolean);
  235. procedure SetStrings(List: TStrings);
  236. end;
  237. implementation
  238. Resourcestring
  239. SErrCouldNotCreatePath = 'Could not create directory "%s"';
  240. const
  241. Brackets : array[0..1] of Char = ('[', ']');
  242. Separator : Char = '=';
  243. Comment : Char = ';';
  244. LF_Escape : Char = '\';
  245. function CharToBool(AChar: char): boolean;
  246. begin
  247. Result := (Achar = '1');
  248. end;
  249. function BoolToChar(ABool: boolean): char;
  250. begin
  251. if ABool then
  252. Result := '1'
  253. else
  254. Result := '0';
  255. end;
  256. function IsComment(const AString: string): boolean;
  257. begin
  258. Result:=(Length(aString)>0) and (Copy(AString, 1, 1) = Comment);
  259. end;
  260. { TStringHash }
  261. constructor TStringHash.Create(ACapacity : Cardinal = 256);
  262. begin
  263. FHashList := TFPDataHashTable.Create;
  264. end;
  265. destructor TStringHash.Destroy;
  266. begin
  267. FreeAndNil(FHashList);
  268. inherited;
  269. end;
  270. procedure TStringHash.Add(const Key: string; Value: Integer);
  271. begin
  272. if Not (AddReplacesExisting and Modify(Key,Value)) then
  273. FHashList.Add(Key, Pointer(Value));
  274. end;
  275. procedure TStringHash.Clear;
  276. begin
  277. FHashList.Clear;
  278. end;
  279. function TStringHash.Modify(const Key: string; Value: Integer): Boolean;
  280. Var
  281. AIndex : Integer;
  282. Node : THTDataNode;
  283. begin
  284. Node := THTDataNode(FHashList.Find(Key));
  285. Result:=Assigned(Node);
  286. if Result Then
  287. Node.Data:=Pointer(Value);
  288. end;
  289. procedure TStringHash.Remove(const Key: string);
  290. begin
  291. FHashList.Delete(Key);
  292. end;
  293. function TStringHash.ValueOf(const Key: string): Integer;
  294. Var
  295. N : THTDataNode;
  296. begin
  297. N:=THTDataNode(FHashList.Find(Key));
  298. If Assigned(N) then
  299. Result:=PTrInt(N.Data)
  300. else
  301. Result:=-1;
  302. end;
  303. { THashedStringList }
  304. destructor THashedStringList.Destroy;
  305. begin
  306. FreeAndNil(FValueHash);
  307. FreeAndNil(FNameHash);
  308. inherited Destroy;
  309. end;
  310. function THashedStringList.IndexOf(const S: String): Integer;
  311. var
  312. I: Integer;
  313. begin
  314. if not FValueHashValid then
  315. UpdateValueHash;
  316. if CaseSensitive then
  317. I := FValueHash.FindIndexOf(S)
  318. else
  319. I := FValueHash.FindIndexOf(AnsiUpperCase(S));
  320. if I >= 0 then
  321. Result := Integer(FValueHash[I])-1
  322. else
  323. Result := -1;
  324. end;
  325. function THashedStringList.IndexOfName(const Name: String): Integer;
  326. var
  327. I: Integer;
  328. begin
  329. if not FNameHashValid then
  330. UpdateNameHash;
  331. if CaseSensitive then
  332. I := FNameHash.FindIndexOf(Name)
  333. else
  334. I := FNameHash.FindIndexOf(AnsiUpperCase(Name));
  335. if I >= 0 then
  336. Result := Integer(FNameHash[I])-1
  337. else
  338. Result := -1;
  339. end;
  340. procedure THashedStringList.Changed;
  341. begin
  342. FValueHashValid := False;
  343. FNameHashValid := False;
  344. inherited Changed;
  345. end;
  346. procedure THashedStringList.UpdateValueHash;
  347. var
  348. I: Integer;
  349. begin
  350. if not Assigned(FValueHash) then
  351. FValueHash := TFPHashList.Create
  352. else
  353. FValueHash.Clear;
  354. for I := 0 to Count - 1 do
  355. if CaseSensitive then
  356. FValueHash.Add(Strings[I], Pointer(I+1))
  357. else
  358. FValueHash.Add(AnsiUpperCase(Strings[I]), Pointer(I+1));
  359. FValueHashValid := True;
  360. end;
  361. procedure THashedStringList.UpdateNameHash;
  362. var
  363. I: Integer;
  364. begin
  365. if not Assigned(FNameHash) then
  366. FNameHash := TFPHashList.Create
  367. else
  368. FNameHash.Clear;
  369. for I := 0 to Count - 1 do
  370. if CaseSensitive then
  371. FNameHash.Add(Names[I], Pointer(I+1))
  372. else
  373. FNameHash.Add(AnsiUpperCase(Names[I]), Pointer(I+1));
  374. FNameHashValid := True;
  375. end;
  376. { TIniFileKey }
  377. constructor TIniFileKey.Create(const AIdent, AValue: string);
  378. begin
  379. FIdent := AIdent;
  380. FValue := AValue;
  381. end;
  382. { TIniFileKeyList }
  383. function TIniFileKeyList.GetItem(Index: integer): TIniFileKey;
  384. begin
  385. Result := nil;
  386. if (Index >= 0) and (Index < Count) then
  387. Result := TIniFileKey(inherited Items[Index]);
  388. end;
  389. function TIniFileKeyList.KeyByName(const AName: string; CaseSensitive : Boolean): TIniFileKey;
  390. var
  391. i: integer;
  392. begin
  393. Result := nil;
  394. if (AName > '') and not IsComment(AName) then
  395. If CaseSensitive then
  396. begin
  397. for i := 0 to Count-1 do
  398. if Items[i].Ident=AName then
  399. begin
  400. Result := Items[i];
  401. Break;
  402. end;
  403. end
  404. else
  405. for i := 0 to Count-1 do
  406. if CompareText(Items[i].Ident, AName) = 0 then begin
  407. Result := Items[i];
  408. Break;
  409. end;
  410. end;
  411. destructor TIniFileKeyList.Destroy;
  412. begin
  413. Clear;
  414. inherited Destroy;
  415. end;
  416. procedure TIniFileKeyList.Clear;
  417. var
  418. i: integer;
  419. begin
  420. for i := Count-1 downto 0 do
  421. Items[i].Free;
  422. inherited Clear;
  423. end;
  424. Function TIniFileSection.Empty : Boolean;
  425. Var
  426. I : Integer;
  427. begin
  428. Result:=True;
  429. I:=0;
  430. While Result and (I<KeyList.Count) do
  431. begin
  432. result:=IsComment(KeyList[i].Ident);
  433. Inc(i);
  434. end;
  435. end;
  436. { TIniFileSection }
  437. constructor TIniFileSection.Create(const AName: string);
  438. begin
  439. FName := AName;
  440. FKeyList := TIniFileKeyList.Create;
  441. end;
  442. destructor TIniFileSection.Destroy;
  443. begin
  444. FKeyList.Free;
  445. end;
  446. { TIniFileSectionList }
  447. function TIniFileSectionList.GetItem(Index: integer): TIniFileSection;
  448. begin
  449. Result := nil;
  450. if (Index >= 0) and (Index < Count) then
  451. Result := TIniFileSection(inherited Items[Index]);
  452. end;
  453. function TIniFileSectionList.SectionByName(const AName: string; CaseSensitive : Boolean): TIniFileSection;
  454. var
  455. i: integer;
  456. begin
  457. Result := nil;
  458. if (AName > '') and not IsComment(AName) then
  459. If CaseSensitive then
  460. begin
  461. for i:=0 to Count-1 do
  462. if (Items[i].Name=AName) then
  463. begin
  464. Result := Items[i];
  465. Break;
  466. end;
  467. end
  468. else
  469. for i := 0 to Count-1 do
  470. if CompareText(Items[i].Name, AName) = 0 then
  471. begin
  472. Result := Items[i];
  473. Break;
  474. end;
  475. end;
  476. destructor TIniFileSectionList.Destroy;
  477. begin
  478. Clear;
  479. inherited Destroy;
  480. end;
  481. procedure TIniFileSectionList.Clear;
  482. var
  483. i: integer;
  484. begin
  485. for i := Count-1 downto 0 do
  486. Items[i].Free;
  487. inherited Clear;
  488. end;
  489. { TCustomIniFile }
  490. function TCustomIniFile.GetOption(AIndex: TIniFileOption): Boolean;
  491. begin
  492. Result:=AIndex in FOptions;
  493. end;
  494. procedure TCustomIniFile.SetOption(AIndex: TIniFileOption; AValue: Boolean);
  495. begin
  496. if AIndex in [ifoStripComments,ifoStripInvalid] then
  497. Raise Exception.Create('Flags ifoStripComments or ifoStripInvalid must be set/unset in the constructor');
  498. if AValue then
  499. Include(FOptions,AIndex)
  500. else
  501. Exclude(FOptions,AIndex)
  502. end;
  503. procedure TCustomIniFile.SetOptions(AValue: TIniFileOptions);
  504. Const
  505. CreateOnlyOptions = [ifoStripComments,ifoStripInvalid];
  506. begin
  507. if FOptions=AValue then Exit;
  508. if (Foptions*CreateOnlyOptions)<>(AValue*CreateOnlyOptions) then
  509. Raise Exception.Create('Can only change StripComments or StripInvalid in constructor');
  510. FOptions:=AValue;
  511. end;
  512. constructor TCustomIniFile.Create(const AFileName: string; AOptions : TIniFileOptions = []);
  513. begin
  514. FFileName := AFileName;
  515. FSectionList := TIniFileSectionList.Create;
  516. FOptions:=AOptions;
  517. FormatSettings := DefaultFormatSettings;
  518. with FormatSettings do begin
  519. DecimalSeparator := '.';
  520. ThousandSeparator := ',';
  521. ListSeparator := ';';
  522. DateSeparator := '/';
  523. TimeSeparator := ':';
  524. ShortDateFormat := 'yyyy/mm/dd';
  525. ShortTimeFormat := 'hh:nn';
  526. LongTimeFormat := 'hh:nn:ss';
  527. end;
  528. end;
  529. constructor TCustomIniFile.Create(const AFileName: string;
  530. AEscapeLineFeeds: Boolean);
  531. begin
  532. if AEscapeLineFeeds then
  533. Create(AFileName,[ifoEscapeLineFeeds])
  534. else
  535. Create(AFileName,[])
  536. end;
  537. constructor TCustomIniFile.Create(const AFileName: string;
  538. ADefaultEncoding: TEncoding; AOwnsEncoding: Boolean;
  539. AOptions: TIniFileOptions);
  540. begin
  541. FEncoding := ADefaultEncoding;
  542. if FEncoding <> nil then
  543. FOwnsEncoding := AOwnsEncoding and not TEncoding.IsStandardEncoding(FEncoding);
  544. Create(AFileName, AOptions);
  545. end;
  546. constructor TCustomIniFile.Create(const AFileName: string;
  547. ADefaultEncoding: TEncoding; AOptions: TIniFileOptions);
  548. begin
  549. FEncoding := ADefaultEncoding;
  550. if FEncoding <> nil then
  551. FOwnsEncoding := not TEncoding.IsStandardEncoding(FEncoding);
  552. Create(AFileName, AOptions);
  553. end;
  554. destructor TCustomIniFile.Destroy;
  555. begin
  556. FSectionList.Free;
  557. if FOwnsEncoding then
  558. FEncoding.Free;
  559. inherited Destroy;
  560. end;
  561. procedure TCustomIniFile.SetBoolStringValues(ABoolValue: Boolean;
  562. Values: array of string);
  563. Var
  564. A : TstringArray;
  565. I : Integer;
  566. begin
  567. SetLength(A,Length(Values));
  568. For I:=0 to length(Values)-1 do
  569. A[i]:=Values[i];
  570. If AboolValue then
  571. FBoolTrueStrings:=A
  572. else
  573. FBoolFalseStrings:=A;
  574. end;
  575. procedure TCustomIniFile.SetEncoding(const aEncoding: TEncoding);
  576. begin
  577. if FEncoding = aEncoding then Exit;
  578. if FOwnsEncoding then
  579. FEncoding.Free;
  580. FEncoding := aEncoding;
  581. FOwnsEncoding := Assigned(FEncoding) and not TEncoding.IsStandardEncoding(FEncoding);
  582. end;
  583. function TCustomIniFile.SectionExists(const Section: string): Boolean;
  584. Var
  585. S : TIniFileSection;
  586. begin
  587. S:=FSectionList.SectionByName(Section,CaseSensitive);
  588. Result:=Assigned(S) and Not S.Empty;
  589. end;
  590. function TCustomIniFile.ReadInteger(const Section, Ident: string; Default: Longint): Longint;
  591. begin
  592. // StrToInfDef() supports hex numbers prefixed with '0x' via val()
  593. Result := StrToIntDef(ReadString(Section, Ident, ''), Default);
  594. end;
  595. procedure TCustomIniFile.WriteInteger(const Section, Ident: string; Value: Longint);
  596. begin
  597. WriteString(Section, Ident, IntToStr(Value));
  598. end;
  599. function TCustomIniFile.ReadInt64(const Section, Ident: string; Default: Int64
  600. ): Int64;
  601. begin
  602. Result := StrToInt64Def(ReadString(Section, Ident, ''), Default);
  603. end;
  604. procedure TCustomIniFile.WriteInt64(const Section, Ident: string; Value: Int64);
  605. begin
  606. WriteString(Section, Ident, IntToStr(Value));
  607. end;
  608. function IndexOfString(A : TStringArray; S : String) : integer;
  609. begin
  610. Result:=Length(A)-1;
  611. While (Result>=0) and (CompareText(A[Result],S)<>0) do
  612. Dec(Result);
  613. end;
  614. function TCustomIniFile.ReadBool(const Section, Ident: string; Default: Boolean): Boolean;
  615. var
  616. s: string;
  617. begin
  618. Result := Default;
  619. s:=ReadString(Section, Ident, '');
  620. if s > '' then
  621. if (Length(FBoolTrueStrings)>0) or (Length(FBoolFalseStrings)>0) then
  622. begin
  623. if IndexOfString(FBoolTrueStrings,S)>=0 then
  624. Result:=True
  625. else if IndexOfString(FBoolFalseStrings,S)>=0 then
  626. Result:=False
  627. end
  628. else
  629. Result := CharToBool(s[1]);
  630. end;
  631. procedure TCustomIniFile.WriteBool(const Section, Ident: string; Value: Boolean);
  632. Var
  633. S : String;
  634. begin
  635. if (ifoWriteStringBoolean in options) then
  636. begin
  637. if Value then
  638. begin
  639. if Length(BoolTrueStrings)>0 then
  640. S:=BoolTrueStrings[0]
  641. else
  642. S:='true';
  643. end
  644. else
  645. begin
  646. if Length(BoolFalseStrings)>0 then
  647. S:=BoolFalseStrings[0]
  648. else
  649. S:='false';
  650. end;
  651. end
  652. else
  653. S:=BoolToChar(Value);
  654. WriteString(Section, Ident, S);
  655. end;
  656. function TCustomIniFile.ReadDate(const Section, Ident: string; Default: TDateTime): TDateTime;
  657. begin
  658. if FormatSettingsActive then begin
  659. if not TryStrToDate(ReadString(Section, Ident, ''), Result, FormatSettings) then
  660. Result := Default;
  661. end else
  662. Result := StrToDateDef(ReadString(Section, Ident, ''),Default);
  663. end;
  664. function TCustomIniFile.ReadDateTime(const Section, Ident: string; Default: TDateTime): TDateTime;
  665. begin
  666. if FormatSettingsActive then begin
  667. if not TryStrToDateTime(ReadString(Section, Ident, ''), Result, FormatSettings) then
  668. Result := Default;
  669. end else
  670. Result := StrToDateTimeDef(ReadString(Section, Ident, ''),Default);
  671. end;
  672. function TCustomIniFile.ReadFloat(const Section, Ident: string; Default: Double): Double;
  673. begin
  674. if FormatSettingsActive then
  675. Result:=StrToFloatDef(ReadString(Section, Ident, ''),Default, FormatSettings)
  676. else
  677. Result:=StrToFloatDef(ReadString(Section, Ident, ''),Default);
  678. end;
  679. function TCustomIniFile.ReadTime(const Section, Ident: string; Default: TDateTime): TDateTime;
  680. begin
  681. if FormatSettingsActive then
  682. Result := StrToTimeDef(ReadString(Section, Ident, ''),Default, FormatSettings.TimeSeparator)
  683. else
  684. Result := StrToTimeDef(ReadString(Section, Ident, ''),Default);
  685. end;
  686. procedure TCustomIniFile.WriteDate(const Section, Ident: string; Value: TDateTime);
  687. begin
  688. if FormatSettingsActive then
  689. WriteString(Section, Ident, DateToStr(Value, FormatSettings))
  690. else
  691. WriteString(Section, Ident, DateToStr(Value));
  692. end;
  693. procedure TCustomIniFile.WriteDateTime(const Section, Ident: string; Value: TDateTime);
  694. begin
  695. if FormatSettingsActive then
  696. WriteString(Section, Ident, DateTimeToStr(Value, FormatSettings))
  697. else
  698. WriteString(Section, Ident, DateTimeToStr(Value));
  699. end;
  700. procedure TCustomIniFile.WriteFloat(const Section, Ident: string; Value: Double);
  701. begin
  702. if FormatSettingsActive then
  703. WriteString(Section, Ident, FloatToStr(Value, FormatSettings))
  704. else
  705. WriteString(Section, Ident, FloatToStr(Value));
  706. end;
  707. procedure TCustomIniFile.WriteTime(const Section, Ident: string; Value: TDateTime);
  708. begin
  709. if FormatSettingsActive then
  710. WriteString(Section, Ident, TimeToStr(Value, FormatSettings))
  711. else
  712. WriteString(Section, Ident, TimeToStr(Value));
  713. end;
  714. function TCustomIniFile.ValueExists(const Section, Ident: string): Boolean;
  715. var
  716. oSection: TIniFileSection;
  717. begin
  718. Result := False;
  719. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  720. if oSection <> nil then
  721. Result := (oSection.KeyList.KeyByName(Ident,CaseSensitive) <> nil);
  722. end;
  723. function TCustomIniFile.ReadBinaryStream(const Section, Name: string; Value: TStream): Integer;
  724. Var
  725. M : TMemoryStream;
  726. S : String;
  727. PB,PR : PByte;
  728. PC : PChar;
  729. H : String[3];
  730. i,l2,code : Integer;
  731. begin
  732. S:=ReadString(Section,Name,'');
  733. Setlength(H,3);
  734. H[1]:='$';
  735. Result:=Length(S) div 2;
  736. If Result>0 then
  737. begin
  738. GetMem(PR,Result);
  739. Try
  740. PC:=PChar(S);
  741. PB:=PR;
  742. For I:=1 to Result do
  743. begin
  744. H[2]:=PC[0];
  745. H[3]:=PC[1];
  746. Val(H,PB^,code);
  747. Inc(PC,2);
  748. Inc(PB);
  749. end;
  750. Value.WriteBuffer(PR^,Result);
  751. finally
  752. FreeMem(PR);
  753. end;
  754. end;
  755. end;
  756. procedure TCustomIniFile.WriteBinaryStream(const Section, Name: string;
  757. Value: TStream);
  758. Var
  759. M : TMemoryStream;
  760. S : String;
  761. PB : PByte;
  762. PC : PChar;
  763. H : String[2];
  764. i : Integer;
  765. begin
  766. M:=TMemoryStream.Create;
  767. Try
  768. M.CopyFrom(Value,0);
  769. SetLength(S,M.Size*2);
  770. If (length(S)>0) then
  771. begin
  772. PB:=M.Memory;
  773. PC:=PChar(S);
  774. For I:=1 to Length(S) div 2 do
  775. begin
  776. H:=HexStr(PB^,2);
  777. PC[0]:=H[1];
  778. PC[1]:=H[2];
  779. Inc(PC,2);
  780. Inc(PB);
  781. end;
  782. end;
  783. WriteString(Section,Name,S);
  784. Finally
  785. M.Free;
  786. end;
  787. end;
  788. procedure TCustomIniFile.ReadSectionValues(const Section: string; Strings: TStrings; Options: TSectionValuesOptions);
  789. type
  790. TOldSectionValues = Procedure (const Section: string; Strings: TStrings) of object;
  791. var
  792. CurrSV,
  793. TCustomSV: TOldSectionValues;
  794. CurrClass : TClass;
  795. begin
  796. if (Options<>[]) then
  797. Raise Exception.Create('Options not supported, options must be empty');
  798. // Redirect calls to old implementation, if it is overridden.
  799. CurrSV:=nil;
  800. CurrClass:=Classtype;
  801. while (CurrClass<>nil) and (CurrClass<>TCustomIniFile) do
  802. CurrClass:=CurrClass.Classparent;
  803. if CurrClass<>nil then
  804. begin
  805. CurrSV:[email protected];
  806. TCustomSV:=@TCustomIniFile(@CurrClass).ReadSectionValues;
  807. if TMethod(TCustomSV).Code=TMethod(CurrSV).Code then
  808. CurrSV:=nil;
  809. end;
  810. if Assigned(CurrSV) then
  811. ReadSectionValues(Section,Strings)
  812. else
  813. Raise Exception.Create('ReadSectionValues not overridden');
  814. end;
  815. procedure TCustomIniFile.ReadSectionValues(const Section: string;
  816. Strings: TStrings);
  817. begin
  818. ReadSectionValues(Section,Strings,[svoIncludeInvalid]);
  819. end;
  820. { TIniFile }
  821. constructor TIniFile.Create(const AFileName: string; AOptions: TIniFileoptions);
  822. begin
  823. If Not (self is TMemIniFile) then
  824. Include(AOptions,ifoStripQuotes);
  825. inherited Create(AFileName,AOptions);
  826. FStream := nil;
  827. ReadIniValues;
  828. end;
  829. constructor TIniFile.Create(AStream: TStream; AEscapeLineFeeds : Boolean);
  830. begin
  831. if AEscapeLineFeeds then
  832. Create(AStream,[ifoEscapeLineFeeds])
  833. else
  834. Create(AStream,[]);
  835. end;
  836. constructor TIniFile.Create(AStream: TStream; ADefaultEncoding: TEncoding;
  837. AOwnsEncoding: Boolean; AOptions: TIniFileOptions);
  838. begin
  839. FEncoding := ADefaultEncoding;
  840. if FEncoding <> nil then
  841. FOwnsEncoding := AOwnsEncoding and not TEncoding.IsStandardEncoding(FEncoding);
  842. Create(AStream, AOptions);
  843. end;
  844. constructor TIniFile.Create(AStream: TStream; ADefaultEncoding: TEncoding;
  845. AOptions: TIniFileOptions);
  846. begin
  847. FEncoding := ADefaultEncoding;
  848. if FEncoding <> nil then
  849. FOwnsEncoding := not TEncoding.IsStandardEncoding(FEncoding);
  850. Create(AStream, AOptions);
  851. end;
  852. constructor TIniFile.Create(AStream: TStream; AOptions: TIniFileoptions);
  853. var
  854. slLines: TStringList;
  855. begin
  856. inherited Create('',AOptions);
  857. FStream := AStream;
  858. slLines := TStringList.Create;
  859. try
  860. // read the ini file values
  861. slLines.LoadFromStream(FStream, FEncoding);
  862. FillSectionList(slLines);
  863. FWriteBOM := Assigned(FEncoding) and ((FEncoding.CodePage=CP_UTF16) or (FEncoding.CodePage=CP_UTF16BE)); // write BOM for UTF16 by default
  864. finally
  865. slLines.Free;
  866. end;
  867. end;
  868. destructor TIniFile.Destroy;
  869. begin
  870. If FDirty and FCacheUpdates then
  871. try
  872. UpdateFile;
  873. except
  874. // Eat exception. Compatible to D7 behaviour, see comments to bug 19046
  875. end;
  876. inherited destroy;
  877. end;
  878. procedure TIniFile.FillSectionList(AStrings: TStrings);
  879. var
  880. i,j,sLen: integer;
  881. sLine, sIdent, sValue: string;
  882. oSection: TIniFileSection;
  883. procedure RemoveBackslashes;
  884. var
  885. i,l: integer;
  886. s: string;
  887. bAppendNextLine, bAppended: boolean;
  888. begin
  889. AStrings.BeginUpdate;
  890. try
  891. For I:=AStrings.Count-2 downto 0 do
  892. begin
  893. S:=AStrings[i];
  894. L:=Length(S);
  895. If (I<AStrings.Count-1) and (L>0) and (S[L]=LF_Escape) then
  896. begin
  897. S:=Copy(S,1,L-1)+AStrings[I+1];
  898. AStrings.Delete(I+1);
  899. AStrings[i]:=S;
  900. end;
  901. end;
  902. finally
  903. AStrings.EndUpdate;
  904. end;
  905. end;
  906. Var
  907. addKey : Boolean;
  908. begin
  909. oSection := nil;
  910. FSectionList.Clear;
  911. if EscapeLineFeeds then
  912. RemoveBackslashes;
  913. for i := 0 to AStrings.Count-1 do begin
  914. sLine := Trim(AStrings[i]);
  915. sLen:=Length(sLine);
  916. if (sLen>0) then
  917. begin
  918. if IsComment(sLine) and (oSection = nil) then
  919. begin
  920. // comment at the beginning of the ini file
  921. if Not (ifoStripComments in Options) then
  922. begin
  923. oSection := TIniFileSection.Create(sLine);
  924. FSectionList.Add(oSection);
  925. end;
  926. continue;
  927. end;
  928. if (sLine[1]=Brackets[0]) and (sLine[sLen]= Brackets[1]) then
  929. begin
  930. // regular section
  931. oSection := TIniFileSection.Create(Copy(sLine, 2, sLen - 2));
  932. FSectionList.Add(oSection);
  933. end
  934. else if oSection <> nil then
  935. begin
  936. if IsComment(sLine) then
  937. begin
  938. AddKey:=Not (ifoStripComments in Options);
  939. // comment within a section
  940. sIdent := sLine;
  941. sValue := '';
  942. end
  943. else
  944. begin
  945. // regular key
  946. j:=Pos(Separator, sLine);
  947. if j=0 then
  948. begin
  949. AddKey:=Not (ifoStripInvalid in Options);
  950. sIdent:='';
  951. sValue:=sLine
  952. end
  953. else
  954. begin
  955. AddKey:=True;
  956. sIdent:=Trim(Copy(sLine, 1, j - 1));
  957. sValue:=Trim(Copy(sLine, j + 1, sLen - j));
  958. end;
  959. end;
  960. if AddKey then
  961. oSection.KeyList.Add(TIniFileKey.Create(sIdent, sValue));
  962. end;
  963. end;
  964. end;
  965. end;
  966. function TIniFile.ReadString(const Section, Ident, Default: string): string;
  967. var
  968. oSection: TIniFileSection;
  969. oKey: TIniFileKey;
  970. J: integer;
  971. begin
  972. Result := Default;
  973. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  974. if oSection <> nil then begin
  975. oKey := oSection.KeyList.KeyByName(Ident,CaseSensitive);
  976. if oKey <> nil then
  977. If StripQuotes then
  978. begin
  979. J:=Length(oKey.Value);
  980. // Joost, 2-jan-2007: The check (J>1) is there for the case that
  981. // the value consist of a single double-quote character. (see
  982. // mantis bug 6555)
  983. If (J>1) and ((oKey.Value[1] in ['"','''']) and (oKey.Value[J]=oKey.Value[1])) then
  984. Result:=Copy(oKey.Value,2,J-2)
  985. else
  986. Result:=oKey.Value;
  987. end
  988. else Result:=oKey.Value;
  989. end;
  990. end;
  991. procedure TIniFile.SetCacheUpdates(const AValue: Boolean);
  992. begin
  993. if FCacheUpdates and not AValue and FDirty then
  994. UpdateFile;
  995. FCacheUpdates := AValue;
  996. end;
  997. procedure TIniFile.SetEncoding(const aEncoding: TEncoding);
  998. begin
  999. if FEncoding = aEncoding then Exit;
  1000. inherited SetEncoding(aEncoding);
  1001. if Assigned(FEncoding) and ((FEncoding.CodePage=CP_UTF16) or (FEncoding.CodePage=CP_UTF16BE)) then
  1002. FWriteBOM := True;
  1003. MaybeUpdateFile;
  1004. end;
  1005. procedure TIniFile.SetWriteBOM(const aWriteBOM: Boolean);
  1006. begin
  1007. if FWriteBOM = aWriteBOM then Exit;
  1008. FWriteBOM := aWriteBOM;
  1009. MaybeUpdateFile;
  1010. end;
  1011. procedure TIniFile.WriteString(const Section, Ident, Value: String);
  1012. var
  1013. oSection: TIniFileSection;
  1014. oKey: TIniFileKey;
  1015. begin
  1016. if (Section > '') and (Ident > '') then
  1017. begin
  1018. // update or add key
  1019. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  1020. if (oSection = nil) then
  1021. begin
  1022. oSection := TIniFileSection.Create(Section);
  1023. FSectionList.Add(oSection);
  1024. end;
  1025. with oSection.KeyList do
  1026. begin
  1027. oKey := KeyByName(Ident,CaseSensitive);
  1028. if oKey <> nil then
  1029. oKey.Value := Value
  1030. else
  1031. oSection.KeyList.Add(TIniFileKey.Create(Ident, Value));
  1032. end;
  1033. end;
  1034. MaybeUpdateFile;
  1035. end;
  1036. procedure TIniFile.ReadSection(const Section: string; Strings: TStrings);
  1037. var
  1038. oSection: TIniFileSection;
  1039. i: integer;
  1040. begin
  1041. Strings.BeginUpdate;
  1042. try
  1043. Strings.Clear;
  1044. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  1045. if oSection <> nil then with oSection.KeyList do
  1046. for i := 0 to Count-1 do
  1047. if not IsComment(Items[i].Ident) then
  1048. Strings.Add(Items[i].Ident);
  1049. finally
  1050. Strings.EndUpdate;
  1051. end;
  1052. end;
  1053. procedure TIniFile.ReadSectionRaw(const Section: string; Strings: TStrings);
  1054. var
  1055. oSection: TIniFileSection;
  1056. i: integer;
  1057. begin
  1058. Strings.BeginUpdate;
  1059. try
  1060. Strings.Clear;
  1061. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  1062. if oSection <> nil then with oSection.KeyList do
  1063. for i := 0 to Count-1 do
  1064. begin
  1065. if Items[i].Ident<>'' then
  1066. Strings.Add(Items[i].Ident + Separator +Items[i].Value)
  1067. else
  1068. Strings.Add(Items[i].Value);
  1069. end;
  1070. finally
  1071. Strings.EndUpdate;
  1072. end;
  1073. end;
  1074. procedure TIniFile.ReadSections(Strings: TStrings);
  1075. var
  1076. i: integer;
  1077. begin
  1078. Strings.BeginUpdate;
  1079. try
  1080. Strings.Clear;
  1081. for i := 0 to FSectionList.Count-1 do
  1082. if not IsComment(FSectionList[i].Name) then
  1083. Strings.Add(FSectionList[i].Name);
  1084. finally
  1085. Strings.EndUpdate;
  1086. end;
  1087. end;
  1088. procedure TIniFile.ReadSectionValues(const Section: string; Strings: TStrings; AOptions : TSectionValuesOptions = [svoIncludeInvalid]);
  1089. var
  1090. oSection: TIniFileSection;
  1091. s: string;
  1092. i,J: integer;
  1093. KeyIsComment,IncludeComments,IncludeInvalid,DoStripQuotes : boolean;
  1094. K : TIniFileKey;
  1095. begin
  1096. IncludeComments:=(svoIncludeComments in AOptions) Or (ifoStripComments in Options);
  1097. IncludeInvalid:=(svoIncludeInvalid in AOptions) Or (ifoStripInvalid in Options);
  1098. DoStripQuotes:=StripQuotes and Not (svoIncludeQuotes in AOptions);
  1099. Strings.BeginUpdate;
  1100. try
  1101. Strings.Clear;
  1102. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  1103. if oSection = nil then
  1104. Exit;
  1105. for i := 0 to oSection.KeyList.Count-1 do
  1106. begin
  1107. K:=oSection.KeyList.Items[i];
  1108. if IncludeInvalid or (K.Ident<>'') then
  1109. begin
  1110. s := K.Value;
  1111. KeyIsComment:=IsComment(K.Ident);
  1112. if IncludeComments Or Not KeyIsComment then
  1113. begin
  1114. If DoStripQuotes then
  1115. begin
  1116. J:=Length(s);
  1117. // Joost, 2-jan-2007: The check (J>1) is there for the case that
  1118. // the value consist of a single double-quote character. (see
  1119. // mantis bug 6555)
  1120. If (J>1) and ((s[1] in ['"','''']) and (s[J]=s[1])) then
  1121. s:=Copy(s,2,J-2);
  1122. end;
  1123. if KeyIsComment then
  1124. S:=K.Ident
  1125. else if k.ident<>'' then
  1126. s:=K.Ident+Separator+s;
  1127. Strings.Add(s);
  1128. end;
  1129. end;
  1130. end;
  1131. finally
  1132. Strings.EndUpdate;
  1133. end;
  1134. end;
  1135. procedure TIniFile.DeleteSection(ASection : TIniFileSection);
  1136. begin
  1137. FSectionList.Delete(FSectionList.IndexOf(ASection));
  1138. ASection.Free;
  1139. end;
  1140. procedure TIniFile.MaybeDeleteSection(ASection: TIniFileSection);
  1141. begin
  1142. If Asection.Empty then
  1143. DeleteSection(ASection);
  1144. end;
  1145. procedure TIniFile.EraseSection(const Section: string);
  1146. var
  1147. oSection: TIniFileSection;
  1148. begin
  1149. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  1150. if oSection <> nil then begin
  1151. { It is needed so UpdateFile doesn't find a defunct section }
  1152. { and cause the program to crash }
  1153. DeleteSection(OSection);
  1154. MaybeUpdateFile;
  1155. end;
  1156. end;
  1157. procedure TIniFile.DeleteKey(const Section, Ident: String);
  1158. var
  1159. oSection: TIniFileSection;
  1160. oKey: TIniFileKey;
  1161. begin
  1162. oSection := FSectionList.SectionByName(Section,CaseSensitive);
  1163. if oSection <> nil then
  1164. begin
  1165. oKey := oSection.KeyList.KeyByName(Ident,CaseSensitive);
  1166. if oKey <> nil then
  1167. begin
  1168. oSection.KeyList.Delete(oSection.KeyList.IndexOf(oKey));
  1169. oKey.Free;
  1170. MaybeUpdateFile;
  1171. end;
  1172. end;
  1173. end;
  1174. procedure TIniFile.UpdateFile;
  1175. var
  1176. slLines: TStringList;
  1177. i, j: integer;
  1178. D : String;
  1179. begin
  1180. slLines := TStringList.Create;
  1181. try
  1182. for i := 0 to FSectionList.Count-1 do
  1183. with FSectionList[i] do begin
  1184. if IsComment(Name) then
  1185. // comment
  1186. slLines.Add(Name)
  1187. else
  1188. // regular section
  1189. slLines.Add(Brackets[0] + Name + Brackets[1]);
  1190. for j := 0 to KeyList.Count-1 do
  1191. if IsComment(KeyList[j].Ident) then
  1192. // comment
  1193. slLines.Add(KeyList[j].Ident)
  1194. else
  1195. // regular key
  1196. slLines.Add(KeyList[j].Ident + Separator + KeyList[j].Value);
  1197. if (i < FSectionList.Count-1) and not IsComment(Name) then
  1198. slLines.Add('');
  1199. end;
  1200. slLines.WriteBOM := FWriteBOM;
  1201. if FFileName > '' then
  1202. begin
  1203. D:=ExtractFilePath(FFileName);
  1204. If D <> '' Then
  1205. if not ForceDirectories(D) then
  1206. Raise EInoutError.CreateFmt(SErrCouldNotCreatePath,[D]);
  1207. slLines.SaveToFile(FFileName, FEncoding);
  1208. end
  1209. else if FStream <> nil then
  1210. begin
  1211. Fstream.Size:=0;
  1212. slLines.SaveToStream(FStream, FEncoding);
  1213. end;
  1214. FillSectionList(slLines);
  1215. FDirty := false;
  1216. finally
  1217. slLines.Free;
  1218. end;
  1219. end;
  1220. procedure TIniFile.MaybeUpdateFile;
  1221. begin
  1222. If FCacheUpdates then
  1223. FDirty:=True
  1224. else
  1225. UpdateFile;
  1226. end;
  1227. procedure TIniFile.ReadIniValues;
  1228. var
  1229. slLines: TStringList;
  1230. begin
  1231. FSectionList.Clear;
  1232. if FileExists(FFileName) then
  1233. begin
  1234. slLines := TStringList.Create;
  1235. try
  1236. // read the ini file values
  1237. if FEncoding<>nil then
  1238. slLines.DefaultEncoding := FEncoding; // TStrings clones the encoding.
  1239. slLines.Options := slLines.Options + [soPreserveBOM];
  1240. slLines.LoadFromFile(FFileName, nil);
  1241. if (FEncoding=nil) or (FEncoding.CodePage<>slLines.Encoding.CodePage) then
  1242. begin
  1243. if FOwnsEncoding then
  1244. FEncoding.Free;
  1245. if TEncoding.IsStandardEncoding(slLines.Encoding) then
  1246. begin
  1247. FEncoding := slLines.Encoding;
  1248. FOwnsEncoding := False;
  1249. end else
  1250. begin
  1251. FEncoding := slLines.Encoding.Clone;
  1252. FOwnsEncoding := True;
  1253. end;
  1254. end;
  1255. FWriteBOM := slLines.WriteBOM;
  1256. FillSectionList(slLines);
  1257. finally
  1258. slLines.Free;
  1259. end;
  1260. end else
  1261. begin
  1262. FWriteBOM := Assigned(FEncoding) and ((FEncoding.CodePage=CP_UTF16) or (FEncoding.CodePage=CP_UTF16BE)); // write BOM for UTF16 by default
  1263. end;
  1264. end;
  1265. { TMemIniFile }
  1266. constructor TMemIniFile.Create(const AFileName: string; AOptions: TIniFileoptions);
  1267. begin
  1268. inherited;
  1269. FCacheUpdates:=True;
  1270. end;
  1271. constructor TMemIniFile.Create(const AFileName: string; AEscapeLineFeeds : Boolean);
  1272. begin
  1273. Inherited;
  1274. FCacheUpdates:=True;
  1275. end;
  1276. procedure TMemIniFile.Clear;
  1277. begin
  1278. FSectionList.Clear;
  1279. end;
  1280. procedure TMemIniFile.GetStrings(List: TStrings);
  1281. var
  1282. i, j: integer;
  1283. oSection: TIniFileSection;
  1284. begin
  1285. List.BeginUpdate;
  1286. try
  1287. for i := 0 to FSectionList.Count-1 do begin
  1288. oSection := FSectionList[i];
  1289. with oSection do begin
  1290. if IsComment(Name) then
  1291. List.Add(Name)
  1292. else
  1293. List.Add(Brackets[0] + Name + Brackets[1]);
  1294. for j := 0 to KeyList.Count-1 do begin
  1295. if IsComment(KeyList[j].Ident) then
  1296. List.Add(KeyList[j].Ident)
  1297. else
  1298. List.Add(KeyList[j].Ident + Separator + KeyList[j].Value);
  1299. end;
  1300. end;
  1301. if i < FSectionList.Count-1 then
  1302. List.Add('');
  1303. end;
  1304. finally
  1305. List.EndUpdate;
  1306. end;
  1307. end;
  1308. procedure TMemIniFile.Rename(const AFileName: string; Reload: Boolean);
  1309. begin
  1310. FFileName := AFileName;
  1311. FStream := nil;
  1312. if Reload then
  1313. ReadIniValues;
  1314. end;
  1315. procedure TMemIniFile.SetStrings(List: TStrings);
  1316. begin
  1317. FillSectionList(List);
  1318. end;
  1319. end.