wini.pas 11 KB

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