inifile

At the time of Win3.0 it became standard to have settings in inifiles.
The following unit implements them.
{
	IniFile - creates and reads and updates an inifile
	--------------------------------------------------
	The inifile is an editable textfile. It is
	line oriented.
	The structure of an ini file is as follows :
	
	There are regions marked with square brackets as :
	[region]
	
	Each region may has several subitems with parameters
	and comments as :
	[region]
	subitem1=xxxxx	;comment
	subitem2=xxxxx	;comment
	..
	
	A comment is defined by a semicolon :
	There are also comments allowed on a single line :
	
	; comment
	
	Due to a definition 'DescriptorSize' in this unit,
	The maximum size of all items is currently restriced to
	32 bytes. These are
	-region names
	-line comments
	-subitem names
	-subitem parameters
	-subitem comments
	--------------------------------------------------
	The internal structure :
	The given file is read/written to/from a tree structure.
	The usual sequence is :
	
	VAR k:IniFileTyp;
	
	< init >
	k.init(filename);
	IF (k.DetectFile) THEN k.ReadFile
	ELSE < build file with 'k.Append' with default values >
	
	< get parameters >
	parameter:=k.GetParameter(region,subitem);
	
	< set parameters >
	IF NOT k.SetParameter(region,subitem,parameter) THEN Error
	
	< add new subitems >
	k.Append(region,subitem,parameter,comment);
	
	< end >
	< if changed > k.WriteFile;
	k.Done;
	--------------------------------------------------
	Created		:9/mar/94
	Last Update	:
	
	Is updated version of :
}
UNIT IniFile;

INTERFACE

USES OPRoot,OPString;

CONST DescriptorSize=32;

TYPE
 Descriptor=STRING[DescriptorSize];
 
 LineNodePtr=^LineNode;
 LineNode=OBJECT(SingleListNode)
  Desc:Descriptor;
  Line:INTEGER;
  CONSTRUCTOR Init(D:Descriptor;L:INTEGER);
  DESTRUCTOR Done;VIRTUAL;
 END;
 
 SubItemNodePtr=^SubItemNode;
 SubItemNode=OBJECT(LineNode)
  Para:Descriptor;
  Cmt:Descriptor;
  CONSTRUCTOR Init(D,P,C:Descriptor;L:INTEGER);
  DESTRUCTOR Done;VIRTUAL;
 END;

 RegionNodePtr=^RegionNode;
 RegionNode=OBJECT(LineNode)
  SubItem:SingleList;
  CONSTRUCTOR Init(D:Descriptor;L:INTEGER);
  PROCEDURE AddSubItem(D,P,C:Descriptor;L:INTEGER);
  FUNCTION GetSubItemName(i:INTEGER):Descriptor;
  FUNCTION GetHighestLine:INTEGER;
  FUNCTION GetMaxLineSize:BYTE; { keyword+'='+para }
  DESTRUCTOR Done;VIRTUAL;
 END;

 IniFilePtr=^IniFileTyp;
 IniFileTyp=OBJECT
  Header:SingleList;
  Regions:SingleList;
  Filename:Descriptor;
  CONSTRUCTOR Init(F:Descriptor);
  DESTRUCTOR Done;
  FUNCTION DetectFile:BOOLEAN;
  PROCEDURE ReadFile;
  PROCEDURE WriteFile;
  FUNCTION GetParameter(r,s:Descriptor):Descriptor;
  FUNCTION SetParameter(r,s,d:Descriptor):BOOLEAN;
  FUNCTION GetSubItemName(i:INTEGER;r:Descriptor):Descriptor;
  PROCEDURE Append(r,s,d,c:Descriptor);
  { internal stuff }
  FUNCTION FindRegion(r:Descriptor):RegionNodePtr;
  FUNCTION FindSubItem(r:RegionNodePtr;s:Descriptor):SubItemNodePtr;
  PROCEDURE IncLinesHigher(x:INTEGER);
 END;

IMPLEMENTATION
{------------ LineNode ---------------------------------}
CONSTRUCTOR LineNode.Init(D:Descriptor;L:INTEGER);
BEGIN
 SingleListNode.Init;
 Desc:=D;
 Line:=L;
