wini.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  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: byte;
  121. C: char;
  122. InString: boolean;
  123. Delimiter: char;
  124. begin
  125. S:=GetText; Delimiter:=#0;
  126. P:=Pos('=',S); P2:=Pos(CommentChar,S);
  127. if (P2<>0) and (P2<P) then P:=0;
  128. if P<>0 then
  129. begin
  130. Tag:=NewStr(copy(S,1,P-1));
  131. P2:=P+1; InString:=false; ValueS:='';
  132. StartP:=P2;
  133. while (P2<=length(S)) do
  134. begin
  135. C:=S[P2];
  136. if (P2=StartP) and (C in ValidStrDelimiters) then begin Delimiter:=C; InString:=true; end else
  137. if C=Delimiter then InString:=not InString else
  138. if (C=CommentChar) and (InString=false) then Break else
  139. ValueS:=ValueS+C;
  140. Inc(P2);
  141. end;
  142. Value:=NewStr(Trim(ValueS));
  143. Comment:=NewStr(copy(S,P2+1,High(S)));
  144. end else
  145. begin
  146. Tag:=nil;
  147. Value:=nil;
  148. Comment:=NewStr(S);
  149. end;
  150. end;
  151. destructor TINIEntry.Done;
  152. begin
  153. inherited Done;
  154. if Text<>nil then DisposeStr(Text);
  155. if Tag<>nil then DisposeStr(Tag);
  156. if Value<>nil then DisposeStr(Value);
  157. if Comment<>nil then DisposeStr(Comment);
  158. end;
  159. constructor TINISection.Init(const AName: string);
  160. begin
  161. inherited Init;
  162. Name:=NewStr(AName);
  163. New(Entries, Init(50,500));
  164. end;
  165. function TINISection.GetName: string;
  166. begin
  167. GetName:=GetStr(Name);
  168. end;
  169. function TINISection.AddEntry(const S: string): PINIEntry;
  170. var E: PINIEntry;
  171. begin
  172. New(E, Init(S));
  173. Entries^.Insert(E);
  174. AddEntry:=E;
  175. end;
  176. procedure TINIFile.ForEachSection(EnumProc: pointer);
  177. var I: Sw_integer;
  178. S: PINISection;
  179. begin
  180. for I:=0 to Sections^.Count-1 do
  181. begin
  182. S:=Sections^.At(I);
  183. CallPointerLocal(EnumProc,PreviousFramePointer,S);
  184. end;
  185. end;
  186. procedure TINISection.ForEachEntry(EnumProc: pointer);
  187. var I: integer;
  188. E: PINIEntry;
  189. begin
  190. for I:=0 to Entries^.Count-1 do
  191. begin
  192. E:=Entries^.At(I);
  193. CallPointerLocal(EnumProc,PreviousFramePointer,E);
  194. end;
  195. end;
  196. function TINISection.SearchEntry(Tag: string): PINIEntry;
  197. function MatchingEntry(E: PINIEntry): boolean; {$ifndef FPC}far;{$endif}
  198. begin
  199. MatchingEntry:=UpcaseStr(E^.GetTag)=Tag;
  200. end;
  201. begin
  202. Tag:=UpcaseStr(Tag);
  203. SearchEntry:=Entries^.FirstThat(@MatchingEntry);
  204. end;
  205. procedure TINISection.DeleteEntry(Tag: string);
  206. var
  207. P : PIniEntry;
  208. begin
  209. P:=SearchEntry(Tag);
  210. if assigned(P) then
  211. Entries^.Free(P);
  212. end;
  213. destructor TINISection.Done;
  214. begin
  215. inherited Done;
  216. if Name<>nil then DisposeStr(Name);
  217. Dispose(Entries, Done);
  218. end;
  219. constructor TINIFile.Init(const AFileName: string);
  220. begin
  221. inherited Init;
  222. FileName:=NewStr(AFileName);
  223. New(Sections, Init(50,50));
  224. Read;
  225. end;
  226. function TINIFile.GetFileName: string;
  227. begin
  228. GetFileName:=GetStr(FileName);
  229. end;
  230. function TINIFile.Read: boolean;
  231. var f: text;
  232. OK: boolean;
  233. S,TS: string;
  234. P: PINISection;
  235. I: integer;
  236. begin
  237. New(P, Init(MainSectionName));
  238. Sections^.Insert(P);
  239. Assign(f,FileName^);
  240. {$I-}
  241. Reset(f);
  242. OK:=EatIO=0;
  243. while OK and (Eof(f)=false) do
  244. begin
  245. readln(f,S);
  246. TS:=Trim(S);
  247. OK:=EatIO=0;
  248. if OK then
  249. if TS<>'' then
  250. if copy(TS,1,1)='[' then
  251. begin
  252. I:=Pos(']',TS); if I=0 then I:=length(TS)+1;
  253. New(P, Init(copy(TS,2,I-2)));
  254. Sections^.Insert(P);
  255. end else
  256. begin
  257. P^.AddEntry(S);
  258. end;
  259. end;
  260. Close(f);
  261. EatIO;
  262. {$I+}
  263. Read:=true;
  264. end;
  265. function TINIFile.IsModified: boolean;
  266. function SectionModified(P: PINISection): boolean; {$ifndef FPC}far;{$endif}
  267. function EntryModified(E: PINIEntry): boolean; {$ifndef FPC}far;{$endif}
  268. begin
  269. EntryModified:=E^.Modified;
  270. end;
  271. begin
  272. SectionModified:=(P^.Entries^.FirstThat(@EntryModified)<>nil);
  273. end;
  274. begin
  275. IsModified:=(Sections^.FirstThat(@SectionModified)<>nil);
  276. end;
  277. function TINIFile.Update: boolean;
  278. var f: text;
  279. OK: boolean;
  280. P: PINISection;
  281. E: PINIEntry;
  282. I,J: integer;
  283. begin
  284. Assign(f,FileName^);
  285. {$I-}
  286. Rewrite(f);
  287. OK:=EatIO=0;
  288. if OK then
  289. for I:=0 to Sections^.Count-1 do
  290. begin
  291. P:=Sections^.At(I);
  292. if I<>0 then writeln(f,'['+P^.GetName+']');
  293. for J:=0 to P^.Entries^.Count-1 do
  294. begin
  295. E:=P^.Entries^.At(J);
  296. writeln(f,E^.GetText);
  297. OK:=EatIO=0;
  298. if OK=false then Break;
  299. end;
  300. if OK and ((I>0) or (P^.Entries^.Count>0)) and (I<Sections^.Count-1) then
  301. writeln(f,'');
  302. OK:=OK and (EatIO=0);
  303. if OK=false then Break;
  304. end;
  305. Close(f);
  306. EatIO;
  307. {$I+}
  308. if OK then
  309. for I:=0 to Sections^.Count-1 do
  310. begin
  311. P:=Sections^.At(I);
  312. for J:=0 to P^.Entries^.Count-1 do
  313. begin
  314. E:=P^.Entries^.At(J);
  315. E^.Modified:=false;
  316. end;
  317. end;
  318. Update:=OK;
  319. end;
  320. function TINIFile.SearchSection(Section: string): PINISection;
  321. function MatchingSection(P: PINISection): boolean; {$ifndef FPC}far;{$endif}
  322. var SN: string;
  323. M: boolean;
  324. begin
  325. SN:=UpcaseStr(P^.GetName);
  326. M:=SN=Section;
  327. MatchingSection:=M;
  328. end;
  329. begin
  330. Section:=UpcaseStr(Section);
  331. SearchSection:=Sections^.FirstThat(@MatchingSection);
  332. end;
  333. function TINIFile.SearchEntry(const Section, Tag: string): PINIEntry;
  334. var P: PINISection;
  335. E: PINIEntry;
  336. begin
  337. P:=SearchSection(Section);
  338. if P=nil then E:=nil else
  339. E:=P^.SearchEntry(Tag);
  340. SearchEntry:=E;
  341. end;
  342. procedure TINIFile.ForEachEntry(const Section: string; EnumProc: pointer);
  343. var P: PINISection;
  344. E: PINIEntry;
  345. I: integer;
  346. begin
  347. P:=SearchSection(Section);
  348. if P<>nil then
  349. for I:=0 to P^.Entries^.Count-1 do
  350. begin
  351. E:=P^.Entries^.At(I);
  352. {$ifdef FPC}
  353. CallPointerMethodLocal(EnumProc,CurrentFramePointer,@Self,E);
  354. {$else}
  355. asm
  356. push E.word[2]
  357. push E.word[0]
  358. push word ptr [bp]
  359. call EnumProc
  360. end;
  361. {$endif}
  362. end;
  363. end;
  364. function TINIFile.GetEntry(const Section, Tag, Default: string): string;
  365. var E: PINIEntry;
  366. S: string;
  367. begin
  368. E:=SearchEntry(Section,Tag);
  369. if E=nil then S:=Default else
  370. S:=E^.GetValue;
  371. GetEntry:=S;
  372. end;
  373. procedure TINIFile.SetEntry(const Section, Tag, Value: string);
  374. var E: PINIEntry;
  375. P: PINISection;
  376. begin
  377. E:=SearchEntry(Section,Tag);
  378. if E=nil then
  379. if (MakeNullEntries=true) or (Value<>'') then
  380. begin
  381. P:=SearchSection(Section);
  382. if P=nil then
  383. begin
  384. New(P, Init(Section));
  385. Sections^.Insert(P);
  386. end;
  387. E:=P^.AddEntry(Tag+'='+Value);
  388. E^.Modified:=true;
  389. end;
  390. if E<>nil then
  391. E^.SetValue(Value);
  392. end;
  393. function TINIFile.GetIntEntry(const Section, Tag: string; Default: longint): longint;
  394. var L: longint;
  395. begin
  396. L:=StrToInt(GetEntry(Section,Tag,IntToStr(Default)));
  397. if LastStrToIntResult<>0 then L:=Default;
  398. GetIntEntry:=L;
  399. end;
  400. procedure TINIFile.SetIntEntry(const Section, Tag: string; Value: longint);
  401. begin
  402. SetEntry(Section,Tag,IntToStr(Value));
  403. end;
  404. procedure TINIFile.DeleteSection(const Section: string);
  405. var P: PINISection;
  406. begin
  407. P:=SearchSection(Section);
  408. if P<>nil then
  409. Sections^.Free(P);
  410. end;
  411. procedure TINIFile.DeleteEntry(const Section, Tag: string);
  412. var P: PINISection;
  413. begin
  414. P:=SearchSection(Section);
  415. if P<>nil then
  416. P^.DeleteEntry(Tag);
  417. end;
  418. destructor TINIFile.Done;
  419. begin
  420. if IsModified then
  421. Update;
  422. inherited Done;
  423. if FileName<>nil then
  424. DisposeStr(FileName);
  425. Dispose(Sections, Done);
  426. end;
  427. END.
  428. {
  429. $Log$
  430. Revision 1.2 2000-08-22 09:41:41 pierre
  431. * first big merge from fixes branch
  432. Revision 1.1.2.3 2000/08/16 18:46:15 peter
  433. [*] double clicking on a droplistbox caused GPF (due to invalid recurson)
  434. [*] Make, Build now possible even in Compiler Messages Window
  435. [+] when started in a new dir the IDE now ask whether to create a local
  436. config, or to use the one located in the IDE dir
  437. Revision 1.1.2.2 2000/08/15 03:40:55 peter
  438. [*] no more fatal exits when the IDE can't find the error file (containing
  439. the redirected assembler/linker output) after compilation
  440. [*] hidden windows are now added always at the end of the Window List
  441. [*] TINIFile parsed entries encapsulated in string delimiters incorrectly
  442. [*] selection was incorrectly adjusted when typing in overwrite mode
  443. [*] the line wasn't expanded when it's end was reached in overw. mode
  444. [*] the IDE now tries to locate source files also in the user specified
  445. unit dirs (for ex. as a response to 'Open at cursor' (Ctrl+Enter) )
  446. [*] 'Open at cursor' is now aware of the extension (if specified)
  447. Revision 1.1.2.1 2000/07/20 11:02:16 michael
  448. + Fixes from gabor. See fixes.txt
  449. Revision 1.1 2000/07/13 09:48:37 michael
  450. + Initial import
  451. Revision 1.10 2000/06/22 09:07:15 pierre
  452. * Gabor changes: see fixes.txt
  453. Revision 1.9 2000/04/18 11:42:39 pierre
  454. lot of Gabor changes : see fixes.txt
  455. Revision 1.8 1999/03/08 14:58:21 peter
  456. + prompt with dialogs for tools
  457. Revision 1.7 1999/03/05 17:53:03 pierre
  458. + saving and opening of open files on exit
  459. Revision 1.6 1999/03/01 15:42:15 peter
  460. + Added dummy entries for functions not yet implemented
  461. * MenuBar didn't update itself automatically on command-set changes
  462. * Fixed Debugging/Profiling options dialog
  463. * TCodeEditor converts spaces to tabs at save only if efUseTabChars is
  464. set
  465. * efBackSpaceUnindents works correctly
  466. + 'Messages' window implemented
  467. + Added '$CAP MSG()' and '$CAP EDIT' to available tool-macros
  468. + Added TP message-filter support (for ex. you can call GREP thru
  469. GREP2MSG and view the result in the messages window - just like in TP)
  470. * A 'var' was missing from the param-list of THelpFacility.TopicSearch,
  471. so topic search didn't work...
  472. * In FPHELP.PAS there were still context-variables defined as word instead
  473. of THelpCtx
  474. * StdStatusKeys() was missing from the statusdef for help windows
  475. + Topic-title for index-table can be specified when adding a HTML-files
  476. Revision 1.5 1999/02/22 02:15:26 peter
  477. + default extension for save in the editor
  478. + Separate Text to Find for the grep dialog
  479. * fixed redir crash with tp7
  480. Revision 1.4 1999/02/10 09:14:57 pierre
  481. * Value was not disposed before overwrite in TINIEntry.SetValue
  482. Revision 1.3 1999/01/21 11:54:33 peter
  483. + tools menu
  484. + speedsearch in symbolbrowser
  485. * working run command
  486. Revision 1.2 1998/12/28 15:47:58 peter
  487. + Added user screen support, display & window
  488. + Implemented Editor,Mouse Options dialog
  489. + Added location of .INI and .CFG file
  490. + Option (INI) file managment implemented (see bottom of Options Menu)
  491. + Switches updated
  492. + Run program
  493. Revision 1.1 1998/12/22 10:39:57 peter
  494. + options are now written/read
  495. + find and replace routines
  496. }