wini.pas 11 KB

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