//==============================================================================
//  This unit contains only pure string manipulation functions
//==============================================================================
unit TextOnly;

interface
uses Classes;

procedure SWriteLn(var s:string; const l:string);
procedure SWriteLnSmart(var s:string; const l:string);
procedure SWrite(var s:string; const l:string);
procedure SWDebug(const dbg:boolean);

function nonNumeralPrefix(const s:string; p:integer):boolean;
function IsWhitespace(const s:string; const st:integer=1):boolean;
function TryStrToIntX(const s:string;var x:integer):boolean;
function GetAssigment( const s:string; var r:string; fromi:integer):boolean;
procedure findchar(const s:string; var pos:integer; const start:integer; const c:char);

procedure Concatenate3(var res:string; const s1,s2,s3:string);
procedure Concatenate4(var res:string; const s1,s2,s3,s4:string);
procedure Concatenate5(var res:string; const s1,s2,s3,s4,s5:string);
procedure Concatenate6(var res:string; const s1,s2,s3,s4,s5,s6:string);
procedure Concatenate7(var res:string; const s1,s2,s3,s4,s5,s6,s7:string);
procedure Concatenate8(var res:string; const s1,s2,s3,s4,s5,s6,s7,s8:string);
procedure Concatenate9(var res:string; const s1,s2,s3,s4,s5,s6,s7,s8,s9:string);
procedure Concatenate10(var res:string; const s1,s2,s3,s4,s5,s6,s7,s8,s9,s10:string);
procedure Concatenate11(var res:string; const s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11:string);
procedure Concatenate12(var res:string; const s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12:string);
procedure Concatenate13(var res:string; const s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13:string);
function RConcatenate3(const s1,s2,s3:string):string;
function RConcatenate6(const s1,s2,s3,s4,s5,s6:string):string;
procedure ConcatenateManySep(var res:string; var s:array of string; const total:integer; const sep:char);

function breakIntoLines(const buf : string):TStringList;
function FindOddCharacters(const s:string; const abound:integer=-1):boolean;


//==============================================================================
implementation
//==============================================================================
uses Constants, SysUtils;

var swdbg : boolean = false;



procedure SWDebug(const dbg:boolean);
begin
    writeln;
    writeln('=============================');
    swdbg := dbg;
end;

