wini.pas 11 KB

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