END;
DESTRUCTOR LineNode.Done;
BEGIN
 SingleListNode.Done;
END;
{-------------- SubItemNode ----------------------------}
CONSTRUCTOR SubItemNode.Init(D,P,C:Descriptor;L:INTEGER);
BEGIN
 LineNode.Init(D,L);
 Para:=P;
 Cmt:=C;
END;
DESTRUCTOR SubItemNode.Done;
BEGIN
 LineNode.Done;
END;
{------------- RegionNode -----------------------------}
CONSTRUCTOR RegionNode.Init(D:Descriptor;L:INTEGER);
BEGIN
 LineNode.Init(D,L);
 SubItem.Init;
END;
PROCEDURE RegionNode.AddSubItem(D,P,C:Descriptor;L:INTEGER);
VAR q:SubItemNodePtr;
BEGIN
 q:=New(SubItemNodePtr,Init(D,P,C,L));
 SubItem.Append(q);
END;
FUNCTION RegionNode.GetSubItemName(i:INTEGER):Descriptor;
VAR q:SubItemNodePtr;
 j:INTEGER;
BEGIN
 q:=SubItemNodePtr(SubItem.Head);j:=1;
 WHILE (jNIL) DO q:=SubItemNodePtr(SubItem.Next(q));
 IF (q<>NIL) THEN GetSubItemName:=q^.Desc
 ELSE GetSubItemName:='';
END;
DESTRUCTOR RegionNode.Done;
BEGIN
 SubItem.Done;
 LineNode.Done;
END;
FUNCTION RegionNode.GetHighestLine:INTEGER;
VAR z:INTEGER;
 s:SubItemNodePtr;
BEGIN
 z:=0;
 s:=SubItemNodePtr(SubItem.Head);
 WHILE (s<>NIL) DO
  BEGIN
   IF (s^.Line>z) THEN z:=s^.Line;
   s:=SubItemNodePtr(SubItem.Next(s));
  END;
 GetHighestLine:=z;
END;
FUNCTION RegionNode.GetMaxLineSize:BYTE;
VAR z,x:BYTE;
 s:SubItemNodePtr;
BEGIN
 z:=0;
 s:=SubItemNodePtr(SubItem.Head);
 WHILE (s<>NIL) DO
  BEGIN
   x:=length(s^.Desc)+length(s^.Para)+1;
   IF (x>z) THEN z:=x;
   s:=SubItemNodePtr(SubItem.Next(s));
  END;
 GetMaxLineSize:=z;
END;

{------------- IniFileTyp -----------------------------}
CONSTRUCTOR IniFileTyp.Init(F:Descriptor);
BEGIN
 Header.Init;
 Regions.Init;
 Filename:=F;
END;
DESTRUCTOR IniFileTyp.Done;
BEGIN
 Header.Done;
 Regions.Done;
END;
FUNCTION IniFileTyp.DetectFile:BOOLEAN;
VAR F:FILE;
BEGIN
 Assign(f,Filename);
 {$i-}
 Reset(f);
 {$I+}
 IF (IOResult=0) THEN
  BEGIN
   DetectFile:=TRUE;
   Close(f);
  END
 ELSE DetectFile:=FALSE;
END;

PROCEDURE IniFileTyp.ReadFile;
VAR F:TEXT;
 s:STRING;
 d,e,g:Descriptor;
 l:LineNodePtr;
 r:RegionNodePtr;
 i:SubItemNodePtr;
 linenr:INTEGER;
 z,u:BYTE;