//==============================================================================
procedure SWriteLn(var s:string; const l:string);
begin
    Insert(l, s, VERYBIG);
    Insert(#13#10, s, VERYBIG);
    if swdbg then begin
        writeln(l);
    end;
end;

//==============================================================================
procedure SWriteLnSmart(var s:string; const l:string);
var len:integer;
begin
    Insert(l, s, VERYBIG);
    if swdbg then begin
        write(l);
    end;
    len:=Length(s);
    if( (len>=2) and (s[len]=#10) and (s[len-1]=#13)) then exit;
    Insert(#13#10, s, VERYBIG);
    if swdbg then begin
        writeln;
    end;
end;

//==============================================================================
procedure SWrite(var s:string; const l:string);
begin
    Insert(l, s, VERYBIG);
    if swdbg then begin
        write(l);
    end;
end;

//==============================================================================
procedure Concatenate3(var res:string; const s1,s2,s3:string);
var
  n:integer;

   procedure add(const s:string);
   var i:integer;
   begin
       for i := 1 to Length(s) do begin
           res[n]:=s[i];
           n:=n+1;
       end;
   end;


begin
  SetLength(res,Length(s1)+Length(s2)+Length(s3));
  n:=1;
  add(s1);add(s2);add(s3);

end;

//==============================================================================
function RConcatenate3(const s1,s2,s3:string):string;
begin
    Concatenate3(result,s1,s2,s3);
end;

//==============================================================================
procedure Concatenate4(var res:string; const s1,s2,s3,s4:string);
var
  n:integer;

   procedure add(const s:string);
   var i:integer;
   begin
       for i := 1 to Length(s) do begin
           res[n]:=s[i];
           n:=n+1;
       end;
   end;


begin
  SetLength(res,Length(s1)+Length(s2)+Length(s3)+Length(s4));
  n:=1;
  add(s1);add(s2);add(s3);add(s4);

end;

//==============================================================================
procedure Concatenate5(var res:string; const s1,s2,s3,s4,s5:string);
var
  n:integer;

   procedure add(const s:string);
   var i:integer;
   begin
       for i := 1 to Length(s) do begin
           res[n]:=s[i];
           n:=n+1;
       end;
   end;


begin
  SetLength(res,Length(s1)+Length(s2)+Length(s3)+Length(s4)+Length(s5));
  n:=1;
  add(s1);add(s2);add(s3);add(s4);add(s5);

end;

//==============================================================================
procedure Concatenate6(var res:string; const s1,s2,s3,s4,s5,s6:string);
var
  n:integer;

   procedure add(const s:string);
   var i:integer;
   begin
       for i := 1 to Length(s) do begin
           res[n]:=s[i];
           n:=n+1;
       end;
   end;


begin
  SetLength(res,Length(s1)+Length(s2)+Length(s3)+Length(s4)+Length(s5)+Length(s6));
  n:=1;
  add(s1);add(s2);add(s3);add(s4);add(s5);add(s6);

end;

//==============================================================================
function RConcatenate6(const s1,s2,s3,s4,s5,s6:string):string;
begin
    Concatenate6(Result,s1,s2,s3,s4,s5,s6);
end;

//==============================================================================
procedure Concatenate7(var res:string; const s1,s2,s3,s4,s5,s6,s7:string);
var
  n:integer;

   procedure add(const s:string);
   var i:integer;
   begin
       for i := 1 to Length(s) do begin
           res[n]:=s[i];
           n:=n+1;
       end;
   end;


begin
  SetLength(res,Length(s1)+Length(s2)+Length(s3)+Length(s4)+Length(s5)+Length(s6)+Length(s7));
  n:=1;
  add(s1);add(s2);add(s3);add(s4);add(s5);add(s6);add(s7);

end;

//==============================================================================
procedure Concatenate8(var res:string; const s1,s2,s3,s4,s5,s6,s7,s8:string);
var
  n:integer;

   procedure add(const s:string);
   var i:integer;
   begin
       for i := 1 to Length(s) do begin
           res[n]:=s[i];
           n:=n+1;
       end;
   end;


begin
  SetLength(res,Length(s1)+Length(s2)+Length(s3)+Length(s4)+Length(s5)+Length(s6)+Length(s7)+Length(s8));
  n:=1;
  add(s1);add(s2);add(s3);add(s4);add(s5);add(s6);add(s7);add(s8);

end;

//==============================================================================
procedure Concatenate9(var res:string; const s1,s2,s3,s4,s5,s6,s7,s8,s9:string);
var
  n:integer;

   procedure add(const s:string);
   var i:integer;
   begin
       for i := 1 to Length(s) do begin
           res[n]:=s[i];
           n:=n+1;
       end;
   end;


begin
  SetLength(res,Length(s1)+Length(s2)+Length(s3)+Length(s4)+Length(s5)+Length(s6)+Length(s7)+Length(s8)+Length(s9));
  n:=1;
  add(s1);add(s2);add(s3);add(s4);add(s5);add(s6);add(s7);add(s8);add(s9);

end;

//==============================================================================
procedure Concatenate10(var res:string; const s1,s2,s3,s4,s5,s6,s7,s8,s9,s10:string);
var
  n:integer;

   procedure add(const s:string);
   var i:integer;
   begin
       for i := 1 to Length(s) do begin
           res[n]:=s[i];
           n:=n+1;
       end;
   end;


begin
  SetLength(res,Length(s1)+Length(s2)+Length(s3)+Length(s4)+Length(s5)+Length(s6)+Length(s7)+Length(s8)+Length(s9)+Length(s10));
  n:=1;
  add(s1);add(s2);add(s3);add(s4);add(s5);add(s6);add(s7);add(s8);add(s9);add(s10);

end;

//==============================================================================
procedure Concatenate11(var res:string; const s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11:string);
var
  n:integer;

   procedure add(const s:string);
   var i:integer;
   begin
       for i := 1 to Length(s) do begin
           res[n]:=s[i];
           n:=n+1;
       end;
   end;


begin
  SetLength(res,Length(s1)+Length(s2)+Length(s3)+Length(s4)+Length(s5)+Length(s6)+Length(s7)+Length(s8)+Length(s9)+Length(s10)+Length(s11));
  n:=1;
  add(s1);add(s2);add(s3);add(s4);add(s5);add(s6);add(s7);add(s8);add(s9);add(s10);add(s11);

end;

//==============================================================================
procedure Concatenate12(var res:string; const s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12:string);
var
  n:integer;

   procedure add(const s:string);
   var i:integer;
   begin
       for i := 1 to Length(s) do begin
           res[n]:=s[i];
           n:=n+1;
       end;
   end;


begin
  SetLength(res,Length(s1)+Length(s2)+Length(s3)+Length(s4)+Length(s5)+Length(s6)+Length(s7)+Length(s8)+Length(s9)+Length(s10)+Length(s11)+Length(s12));
  n:=1;
  add(s1);add(s2);add(s3);add(s4);add(s5);add(s6);add(s7);add(s8);add(s9);add(s10);add(s11);add(s12);

end;

//==============================================================================
procedure Concatenate13(var res:string; const s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13:string);
var
  n:integer;

   procedure add(const s:string);
   var i:integer;
   begin
       for i := 1 to Length(s) do begin
           res[n]:=s[i];
           n:=n+1;
       end;
   end;


begin
  SetLength(res,Length(s1)+Length(s2)+Length(s3)+Length(s4)+Length(s5)+Length(s6)+Length(s7)+Length(s8)+Length(s9)+Length(s10)+Length(s11)+Length(s12)+Length(s13) );
  n:=1;
  add(s1);add(s2);add(s3);add(s4);add(s5);add(s6);add(s7);add(s8);add(s9);add(s10);add(s11);add(s12);add(s13);

end;

//==============================================================================
procedure ConcatenateManySep(var res:string; var s:array of string; const total:integer; const sep:char);
var
  n,i,j:integer;
//  debugstr:string;

begin


//   debugstr:='';
   n:=0;
   for i := 0 to total - 1 do begin
//      debugstr:=s[i]+';;';
      n:=n+Length(s[i]);
   end;
   n:=n+total-1;


   SetLength(res,n);

   n:=1;
   if(total>0) then begin
       for j := 1 to Length(s[0]) do begin
           if(Length(res)<n) then raise Exception.Create('wtf1');
           res[n]:=s[0][j];
           n:=n+1;
       end;

       if(total>1) then for i := 1 to total - 1 do begin
           res[n]:=sep;
           n:=n+1;
           for j := 1 to Length(s[i]) do begin
              if(Length(res)<n) then raise Exception.Create('wtf2 '); //+IntToStr(n)+' , '+IntToStr(Length(res))+' ... '+IntToStr(total)+': '+debugstr);
              res[n]:=s[i][j];
              n:=n+1;
           end;
       end;
   end;
 //  debugstr:= res+'//'+debugstr;
  // res:=debugstr;

end;







//==============================================================================
function nonNumeralPrefix(const s:string; p:integer):boolean;

begin
    result:=false;
    while (p>0) and (s[p] in NUMBERS) do begin
        p:=p-1;
    end;
    if(p<=0) then exit;
    if(s[p] in SEPARATORS) then exit;

    if (s[p] in NONNUMBERSIDENTIFCHAR ) then result:=true;
end;

//==============================================================================
//  treats single-line comments as whitespace
//  TODO: add support for multiline comments?
//==============================================================================
function IsWhitespace(const s:string; const st:integer=1):boolean;
var i,L:integer;
    inComment:boolean;
begin
    i := st;
    L := Length(s);
    inComment:=false;
    Result := true;
    while (i<=L) do begin
      case s[i] of
          ' ', #9:;
          #10, #13:
          begin
              inComment := false;
          end;
          '/':
          begin
            if not inComment then begin
              if (s[i+1]='/') and (i<L) then begin
                  inComment := true;
                  i := i + 1;
              end else begin
                  Result := false;
                  break;
              end;
            end;
          end;
          else
          begin
            if (not inComment) then begin
              Result := false;
              break;
            end;
          end;
      end; // case
      i := i + 1;
    end; // while
end;

//==============================================================================
function TryStrToIntX(const s:string;var x:integer):boolean;
var
   min,num,oct:boolean;


   i,r:integer;
begin
    min:=false;
    num:=false;
    oct:=false;
    r:=0;
    for i := 1 to Length(s) do begin
        if (not num) then begin
            if (s[i]=' ') then
            else if (s[i]='-') then begin
                 if(min) then begin result:=false; exit; end;
                 min:=true;
            end else if (s[i] in ['0'..'9']) then begin
                r:=integer(s[i])-integer('0');
                num:=true;
                if(s[i]='0') then oct:=true;
            end else begin
                result:=false;
                exit;
            end;


        end else if (s[i] in ['0'..'9']) then begin
            if(oct) then
                r:=r*8+integer(s[i])-integer('0')
            else
                r:=r*10+integer(s[i])-integer('0');

        end else begin
             result:=false;
             exit;
        end;
    end;
    result:=num;
    if(min) then r:=-r;
    if(result) then x:=r;

end;

//==============================================================================
//assigns to r an assignment if it can be found from i.
function GetAssigment( const s:string; var r:string; fromi:integer):boolean;
var
   i,L,j:integer;
begin
    Result:=true;
    i:=fromi;
    L:=Length(s);
    r:='';
    while( (i<=L) and (s[i]<>'=') ) do begin
        if (s[i]='/') and (i<L) and (s[i+1]='/') then begin
            i:=L;
        end;
        i:=i+1;
    end;
    j := L - 1;
    while( j>=1 ) do begin
         if(s[j]='/') and (s[j+1]='/') then begin
             L:=j-1;
             break;
         end;
         j := j  -1;
    end;

    if(i>L) then Result:=false
    else r:=Copy(s,i+1, L-i);
end;


//==============================================================================
procedure findchar(const s:string; var pos:integer; const start:integer; const c:char);
var
  L:integer;
begin
   L:=Length(s);
   pos:=start;
   while(pos<=L) do begin
	   if(s[pos]=c) then break;
	   pos:=pos+1;
   end;
end;

//==============================================================================
//  TStringList parameter must be initialized
//==============================================================================
function breakIntoLines(const buf : string): TStringList;
var
    line:string;
    i,k,L: integer;
    Lines : TStringList;
begin
    Lines := TStringList.Create();
    L:= Length(buf);

    i:=1;
    k:=0;

    while ( i<=L+1) do begin
        if (i=L+1) or ( buf[i] = #13 ) or (buf[i]=#10) then begin
            line := System.Copy(buf,k+1, i-k - 1);
            if( i+1<=L ) and (buf[i] =#13) and (buf[i+1]=#10) then
                i:= i +1;
            k:=i;
            Lines.Add(line);
        end;
        i:=i+1;
    end;
    Result := Lines;
end;

//==============================================================================
function FindOddCharacters(const s:string; const abound:integer=-1):boolean;
var
    bound:integer;
begin
    if(abound=-1) then bound := Length(s)
    else bound:=abound;
    result:=true;
    while(bound>=1) do begin
       if ( s[bound] in SEPARATORS ) and not(s[bound]=' ') and not(s[bound]=#9) then
           exit;
       dec(bound);
    end;
    result:=false;
end;



end.
