unit memini; interface function OpenStorage(fname:pAnsiChar):pointer; function OpenStorageBuf(buf:pAnsiChar):pointer; procedure CloseStorage(storage:pointer); function GetSectionList(storage:pointer;namespace:pAnsiChar=nil):pAnsiChar; procedure FreeSectionList(ptr:pAnsiChar); function GetParamStr(storage:pointer;section,param:pAnsiChar;default:pAnsiChar=nil; namespace:pAnsiChar=nil):pAnsiChar; function GetParamInt(storage:pointer;section,param:pAnsiChar;default:integer=0; namespace:pAnsiChar=nil):integer; function SearchSection(storage:pointer;section:pAnsiChar;namespace:pAnsiChar=nil):pointer; function GetSectionName(section:pointer):pAnsiChar; function GetParamSectionStr(section:pointer;param:pAnsiChar;default:pAnsiChar=nil):pAnsiChar; function GetParamSectionInt(section:pointer;param:pAnsiChar;default:integer=0):integer; implementation uses windows,io,common; const arstep = 8; const ns_separator = ':'; line_separator = '\'; type pParam = ^tParam; tParam = record hash :integer; // param name hash name :pAnsiChar; // points to source (for write only) value :pAnsiChar; // points to source? or modified assign:boolean; // newly assigned value or in INI buffer end; pSection = ^tSection; tSection = record ns :integer; // namespace hash code :integer; // section name hash full :integer; // namespace+section name hash fullname:pAnsiChar; // pointer to namespace:name name :pAnsiChar; // pointer to name only numparam:integer; arParams:array of tParam; end; pStorage = ^tStorage; tStorage = record Name :pAnsiChar; // filename buffer :pAnsiChar; // source (INI) text numsect :integer; arSection: array of tSection; end; function HashOf(txt:pAnsiChar):integer; begin result:=Hash(txt,StrLen(txt)); { result:=0; while txt^<>#0 do begin result:=((result shl 2) or (result shr (SizeOf(result)*8-2))) xor Ord(UpCase(txt^)); inc(txt); end; } end; // sections adds 1 by 1, without duplicate check procedure AddSection(data:pStorage;anamespace,aname:pAnsiChar); var c:AnsiChar; begin // search section with same name? // add section if data.numsect>High(data.arSection) then SetLength(data.arSection,Length(data.arSection)+arstep); FillChar(data.arSection[data.numsect],SizeOf(tSection),0); with data.arSection[data.numsect] do begin fullname:=anamespace; name :=aname; full:=HashOf(anamespace); if anamespace<>aname then begin c:=(aname-1)^; (aname-1)^:=#0; code:=HashOf(aname); ns :=HashOf(anamespace); (aname-1)^:=c; end; end; inc(data.numsect); end; procedure AddParam(data:pStorage;aname,avalue:pAnsiChar;assignvalue:boolean); begin // search param with same name? with data.arSection[data.numsect-1] do begin // add param if numparam>High(arParams) then SetLength(arParams,Length(arParams)+arstep); FillChar(arParams[numparam],SizeOf(tParam),0); with arParams[numparam] do begin hash :=HashOf(aname); name :=aname; value :=avalue; assign:=assignvalue; end; inc(numparam); end; end; // quotes, multiline etc // result = pointer to non-parameter line // pointers: start of value, start of current line, end of value in line, end of current line function ProcessParamValue(var start:pAnsiChar):pAnsiChar; var lineend,eol,dst,bov:pAnsiChar; multiline,crlf:boolean; begin dst:=start; bov:=start; result:=nil; repeat multiline:=false; crlf :=false; // skip starting spaces while start^ in [#9,' '] do inc(start); if start^ in [#0,#10,#13] then // empty value or end begin while start^ in [#10,#13] do inc(start); exit; end; lineend:=start; while not (lineend^ in [#0,#10,#13]) do inc(lineend); eol:=lineend; dec(lineend); while lineend^ in [#9,' '] do dec(lineend); if lineend^=line_separator then // multiline or part of value begin if (lineend-1)^ in [#9,' '] then // multiline begin dec(lineend); multiline:=true; while lineend^ in [#9,' '] do dec(lineend); end // double separator = multiline + crlf saving else if ((lineend-1)^=line_separator) and ((lineend-2)^ in [#9,' ']) then begin dec(lineend,2); multiline:=true; crlf :=true; while lineend^ in [#9,' '] do dec(lineend); end; end; // lineend points to last char // start points to first char // eol points to end of line //!! now just starting/ending quotes if (start^ in ['''','"']) and (lineend^ in ['''','"']) then begin inc(start); dec(lineend); end; while start<=lineend do begin dst^:=start^; inc(dst); inc(start); end; if crlf then begin dst^:=#13; inc(dst); dst^:=#10; inc(dst); end; start:=eol; while start^ in [#10,#13] do inc(start); until not multiline; dst^:=#0; result:=bov; end; procedure TranslateData(data:pStorage); var pc2,pc1,pc:pAnsiChar; begin pc:=data^.buffer; data.numsect:=0; while pc^<>#0 do begin while pc^ in [#9,#10,#13,' '] do inc(pc); // comment if pc^=';' then begin // skip to next line (or end) while not (pc^ in [#0,#10,#13]) do inc(pc); // skip empty while pc^ in [#9,#10,#13,' '] do inc(pc); end // section else if pc^='[' then begin inc(pc); //!! without #0 check pc1:=pc; pc2:=pc; while pc^ in sLatWord do inc(pc); // namespace if pc^=ns_separator then begin inc(pc); pc2:=pc; end; while pc^ <> ']' do inc(pc); pc^:=#0; //!! AddSection(data,pc1,pc2); inc(pc); end // parameter else if pc^ in sIdFirst then begin pc1:=pc; // skip param name while pc^ in sLatWord do inc(pc); pc^:=#0; //!! // skip spaces while pc^ in [#9,' '] do inc(pc); inc(pc); // must be "=" // skip spaces while pc^ in [#9,' '] do inc(pc); // pc2:=pc; // parameter can be quoted // here need to cut spaces, comments but join next lines pc2:=ProcessParamValue(pc); AddParam(data,pc1,pc2,false); end; end; end; function OpenStorageBuf(buf:pAnsiChar):pointer; begin result:=nil; if (buf<>nil) and (buf^<>#0) then begin GetMem(result,SizeOf(tStorage)); FillChar(result^,SizeOf(tStorage),0); StrDup(pStorage(result)^.buffer,buf); TranslateData(pStorage(result)); end; end; function OpenStorage(fname:pAnsiChar):pointer; var h:THANDLE; size:integer; begin result:=nil; if FileExists(fname) then begin h:=Reset(fname); if h<>THANDLE(INVALID_HANDLE_VALUE) then begin size:=FileSize(h); if size>0 then begin GetMem(result,SizeOf(tStorage)); FillChar(result^,SizeOf(tStorage),0); // save name too? GetMem(pStorage(result)^.buffer,size+1); BlockRead(h,pStorage(result)^.buffer^,size); pStorage(result)^.buffer[size]:=#0; end; CloseHandle(h); TranslateData(pStorage(result)); end; end; end; procedure CloseStorage(storage:pointer); var i:integer; begin if storage=nil then exit; with pStorage(storage)^ do begin if name<>nil then FreeMem(name); // cycle by sections for i:=0 to HIGH(arSection) do SetLength(arSection[i].arParams,0); SetLength(arSection,0); FreeMem(buffer); end; FreeMem(storage); end; function GetSectionList(storage:pointer;namespace:pAnsiChar=nil):pAnsiChar; var i,size,ns:integer; pc:pAnsiChar; begin if storage=nil then begin result:=nil; exit; end; // calculate size size:=0; ns:=0; if (namespace<>nil) and (namespace^<>#0) then ns:=HashOf(namespace); with pStorage(storage)^ do begin for i:=0 to HIGH(arSection) do begin if (namespace<>nil) and (namespace^<>#0) then begin if ns<>arSection[i].ns then continue; inc(size,StrLen(arSection[i].name)+1); end else inc(size,StrLen(arSection[i].fullname)+1); end; inc(size); // get memory GetMem(pc,size); result:=pc; // fill for i:=0 to HIGH(arSection) do begin if (namespace<>nil) and (namespace^<>#0) then begin if ns<>arSection[i].ns then continue; pc:=StrCopyE(pc,arSection[i].name); end else pc:=StrCopyE(pc,arSection[i].fullname); inc(pc); end; pc^:=#0; end; end; procedure FreeSectionList(ptr:pAnsiChar); begin FreeMem(ptr); end; function SearchSection(storage:pointer;section:pAnsiChar;namespace:pAnsiChar=nil):pointer; var i:integer; nsn,nss:integer; begin result:=nil; nss:=HashOf(section); if namespace=nil then begin with pStorage(storage)^ do for i:=0 to numsect-1 do begin if arSection[i].full=nss then begin result:=@arSection[i]; break; end; end; end else begin nsn:=HashOf(namespace); with pStorage(storage)^ do begin for i:=0 to numsect-1 do begin if (arSection[i].ns=nsn) and (arSection[i].code=nss) then begin result:=@arSection[i]; break; end; end; end; end; end; function GetSectionName(section:pointer):pAnsiChar; begin if section=nil then result:=nil else result:=pSection(section).name; end; function SearchParameter(section:pointer;param:pAnsiChar):pointer; var i:integer; nsp:integer; begin result:=nil; if section<>nil then begin nsp:=HashOf(param); with pSection(section)^ do begin for i:=0 to numparam-1 do begin if arParams[i].hash=nsp then begin result:=@arParams[i]; break; end; end; end; end; end; function GetParamSectionStr(section:pointer;param:pAnsiChar;default:pAnsiChar=nil):pAnsiChar; var pn:pParam; begin result:=default; if section<>nil then begin pn:=SearchParameter(section,param); if pn<>nil then result:=pn.value //StrCopy(buffer,value,len-1); end; end; function GetParamSectionInt(section:pointer;param:pAnsiChar;default:integer=0):integer; var pn:pParam; begin result:=default; if section<>nil then begin pn:=SearchParameter(section,param); if pn<>nil then begin if pn.value[0]='$' then result:=HexToInt(pAnsiChar(@pn.value[1])) else result:=StrToInt(pn.value); end; end; end; function GetParamStr(storage:pointer;section,param:pAnsiChar;default:pAnsiChar=nil; namespace:pAnsiChar=nil):pAnsiChar; var sn:pSection; begin if storage=nil then begin result:=default; exit; end; sn:=SearchSection(storage,section,namespace); result:=GetParamSectionStr(sn,param,default); end; function GetParamInt(storage:pointer;section,param:pAnsiChar;default:integer=0; namespace:pAnsiChar=nil):integer; var sn:pSection; begin if storage=nil then begin result:=default; exit; end; sn:=SearchSection(storage,section,namespace); result:=GetParamSectionInt(sn,param,default); end; end.