unit confu;{config SYS.DLL}
{$I define.pas}

{general methods for reading and writing of program.ini files}
{ at the beggining of program.init before (inherited init) call config.init
  (calls: FindName to find config, Open to read in .ini texts, and Read
  to init data segment (global) variables, Read also sends message to
  App to ask for reading config, but at the start App is nil)

  All objects should call Config^.ReadWriteValue for its variables during
  their init and done

  Use proc AddGlobalVar in unit's init sections to add
  global vars (DEFINE NEWCONFIG must be then used)

  All .ini texts will be red in memory and data segment (global) variables get
  read in automatically when defined in TRecordArray type variable
  passed as config.init parameter

  Override procedures:
    SetDefault (to define default values) and
    SetOptions to process command line parameters
    AfterRead to make adjustments according to values obtained by above methods

  During initialization of heap objects, call config.readvalue
  for objects's variables with appropriate params, if corresponding
  section will not be found, variables stay unchanged

  at the end of program.init call config.close, to dispose .ini texts
  if you want.

  respond to :
    cmWriteConfig = cmBroadcastUser + 8;
    cmReadConfig = cmBroadcastUser + 8;
  if you want to save values to config on SaveConfigTo command

  At the beginning of program.done section  call config.open - texts get
  read in memory, but don't overwrite variables, than call in objects
  done methods config.writevalue, at the end of program.done call
  config.done (data segment vars get written, alse message to app is send,
  but that is nil again), that calls Write if global variable SaveConfigOnExit
  is true.
}

interface
uses
  MyType, TVType, Stru, MyLib, Fileu, IOType, IOu{streams}, {dates,}
  BinHex, {v0.48}UtlType{/v0.48}, ConfType, ListType, Listu
  {$IFDEF WINDOWS}
  ,SysUtils
  {$ENDIF}
  ;

const
  MaxVarsCount = 250;

  StreamBufferSize = 1024;
  ValueDelimitor = '=';
  VarNameLen = 20;
  VarValueStrLen = 80;
