wini.pas 11 KB

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