unit strstrm;
{$I define.pas}
{$I nodebug.pas}

interface
uses
  SysUtils, Classes,
  MyType, MyLib;

type

  TStrStream = class(TStringStream)
    constructor Create(S:string;
      const AReadDelimiters: SetOfChar;
      AWriteDelimiter: TCharStr); reintroduce;
    procedure Reset;
    procedure Rewrite;
    function ReadStr(var s:string):boolean;
    function ReadByte(var b:byte):boolean;
    function ReadBoolean(var b:boolean):boolean;{in form 0,1}
    function ReadInteger(var i:integer):BOOLEAN;
    function ReadLongint(var l:longint):boolean;
    function ReadWord(var w:word):boolean;
    function ReadChar(var c:char):boolean;

    function WriteStr(const S: string): boolean;
    function WriteInteger(const i:integer):boolean;
    function WriteLongint(const l:longint):boolean;
    function WriteBoolean(b:boolean):boolean;
    function WriteByte(b:byte):boolean;
    function WriteWord(w:word):boolean;
    function WritePChar(var Rec; MaxCount:integer):boolean;
    procedure SetBuffer(const s: string);
    procedure SetDelimiters(const ADelimiters: SetOfChar);
    procedure SetDelimiter(ADelimiter: TCharStr);
    function EOS:boolean;{end of string reached?}
    function Reading:boolean;
    function Writing:boolean;
    function UpdateString(var S:String):boolean;
    function UpdateInteger(var i:integer):boolean;
    function UpdateLongint(var i:Longint):boolean;
    function UpdateByte(var b:byte):boolean;
    function UpdateBoolean(var b:boolean):boolean;
    function UpdatePString(var S:PString):boolean;
    function UpdateWord(var w:word):boolean;
  private
    FDelimiters: setofchar;
    FDelimiter: TCharStr;
    FLastEndDelimChar: char;
    FShouldSkipMultiDelims: boolean;
    rw:byte;
      { reading or writing?, set by reset to rwRead and rewrite to rwWrite }
  end;

  TNameAndValueStringStream = class(TStrStream)
    constructor Create(S: string); reintroduce;
    procedure SetNameAndValueDelimiters(AValueDelimiter, ALineDelimiter: char);
    function GetNameAndValue(var AName: string; var AValue: string): boolean;
  private
    ValueDelimiter,
    LineDelimiter: char;
  end;

  function GetStringSection(AString: String; ASectionNum: integer; ADelimiter: char): string;