BEGIN
 Assign(f,Filename);
 Reset(f);
 linenr:=0;r:=NIL;
 WHILE (Not EOF(f)) DO
  BEGIN
   ReadLn(f,s);
   inc(linenr);
   IF (s='') THEN
    BEGIN
     d:='';
     l:=New(LineNodePtr,Init(d,LineNr));
     Header.Append(l);
    END;
   IF (s<>'')AND(s[1]=';') THEN
    BEGIN
     d:=Copy(s,1,DescriptorSize);
     l:=New(LineNodePtr,Init(d,LineNr));
     Header.Append(l);
    END;
   IF (s<>'')AND(s[1]<>';') THEN
    BEGIN
     IF (s[1]='[') THEN  { new region }
      BEGIN
       z:=Pos(']',s);
       d:=Copy(s,2,z-2);
       r:=New(RegionNodePtr,Init(D,linenr));
       Regions.Append(r);
      END { new region }
     ELSE
      BEGIN { new subitem }
       z:=Pos('=',s);
       d:=Copy(s,1,z-1);
       u:=pos(';',s);
       g:='';
       IF (u=0) THEN e:=Copy(s,z+1,DescriptorSize)
       ELSE
	BEGIN
	 e:=Copy(s,z+1,u-z-1);
	 g:=Copy(s,u+1,DescriptorSize);
	END;
       e:=Trim(e);
       i:=New(SubItemNodePtr,Init(d,e,g,linenr));
       IF (r<>NIL) THEN
	BEGIN
	 r^.Subitem.Append(i);
	END
      END; { new subitem }
    END; { s<>'')AND(s[1]<>';' }
  END; { Not EOF(f) }
 Close(f);
END;
PROCEDURE IniFileTyp.WriteFile;
VAR f:TEXT;
 s:STRING;
 d,e,g:Descriptor;
 l:LineNodePtr;
 r:RegionNodePtr;
 i:SubItemNodePtr;
 linenr,kz:INTEGER;
 z,x:BYTE;
 quit:BOOLEAN;
BEGIN
 Assign(f,FileName);
 Rewrite(f);
 { get longest line }
 r:=RegionNodePtr(Regions.Head);
 z:=0;
 WHILE (r<>NIL) DO
  BEGIN
   x:=r^.GetMaxLineSize;
   IF (x>z) THEN z:=x;
   r:=RegionNodePtr(Regions.Next(r));
  END;
 lineNr:=1;
 l:=LineNodePtr(Header.Head);
 r:=RegionNodePtr(Regions.Head);
 IF (r<>NIL) THEN i:=SubItemNodePtr(r^.SubItem.Head)
 ELSE i:=NIL;
{ IF (l<>NIL) THEN kz:=l^.line ELSE kz:=0;}
{ quit:=FALSE;}
 REPEAT
  IF (l<>NIL)AND(l^.line=lineNr) THEN { insert commentline }
   BEGIN
    IF (l^.desc='')THEN Writeln(f)
    ELSE Writeln(f,l^.desc);
    l:=LineNodePtr(Header.Next(l));
    IF (l<>NIL) THEN kz:=l^.line ELSE kz:=0;
   END;
  IF (r<>NIL)AND(r^.line=linenr) THEN
   BEGIN
    Writeln(f,'[',r^.desc,']');
    IF (i=NIL) THEN
     BEGIN
      r:=RegionNodePtr(Regions.Next(r));
      IF (r=NIL) THEN i:=NIL
      ELSE i:=SubItemNodePtr(r^.SubItem.Head);
     END;
   END;
  IF (i<>NIL)AND(i^.line=linenr) THEN
   BEGIN
    s:=i^.desc+'='+i^.para;
    IF (i^.cmt<>'') THEN
     BEGIN
      s:=pad(s,z+4);
      s:=s+';'+i^.cmt;
     END;
    Writeln(f,s);
    i:=SubItemNodePtr(r^.SubItem.Next(i));
    IF (i=NIL) THEN
     BEGIN
      r:=RegionNodePtr(Regions.Next(r));
      IF (r<>NIL) THEN i:=SubItemNodePtr(r^.SubItem.Head);
     END;
   END;
  Inc(LineNr);
 UNTIL (r=NIL)AND(i=NIL)AND(l=NIL);
 Close(f);
END;
FUNCTION IniFileTyp.FindRegion(r:Descriptor):RegionNodePtr;
VAR p:RegionNodePtr;
BEGIN
 p:=RegionNodePtr(Regions.Head);
 WHILE (p<>NIL)AND(p^.Desc<>r)DO p:=RegionNodePtr(Regions.Next(p));
 FindRegion:=p;
END;

FUNCTION IniFileTyp.FindSubItem(r:RegionNodePtr;s:Descriptor):SubItemNodePtr;
VAR p:SubItemNodePtr;
BEGIN
 p:=SubItemNodePtr(r^.SubItem.Head);
 WHILE (p<>NIL)AND(p^.Desc<>s)DO p:=SubItemNodePtr(r^.SubItem.Next(p));
 FindSubItem:=p;
