wini.pas 11 KB

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