const
  WordDelimiters: setofchar = [' ',',',';','.',#9];

implementation

const
  rwRead = 0;  {direction constants for statusobjects' methods ReadWriteConfig}
  rwWrite = 1;

constructor TStrStream.Create(S:string;
  const AReadDelimiters: SetOfChar; AWriteDelimiter: TCharStr);
begin
  inherited Create(S);
  FDelimiters := AReadDelimiters;{WordDelimiters;}
  FDelimiter := AWriteDelimiter{','};
  FLastEndDelimChar := #0;
  FShouldSkipMultiDelims := true;
end;

procedure TStrStream.Reset;
begin
  Position := 0;
  rw := rwRead;
end;

procedure TStrStream.Rewrite;
begin
  Size := 0;
  rw := rwWrite;
end;

function TStrStream.WriteStr(const S: string): boolean;
begin
  WriteStr := false;
  if S = '' then begin
    if Size <> 0 then begin
      if DataString[Size] <> FDelimiter then
        WriteString(FDelimiter);
    end;
    DataString := DataString + FDelimiter
  end else begin
    if DataString = '' then
      DataString := DataString + s
    else begin
      if DataString[length(DataString)] = FDelimiter then
        DataString := DataString + s
      else
        DataString := DataString + FDelimiter + s;
    end;
  end;
  WriteStr := true;
end;

function TStrStream.ReadStr(var s:string):boolean;
var p2:integer;
begin
  ReadStr := false;
  if p > length(DataString) then begin
    S := '';
    exit;
  end;

  if FShouldSkipMultiDelims then begin
    inc(p);
    while (p <= length(DataString)) and ((DataString[p] in FDelimiters)) do begin
      if p = 255 then
        break;
      inc(p);
    end;
  end else begin
{    if (p <= length(DataString)) and ((DataString[p] in FDelimiters)) and (p <> 1) then}
    inc(p);
  end;

  p2 := p;
  while (p2 <= length(DataString)) and (not (DataString[p2] in FDelimiters)) do begin
    if p2 = 255 then
      break;
    inc(p2);
  end;
  s := copy(DataString, p, p2 - p);
  if p2 <= length(DataString) then
    FLastEndDelimChar := DataString[p2]
  else
    FLastEndDelimChar := #0;
  p := p2;

{
  if FShouldSkipMultiDelims then begin
    while (p <= length(DataString)) and (DataString[p] in FDelimiters) do
      inc(p);
  end else begin
    if p <= length(DataString) then
      inc(p);
  end;
}
  ReadStr := true;
end;

function TStrStream.ReadBoolean(var b:boolean):boolean;{in form 0,1}
var by:byte absolute b;
begin
  ReadBoolean := ReadByte(by);
end;

function TStrStream.ReadByte(var b:byte):boolean;
var
  s:string;
  code :integer;
begin
  ReadByte := false;
  if not ReadStr(s) then
    exit;
  val(trim(s), b, code);
  if code <> 0 then
    ReadByte := false
  else
    ReadByte := true;
end;


function TStrStream.ReadInteger(var i:integer):boolean;
var
  s:string;
  code :integer;
begin
  ReadInteger := false;
  if not ReadStr(s) then
    exit;
  val(trim(s), i, code);
  if code <> 0 then
    ReadInteger := false
  else
    ReadInteger := true;
end;

function TStrStream.ReadLongint(var l:longint):boolean;
var
  s:string;
  code:integer;
begin
  ReadLongint := false;
  if not ReadStr(s) then
    exit;
  val(trim(s),l, code);
  if code <> 0 then
    ReadLongint := false
  else
    ReadLongint := true;
end;

function TStrStream.ReadWord(var w:word):boolean;
var
  s:string;
  code :integer;
begin
  ReadWord := false;
  if not ReadStr(s) then
    exit;
  val(trim(s), w, code);
  if code <> 0 then
    ReadWord := false
  else
    ReadWord := true;
end;

function TStrStream.ReadChar(var c:char):boolean;
var s:string;
begin
  ReadChar := false;
  if not ReadStr(s) then
    exit;
  if s =  '' then
    exit;
  c := s[1];
  ReadChar := true;
end;


function TStrStream.WriteLongint(const l:longint):boolean;
var
  s:string[20];
begin
  str(l, s);
  WriteLongint := WriteStr(s);
end;

function TStrStream.WriteInteger(const i:integer):boolean;
var s:string[5];
begin
  str(i,s);
  WriteInteger := WriteStr(s);
end;

function TStrStream.WriteWord(w:Word):boolean;
var s:string[5];
begin
  str(w,s);
  WriteWord := WriteStr(s);
end;

function TStrStream.WriteByte(b:byte):boolean;
var s:string[3];
begin
  str(b,s);
  WriteByte := WriteStr(s);
end;

function TStrStream.WriteBoolean(b:boolean):Boolean;
begin
  WriteBoolean := WriteByte(byte(b));
end;

function TStrStream.GetBuffer:string;
begin
  GetBuffer := DataString;
end;

procedure TStrStream.SetBuffer(const s:string);
begin
  DataString := s;
  p := 0;
end;

procedure TStrStream.SetDelimiters(const ADelimiters:setofchar);
begin
  FDelimiters := ADelimiters;
end;

procedure TStrStream.SetDelimiter(ADelimiter:TCharStr);
begin
  FDelimiter := ADelimiter;
end;

function TStrStream.EOS:boolean;{end of string reached?}
begin
  EOS := (p > length(DataString));
end;

function TStrStream.Reading:boolean;
begin
  Reading := (rw = rwRead);
end;

function TStrStream.Writing:boolean;
begin
  Writing := (rw = rwWrite);
end;

function TStrStream.WritePChar(var Rec; MaxCount:integer):boolean;
var c:array[0..255] of char absolute Rec;
begin
  WritePChar := WriteStr(copy(StrPas(c), 1, MaxCount));
end;

{$IFDEF GHWORK}
function TStrStream.UpdateString(var S:String):boolean;
begin
  if rw = rwRead then
    UpdateString := ReadStr(S)
  else
    UpdateString := WriteStr(S)
end;

function TStrStream.UpdateInteger(var i:integer):boolean;
begin
  if rw = rwRead then
    UpdateInteger := ReadInteger(i)
  else
    UpdateInteger := WriteInteger(i)
end;

function TStrStream.UpdateWord(var w:Word):boolean;
begin
  if rw = rwRead then
    UpdateWord := ReadWord(w)
  else
    UpdateWord := WriteWord(w)
end;

function TStrStream.UpdateLongint(var i:Longint):boolean;
begin
  if rw = rwRead then
    UpdateLongint := ReadLongint(i)
  else
    UpdateLongint := WriteLongint(i)
end;

function TStrStream.UpdateByte(var b:byte):boolean;
begin
  if rw = rwRead then
    UpdateByte := ReadByte(b)
  else
    UpdateByte := WriteByte(b)
end;

function TStrStream.UpdateBoolean(var b:boolean):boolean;
begin
  if rw = rwRead then
    UpdateBoolean := ReadBoolean(b)
  else
    UpdateBoolean := WriteBoolean(b)
end;

function TStrStream.UpdatePString(var S:PString):boolean;
var V:String;
begin
  if rw = rwRead then begin
    if not ReadStr(V) then
      exit;
    if V <> '' then begin
      if S <> nil then
        DisposeStr(S);
      S := NewStr(V);
      if S = nil then
        exit;
    end else
      S := nil;
  end else begin
    if S = nil then
      V := ''
    else
      V := S^;
    if not WriteStr(V) then
      exit;
  end;
  UpdatePString := true;
end;

{$ENDIF}
function GetStringSection(AString:String; ASectionNum:integer; ADelimiter:char):string;
var
  ss:TStrStream;
begin
  ss.Init(AString);
  ss.SetDelimiters([ADelimiter]);
  repeat
    ss.ReadStr(AString);
    dec(ASectionNum);
  until ASectionNum <= -1;
  GetStringSection := AString;
end;
{***************************************************************}
constructor TNameAndValueStringStream.Init(const s:string);
begin
  if not inherited Init(S) then
    Fail;
  ValueDelimiter := '=';
  LineDelimiter := #13;
end;

procedure TNameAndValueStringStream.SetNameAndValueDelimiters(AValueDelimiter, ALineDelimiter:char);
begin
  ValueDelimiter := AValueDelimiter;
  LineDelimiter := ALineDelimiter;
end;

function TNameAndValueStringStream.GetNameAndValue(var AName:string; var AValue:string):boolean;
begin
  GetNameAndValue := false;
  SetDelimiters([ValueDelimiter]);
  if not ReadStr(AName) then
    exit;
  SetDelimiters([LineDelimiter]);
  if not ReadStr(AValue) then
    exit;
  GetNameAndValue := true;
end;

end.