(*
  rwRead = 0;  {direction constants for statusobjects' methods ReadWriteConfig}
  rwWrite = 1;
  rwWriteFlush = 2;
*)
  IniTempExt = '.I$$';
  IniExt = '.INI';

  LastDateTime : longint = 0;{TDateTimeString = '';}
  SaveConfigOnExit : boolean = true;

type
  PVarName = ^TVarName;
  TVarName = string[VarNameLen];
  TVarValueStr = string[VarValueStrLen];
  TConfigLine = string[VarNameLen + VarValueStrLen + 1];

  {for data segment (global) variables:}
  TVarRecord = record
    VarName: TVarName;
    Adr: Pointer; {address of the variable, if adr=nil, default to 0 or #0 or ''}
    Typ: TPropertyType;
  end;

  {typ: b:byte,boolean(0,1)
        i:integer;
        w:word;
        l:longint;
        r:real;
        s:string;
        c:char;
        p:pchar (0 terminated string)
        m:pointer; (pointer to method called)
        0:no type, for section names
  }

  {procs for converting of string to more then one value:}
  TConfigProc = procedure (var st:TConfigLine; ReadWrite: Boolean);

  PVarRecordArray = ^TVarRecordArray;
  TVarRecordArray = array[0..pred(MaxVarsCount)] of TVarRecord;

{assign by exported proc  ConfigSetGlobals by first unit in program}
const
  GlobalsArray :PVarRecordArray = nil;{set to @GlobalConfigVars in unit init section}
  MaxGlobals:integer = MaxVarsCount;


type

  {PConfig = TConfig;}
  TConfig = class(TObject)
    Vars: PVarRecordArray;
    Name: string;
    Stream: THIO;{PQuietMemStream} {reads all text to memory as block, then
      parses to strings, ignore StreamError variable => don't popup error boxes}
    Lines: TLst;{collection of inistring collections}
    Found: boolean;{was the config file found at the program start?}
    Status: integer;
    Changed:boolean;
    OpenCounter:integer;

    constructor Create(AVars: PVarRecordArray; AMaxGlobals:integer);
    {calls SetDefault, SetOptions, FindName, Open, Read}
    procedure SetDefault; {virtual;} {called before Read, does nothing here}
    procedure AfterRead; {virtual;} {called after Read, whether file found or not, makes
      necessary adjustents of values obtained from read or as defaults}
    procedure SetOptions; {virtual; }{overide to change default values according to
                                  command line options}
    procedure BeforeWrite;{virtual;}{called inside write just before calling
      real writing, adjust whatever is necessary before saving (conversion or whatever)}

{!!!THE MAIN FUNCTIONS for retrieving and saving variables:}
    procedure ReadWriteValue(what:{v0.48}TReadWrite{/v0.48 byte}; ASection, AVarName: TVarName; var AValue; typ:TPropertyType);
      {... calls the below two:}
    procedure ReadValue(ASection, AVarName: TVarName; var AValue; typ:TPropertyType);
      {reads value from config}
    procedure WriteValue(ASection, AVarName: TVarName; var AValue; typ:TPropertyType);
      {writes value to config}
    procedure SetLastDateTime; {set global var LastDateTime to the date of config file at
                               the program start}
    procedure Read; {virtual; }{change default values of global variables
      according to content of config file, actually reads from Lines
      colletion}
    procedure Write; {virtual;}{writes global vars into Lines collection}
{!!!}
    procedure IfOpen;{as open, but increases OpenCounter (doesn't work, i think)}
    procedure IfClose;{as close, but closes only if OpenCounter = 0 (after decrementing it)}

    procedure Open;{virtual;}{reads config file content into the Lines collection}
    function IsOpen:boolean;
    procedure Close;{writes Lines, eventually modified by Write* procs, to file}

    procedure ReadFrom;{virtual;}{calls filedialog to read from choosen file}
    procedure WriteAs; {virtual;}{calls filedialog to change name}
{    procedure ReadGlobalValue(ASection, AVarName: TVarName; var AValue: string);}

{$IFNDEF DEBUG}
{    private}
{$ENDIF}
    function GetName: string; {returns Name}
    procedure SetName(AName: string); {sets new Name for subsequent read or write}

    procedure FindName; {virtual;}{find proper file, name as program, ext. .ini
      (order: def.dir, progdir), if not found, set found to false
     and creates name for new config for write, default dir: prog dir,
      }

    procedure ReadGlobalValues;{virtual;}{called by Read}
      {reads from config collection (or file) to the global var}
    procedure WriteGlobalValues;{virtual;}{called by write}
      {writes global variable to config collection (or file)}
    function GetVarValueStr(sec, key:TVarName):TVarValueStr;
    procedure UpdateConfigLine(Section:TVarName; ConfigLine:TConfigLine);
    function FindConfigLine(sec, key:TVarName; var i:integer; var secfound:boolean):boolean;
      {return number of wanted configline in i if found, otherwise postition
       where to AtInsert the configline,works only on opened Lines!!!}
    procedure SplitConfigLine(cl:TConfigLine; var AName:TVarName; var AValue:TVarValueStr);

{    procedure WriteGlobalValue(ASection, AVarName: TVarName; var AValue: string);}
{    function FixSectionName(sec:TVarName):TVarName;{converts to uppercase,
      makes sure square parentheses are around}

(*
    procedure WriteValue(var value; typ: char);
    {typ: b:byte,boolean(0,1)
          i:integer;
          w:word;
          l:longint;
          r:real;
          s:string;
          c:char;
          p:pchar (0 terminated string) ;
          h:hexadecimal string(-> word)
     }

    procedure ReadValue(var value; typ: char);
*)
    destructor Destroy;override;
  end;

var
  ConfigProc: TConfigProc;

const
  Config: TConfig = nil;
  ConfigFileName:string = ''; {if set to something else before call Create,
    than this name is used instead of exe name}

function GetConfigName(var AConfigName: string; ext: string):boolean;


type
  {PStart = ^TStart;}
  TStart = class( TObject)
    Dir: string;
    constructor Create;
    procedure StoreDir;
    function GetDir:string;
    procedure RestoreDir;
    destructor Destroy;override;
  end;

var Start: TStart;
{$IFDEF DEBUG}
{$IFDEF TEST}
{$IFDEF DEBCONFIGU}
{procedure Test;}
{$ENDIF}
{$ENDIF}
{$ENDIF}

{1}procedure ConfigInit(AFileName:string);
  {$IFDEF PMODE}export;{$ENDIF}
{2}procedure ConfigReadWriteValue(what:TReadWrite; section:string; name:string; address:pointer; typ:TPropertyType);
  {$IFDEF PMODE}export;{$ENDIF}
{3}procedure AddGlobalVar(ASection:string; AVarName:string; AAddress:Pointer; ATyp:TPropertyType);
  {$IFDEF PMODE}export;{$ENDIF}
{4}procedure ConfigDone;
  {$IFDEF PMODE}export;{$ENDIF}
{5}procedure ConfigSetGlobals(AGlobals:pointer; AMaxGlobals:integer);
  {$IFDEF PMODE}export;{$ENDIF}
{6}procedure ConfigRegister;
  {$IFDEF PMODE}export;{$ENDIF}
{7}function ConfigGet(AProperty:integer; var AValue:pointer):boolean;
  {$IFDEF PMODE}export;{$ENDIF}

{v0.21}
function ConfigOpen: boolean;{$IFDEF PMODE}export;{$ENDIF}
  { open config already initialized by call ConfigInit and closed by ConfigClose call,
    use when more ConfigReadWriteValue calls will be called to speed up operation }

function ConfigClose: boolean;{$IFDEF PMODE}export;{$ENDIF}
  { close config opened by call ConfigInit or ConfigOpen }
{/v0.21}

function SaveConfigVar(ASection:TVarName; AVarName:TVarName; AAddress:Pointer; ATyp:TPropertyType):boolean;
{saves immediately to config file.}

{procedure to be called by unit's Create section to add their variables}

implementation
uses
  msgu
;

function FixSectionName(sec:TVarName):TVarName;
begin
{  sec := UppCase(sec);}
  if sec = '' then begin
    sec := '[]';
  end else begin
    if sec[1] <> '[' then
      sec := '[' + sec;
    if sec[length(sec)] <> ']' then
      sec := sec + ']';
  end;
  FixSectionName := sec;
end;

{$IFDEF DEBUG}
procedure SetTvProc(var st:string; ReadWrite: Boolean);forward;
type
  tv=record
    a:string;
    b:longint;
  end;


const

  b:byte=1;
  bo:boolean=true;
  i:integer=10;
  l:longint=20;
  r:real=30;
  s:string='string s';
  c:char='c';
  p:array[0..20]of char='hjjjusky'+#0;
  m:pointer=nil;
  v:tv=(a:'hus';b:1444);
{$ENDIF}


{
    (VarName:'[sec1]';adr:nil;typ:'0'),
    (VarName:'b';adr:@b;typ:'b'),
    (VarName:'bo';adr:@bo;typ:'b'),
    (VarName:'[sec2]';adr:nil;typ:'0'),
    (VarName:'i';adr:@i;typ:'i'),
    (VarName:'l';adr:@l;typ:'l'),
    (VarName:'r';adr:@r;typ:'r'),
    (VarName:'[sec3]';adr:nil;typ:'0'),
    (VarName:'s';adr:@s;typ:'s'),
    (VarName:'c';adr:@c;typ:'c'),
    (VarName:'p';adr:@p;typ:'p'),
    (VarName:'v';adr:@settvproc;typ:'m'),
}
const
  GlobalConfigVars: TVarRecordArray = (
    (VarName:EnvSectionName;adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone),
    (VarName:'';adr:nil;typ:ptNone)
);

function SaveConfigVar(ASection:TVarName; AVarName:TVarName; AAddress:Pointer; ATyp:TPropertyType):boolean;
var wasOpen:boolean;
begin
  SaveConfigVar := false;
  if Config = nil then
    exit;
  wasOpen := (Config.Lines <> nil);
  Config.WriteValue(ASection, AVarName, AAddress^, atyp);
  if wasOpen then
    Config.Write;
  SaveConfigVar := true;{well, we don't know}
end;

procedure ConfigReadWriteValue(what:TReadWrite; section:string; name:string; address:pointer; typ:TPropertyType);
var
  wasOpen:boolean;
  flush:boolean;
begin
  if Config = nil then
    exit;
  if (what = rwRead) and (not Config.Found) then
    exit;
  if address = nil then
    exit;
  flush := false;

  if what = rwWriteFlush then begin
    what := rwWrite;
    flush := true;
  end;

  wasOpen := (Config.Lines <> nil);
  Config.ReadWriteValue(what, section, name, address^, typ);
  if wasOpen and flush then
    Config.Write;
end;

{v0.21}
function ConfigOpen: boolean;
  { open config already initialized by call ConfigInit and closed by ConfigClose call,
    use when more ConfigReadWriteValue calls will be called to speed up operation }
begin
  Result := false;
  if Config = nil then
    exit;
  Config.IfOpen;
  Result := Config.IsOpen;
end;

function ConfigClose: boolean;
  { close config opened by call ConfigInit or ConfigOpen }
begin
  Result := false;
  if Config = nil then
    exit;
  Config.IfClose;
  Result := true;
end;
{/v0.21}

procedure AddGlobalVar(ASection:string; AVarName:string; AAddress:Pointer; ATyp:TPropertyType);
var
  i:integer;

  procedure AddSectionAndVar;
  begin
    if (i >= (MaxGlobals{MaxVarsCount} - 1)) then begin
      writeln('Too many global config vars.' , AVarName, ' ignored. Press Enter to cont.');
      readln;
      exit;
    end;
    with GlobalsArray^[i] do begin
      VarName := ASection;
    end;
    with GlobalsArray^[i+1] do begin
      VarName := AVarName;
      Adr := AAddress;
      Typ := ATyp;
    end;
  end;

  procedure InsertVar;
  var j:integer;
  begin
    for j := MaxGlobals{MaxVarsCount} - 1 downto (i + 1) do
      GlobalsArray^[j] := GlobalsArray^[j-1];
    with GlobalsArray^[i] do begin
      VarName := AVarName;
      Adr := AAddress;
      Typ := ATyp;
    end;
  end;

  procedure AddVarToSection;
  begin
    while (i < MaxGlobals{MaxVarsCount}) and (GlobalsArray^[i].VarName <> '') do begin
      if (GlobalsArray^[i].VarName[1] = '[') then
      begin
        {new section name encountered}
        InsertVar;
        exit;
      end;
      if (AVarName = GlobalsArray^[i].VarName) then
        exit;{already entered variable}
      inc(i);
    end;
    if i = MaxGlobals{MaxVarsCount} then begin
      writeln('Too many global config vars.' , AVarName, ' ignored.');
      exit;{no more space for config vars}
    end;
    with GlobalsArray^[i] do begin
      VarName := AVarName;
      Adr := AAddress;
      Typ := ATyp;
    end;
  end;

begin
  if GlobalsArray = nil then begin
    ConfigSetGlobals(nil,0);
  end;
  if GlobalsArray = nil then begin
    SysError('CONFU.AddGlobalVar: ' + ASection + ' ' + AVarName + ' : No GlobalArray set yet.');
    exit;
  end;
  if ASection = '' then
    ASection := EnvSectionName;
  ASection := FixSectionName(ASection);
  i := 0;
  while (i < MaxGlobals {MaxVarsCount})
    and (GlobalsArray^[i].VarName <> '')
    and (ASection <> GlobalsArray^[i].VarName)
  do
    inc(i);
  if i = MaxGlobals{MaxVarsCount} then begin
    writeln('No more space for global Config vars!!.');
    halt;
{    exit;{no more space for config vars}
  end;

  if (GlobalsArray^[i].VarName = '')  then begin
    AddSectionAndVar;
  end else if GlobalsArray^[i].VarName = ASection then begin
    inc(i);
    AddVarToSection;
  end;
end;

procedure ConfigRegister;
begin
end;

function ConfigGet(AProperty:integer; var AValue:pointer):boolean;
begin
  ConfigGet := false;
  if Config = nil then
    exit;
  case AProperty of
    cpIniFileFound: begin
      ConfigGet := true;
      AValue := pointer(Config.Found);
    end;
  end;
end;


{$IFDEF DEBUG}
var
  Example : TVarRecordArray absolute GlobalConfigVars;

{$ENDIF}

{$IFDEF debug}
{example procedure for reading end writing values from read and write
method of Config.}

procedure SetTvProc(var st:string; ReadWrite: Boolean);
var i,j:integer;
begin
  if ReadWrite then begin
    with v do begin
      b:=0;
      a:='';
      if st='' then exit;
      i:=pos(',',st);

      if i>0 then begin
        a:=copy(st,1,pred(i));
      end;
      val(copy(st,succ(i),length(st)-i),b,j);
    end;
  end else begin
    str(b,st);
    st:=v.a+','+st;
  end;
end;
{$ENDIF}

function GetConfigName(var AConfigName: string; Ext: string):boolean;
{creates name of config file, tries to find one in current, then in exe dir,
 returns true if finds one, false otherwise, return name for config to create
 by writeconfig proc}
var
  d:DirStr;
  n:NameStr;
  e:ExtStr;

  fname,aname:string;
  found:boolean;
begin
  Found := false;
  if AConfigName <> '' then begin
    AName := ReplaceExt(AConfigName, Ext, true);
    {FSplit(AName, d, n, e);
    if d = '' then
      AName := GetExeDir + n + e;}
  end else
    AName := ReplaceExt(GetExeName, Ext, true);
  FSplit(AName,d,n,e);
  FName := FSearch(n+e, Start.GetDir + ';' + d);{filesearch}
  if FName <> '' then begin

    AName:= FExpand(FName);
    Found:=true;

  end else begin

    {try to find ini temp file for the case power went down during last save:}
    FName := FSearch(n + IniTempExt, Start.GetDir + ';' + d);
    if FName <> '' then begin
      AName := FExpand(FName);
      FName := ReplaceExt(AName, Ext, true);
      if RenameFile(AName, FName) then begin
        AName := FName;
        Found := true;
      end;
    end;

  end;
  AConfigName:= AName;
  GetConfigName:= Found;
end;


constructor TConfig.Create(AVars: PVarRecordArray; AMaxGlobals:integer); {calls SetDefault, SetOptions, FindName, Read}
begin
  inherited Create;
  Start := TStart.Create; {store start directory}
  if AVars = nil then begin
    Vars := GlobalsArray;
    MaxGlobals := MaxVarsCount;
  end else begin
    Vars := AVars;
    MaxGlobals := AMaxGlobals;
  end;
  Found:= false;
  Lines := nil;
  OpenCounter := 0;
  Name:='';
  Stream := NoHIO{nil};
  Status:=0;
  Changed := false;
  SetDefault;
  FindName;
  Open;
  if Found then begin
    SetLastDateTime;
    Read;
  end;
  SetOptions;
  AfterRead;
  Config := @Self;
end;

procedure TConfig.SetLastDateTime;
var l:longint;
  {dt:datetime;}
begin
  l := GetFileTime(GetName);
{  UnpackTime(l, dt);}
  LastDateTime := l;{GetDateString(dt.year, dt.month, dt.day) +
                  GetTimeString(dt.hour, dt.min, dt.sec);}
end;

procedure TConfig.FindName;{find proper file, name as program, ext. .ini
      (order: def.dir, progdir), if not found, set found to false
     and creates name for new config for write, default dir: prog dir,
      }
var aname:string;
begin
  aname := ConfigFileName;
  Found := GetConfigName(aname, IniExt);
  SetName(AName);
end;

function TConfig.GetName: string; {returns Name}
begin
  GetName:= Name;
end;

procedure TConfig.SetName(AName: string); {sets new Name for subsequent read of write}
begin
  Name:= AName;
end;

procedure  TConfig.SetDefault; {overide to set default startup values}
begin
end;

procedure  TConfig.SetOptions;{overide to change default values according to command line}
begin
end;

function TConfig.IsOpen:boolean;
begin
  IsOpen := Lines <> nil;
end;

procedure TConfig.IfOpen;
begin
  inc(OpenCounter);
  if not IsOpen then
    Open
  else begin
    if OpenCounter = 1 then
      inc(OpenCounter){was already open}
  end;
end;

procedure TConfig.IfClose;
begin
  if OpenCounter > 0 then
    dec(OpenCounter);
  if OpenCounter = 0 then
    Close;{clears also opencounter}
end;

procedure TConfig.Open;
var
  {s,}st:shortstring;
begin
  if Lines <> nil then
    exit;
  Status := 0;{listu}
  if not ListInit(ltStrings + ltAutoDestroy, NoListInfo, Lines) then
  begin
    {Lines := new(PTextLinesCollection, Create(100,20));
    {if Lines = nil then begin}
    exit;
  end;
  ListSetPropInt(Lines, lpCapacity, 100);
  if not FExists(GetName) then begin
    Found := false;
    exit;
  end else
    Found := true;
    { iou New(PQuietMemStream, Create(ConvStreamName(GetName), stOpenRead, StreamBufferSize));}
  if IOInit(GetName, stOpenRead, Stream) <> irOK then begin
    Status:= stMemoryError;
    exit;
  end;
  while (IOReadLine(Stream, st, 255{sizeof(st)}) = irOK) do begin
    if st = '' then
      st := ' ';{the stupid colection doesn't take empty strings}
    ListAdd(Lines, NewStr(st));
  end;
  IOGetProp(Stream, ipStatus, @Status);
  IODone(Stream);
end;
 {change default values according to content of config file}

procedure TConfig.Read;
var
  wasopen:boolean;
begin
  if Lines = nil then begin
    wasopen := false;
    Open;
    if Lines = nil then
      exit;
  end else
    wasopen := true;

  ReadGlobalValues;
  {SendMsgToApp(cmReadConfig, nil);}
  if not wasopen then
    Close;
end;


procedure VarToStr(var value; var st:TConfigLine; t:TPropertyType);
var
  l:longint absolute value;
  b:byte absolute value;
  i:smallint absolute value;
  w:word absolute value;
  s:shortstring absolute value;
  r:real absolute value;
  c:char absolute value;
  p: array[0..pred(maxword)] of char absolute value;
  tp:PString absolute value;
  si: single absolute value;
  {len:byte;}
begin
  st:='';
  case t of
    ptLongint: str(l,st);
    ptByte: str(b,st);
    ptInteger: str(i,st);
    ptWord: str(w,st);
    ptReal: str(r,st);
    ptSingle: str(si,st);
    ptChar: st:=c;
    ptPChar: st:= StrPas(p);
    ptString: st:=s;
    ptHex: st:= wordtohex(w);
    ptPString: st := GetString(tp)
  else
    begin
      SysError('Confu.VarToStr invalid pt');
      {st := system.copy(s, 1, ord(t));}
    end;
  end;
end;

procedure StrToVar(st:string; var valu; t:TPropertyType);
var
  code:integer;
  s:shortstring absolute valu;
  i:smallint absolute valu;
  b:byte absolute valu;
  w:word absolute valu;
  r:real absolute valu;
  c:char absolute valu;
  p:array[0..pred(maxword)] of char absolute valu;
  l:LongInt absolute valu;
  tp:PString absolute valu;
  si:single absolute valu;

begin
  st:=trim(st);
  if not (t in PropertyTypes) then begin
    SysError('Confu.StrToVar invalid pt');
    exit;
  end;
  {if ord(t) < ord('A') then begin
    t := 's';
    st := copy(st, 1, ord(t));
  end;}
  case t of
    ptString: s:=st;
    ptInteger: val(st, i, code);
    ptLongint: val(st, l, code);
    ptReal: val(st, r, code);
    ptSingle: val(st, si, code);
    ptChar: c:=st[1];
    ptWord: val(st, w, code);
    ptPChar: StrPCopy(P, st);
    ptByte: val(st, b, code);
    ptHex: w := hextoword(st);
    ptPString: SetString(tp, st);
  end;
end;

procedure TConfig.ReadGlobalValues;
var
  i:integer;
  t:TPropertyType;
  ptr:PByteBuffer;
  s, k:TVarName;
  proc: TConfigProc;
  v:TVarValueStr;
  wasopen:boolean;
  cl:TConfigLine;
begin
  if Vars=nil then
    exit;
  if Lines = nil then begin
    wasopen := false;
    Open;
    if Lines = nil then
      exit;
  end else
    wasopen := true;

  i := 0;
  s := '';
  while ((i< MaxGlobals{MaxVarsCount}) and (Vars^[i].VarName<>'') ) do
  with Vars^[i] do begin
    k := USUppCase(VarName);
    if k[1]='[' then begin
      s := k; {set section name}
    end else begin
      {set global value}
      ptr := PByteBuffer(adr);
      t := typ;
      v := GetVarValueStr(s, k);
      if (v = '') then begin
        inc(i);
        continue;
      end;
      if (t = ptMethod) and (adr <> nil) then begin
        proc:= TConfigProc(adr);
        cl := v;
        proc(cl, true);
      end else
        StrToVar(v, ptr^,t);
    end;
    inc(i);
  end;
  if not wasopen then
    Close;
end;

function TConfig.FindConfigLine(sec, key:TVarName; var i:integer; var secfound:boolean):boolean;
{returns number of wanted configline in i if found, otherwise postition
 where to AtInsert the configline, works only on opened Lines!!!}
var
  insec, {are we in the correct section?}
  keyfound:boolean; {key found?}
  l:byte; {length of key}
  cl:TConfigLine; {current config line}
begin
  i := -1;
  secfound := false;
  FindConfigLine := false;
  if Lines = nil then
    exit;

  sec := USuppcase(FixSectionName(sec));
  key := USuppcase(key);
  l := length(key);
  i := 0;
  if sec = '[]' then {no section name means put at the begining of the config file}
    insec := true
  else
    insec := false;
  while (i < ListGetPropInt(Lines, lpCount)) and (not insec) do begin
    cl := GetString(ListAt(Lines, i));{listu}
    if (cl <> '') and (cl[1] = '[') and (USuppcase(cl) = sec) then
      insec := true;
    inc(i);
  end;
  secfound := insec;
  keyfound := false;
  while (i < ListGetPropInt(Lines, lpCount)) and (not keyfound) and insec do
  begin
    cl := GetString(ListAt(Lines, i));
    if cl[1] = '[' then begin
      {another section start found, end of loop}
      insec := false;
    end else if USuppcase(copy(cl,1,l)) = key then begin
      keyfound := true {key found, end of loop}
    end else
      inc(i); {nothing found, continue search in loop}
  end;
  FindConfigLine := keyfound;
end;

function TConfig.GetVarValueStr(sec, key:TVarName):TVarValueStr;
var
  res : TVarValueStr;
  cn:TVarName;
  wasopen, secfound:boolean;
  i:integer;
begin
  GetVarValueStr := '';
  res := '';
  if Lines = nil then begin
    wasopen := false;
    Open;
  end else
    wasopen := true;

  if Lines = nil then
    exit;

  if FindConfigLine(sec, key, i, secfound) then begin
    SplitConfigLine(GetString(ListAt(Lines, i)), cn, res);
  end;

  if not wasopen then
    Close;
  GetVarValueStr := res;
end;

procedure TConfig.SplitConfigLine(cl:TConfigLine; var AName:TVarName; var AValue:TVarValueStr);
var i:byte;
begin
  AName := '';
  AValue := '';
  i := pos('=', cl);
  if i = 0 then begin {delimiter not found, i.e. only name of the var is present}
    AName := cl;
    exit;
  end;
  AName := copy(cl, 1, pred(i));
  AValue := copy(cl, succ(i), length(cl) - i);
end;


(*
procedure TConfig.ReadGlobalValue(ASection, AVarName: TVarName; AValue: String);
var
  i:integer;
  t:char;
  ptr:PByteBuffer;
  k:TVarName;sec:boolean;
  proc: TConfigProc;

begin
  i:=0;sec:=false;
  if Vars=nil then exit;
  while ((i<MaxGlobals MaxVarsCount)and(Vars^[i].VarName<>'') ) do
  with Vars^[i] do begin
    k:=uppcase(VarName);
    if k[1]='[' then begin
      if k=ASection then sec:=true else sec:=false;
    end else begin
      if sec then begin
        if k=AVarName then begin
          ptr:=PByteBuffer(adr);
          t:=upcase(typ);
          if t='M' then begin
            proc:= TConfigProc(adr);
            proc(AValue, true);
          end else StrToVar(AValue,ptr^,t);
        end;
      end;
    end;
    inc(i);
  end;
end;
*)
procedure  TConfig.WriteGlobalValues;
var
  i:integer;
  st:TConfigLine;
  sect : TVarName;
  proc: TConfigProc;
  ptr: PByteBuffer;
  wasopen:boolean;
begin
  Status:=0;
  sect := '';
  if Lines = nil then begin
    wasopen := false;
    Open;
  end else
    wasopen := true;
  if Lines = nil then
    exit;
  if Vars=nil then
    exit;
  i:=0;
  Changed := true;
  while ((i < MaxGlobals{MaxVarsCount}) and (Vars^[i].VarName<>'')) do begin
    with Vars^[i] do begin
      st := USuppcase(VarName);
      if VarName[1] = '[' then
        sect := FixSectionName(VarName)
      else
      begin
        ptr := PByteBuffer(adr);
        if (typ = ptMethod) and (adr <> nil) then begin
          proc := TConfigProc(adr);
          proc(st, false);
        end else
          VarToStr(ptr^, st, typ);
        st := VarName + '=' + st;
        UpdateConfigLine(sect, st);
      end;
    end;
    inc(i);
  end;
  if not wasopen then
    Close;
end;

procedure TConfig.ReadWriteValue(what:{v0.48}TReadWrite{/v0.48 byte}; ASection, AVarName: TVarName; var AValue; typ:TPropertyType);
begin
  if what = rwRead then
    ReadValue(ASection, AVarName, AValue, typ)
  else
    WriteValue(ASection, AVarName, AValue, typ);
end;

procedure TConfig.ReadValue(ASection, AVarName: TVarName; var AValue; typ:TPropertyType);
{reads value from config}
var
 vs:TVarValueStr;
 wasopen:boolean;
begin
  if Lines = nil then begin
    wasopen := false;
    Open;
    if Lines = nil then
      exit;
  end else
    wasopen := true;

  vs := GetVarValueStr(ASection, AVarName);
  if vs = '' then
    exit;
  StrToVar(vs, AValue, typ);
  if not wasopen then
    Close;
end;

procedure TConfig.WriteValue(ASection, AVarName: TVarName; var AValue; typ:TPropertyType);
{writes value to config}
var
 cl : TConfigLine;
 wasopen:boolean;
begin
  if Lines = nil then begin
    wasopen := false;
    Open;
    if Lines = nil then
      exit;
  end else
    wasopen := true;

  Changed := true;
  VarToStr(AValue, cl, typ);
  cl := AVarName + '=' + cl;
  UpdateConfigLine(ASection, cl);
  if not wasopen then
    Close;
end;


procedure TConfig.UpdateConfigLine(Section:TVarName; ConfigLine:TConfigLine);
{updates config by new configline in section Section}
var
  cn:TVarName;
  cv:TVarValueStr;
  secfound:boolean; {found at least the section?}
  i:integer;
  item:PString;
begin
  if Lines = nil then
    exit;
  Section := FixSectionName(Section);
  SplitConfigLine(ConfigLine, cn, cv);
  if FindConfigLine(Section, cn, i, secfound) then begin
  {replace old configline}
    Item := ListAt(Lines, i);
    ListReplace(Lines, i, NewStr(ConfigLine));
    FreePString(@Item);
    {Lines^.FreeItem(Lines^.At(i));
    Lines^.AtPut(i, NewStr(ConfigLine));}
  end else if (i <> -1) then begin
  {create new config line at i}
    if not secfound then begin
      {create new section:}
      ListAdd(Lines, NewStr(' '));
      ListAdd(Lines, NewStr(Section));
      ListAdd(Lines, NewStr(ConfigLine));
      {
      Lines^.Insert(NewStr(' '));
      Lines^.Insert(NewStr(Section));
      Lines^.Insert(NewStr(ConfigLine));}
    end else begin
      {just add the item to existing section:}
      if (i > 0) and (trim(GetString(ListAt(Lines, i - 1)))='') then
        dec(i);{insert before empty line}
      ListInsert(Lines, i, NewStr(ConfigLine));
      {Lines^.AtInsert(i, NewStr(ConfigLine));}
    end;
  end;
end;

procedure TConfig.BeforeWrite;
begin
end;

procedure  TConfig.Write;
var
  wasopen:boolean;
begin
  if Lines = nil then begin
    wasopen := false;
    Open;
    if Lines = nil then
      exit;
  end else
    wasopen := true;
  BeforeWrite;
  WriteGlobalValues;
  {SendMsgToApp(cmWriteConfig, nil);}
  if not wasopen then
    Close;
end;

procedure  TConfig.ReadFrom;{calls filedialog to read from choosen file}
begin
end;
(*var oname,aname: string;d:string;n:string;e:string;ev:tevent;
begin
  if name = '' then exit;
  oname := getname;
  fsplit(oname,d,n,e);
  {$IFDEF WINDOWS}
    aname := ReturnOpenFileName(d + n + e, '*' + e,deltilde(gettxt(txRetrieveOptions)));
  {$ELSE}
    aname := ReturnFileName('*'{n} + e, deltilde(gettxt(txRetrieveOptions)));
  {$ENDIF}

  if aname <> '' then begin
    SetName(ReplaceExt(AName,e,false));
    Read;
    AfterRead;
    if Status = 0 then begin
{$IFNDEF WINDOWS}
      restart := true;
      ev.what := evCommand; ev.Command := cmQuit;
      Desktop^.putevent(ev);
{$ENDIF}
    end else SetName(oname);
  end;
end;
*)
procedure  TConfig.WriteAs; {calls filedialog to change name}
{var oname,aname: string;d:string;n:string;e:string;ev:tevent;}

begin
(*
  if name = '' then exit;
  oname := getname;
  fsplit(oname,d,n,e);
  {$IFDEF WINDOWS}
    aname := ReturnSaveFileName(d + n + e, '*' + e,deltilde(gettxt(txSaveOptionsAs)));
  {$ELSE}
    aname := ReturnFileName('*'{n} + e + ';'+n+e, {i.e. mask;default_name}
    deltilde(gettxt(txSaveOptionsAs)));
  {$ENDIF}

  if aname <> '' then begin
    SetName(ReplaceExt(AName,e,false));
    Write;
    if Status <> 0 then SetName(oname);
  end;
*)
end;

procedure TConfig.AfterRead;
begin
end;
{
procedure  TConfig.WriteValue(ASectVarName: TVarName; var value; typ: char);
var st:string;code:integer;
    l:longint absolute value;
    b:byte absolute value;
    i:integer absolute value;
    w:word absolute value;
    s:string absolute value;
    r:real absolute value;

begin
  if Stream<>nil then with Stream^ do begin
    st:='';
    case upcase(typ) of
      'L': str(l,st);
      'B': str(b,st);
      'I': str(i,st);
      'W': str(w,st);
      'R': str(r,st);
      'S': st:=s;
    end;
    st:=VarName+'='+st;
    writeln(st);
  end;
end;
}

procedure TConfig.Close;
var
  i{, mode}:integer;
  aname,tempname:PathStr;
  s:shortstring;
label ex;
begin
  if Lines <> nil then begin
    if true{Changed} then begin
{
      if FExists(GetName) then
        Mode := stOpenWrite
      else
        Mode := stCreate;
}
      aname := GetName;
      tempname := ReplaceExt(aname, IniTempExt, true);
      {Stream := New(PQuietMemStream, Create(ConvStreamName(tempname), stCreate, StreamBufferSize));
      if Stream=nil then begin
        Status:= stMemoryError;
        goto ex;
      end;}
      if IOInit(tempname, stCreate, Stream) <> irOK then begin
        Status := stMemoryError;
        goto ex;
      end;


      if (IOGetProp(Stream, ipStatus, @Status) <> irOK) or (Status <> irOK) then begin
        {Status := Stream^.Status;
        Dispose(stream,done);}
        IODone(Stream);
        EraseFile(tempname);
        goto ex;
      end;

      for i:= 0 to pred(ListGetPropInt(Lines, lpCount)) do begin
        s := GetString(ListAt(Lines,i));
        IOWriteLine(Stream, s, length(s));
      end;
      IOGetProp(Stream, ipStatus, @Status);
      IODone(Stream);
      if Status = irOK then begin
        EraseFile(aname);
        RenameFile(tempname, aname);
      end else
        EraseFile(tempname);
    end;
ex:
    ListDone(Lines);
    {Dispose(Lines, Done);
    Lines := nil;}
    Changed := false;
  end;
  OpenCounter := 0;
end;

destructor TConfig.Destroy;
begin
  if SaveConfigOnExit then begin
    Write;
    Close
  end else
  if Lines <> nil then begin
    ListDone(Lines);
  end;
  Start.Free;
  Config := nil;
  inherited Destroy;
end;



{*************************  TStart  **********************************}

constructor TStart.Create;
begin
  inherited Create;
  StoreDir;
end;

procedure TStart.StoreDir;
begin
  System.GetDir(0,Dir);
  Dir := AddBackSlash(Dir);
end;

function TStart.GetDir: string;
begin
  GetDir:= Dir;
end;

procedure TStart.RestoreDir;
begin
  ChDir(DelBackSlash(Dir));
end;

destructor TStart.Destroy;
begin
  RestoreDir;
  inherited Destroy;
end;

{***********************************************************}
procedure ConfigSetGlobals(AGlobals:pointer; AMaxGlobals:integer);
begin
  if AGlobals = nil then begin
    GlobalsArray := @GlobalConfigVars;
    MaxGlobals := MaxVarsCount;
  end else begin
    GlobalsArray := AGlobals;
    MaxGlobals := AMaxGlobals;
  end;
end;

procedure ConfigInit(AFileName:string);
begin
  FreeObject(@Config);
  ConfigFileName := AFileName;
  Config := TConfig.Create(GlobalsArray, MaxGlobals);
end;

procedure ConfigDone;
begin
  FreeObject(@Config);
end;

initialization
{  ConfigSetGlobals(@GlobalConfigVars, MaxGlobals);}
end.
