wini.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  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.2 2001-08-07 22:24:43 pierre
  432. * avoid infinite loop in split method
  433. Revision 1.1 2001/08/04 11:30:26 peter
  434. * ide works now with both compiler versions
  435. Revision 1.1.2.3 2000/08/16 18:46:15 peter
  436. [*] double clicking on a droplistbox caused GPF (due to invalid recurson)
  437. [*] Make, Build now possible even in Compiler Messages Window
  438. [+] when started in a new dir the IDE now ask whether to create a local
  439. config, or to use the one located in the IDE dir
  440. Revision 1.1.2.2 2000/08/15 03:40:55 peter
  441. [*] no more fatal exits when the IDE can't find the error file (containing
  442. the redirected assembler/linker output) after compilation
  443. [*] hidden windows are now added always at the end of the Window List
  444. [*] TINIFile parsed entries encapsulated in string delimiters incorrectly
  445. [*] selection was incorrectly adjusted when typing in overwrite mode
  446. [*] the line wasn't expanded when it's end was reached in overw. mode
  447. [*] the IDE now tries to locate source files also in the user specified
  448. unit dirs (for ex. as a response to 'Open at cursor' (Ctrl+Enter) )
  449. [*] 'Open at cursor' is now aware of the extension (if specified)
  450. Revision 1.1.2.1 2000/07/20 11:02:16 michael
  451. + Fixes from gabor. See fixes.txt
  452. Revision 1.1 2000/07/13 09:48:37 michael
  453. + Initial import
  454. Revision 1.10 2000/06/22 09:07:15 pierre
  455. * Gabor changes: see fixes.txt
  456. Revision 1.9 2000/04/18 11:42:39 pierre
  457. lot of Gabor changes : see fixes.txt
  458. Revision 1.8 1999/03/08 14:58:21 peter
  459. + prompt with dialogs for tools
  460. Revision 1.7 1999/03/05 17:53:03 pierre
  461. + saving and opening of open files on exit
  462. Revision 1.6 1999/03/01 15:42:15 peter
  463. + Added dummy entries for functions not yet implemented
  464. * MenuBar didn't update itself automatically on command-set changes
  465. * Fixed Debugging/Profiling options dialog
  466. * TCodeEditor converts spaces to tabs at save only if efUseTabChars is
  467. set
  468. * efBackSpaceUnindents works correctly
  469. + 'Messages' window implemented
  470. + Added '$CAP MSG()' and '$CAP EDIT' to available tool-macros
  471. + Added TP message-filter support (for ex. you can call GREP thru
  472. GREP2MSG and view the result in the messages window - just like in TP)
  473. * A 'var' was missing from the param-list of THelpFacility.TopicSearch,
  474. so topic search didn't work...
  475. * In FPHELP.PAS there were still context-variables defined as word instead
  476. of THelpCtx
  477. * StdStatusKeys() was missing from the statusdef for help windows
  478. + Topic-title for index-table can be specified when adding a HTML-files
  479. Revision 1.5 1999/02/22 02:15:26 peter
  480. + default extension for save in the editor
  481. + Separate Text to Find for the grep dialog
  482. * fixed redir crash with tp7
  483. Revision 1.4 1999/02/10 09:14:57 pierre
  484. * Value was not disposed before overwrite in TINIEntry.SetValue
  485. Revision 1.3 1999/01/21 11:54:33 peter
  486. + tools menu
  487. + speedsearch in symbolbrowser
  488. * working run command
  489. Revision 1.2 1998/12/28 15:47:58 peter
  490. + Added user screen support, display & window
  491. + Implemented Editor,Mouse Options dialog
  492. + Added location of .INI and .CFG file
  493. + Option (INI) file managment implemented (see bottom of Options Menu)
  494. + Switches updated
  495. + Run program
  496. Revision 1.1 1998/12/22 10:39:57 peter
  497. + options are now written/read
  498. + find and replace routines
  499. }