wini.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. {
  2. $Id$
  3. This file is part of the Free Pascal Integrated Development Environment
  4. Copyright (c) 1998 by B‚rczi G bor
  5. Reading and writing .INI files
  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. unit WINI;
  13. interface
  14. uses Objects;
  15. type
  16. PINIEntry = ^TINIEntry;
  17. TINIEntry = object(TObject)
  18. constructor Init(const ALine: string);
  19. function GetText: string;
  20. function GetTag: string;
  21. function GetComment: string;
  22. function GetValue: string;
  23. procedure SetValue(const S: string);
  24. destructor Done; virtual;
  25. private
  26. TagHash : Cardinal;
  27. Tag : PString;
  28. Value : PString;
  29. Comment : PString;
  30. Text : PString;
  31. Modified : boolean;
  32. procedure Split;
  33. end;
  34. PINISection = ^TINISection;
  35. TINISection = object(TObject)
  36. constructor Init(const AName: string);
  37. function GetName: string;
  38. function AddEntry(const S: string): PINIEntry;
  39. function SearchEntry(Tag: string): PINIEntry; virtual;
  40. procedure DeleteEntry(Tag: string);
  41. procedure ForEachEntry(EnumProc: pointer); virtual;
  42. destructor Done; virtual;
  43. private
  44. NameHash : Cardinal;
  45. Name : PString;
  46. Entries : PCollection;
  47. end;
  48. PINIFile = ^TINIFile;
  49. TINIFile = object(TObject)
  50. MakeNullEntries: boolean;
  51. constructor Init(const AFileName: string);
  52. function GetFileName: string;
  53. function Read: boolean; virtual;
  54. function Update: boolean; virtual;
  55. function IsModified: boolean; virtual;
  56. function SearchSection(Section: string): PINISection; virtual;
  57. function SearchEntry(const Section, Tag: string): PINIEntry; virtual;
  58. procedure ForEachSection(EnumProc: pointer); virtual;
  59. procedure ForEachEntry(const Section: string; EnumProc: pointer); virtual;
  60. function GetEntry(const Section, Tag, Default: string): string; virtual;
  61. procedure SetEntry(const Section, Tag, Value: string); virtual;
  62. function GetIntEntry(const Section, Tag: string; Default: longint): longint; virtual;
  63. procedure SetIntEntry(const Section, Tag: string; Value: longint); virtual;
  64. procedure DeleteSection(const Section: string); virtual;
  65. procedure DeleteEntry(const Section, Tag: string);
  66. destructor Done; virtual;
  67. private
  68. { ReadOnly: boolean;}
  69. Sections: PCollection;
  70. FileName: PString;
  71. end;
  72. const MainSectionName : string[40] = 'MainSection';
  73. CommentChar : char = ';';
  74. ValidStrDelimiters: set of char = ['''','"'];
  75. implementation
  76. uses
  77. WUtils;
  78. function CalcHash(const s: String): Cardinal;
  79. var
  80. i: integer;
  81. begin
  82. CalcHash := 0;
  83. for i := 1 to Length(s) do
  84. CalcHash := CalcHash shl 9 - CalcHash shl 4 + Ord(S[I]);
  85. end;
  86. constructor TINIEntry.Init(const ALine: string);
  87. begin
  88. inherited Init;
  89. Text:=NewStr(ALine);
  90. Split;
  91. end;
  92. function TINIEntry.GetText: string;
  93. var S,CoS: string;
  94. begin
  95. if Text=nil then
  96. begin
  97. CoS:=GetComment;
  98. S:=GetTag+'='+GetValue;
  99. if Trim(S)='=' then S:=CoS else
  100. if CoS<>'' then S:=S+' '+CommentChar+' '+CoS;
  101. end
  102. else S:=Text^;
  103. GetText:=S;
  104. end;
  105. function TINIEntry.GetTag: string;
  106. begin
  107. GetTag:=GetStr(Tag);
  108. end;
  109. function TINIEntry.GetComment: string;
  110. begin
  111. GetComment:=GetStr(Comment);
  112. end;
  113. function TINIEntry.GetValue: string;
  114. begin
  115. GetValue:=GetStr(Value);
  116. end;
  117. procedure TINIEntry.SetValue(const S: string);
  118. begin
  119. if GetValue<>S then
  120. begin
  121. if Text<>nil then DisposeStr(Text); Text:=nil;
  122. if Value<>nil then DisposeStr(Value);
  123. Value:=NewStr(S);
  124. Modified:=true;
  125. end;
  126. end;
  127. procedure TINIEntry.Split;
  128. var S,ValueS: string;
  129. P,P2,StartP: longint;
  130. { using byte for P2 lead to infinite loops PM }
  131. C: char;
  132. InString: boolean;
  133. Delimiter: char;
  134. begin
  135. S:=GetText; Delimiter:=#0;
  136. P:=Pos('=',S); P2:=Pos(CommentChar,S);
  137. if (P2<>0) and (P2<P) then P:=0;
  138. if P<>0 then
  139. begin
  140. Tag:=NewStr(copy(S,1,P-1));
  141. TagHash:=CalcHash(UpcaseStr(Tag^));
  142. P2:=P+1; InString:=false; ValueS:='';
  143. StartP:=P2;
  144. while (P2<=length(S)) do
  145. begin
  146. C:=S[P2];
  147. if (P2=StartP) and (C in ValidStrDelimiters) then begin Delimiter:=C; InString:=true; end else
  148. if C=Delimiter then InString:=not InString else
  149. if (C=CommentChar) and (InString=false) then Break else
  150. ValueS:=ValueS+C;
  151. Inc(P2);
  152. end;
  153. Value:=NewStr(Trim(ValueS));
  154. Comment:=NewStr(copy(S,P2+1,High(S)));
  155. end else
  156. begin
  157. Tag:=nil;
  158. TagHash:=0;
  159. Value:=nil;
  160. Comment:=NewStr(S);
  161. end;
  162. end;
  163. destructor TINIEntry.Done;
  164. begin
  165. inherited Done;
  166. if Text<>nil then DisposeStr(Text);
  167. if Tag<>nil then DisposeStr(Tag);
  168. if Value<>nil then DisposeStr(Value);
  169. if Comment<>nil then DisposeStr(Comment);
  170. end;
  171. constructor TINISection.Init(const AName: string);
  172. begin
  173. inherited Init;
  174. Name:=NewStr(AName);
  175. NameHash:=CalcHash(UpcaseStr(AName));
  176. New(Entries, Init(50,500));
  177. end;
  178. function TINISection.GetName: string;
  179. begin
  180. GetName:=GetStr(Name);
  181. end;
  182. function TINISection.AddEntry(const S: string): PINIEntry;
  183. var E: PINIEntry;
  184. begin
  185. New(E, Init(S));
  186. Entries^.Insert(E);
  187. AddEntry:=E;
  188. end;
  189. procedure TINIFile.ForEachSection(EnumProc: pointer);
  190. var I: Sw_integer;
  191. S: PINISection;
  192. begin
  193. for I:=0 to Sections^.Count-1 do
  194. begin
  195. S:=Sections^.At(I);
  196. CallPointerLocal(EnumProc,get_caller_frame(get_frame),S);
  197. end;
  198. end;
  199. procedure TINISection.ForEachEntry(EnumProc: pointer);
  200. var I: integer;
  201. E: PINIEntry;
  202. begin
  203. for I:=0 to Entries^.Count-1 do
  204. begin
  205. E:=Entries^.At(I);
  206. CallPointerLocal(EnumProc,get_caller_frame(get_frame),E);
  207. end;
  208. end;
  209. function TINISection.SearchEntry(Tag: string): PINIEntry;
  210. var
  211. P : PINIEntry;
  212. I : Sw_integer;
  213. Hash : Cardinal;
  214. begin
  215. SearchEntry:=nil;
  216. Tag:=UpcaseStr(Tag);
  217. Hash:=CalcHash(Tag);
  218. for I:=0 to Entries^.Count-1 do
  219. begin
  220. P:=Entries^.At(I);
  221. if (P^.TagHash=Hash) and (UpcaseStr(P^.GetTag)=Tag) then
  222. begin
  223. SearchEntry:=P;
  224. break;
  225. end;
  226. end;
  227. end;
  228. procedure TINISection.DeleteEntry(Tag: string);
  229. var
  230. P : PIniEntry;
  231. begin
  232. P:=SearchEntry(Tag);
  233. if assigned(P) then
  234. Entries^.Free(P);
  235. end;
  236. destructor TINISection.Done;
  237. begin
  238. inherited Done;
  239. if Name<>nil then DisposeStr(Name);
  240. Dispose(Entries, Done);
  241. end;
  242. constructor TINIFile.Init(const AFileName: string);
  243. begin
  244. inherited Init;
  245. FileName:=NewStr(AFileName);
  246. New(Sections, Init(50,50));
  247. Read;
  248. end;
  249. function TINIFile.GetFileName: string;
  250. begin
  251. GetFileName:=GetStr(FileName);
  252. end;
  253. function TINIFile.Read: boolean;
  254. var f: text;
  255. OK: boolean;
  256. S,TS: string;
  257. P: PINISection;
  258. I: integer;
  259. begin
  260. New(P, Init(MainSectionName));
  261. Sections^.Insert(P);
  262. Assign(f,FileName^);
  263. {$I-}
  264. Reset(f);
  265. OK:=EatIO=0;
  266. while OK and (Eof(f)=false) do
  267. begin
  268. readln(f,S);
  269. TS:=Trim(S);
  270. OK:=EatIO=0;
  271. if OK then
  272. if TS<>'' then
  273. if copy(TS,1,1)='[' then
  274. begin
  275. I:=Pos(']',TS); if I=0 then I:=length(TS)+1;
  276. New(P, Init(copy(TS,2,I-2)));
  277. Sections^.Insert(P);
  278. end else
  279. begin
  280. P^.AddEntry(S);
  281. end;
  282. end;
  283. Close(f);
  284. EatIO;
  285. {$I+}
  286. Read:=true;
  287. end;
  288. function TINIFile.IsModified: boolean;
  289. function SectionModified(P: PINISection): boolean;
  290. function EntryModified(E: PINIEntry): boolean;
  291. begin
  292. EntryModified:=E^.Modified;
  293. end;
  294. begin
  295. SectionModified:=(P^.Entries^.FirstThat(@EntryModified)<>nil);
  296. end;
  297. begin
  298. IsModified:=(Sections^.FirstThat(@SectionModified)<>nil);
  299. end;
  300. function TINIFile.Update: boolean;
  301. var f: text;
  302. OK: boolean;
  303. P: PINISection;
  304. E: PINIEntry;
  305. I,J: integer;
  306. begin
  307. Assign(f,FileName^);
  308. {$I-}
  309. Rewrite(f);
  310. OK:=EatIO=0;
  311. if OK then
  312. for I:=0 to Sections^.Count-1 do
  313. begin
  314. P:=Sections^.At(I);
  315. if I<>0 then writeln(f,'['+P^.GetName+']');
  316. for J:=0 to P^.Entries^.Count-1 do
  317. begin
  318. E:=P^.Entries^.At(J);
  319. writeln(f,E^.GetText);
  320. OK:=EatIO=0;
  321. if OK=false then Break;
  322. end;
  323. if OK and ((I>0) or (P^.Entries^.Count>0)) and (I<Sections^.Count-1) then
  324. writeln(f,'');
  325. OK:=OK and (EatIO=0);
  326. if OK=false then Break;
  327. end;
  328. Close(f);
  329. EatIO;
  330. {$I+}
  331. if OK then
  332. for I:=0 to Sections^.Count-1 do
  333. begin
  334. P:=Sections^.At(I);
  335. for J:=0 to P^.Entries^.Count-1 do
  336. begin
  337. E:=P^.Entries^.At(J);
  338. E^.Modified:=false;
  339. end;
  340. end;
  341. Update:=OK;
  342. end;
  343. function TINIFile.SearchSection(Section: string): PINISection;
  344. var
  345. P : PINISection;
  346. I : Sw_integer;
  347. Hash : Cardinal;
  348. begin
  349. SearchSection:=nil;
  350. Section:=UpcaseStr(Section);
  351. Hash:=CalcHash(Section);
  352. for I:=0 to Sections^.Count-1 do
  353. begin
  354. P:=Sections^.At(I);
  355. if (P^.NameHash=Hash) and (UpcaseStr(P^.GetName)=Section) then
  356. begin
  357. SearchSection:=P;
  358. break;
  359. end;
  360. end;
  361. end;
  362. function TINIFile.SearchEntry(const Section, Tag: string): PINIEntry;
  363. var P: PINISection;
  364. E: PINIEntry;
  365. begin
  366. P:=SearchSection(Section);
  367. if P=nil then E:=nil else
  368. E:=P^.SearchEntry(Tag);
  369. SearchEntry:=E;
  370. end;
  371. procedure TINIFile.ForEachEntry(const Section: string; EnumProc: pointer);
  372. var P: PINISection;
  373. E: PINIEntry;
  374. I: integer;
  375. begin
  376. P:=SearchSection(Section);
  377. if P<>nil then
  378. for I:=0 to P^.Entries^.Count-1 do
  379. begin
  380. E:=P^.Entries^.At(I);
  381. CallPointerMethodLocal(EnumProc,get_frame,@Self,E);
  382. end;
  383. end;
  384. function TINIFile.GetEntry(const Section, Tag, Default: string): string;
  385. var E: PINIEntry;
  386. S: string;
  387. begin
  388. E:=SearchEntry(Section,Tag);
  389. if E=nil then S:=Default else
  390. S:=E^.GetValue;
  391. GetEntry:=S;
  392. end;
  393. procedure TINIFile.SetEntry(const Section, Tag, Value: string);
  394. var E: PINIEntry;
  395. P: PINISection;
  396. begin
  397. E:=SearchEntry(Section,Tag);
  398. if E=nil then
  399. if (MakeNullEntries=true) or (Value<>'') then
  400. begin
  401. P:=SearchSection(Section);
  402. if P=nil then
  403. begin
  404. New(P, Init(Section));
  405. Sections^.Insert(P);
  406. end;
  407. E:=P^.AddEntry(Tag+'='+Value);
  408. E^.Modified:=true;
  409. end;
  410. if E<>nil then
  411. E^.SetValue(Value);
  412. end;
  413. function TINIFile.GetIntEntry(const Section, Tag: string; Default: longint): longint;
  414. var L: longint;
  415. begin
  416. L:=StrToInt(GetEntry(Section,Tag,IntToStr(Default)));
  417. if LastStrToIntResult<>0 then L:=Default;
  418. GetIntEntry:=L;
  419. end;
  420. procedure TINIFile.SetIntEntry(const Section, Tag: string; Value: longint);
  421. begin
  422. SetEntry(Section,Tag,IntToStr(Value));
  423. end;
  424. procedure TINIFile.DeleteSection(const Section: string);
  425. var P: PINISection;
  426. begin
  427. P:=SearchSection(Section);
  428. if P<>nil then
  429. Sections^.Free(P);
  430. end;
  431. procedure TINIFile.DeleteEntry(const Section, Tag: string);
  432. var P: PINISection;
  433. begin
  434. P:=SearchSection(Section);
  435. if P<>nil then
  436. P^.DeleteEntry(Tag);
  437. end;
  438. destructor TINIFile.Done;
  439. begin
  440. if IsModified then
  441. Update;
  442. inherited Done;
  443. if FileName<>nil then
  444. DisposeStr(FileName);
  445. Dispose(Sections, Done);
  446. end;
  447. END.
  448. {
  449. $Log$
  450. Revision 1.5 2004-11-06 17:22:53 peter
  451. * fixes for new fv
  452. Revision 1.4 2004/11/02 23:53:19 peter
  453. * fixed crashes with ide and 1.9.x
  454. Revision 1.3 2002/09/07 15:40:50 peter
  455. * old logs removed and tabs fixed
  456. }