END;
PROCEDURE IniFileTyp.IncLinesHigher(x:INTEGER);
VAR l:LineNodePtr;
    r:RegionNodePtr;
    s:SubItemNodePtr;

BEGIN
 l:=LineNodePtr(Header.Head);
 WHILE (l<>NIL) DO
  BEGIN
   IF (l^.Line>=x) THEN inc(l^.line);
   l:=LineNodePtr(Header.Next(l));
  END;
 r:=RegionNodePtr(Regions.Head);
 WHILE (r<>NIL) DO
  BEGIN
   IF (r^.Line>=x) THEN inc(r^.Line);
   s:=SubItemNodePtr(r^.SubItem.Head);
   WHILE (s<>NIL) DO
    BEGIN
     IF (s^.Line>=x) THEN inc(s^.Line);
     s:=SubItemNodePtr(r^.SubItem.Next(s));
    END;
   r:=RegionNodePtr(Regions.Next(r));
  END;
END;
FUNCTION IniFileTyp.GetParameter(r,s:Descriptor):Descriptor;
VAR p:RegionNodePtr;
    q:SubItemNodePtr;
    d:Descriptor;
BEGIN
 d:='';
 p:=FindRegion(r);
 IF (p<>NIL) THEN
  BEGIN
   q:=FindSubItem(p,s);
   IF (q<>NIL) THEN d:=q^.para;
  END;
 GetParameter:=d;
END;
FUNCTION IniFileTyp.SetParameter(r,s,d:Descriptor):BOOLEAN;
VAR p:RegionNodePtr;
    q:SubItemNodePtr;
    ok:BOOLEAN;
BEGIN
 ok:=FALSE;
 p:=FindRegion(r);
 IF (p<>NIL) THEN
  BEGIN
   q:=FindSubItem(p,s);
   IF (q<>NIL) THEN
    BEGIN
     q^.para:=d;
     ok:=TRUE;
    END;
  END;
 SetParameter:=ok;
END;
FUNCTION IniFileTyp.GetSubItemName(i:INTEGER;r:Descriptor):Descriptor;
VAR p,pa:RegionNodePtr;
    q:SubItemNodePtr;
    z:INTEGER;
BEGIN
 p:=FindRegion(r);
 IF (p=NIL) THEN GetSubItemName:=''
 ELSE GetSubItemName:=p^.GetSubItemName(i);
END;
  

PROCEDURE IniFileTyp.Append(r,s,d,c:Descriptor);
VAR p,pa:RegionNodePtr;
    q:SubItemNodePtr;
    z:INTEGER;
BEGIN
 p:=FindRegion(r);
 IF (p=NIL) THEN
  BEGIN
   p:=RegionNodePtr(Regions.Head);pa:=p;
   WHILE (pa<>NIL) DO
    BEGIN
     pa:=RegionNodePtr(Regions.Next(p));
     IF (pa<>NIL) THEN p:=pa;
    END;
   z:=0;
   IF (p<>NIL) THEN z:=p^.GetHighestLine;
   IncLinesHigher(z+1);
   p:=New(RegionNodePtr,Init(r,z+1));
   Regions.Append(p);
  END;
 q:=FindSubItem(p,s);
 IF (q=NIL) THEN
  BEGIN
   z:=p^.GetHighestLine;
   IF (z=0) THEN z:=p^.line;
   IncLinesHigher(z+1);
   q:=New(SubItemNodePtr,Init(s,d,c,z+1));
   p^.SubItem.Append(q);
  END;
END;


BEGIN	{ autoinit of this unit}

END.	{ unit }

a sample app

PROGRAM inifiletest;

USES IniFile;

VAR 
 k:IniFileTyp;
 
BEGIN
 k.Init('TEST.INI');
 IF (k.DetectFile) THEN
  BEGIN
   k.ReadFile;
  END;
{ K.Append('keyboard','mist','1','');}
 IF k.SetParameter('keyboard','language.dll','English') THEN ;
 K.WriteFile;
 k.done;
END.


home

last updated: 29.nov.99

Copyright (99,2000) Ing.Büro R.Tschaggelar