1 {
2 ���������������������������������������������������������������������������Ŀ
3 �                                                                           �
4 �      ��۰ ۰  ۰ ��۰   ��۰ ��۰ ۰   ��۰   ۰ ۰ ۰  ۰ ��۰ ��۰      �
5 �       ۰  �۰ ۰  ۰    ۰    ۰  ۰   ۰     ۰ ۰ �۰ ۰  ۰   ۰       �
6 �       ۰  ۰۰۰  ۰    �۰   ۰  ۰   �۰    ۰ ۰ ۰۰۰  ۰   ۰       �
7 �       ۰  ۰ �۰  ۰    ۰    ۰  ۰   ۰     ۰ ۰ ۰ �۰  ۰   ۰       �
8 �      ��۰ ۰  ۰ ��۰   ۰   ��۰ ��۰ ��۰   ���۰ ۰  ۰ ��۰  ۰       �
9 �                                                                           �
10 �      Ver. : 1.00b ...... (c) 1996 by Stephan Zehrer ...... FreeWare       �
11 �                                                                           �
12 ���������������������������������������������������������������������������Ĵ
13 � IniFile Unit dient zur verarbeitung von config Dateien in Textform wie    �
14 � INI oder CFG.                                                             �
15 � Hier einige Features von IniFile Unit :                                   �
16 �   - Einfache Bedinung (4 Routinen fuer Grund Funktionen)                  �
17 �   - Alles in einem Object, daduch mehrer Listen gleichzeitig nutzbar      �
18 �   - Aufleilung in Untergruppen (Sectionen)                                �
19 �   - Kommentarverwaltung von jede Option (';')                             �
20 �   - kommplette Einlesen der Ini File in eine einfach verkettete Liste     �
21 �   - schnelle Verarbeitung da keine lese/schreib zugriffe Waeren des       �
22 �     ein und auslesens.                                                    �
23 �   - DataBase Funktionen                                                   �
24 �     ( Aufbau von kleine Datenbanken in Textform wie z.B. PLZ Listen oder  �
25 �       Listen von Telefon Nummern )                                        �
26 �                                                                           �
27 �     !!! Achtung Version ohne externe Functionen (siehe Dateiende) !!!     �
28 �                                                                           �
29 ���������������������������������������������������������������������������Ĵ
30 � Diese Version wurde ausgibig von mir getestet. Es kann trotzdem vorkommen �
31 � das sich Fehler einschleichen.                                            �
32 � Bitte schicken Sie einen Bug report an : Stephan Zehrer@2:2487/9001.14    �
33 �                                                                           �
34 � Der Autor uebernimmt keine Haftung fuer Schaeden, die durch dieses Programm�
35 � entstanden sind.                                                          �
36 �����������������������������������������������������������������������������
37 
38 changed by Sascha Silbe
39 }
40 {$X+}{$R-}{ $L-}{ $D-}{$Y-}{$X+}{$G+}
41 unit IniFile;
42 
43 interface
44 
45 uses DOS;
46 
47 type FileStr    = {String [12]} String[128]; {Externer Typ}
48      pOptRec = ^tOptRec;
49      tOptRec = record               {Record fuer die Optionen}
50                  Name    : String[20]; {Name, wenn ';' dann nur Kommentarzeile}
51 {$IfDef MKNL}
52                  Value   : String; {Wert}
53 {$Else}
54                  Value   : String[80]; {Wert}
55 {$EndIf}
56                  Comment : String[100]; {Kommentar}
57                  PrevOpt : pOptRec; {Zeiger auf vorherige Option}
58                  NextOpt : pOptRec; {Zeiger auf naechste Option}
59                end;
60      pSecRec = ^tSecRec;
61      tSecRec = record               {Record fuer die Sectionen}
62                  Name : String[20]; {Name der Secion, immer grossbustaben}
63                  Options : pOptRec; {Zeiger auf die Optionen}
64                  PrevSec : pSecRec; {Zeiger auf vorherige Section}
65                  NextSec : pSecRec; {Zeiger auf naechste Section}
66                end;
67 
68 type IniObj = object
69                 FileName   : FileStr;
70                 Sep        : Char;    {default = '='}
71                                       {Achtung aenderungen werden nicht
72                                        in der ini Datei gespeichert}
73                 CommentPos : byte;    {Wenn 0 dann deaktiviert, sonnst
74                                        position an der die Kommentare
75                                        ausgerichtet werden, natuerlich nur
76                                        wenn der normal Eintrag nicht laenger
77                                        ist. default = 0}
78                 NotFound   : String[20];  {String der zurueckgegeben wird wenn
79                                        ein eintrag nicht gefunden wurde
80                                        default = ''}
81                 f          : Text;
82                 IniTree    : pSecRec;  {Zeiger auf die 1. Section}
83                 AktSec     : pSecRec;  {nur intern benutzt}
84                 AktOpt     : pOptRec;  {nur intern benutzt}
85                 constructor Init (AFileName : FileStr);
86                 { Initalisiert die Ini Datei }
87                 destructor Done;
88                 { Schreibt die Eintraege und loeschen den Verkettung }
89                 procedure SetSeperator (ASep : Char);
90                 { Aendert den Seperator, default (=) }
91                 procedure SetNotFound  (ANotFound : String);
92                 { Aendert den NoFound String, default () }
93                 procedure SetCommentPos (ACommentPos : byte);
94                 { Aendert die CommentPos, default (0) }
ReadEntrynull95                 function ReadEntry ( Section : String;
96                                      Option  : String ) : String;
97                 { Liefert den Wert des Eintrags }
98                 procedure WriteEntry ( Section : String;
99                                        Option  : String;
100                                        Value   : String;
101                                        Comment : String);
102                 { Aendert/ Definiert eine Eintrag }
103                 procedure AddEntry ( Section : String;
104                                      Option  : String;
105                                      Value   : String;
106                                      Comment : String);
107                 { Fuegt einen Eintrag ans Ende der Section an }
108                 procedure InsertEntry ( Section : String;
109                                         Option  : String;
110                                         Value   : String;
111                                         Comment : String);
112                 { Fuegt einen Eintrag an der aktuellen Position ein }
113                 procedure DelEntry ( Section : String;
114                                      Option  : String;
115                                      DelSec  : Boolean );
116                 { Loescht eine Eintrag }
117                 { --------- Datenbank Routinen -------- }
118                 { Mit den Datenbank Functionen ist es moeglich
119                   auch Eintraege zu Lesen deren Options Name
120                   man nicht kennt. Wie kleine Datenbanken, wie
121                   z.B. eine kleine Liste von PLZ und ihren Ortnamen
122                   oder Telefonnummern ...
123                   Die Functionen sind Sections orientiert geschrieben,
124                   es ist dadurch moeglich Eine Datenbank und Programm
125                   Einstellungen zu kombinieren.
126                   Die Daten werden nicht Sortiert, neue Eintraege werden
127                   einfach hinten angehaengt.}
128                 procedure SetSection (Section : String);
129                 { Setzt die Interen Variabel AktSec, existierd die Section
130                   nicht wird eine neu Section erstellet.
131                   Setzt AktOpt auf den ersten gueltigen Eintrag
132                   ( wenn es einen gibt !!) }
133                 { DIESE PROCEDURE SOLLT ALS ERSTES AUFGREUFEN WERDEN}
134                 procedure ReSetSec;
135                 { Setzt AktOpt wirde auf den 1 Wert. }
GetSecNumnull136                 function GetSecNum (Section : String ) : integer;
137                 { gibt die Anzahl der Eintraegein einer Section }
138                 { dabei werden Kommentarzeilen ingnoriert. }
139                 { zurueck. Wenn Rueckgabewert 0 ist dann ist entweder kein }
140                 { Eintrag vorhanden oder die Section existiert nicht.}
SetNextOptnull141                 function SetNextOpt : boolean;
142                 { Setzt AktSec auf die naechste Option und gibt false
143                   zurueck falls wenn das ende Erreicht ist.
144                   Kommentarzeilen werden uebergangen}
SetPrevOptnull145                 function SetPrevOpt : boolean;
146                 { Setzt AktSec auf die vorherige Option und gibt false
147                   zurueck falls wenn der Anfang Erreicht ist.
148                   Kommentarzeilen werden uebergangen}
ReSecEnNamenull149                 function ReSecEnName  : String;
150                 { Was so viel heisst wie ReadSectionEntryName }
151                 { gibt den Namen der Aktuellen Option zurueck }
152                 { Ist AktOpt = NIL wird der NotFound String zurueck gegeben }
ReSecEnValuenull153                 function ReSecEnValue : String;
154                 { gibt den Wert der Aktuellen Option zurueck }
155                 { Ist AktOpt = NIL wird der NotFound String zurueck gegeben }
SearchSecEntrynull156                 function SearchSecEntry (Name : String ) : String;
157                 { Sucht nach einen Eintrag und gibt seine Wert zurueck. }
158                 { AktOpt wird nicht geaendert !! }
159                 procedure WriteSecEntry (Name    : String;
160                                          Value   : String;
161                                          Comment : String);
162                 { Schreibz den Eintrag neu oder aendert den Wert, der
163                   Aktuellen Section
164                   AktOpt wird nicht geaendert !!
165                   Achtung !! wurde keine Section gesetzt wird der Eintrag
166                   in eine falsche oder in keine Section geschrieben !!}
167                 procedure AddSecEntry (Name    : String;
168                                        Value   : String;
169                                        Comment : String);
170                 { Haengt einen neuen Eintrag hinten an, der Aktuellen Section
171                   AktOpt wird nicht geaendert !!
172                   Achtung !! wurde keine Section gesetzt wird der Eintrag
173                   in eine falsche oder in keine Section geschrieben !!}
174                 procedure InsertSecEntry (Name    : String;
175                                           Value   : String;
176                                           Comment : String);
177                 { Fuegt einen neuen Eintrag ein, der Aktuellen Section
178                   AktOpt wird nicht geaendert !!
179                   Achtung !! wurde keine Section gesetzt wird der Eintrag
180                   in eine falsche oder in keine Section geschrieben !!}
181                 procedure DelSecEntry (Name : String);
182                 { Loescht eine Eintrag in der Aktuellen Section
183                   AktOpt wird nicht geaendert !!
184                   Achtung !! wurde keine Section gesetzt wird kein oder
185                   ein falscher Eintrag geloescht }
186                 procedure DelCurEntry (DelSec  : Boolean);
187                 { Loescht eine Eintrag in der Aktuellen Section
188                   AktOpt wird nicht geaendert !!
189                   Achtung !! wurde keine Section gesetzt wird kein oder
190                   ein falscher Eintrag geloescht }
191                 { ---------- interen Routinen --------- }
SearchSectionnull192                 function SearchSection (ASection : String;
193                                         Last : Boolean ) : boolean;
SearchOptionnull194                 function SearchOption  (AOption : String;
195                                         Last : Boolean ) : boolean;
196                 procedure ReadIni;
197                 procedure WriteIni;
198                 procedure DelIni;
199                 procedure ShowTree;
200               end;
201 
202 implementation
203 {****************************** externe Routinen ****************************}
204 { Die hier externen Routinen stammen aus meiner Toolbox welche ich aus alle
205   moeglichen Quellen zusammen gestellt habe. Hier die Routinen die fuer
206   IniFile Unit noetig sind. Quellen : PRUSSG,SWAG,TOOLBOX,DOS,FIDO...        }
207 
208 {Function FileExists(FileName: string; attr : Word) : Boolean;
209 var f: SearchRec;
210 begin
211   findfirst(Filename, attr, f);
212   if doserror = 0 then Fileexists := true else Fileexists := false;
213 end;}
214 
FillStrnull215 Function FillStr (AStr : String; Len : byte; Ch : Char) : String;
216 begin
217   if Length (AStr) > Len then Exit;
218   while Length (AStr) < Len do AStr := AStr + Ch;
219   FillStr := AStr;
220 end;
StrCutnull221 Function StrCut (AStr : String) : String;
222 begin
223   while (Length(AStr) - 1 >= 0) AND
224         (AStr [Length(AStr)] in [#32,#9]) DO
225     AStr[0] := chr(Length(AStr) - 1);
226   StrCut := AStr;
227 end;
CutStrnull228 Function CutStr (AStr : String) : String;
229 begin
230   while (Length(AStr) - 1 >= 0) AND
231         (AStr [1] in [#32,#9]) DO
232     AStr := Copy (AStr,2,Length(AStr) - 1);
233   CutStr := AStr;
234 end;
235 
UpStrnull236 Function UpStr(S1:String) : String;
237 {Asm Code replaced by Sascha Silbe}
238 Var
239   s:String;
240   i:Byte;
241 
242 Begin
243 s[0]:= S1[0];
244 For i:= 1 to Byte(s1[0]) do s[i]:= UpCase(s1[i]);
245 UpStr:= s;
246 End;
247 
KillSpcsnull248 Function KillSpcs(s: String): String; {kill leading and trailing spaces}
249  Begin
250  While (s[1] in [' ', #9]) and (Length(s) > 0) do Delete(s, 1, 1);
251  While (s[Length(s)] in [' ', #9]) and (Length(s) > 0) do Delete(s, Length(s), 1);
252  KillSpcs := s;
253  End;
254 
255 
256 {****************************** externe Routinen ****************************}
NewSecnull257 function NewSec : pSecRec;
258 var ASec : pSecRec;
259 begin
260   New (ASec);
261   ASec^.Name := '';
262   ASec^.Options := NIL;
263   ASec^.PrevSec := NIL;
264   ASec^.NextSec := NIL;
265   NewSec := ASec;
266 end;
NewOptnull267 function NewOpt : pOptRec;
268 var AOpt : pOptRec;
269 begin
270   New (AOpt);
271   AOpt^.Name := '';
272   AOpt^.Value := '';
273   AOpt^.Comment := '';
274   AOpt^.PrevOpt := NIL;
275   AOpt^.NextOpt := NIL;
276   NewOpt := AOpt;
277 end;
278 {****************************************************************************}
279 constructor IniObj.Init(AFileName: FileStr);
280 begin
281   FileName := AFileName;
282   Assign (f,FileName);
283   Sep := '=';
284   NotFound := '';
285   CommentPos := 0;
286   IniTree := NIL;
287   AktSec := NIL;
288   AktOpt := NIL;
289   ReadIni;
290 end;
291 destructor IniObj.Done;
292 begin
293   WriteIni;
294   DelIni;
295 end;
296 {****************************************************************************}
297 
298 procedure IniObj.SetSeperator(ASep : Char);
299 begin
300   Sep := ASep;
301 end;
302 procedure IniObj.SetNotFound(ANotFound: String);
303 begin
304   NotFound := ANotFound;
305 end;
306 procedure   IniObj.SetCommentPos(ACommentPos: Byte);
307 begin
308   CommentPos := ACommentPos;
309 end;
310 {****************************************************************************}
311 
312 procedure IniObj.ReadIni;
313 var ASec : pSecRec;
314     AOpt : pOptRec;
315     X,Y  : byte;
316     Str  : String;
317 begin
318  {$I-} Reset (F); {$I+}
319  If IOResult <> 0 then
320    Begin
321 {    WriteLn('ReadIni: Couldn''t open file!');}
322    Exit;
323    end;
324  while not Eof(F) do
325   Begin
326   ReadLn (f,Str);
327   While (Str[Length(Str)] in [#13, #10]) do Dec(Str[0]);
328   KillSpcs(Str);
329   if Length (Str) > 0 then
330    begin
331    if Str[1] = '[' then
332     begin
333     ASec := NewSec;
334     X := Pos (']',Str);
335     if X > 1 then Dec (X,2) else Dec(x, 1);
336     ASec^.Name := UpStr(KillSpcs(Copy (Str,2,X)));
337     if IniTree = NIL then IniTree := ASec;
338     if AktSec <> NIL then
339      Begin
340      AktSec^.NextSec := ASec;
341      ASec^.PrevSec := AktSec;
342      End;
343     AktSec := ASec;
344     end
345    else
346     begin
347     X := Pos (Sep,Str); Y := Pos (';',Str);
348     if (Y > 0) or (X > 0) then
349      begin
350      AOpt := NewOpt;
351      if Str[1] = ';' then
352       begin
353       AOpt^.Name := ';'; {Kommentarzeile}
354       AOpt^.Comment := Copy (Str,2,Length(Str)-1);
355       end
356      else
357       begin
358       If (x = 0) then
359        begin
360        AOpt^.Name := KillSpcs(Copy (Str, 1, y-1));
361        AOpt^.Value := '';
362        AOpt^.Comment := KillSpcs(Copy(Str, y+1, Length(str)-y));
363        end
364       else
365        begin
366        AOpt^.Name := KillSpcs(Copy (Str,1,X-1));
367        if Y <> 0 then
368         begin
369         AOpt^.Value := KillSpcs(Copy (Str,X+1,Y-X-1));
370         AOpt^.Comment := KillSpcs(Copy (Str,Y+1,Length(Str)-Y));
371         end
372        else
373         begin
374         AOpt^.Value := KillSpcs(Copy (Str,X+1,Length(Str)-X));
375         AOpt^.Comment := '';
376         end;
377        end;
378       end;
379      end
380     else
381      begin
382      AOpt^.Name := KillSpcs(Str);
383      AOpt^.Value := '';
384      AOpt^.Comment := '';
385      end;
386     if IniTree = NIL then
387      begin
388      IniTree := NewSec;
389      AktSec := IniTree;
390      end;
391     if AktSec^.Options = NIL then AktSec^.Options := AOpt
392     else AktOpt^.NextOpt := AOpt;
393     AOpt^.PrevOpt := AktOpt;
394     AktOpt := AOpt;
395     end;
396    end;
397   end;
398  Close (f);
399 end;
400 procedure IniObj.WriteIni;
401 const Comment : String = '';
402       OptVal  : String = '';
403 begin
404   if IniTree = Nil then Exit;
405 {  FileMode := 2;}
406   {$I-} Rewrite (f); {$I+}
407   If IOResult <> 0 then
408     Begin
409 {    WriteLn('WriteIni: Couldn''t open file!');}
410     Exit;
411     End;
412   AktSec := IniTree;
413   AktOpt := AktSec^.Options;
414   {$I-}
415   while AktSec <> NIL do begin
416     if AktSec^.Name <> '' then WriteLn (f,'['+UpStr(AktSec^.Name)+']');
417     while AktOpt <> NIL do begin
418       if AktOpt^.Name = ';' then WriteLn (f,AktOpt^.Name+AktOpt^.Comment)
419       else begin
420         OptVal :=  AktOpt^.Name+'='+ AktOpt^.Value;
421         if AktOpt^.Comment <> '' then begin
422           if CommentPos <> 0 then OptVal := FillStr (OptVal,CommentPos,' ');
423           Comment := '  ;'+AktOpt^.Comment
424         end;
425         WriteLn (f, OptVal + Comment);
426         Comment := ''; OptVal := '';
427       end;
428       AktOpt := AktOpt^.NextOpt;
429     end;
430     AktSec := AktSec^.NextSec;
431     If AktSec <> Nil then AktOpt := AktSec^.Options;
432   end;
433   If IOResult <> 0 then
434     Begin
435 {    WriteLn('WriteIni: Couldn''t write file!');}
436     End;
437 
438   Close (f); {$I-}
439   If IOResult <> 0 then
440     Begin
441 {    WriteLn('WriteIni: Couldn''t close file!');}
442     End;
443 end;
444 procedure IniObj.DelIni;
445 var ASec : pSecRec;
446     AOpt : pOptRec;
447     X    : byte;
448     Str  : String;
449 begin
450   if IniTree = Nil then Exit;
451   AktSec := IniTree;
452   AktOpt := AktSec^.Options;
453   while AktSec <> NIL do begin
454     ASec := AktSec^.NextSec;
455     if AktSec <> NIl then Dispose (AktSec);
456     while AktOpt <> NIL do begin
457       AOpt := AktOpt^.NextOpt;
458       if AktOpt <> NIL then Dispose (AktOpt);
459       AktOpt := AOpt;
460     end;
461     AktSec := ASec;
462     If AktSec <> Nil then AktOpt := AktSec^.Options;
463   end;
464 end;
465 {****************************************************************************}
IniObj.SearchSectionnull466 function IniObj.SearchSection (ASection : String;
467                                Last     : boolean ) : boolean;
468 { Suchen nach der Section. Ist Last auf true wird AktSec auf die Section
469   vor der gesuchten gesetzt, ist Last auf false wird AktSec auf die
470   gesuchte Section gesetzt, oder bei nicht finden auf die letzte der
471   Verkettung. Wird eine Section gefunde ist der Rueckgabewert der Function
472   true, ansonsten false.
473   Zur beschleunigung der Suche : ist AktSec schon das gesuchte wird nicht
474   gesucht, wie z.B. durch WriteSecEntry }
475 var Found : boolean;
476     ASec  : pSecRec;
477 begin
478   if IniTree = Nil then begin
479     SearchSection := false;
480     Exit;
481   end;
482   found := false;
483   ASection := UpStr (ASection);
484   ASec := NIL;
485   If AktSec = NIL then AktSec := IniTree
486   Else
487     if (AktSec^.Name = ASection) and (not Last) then Found := true
488     else AktSec := IniTree;
489   while (AktSec <> NIL) and (not Found) do begin
490     if ASection = AktSec^.Name then Found := true
491     Else
492      begin
493       ASec := AktSec;
494       AktSec := AktSec^.NextSec;
495      end;
496   end;
497   if Last or (AktSec = NIL) then AktSec := ASec;
498   SearchSection := found;
499 end;
IniObj.SearchOptionnull500 function IniObj.SearchOption (AOption : String;
501                               Last    : boolean) : boolean;
502 { Suchen nach der Option. Ist Last auf true wird AktOpt auf die Option
503   vor der gesuchten gesetzt, ist Last auf false wird AktOpt auf die
504   gesuchte Option gesetzt, oder bei nicht finden auf die letzte der
505   Verkettung, Nur wenn gar keine Verkettung unter dieser Section
506   existiert wird AktOpt auf NIL gesetzt. Aber dann ist auch der
507   rueckgabewert der Function false
508   Zur beschleunigung der Suche : ist AktOpt schon das gesuchte wird nicht
509   gesucht}
510 
511 var Found : boolean;
512     AOpt  : pOptRec;
513 begin
514   if IniTree = Nil then begin
515     SearchOption := false;
516     Exit;
517   end;
518   found := false;
519   AOpt := NIL;
520   if (UpStr(AktOpt^.Name) = UpStr(AOption)) and (not Last) then Found := true
521   else AktOpt := AktSec^.Options;
522   while (AktOpt <> NIL) and (not Found) do begin
523     if UpStr(AOption) = UpStr(AktOpt^.Name) then Found := true;
524     if not Found then begin
525       AOpt :=  AktOpt;
526       AktOpt := AktOpt^.NextOpt;
527     end;
528   end;
529   If Last or (AktOpt = NIL) then AktOpt := AOpt;
530   SearchOption := found;
531 end;
532 {****************************************************************************}
IniObj.ReadEntrynull533 function IniObj.ReadEntry ( Section : String; Option  : String ) : String;
534 Var Value : String;
535 begin
536   if SearchSection (Section,false) then
537     if SearchOption (Option,false) then
538      Value := AktOpt^.Value
539     else Value := NotFound;
540   ReadEntry := Value;
541 end;
542 procedure IniObj.WriteEntry(Section, Option, Value, Comment: String);
543 begin
544   if SearchSection (Section,false) then
545     if SearchOption (Option,false) then
546      begin
547        AktOpt^.Value := Value;
548        if Comment <> '' then AktOpt^.Comment := Comment;
549      end else
550      begin
551        AktOpt^.NextOpt := NewOpt;
552        AktOpt := AktOpt^.NextOpt;
553        AktOpt^.Name := Option;
554        AktOpt^.Value := Value;
555        AktOpt^.Comment := Comment;
556      end
557   else begin
558     if IniTree = NIL then begin
559       IniTree := NewSec;
560       AktSec := IniTree;
561     end else begin
562       AktSec^.NextSec := NewSec;
563       AktSec := AktSec^.NextSec;
564     end;
565     AktSec^.Name := UpStr(Section);
566     AktSec^.Options := NewOpt;
567     AktOpt := AktSec^.Options;
568     AktOpt^.Name := Option;
569     AktOpt^.Value := Value;
570     AktOpt^.Comment := Comment;
571   end;
572 end;
573 procedure IniObj.AddEntry(Section, Option, Value, Comment: String);
574 Var
575   AOpt : pOptRec;
576 begin
577   if SearchSection (Section,false) then
578      begin
579        AOpt := AktOpt;
580        While (AOpt^.NextOpt <> Nil) do AOpt := AOpt^.NextOpt;
581        AOpt^.NextOpt := NewOpt;
582        AOpt^.PrevOpt := AktOpt;
583        AOpt := AOpt^.NextOpt;
584        AOpt^.Name := Option;
585        AOpt^.Value := Value;
586        AOpt^.Comment := Comment;
587      end
588   else begin
589     if IniTree = NIL then begin
590       IniTree := NewSec;
591       AktSec := IniTree;
592     end else begin
593       AktSec^.NextSec := NewSec;
594       AktSec^.NextSec^.PrevSec := AktSec;
595       AktSec := AktSec^.NextSec;
596     end;
597     AktSec^.Name := UpStr(Section);
598     AktSec^.Options := NewOpt;
599     AktOpt := AktSec^.Options;
600     AktOpt^.Name := Option;
601     AktOpt^.Value := Value;
602     AktOpt^.Comment := Comment;
603   end;
604 end;
605 procedure IniObj.InsertEntry(Section, Option, Value, Comment: String);
606 Var
607   AOpt : pOptRec;
608 begin
609   if SearchSection (Section,false) then
610      begin
611        AOpt := AktOpt^.NextOpt;
612        AktOpt^.NextOpt := NewOpt;
613        AktOpt^.NextOpt^.PrevOpt := AktOpt;
614        AktOpt^.NextOpt^.NextOpt := AOpt;
615        AOpt := AktOpt^.NextOpt;
616        AOpt^.Name := Option;
617        AOpt^.Value := Value;
618        AOpt^.Comment := Comment;
619      end
620   else begin
621     if IniTree = NIL then begin
622       IniTree := NewSec;
623       AktSec := IniTree;
624     end else begin
625       AktSec^.NextSec := NewSec;
626       AktSec^.NextSec^.PrevSec := AktSec;
627       AktSec := AktSec^.NextSec;
628     end;
629     AktSec^.Name := UpStr(Section);
630     AktSec^.Options := NewOpt;
631     AktOpt := AktSec^.Options;
632     AktOpt^.Name := Option;
633     AktOpt^.Value := Value;
634     AktOpt^.Comment := Comment;
635   end;
636 end;
637 procedure IniObj.DelEntry ( Section : String;
638                             Option  : String;
639                             DelSec  : Boolean );
640 var AOpt : pOptRec;
641     ASec : pSecRec;
642 begin
643   if SearchSection (Section,false) then
644     if SearchOption (Option,false) then begin
645       AOpt := AktOpt;
646       SearchOption (Option,true);
647       if AktOpt <> NIL then
648         Begin
649         AktOpt^.NextOpt := AOpt^.NextOpt;
650         AOpt^.NextOpt^.PrevOpt := AktOpt;
651         End
652       else
653         Begin
654         AktSec^.Options := AOpt^.NextOpt;
655         AOpt^.NextOpt^.PrevOpt := Nil;
656         End;
657       if AOpt <> NIL then Dispose (AOpt);
658       if (AktSec^.Options = NIL) and DelSec then begin
659         ASec := AktSec;
660         SearchSection (Section,true);
661         if AktSec <> NIL then
662           Begin
663           AktSec^.NextSec := ASec^.NextSec;
664           ASec^.NextSec^.PrevSec := AktSec;
665           End
666         else
667           Begin
668           IniTree := ASec^.NextSec;
669           ASec^.NextSec^.PrevSec := Nil;
670           End;
671         if ASec <> NIL then Dispose (ASec);
672       end;
673     end;
674 end;
675 {****************************************************************************}
676 procedure IniObj.ShowTree;
677 const Comment : String = '';
678       OptVal  : String = '';
679 begin
680   if IniTree = Nil then Exit;
681   AktSec := IniTree;
682   AktOpt := AktSec^.Options;
683   while AktSec <> NIL do begin
684     if AktSec^.Name <> '' then WriteLn ('['+AktSec^.Name+']');
685     while AktOpt <> NIL do begin
686       if AktOpt^.Name = ';' then WriteLn (AktOpt^.Name+AktOpt^.Comment)
687       else begin
688         OptVal :=  AktOpt^.Name+'='+ AktOpt^.Value;
689         if AktOpt^.Comment <> '' then begin
690           if CommentPos <> 0 then OptVal := FillStr (OptVal,CommentPos,' ');
691           Comment := '  ;'+AktOpt^.Comment
692         end;
693         WriteLn (OptVal + Comment);
694         Comment := ''; OptVal := '';
695       end;
696       AktOpt := AktOpt^.NextOpt;
697     end;
698     AktSec := AktSec^.NextSec;
699     If (AktSec <> Nil) then AktOpt := AktSec^.Options;
700   end;
701 end;
702 
703 {****************************************************************************}
IniObj.SetPrevOptnull704 function IniObj.SetPrevOpt : boolean;
705 
706 begin
707   if AktOpt^.PrevOpt <> Nil then begin
708     repeat AktOpt := AktOpt^.PrevOpt
709     until (AktOpt^.Name <> ';') or (AktOpt^.PrevOpt = Nil);
710     if (AktOpt^.PrevOpt = Nil) and ( AktOpt^.Name = ';')
711      then SetPrevOpt := false else SetPrevOpt := true;
712   end else SetPrevOpt := false;
713 end;
IniObj.SetNextOptnull714 function IniObj.SetNextOpt : boolean;
715 
716 begin
717   if AktOpt^.NextOpt <> Nil then begin
718     repeat AktOpt := AktOpt^.NextOpt
719     until (AktOpt^.Name <> ';') or (AktOpt^.NextOpt = Nil);
720     if (AktOpt^.NextOpt = Nil) and ( AktOpt^.Name = ';')
721      then SetNextOpt := false else SetNextOpt := true;
722   end else SetNextOpt := false;
723 end;
IniObj.GetSecNumnull724 function IniObj.GetSecNum (Section : String ) : integer;
725 var Num  : Integer;
726     AOpt : pOptRec;
727 begin
728   Num := 0;
729   AOpt := AktOpt;
730   if SearchSection (Section,false) then begin
731     AktOpt := AktSec^.Options;
732     while AktOpt <> NIL do begin
733       if AktOpt^.Name[1] <> ';' then Inc (Num);
734       if not SetNextOpt then AktOpt := NIL;
735     end;
736   end;
737   AktOpt := AOpt;
738   GetSecNum := Num;
739 end;
740 procedure IniObj.SetSection (Section : String);
741 begin
742   if not SearchSection (Section,false) then begin
743     WriteEntry (Section,';','','');
744   end;
745   ReSetSec;
746 end;
747 procedure IniObj.ReSetSec;
748 begin
749   AktOpt := AktSec^.Options;
750   if AktOpt^.Name = ';' then SetNextOpt;
751 end;
IniObj.ReSecEnNamenull752 function IniObj.ReSecEnName  : String;
753 begin
754   if AktOpt = NIL then begin
755     ReSecEnName := NotFound;
756     Exit;
757   end;
758   ReSecEnName := AktOpt^.Name;
759 end;
760 
IniObj.ReSecEnValuenull761 function IniObj.ReSecEnValue : String;
762 begin
763   if AktOpt = NIL then begin
764     ReSecEnValue := NotFound;
765     Exit;
766   end;
767   ReSecEnValue := AktOpt^.Value;
768 end;
IniObj.SearchSecEntrynull769 function  IniObj.SearchSecEntry (Name : String ) : String;
770 var AOpt : pOptRec;
771 begin
772   if AktSec = NIL then begin
773     SearchSecEntry := NotFound;
774     Exit;
775   end;
776   AOpt := AktOpt;
777   SearchSecEntry := ReadEntry (AktSec^.Name,Name);
778   AktOpt := AOpt;
779 end;
780 procedure IniObj.WriteSecEntry (Name    : String;
781                                 Value   : String;
782                                 Comment : String);
783 var AOpt : pOptRec;
784 begin
785   if AktSec = NIL then Exit;
786   AOpt := AktOpt;
787   WriteEntry (AktSec^.Name,Name,Value,'');
788   AktOpt := AOpt;
789 end;
790 procedure IniObj.AddSecEntry (Name    : String;
791                               Value   : String;
792                               Comment : String);
793 var AOpt : pOptRec;
794 begin
795   if AktSec = NIL then Exit;
796   AOpt := AktOpt;
797   AddEntry (AktSec^.Name,Name,Value,'');
798   AktOpt := AOpt;
799 end;
800 procedure IniObj.InsertSecEntry (Name    : String;
801                                  Value   : String;
802                                  Comment : String);
803 var AOpt : pOptRec;
804 begin
805   if AktSec = NIL then Exit;
806   AOpt := AktOpt;
807   InsertEntry (AktSec^.Name,Name,Value,'');
808   AktOpt := AOpt;
809 end;
810 procedure IniObj.DelSecEntry (Name  : String);
811 var AOpt : pOptRec;
812 begin
813   if AktSec = NIL then Exit;
814   AOpt := AktOpt;
815   DelEntry (AktSec^.Name,Name,false);
816   AktOpt := AOpt;
817 end;
818 procedure IniObj.DelCurEntry(DelSec: Boolean);
819 var AOpt : pOptRec;
820     ASec : pSecRec;
821 begin
822 If (AktOpt = Nil) then Exit
823 Else If (AktOpt = AktSec^.Options) Then
824   Begin
825   AktSec^.Options := AktOpt^.NextOpt;
826   AktOpt^.NextOpt^.PrevOpt := Nil;
827   End
828 Else If (AktOpt^.NextOpt = Nil) then
829   Begin
830   AktOpt^.PrevOpt^.NextOpt := Nil;
831   End
832 Else
833   Begin
834   AktOpt^.PrevOpt^.NextOpt := AktOpt^.NextOpt;
835   AktOpt^.NextOpt^.PrevOpt := AktOpt^.PrevOpt;
836   End;
837 Dispose(AktOpt);
838 if (AktSec^.Options = NIL) and DelSec then
839   Begin
840   If (AktSec^.PrevSec = Nil) then
841     Begin
842     If (AktSec^.NextSec <> Nil) then
843       Begin
844       AktSec^.NextSec^.PrevSec := Nil;
845       IniTree := AktSec^.NextSec;
846       End
847     Else IniTree := Nil;
848     End
849   Else
850     Begin
851     AktSec^.PrevSec^.NextSec := AktSec^.NextSec;
852     AktSec^.NextSec^.PrevSec := AktSec^.PrevSec;
853     End;
854   Dispose(AktSec);
855   End;
856 end;
857 
858 {****************************************************************************}
859 end.
860 { Externale Prceduren,Functionen & Typen
861   FileStr (T) : String Type fuer Dateiname. (String[12])
862   UpStr   (F) : wandelt alle Buchstaben in Grossbuchstaben.
863   CutStr  (F) : Schneidet vorstehende Leerzeichen und Tabs ab.
864   StrCut  (F) : Schneidet nachstehende Leerzeichen und Tabs ab.
865   FillStr (F) : Fuellt eine String mit eine Zeichen auf eine bestimmte Laenge
866   FileExists (F) : Prueft ob eine Datei Existiert (siehe Online Hilfe) mit
867                    Attribut ueberpruefung
868 }
869 {05.04.96 : Begin mit der Grundstruktur, Aufbau eines Objectes, Bug Fixes
870  06.04.96 : BugFixes, Implementierung Comments,SetCommentPos,NotFound,
871             DelEntry, ausgiebiger Test der Unit
872  07.04.96 : DataBase Routinen (Tested)
873 
874 Sascha Silbe:
875  12.07.98 : fixed some bugs
876 }
877