
{section  AddBackSlash }
Function  AddBackSlash(s1 : string) : string;
var s : string;
     begin
     if (length(s1) > 0) and (s1[length(s1)] <> '\') then s := s1 + '\'
     else s := s1;
     AddBackSlash := s;
     end;


{section  BooleanStr }
Function  BooleanStr( B : boolean ) : string;
var S : string[5];
    begin
    if B then
         BooleanStr := 'YES'
    else BooleanStr := 'NO ';
    end;


{section  BreakLine }
Function  BreakLine(var s : string; bklen : integer) : string;
var s1 : string;
    ll  : integer;
    done : boolean;
     begin
     RemoveTrailing(s,' ');
     s1 := s;
     if length(s) > bklen then
          begin
          ll := bklen;
          done := false;
          while (ll > 0) and not done do
               begin
               if      s[ll] = ' ' then done := true
               else if s[ll] = ',' then done := true
               else dec(ll);
               end;
          if ll > 1 then
               begin
               s1 := copy(s,1,ll);
               delete(s,1,ll);
               end
          else begin
               s1 := copy(s,1,bklen);
               delete(s,1,bklen);
               end;
          end
     else s := '';
     Breakline := s1;
     end;


{section  BreakLineChr }
Function  BreakLineChr(var s : string; bklen : integer; ch : char) : string;
var s1 : string;
    ll  : integer;
    done : boolean;
     begin
     RemoveTrailing(s,' ');
     s1 := s;
     if length(s) > bklen then
          begin
          ll := bklen;
          done := false;
          while (ll > 0) and not done do
               begin
               if      s[ll] = ch then done := true
               else dec(ll);
               end;
          if ll > 1 then
               begin
               s1 := copy(s,1,ll);
               delete(s,1,ll);
               end
          else begin
               s1 := copy(s,1,bklen);
               delete(s,1,bklen);
               end;
          end
     else s := '';
     BreakLineChr := s1;
     end;


{SECTION Buf16ToHexStr }
Function Buf16ToHexStr(addr : longint; count : integer; var xbuf; flag : boolean) : string;
            {[STRING] One line of the DUMP output}
var s,asc : string;
    i,j   : integer;
    buf   : array[1..16] of byte;
     begin
     s := ''; asc := '';
     move(xbuf,buf,16);
     j := 16;
     if count < 16 then j := count;
     if count < 1  then j := 1;
     for i := 1 to j do
          begin
          s := s + ByteToHex(buf[i]) + ' ';
          if buf[i] > 31 then asc := asc + chr(buf[i])
          else asc := asc + '.';
          end;
     Buf16ToHexStr := FmtAddress(addr,6,flag)+': '+ leftstr(s,48) +
                                 ' | ' + asc;
     end;


{section  ByteToHex }
Function  ByteToHex( B : byte) : string;
var s : string[2];
    b1 : byte;
     begin
     s := '00';
     b1 := (b and $F0) div 16;
     if b1 < 10 then s[1] := chr(b1+48)
     else s[1] := chr(b1+55);
     b1 := b and $0F;
     if b1 < 10 then s[2] := chr(b1+48)
     else s[2] := chr(b1+55);
     ByteToHex := s;
     end;


{section CenterStr }
Function CenterStr(s : string; w : byte) : string;
{ Centers a string in a field of specified width }
var NewStr : string;
    i       : word;
    p       : word;
     begin
     FillChar(NewStr, SizeOf(NewStr), ' ');
     NewStr[0] := CHR(w);
     p         := (w - length(s)) SHR 1;
     for i := 1 to length(s) do NewStr[p + i] := s[i];
     CenterStr := NewStr
     end;


{SECTION  Compare }
Function  Compare(s1,s2 :string) : boolean;
                        {[STRING] Compares s1 to s2 - s2 can have wildcards }
var i    : integer;
    done : boolean;
    ch   : char;
     begin
    { writeln('Compare  [',s1,'] [',s2,']');}
     Compare := true; i := 0; done := false;
     while (i < length(s2)) and not done do
          begin
          inc(i);
          ch := s2[i];
          case ch of
               '?'   : begin end;   {match fine}
               '*'   : begin Compare := true; done := true; end;
               else    begin
                       if s1[i] <> ch then
                            begin
                           { writeln('char ',i,' ',s1[i],' ',ch); }
                            Compare := false;
                            done := true;
                            end;
                       end;
               end;
          end;
     if not done and (i <> length(s1)) then
          begin
         { writeln('ending ',i,' ',length(s1)); }
          Compare := false;
          end;
     end;


{SECTION  CompareL }
Function  CompareL(s1,s2 :string; len : integer) : boolean;
                        {[STRING] Compares s1 to s2 for length len }
     begin
     CompareL := Compare(leftstr(s1,len),leftstr(s2,len));
     end;


{SECTION  CompareUpL }
Function  CompareUpL(s1,s2 :string; len : integer) : boolean;
                        {[STRING] Compares s1 to s2 for length len (s1,s2 shifted UP)}
     begin
     CompareUpL := Compare(UpCaseStr(leftstr(s1,len)),
                           UpCaseStr(leftstr(s2,len)));
     end;


{section CompressStr }
Function CompressStr(s1 : string) : string;
var ls,j,rc : integer;
    s,s2    : string;
    ch      : char;
    begin
     S := S1;
    ls := length(s);
    if ls < 3 then
        begin
        CompressStr := s;
        exit;
        end;
    s2 := '';
    j := 1;
    while j <= ls  do
        begin
        if (j > (ls-2)) or (s[j] <> s[j+1]) or (s[j] <> s[j+2]) then
            s2 := s2 + s[j]
        else
            begin
            ch := s[j];
            inc(j);
            rc := 0;
            s2 := s2 + s[j];
            while (j <= ls) and (s[j] = ch) do
                begin
                inc(rc);
                inc(j);
                end;
            s2 := s2 + chr(160+rc);
            if j <= ls then s2 := s2 + s[j];
            end;
        inc(j);
        end;
    CompressStr := s2;
    end;


{section  ConstStr }
Function  ConstStr(C : Char; N : Integer) : string;
    (* returns a string with N characters of value C *)
var S : string;
    begin
    if N < 0 then N := 0;
    S[0] := Chr(N);
    FillChar(S[1],N,C);
    ConstStr := s;
    end;



{section  CopyRemove }
Function  CopyRemove(var s : string; f,l : integer) : string;
                            {[STRING] copies then deletes a substring }
var len : integer;
     begin
     CopyRemove := '';
     if (f > 0) and (f <= l) and (l <= length(s)) then
          begin
          len := (l - f) + 1;
          CopyRemove := copy(s,f,len);
          delete(s,f,len);
          end;
     end;


{section  CurrDTimeString }
Function  CurrDTimeString : string;
    var
        temp1,temp2       : string;
        Yr, Mo, Da, dow   : word;
        Hr, Mn, Sc, sc100 : word;
        i                 : integer;
        l                 : longint;
    begin
    GetDate(yr,mo,da,dow);
    l := (yr-1900)*tenthousand + mo*onehundred +da;
    str(l:6,temp1);
    GetTime(hr,mn,sc,sc100);
    l := hr*tenthousand + mn*onehundred +sc;
    str(l:6,temp2);
    for i := 1 to 6 do
        begin
        if temp1[i] = ' ' then temp1[i] := '0';
        if temp2[i] = ' ' then temp2[i] := '0';
        end;
    CurrDTimeString := temp1+temp2;
    end;


{section  DefaultDriveStr }
Function  DefaultDriveStr : string;
var s : string;
    begin
    GetDir(0,s);
    DefaultDriveStr := s;
    end;


{section  DeleteBackSlash }
Function  DeleteBackSlash(s1 : string) : string;
var s : string;
     begin
     if (length(s1) > 0) and (s1[length(s1)] = '\') then
          s := copy(s1,1,length(s1)-1)
     else s := s1;
     DeleteBackSlash := s;
     end;


{section  DirTag }
Function  DirTag(path : string) : string;
var s : string;
    i : integer;
     begin
     s := path;
     i := pos('\',s);
     while i > 0 do
          begin
          delete(s,1,i);
          i := pos('\',s);
          end;
     Dirtag := s;
     end;


{section DnCaseStr }
Function DnCaseStr(s : string) : string;
{ Converts a string to lower case characters }
var i : integer;
    b : byte;
     begin
     for i := 1 to length(s) do
          begin
          b := ord(s[i]);
          if (b > 64) and (b < 91) then s[i] := chr(b+32);
          end;
     DnCaseStr := s;
     end;


{section DollarStr }
Function DollarStr( R : real; L : integer ) : string;
var S : string;
    begin
    S := '';
    case L of
        4..15  : Str(R:L:2,S);
        else     S := ConstStr('*',L);
        end;
    DollarStr := s;
end;



Function  DOSErrStr(err : integer) : string;
{ DOS file error returns }
var s : string;
     begin
     case err of
         0        : s :=  'ok ' ;
         1        : s :=  'Invalid function number' ;
         2        : s :=  'file not found' ;
         3        : s :=  'Path not found' ;
         4        : s :=  'Too many open files' ;
         5        : s :=  'File access denied' ;
         6        : s :=  'Invalid file handle' ;
         12       : s :=  'Invalid file access code' ;
         15       : s :=  'Invalid drive number' ;
         18       : s :=  'No More files' ;
         100      : s :=  'Disk read error' ;
         101      : s :=  'Disk write error' ;
         102      : s :=  'File not assigned' ;
         103      : s :=  'File not open' ;
         104      : s :=  'File not opened for input' ;
         105      : s :=  'File not opened for output' ;
         150      : s :=  'Disk is write protected' ;
         152      : s :=  'drive not ready' ;
         159      : s :=  'Printer out of paper' ;
         160      : s :=  'Device write fault' ;
         162      : s :=  'Hardware failure' ;
         200      : s :=  'Division by zero' ;
         201      : s :=  'Range check' ;
         202      : s :=  'Stack overflow' ;
         203      : s :=  'Heap overflow' ;
         204      : s :=  'Invalid pointer operation' ;
         205..207 : s := 'Floating point problem' ;
         208..209 : s := 'Overlay problem' ;
         210..214 : s := 'Object problem' ;
         else       s := 'USER ERR ';
         end;
     DOSErrStr := 'DOS Error('+integerstr(err,4)+') '+s+'. ';
     end;


{section  DumpRecBufInHex }
Procedure DumpRecBufInHex(recnum : longint; recsiz : integer; var rec);
            {[DEBUG] Dumps a record buffer in HEX }
var l,rs : longint;
    rbuf : array[1..2048] of byte;
    zbuf : array[1..16] of byte;
    i,j  : integer;
     begin
     i := 1; rs := recsiz;
     if rs > sizeof(rbuf) then rs := sizeof(rbuf);
     fillchar(rbuf,sizeof(rbuf),0);
     move(rec,rbuf,rs);
     l := (recnum-1)*recsiz;
     writeln('Record - ',recnum,'    size=',rs,
             '    fileaddr:',l);
     while i < recsiz do
          begin
          move(rbuf[i],zbuf,16);
          writeln(Buf16ToHexStr(i,(recsiz-i),zbuf,false));
          i := i + 16;
          end;
     if recsiz > 16 then writeln(' ');
     end;



{section  EquivalentFile }
Function  EquivalentFile(fn1,fn2 : string) : boolean;
var same : boolean;
    sr1, sr2 : searchrec;
     begin
     same := false;
     if (fileInfo(fn1,'',sr1) = 0) and
        (fileInfo(fn2,'',sr2) = 0) then
          begin
          if (sr1.size = sr2.size) and
             (sr1.time = sr2.time) then same := true;
          end;
     EquivalentFile := same;
     end;


{section  EraseFile }
Procedure EraseFile(s : string);
var f : file;
    ch : char;
    begin
    assign (f,s);
    {$I-}
    reset (f);
    {$I+}
    if IOResult = 0 then
        begin
        close(f);
        Erase(f);
        end;
    end;


{section  ExtractDelimitedStr }
Function  ExtractDelimitedStr(var s : string; lchar,rchar : char) : string;
                       {[STRING] extracts inside of a delimited substring }
var i,j  : integer;
    s1   : string;
     begin
     ExtractDelimitedStr :=  '';
     i := pos(lchar,s);
     if i > 0 then
          begin
          j := pos(rchar,s);
          if (j > i) then
               begin
               s1 :=  CopyRemove(s,i,j);
               delete(s1,1,1);
               if length(s1) > 0 then delete(s1,length(s1),1);
               ExtractDelimitedStr :=  s1;
               end;
          end;
     end;


{section ExtractPath }
Function ExtractPath(var fname : string) : string;
var i : integer;
    npath : string;
    begin
    npath := '';
    i := pos('\',fname);
    while i > 0 do
         begin
         npath := npath + copy(fname,1,i);
         delete(fname,1,i);
         i := pos('\',fname);
         end;
    ExtractPath := npath;
    end;


{section FileDate }
Function FileDate(fname : string; ext : string) : longint;
var l : longint;
    fn : string;
    SR : searchrec;
     begin
     fn := fname;
     l := 0;
     if ext <> '' then ForceExt(fn,ext);
     FindFirst(fn,anyfile,SR);
     if dosError = 0 then l := SR.time;
     FileDate := l;
     end;


{section FileExists }
Function FileExists(FName : String) : boolean;
var f     : file;
    fAttr : word;
     begin
     assign(f, FName);
     GetFAttr(f, fAttr);
     FileExists := (DosError = 0)
           and ((fAttr and Directory) = 0)
           and ((fAttr and VolumeID)  = 0)
     end;  { FileExists }



{section  FileExt }
Function  FileExt(fname : string) : string;
var i : integer;
    ext : string[3];
    begin    {doesn't use FSplit - maybe smaller }
    ext := '';
    i := pos('.',fname);
    if i > 0 then ext := copy(fname,i+1,3);
    FileExt := ext;
    end;


{section FileInfo }
Function FileInfo(filespec : string; ext : string;
                   var SR : searchrec) : integer;
var fn : string;
    err : integer;
     begin
     err := 0;
     fn := filespec;
     if ext <> '' then ForceExt(fn,ext);
     FindFirst(fn,anyfile,SR);
     FileInfo := dosError;
     end;


{section  FileExtStr }
Function  FileExtStr(fname : string) : string;
var dir,nam,ext : string;
     begin
     FSplit(fname,dir,nam,ext);
     FileExtStr := ext;
     end;


{section  FilePathStr }
Function  FilePathStr(fname : string) : string;
var dir,nam,ext : string;
     begin
     FSplit(fname,dir,nam,ext);
     FilePathStr := dir;
     end;


{section  FileRootStr }
Function  FileRootStr(fname : string) : string;
var dir,nam,ext : string;
     begin
     FSplit(fname,dir,nam,ext);
     FileRootStr := nam;
     end;


{section FindAndReplaceStr }
Function FindAndReplaceStr(str,fstr,rstr : string; both,all : boolean) : string;
                 {[STRING] finds fstr replaces with rstr, options}
var s,s1,f1s : string;
    i,j    : integer;
    ok : boolean;
     begin
     s   := str;
     if both then
          begin
          f1s := UpCaseStr(fstr);
          s1  := UpCaseStr(s);
          end
     else begin
          f1s := fstr;
          s1  := s;
          end;
     ok := true;
     j := 0;
     while ok do
          begin
          i := pos(f1s,s1);
          if (i > 0) and (j < i) then    {recursion problem}
               begin
               j := i;
               delete(s,i,length(f1s));
               insert(rstr,s,i);
               delete(s1,i,length(f1s));
               insert(rstr,s1,i);
               end
          else ok := false;
          if not all then ok := false;
          if i > 200 then ok := false;  { by 'a' -> 'aa' }
          end;
     FindAndReplaceStr := s;
     end;


{SECTION FmtAddress }
Function FmtAddress( a : longint; l : integer; flag : boolean) : string;
          {[STRING] formats a longint optionally as hex - for DUMP }
var s : string;
    x : byte;
     begin
     if not Flag then
          s := LongIntStr(a,l)
     else begin
          s := '  ';
          x := byte(a div 256);
          s := s + ByteToHex(x);
          x := byte(a AND $FF);
          s := s + ByteToHex(x);
          end;
     FmtAddress := s;
     end;



{section FmtChr }
Function FmtChr(b : byte) : string;
var s : string[5];
    begin
    s := '<--->';
    case b of
        0..31, 127  : s := '<' + FmtCvtChr(b) + '>';
        32..126     : s :=  chr(b);
        160..254    : begin
                      str(b:3,s);
                      s := '<' + s + '>';
                      end;
       end;
    FmtChr := s;
    end;


{section FmtCvtChr }
Function FmtCvtChr(b : byte) : string;
var s : string[3];
    begin
    s := '---';
    case b of
        0  : s := 'NUL';
        1  : s := 'SOH';
        2  : s := 'STX';
        3  : s := 'ETX';
        4  : s := 'EOT';
        5  : s := 'ENQ';
        6  : s := 'ACK';
        7  : s := 'BEL';
        8  : s := 'BS ';
        9  : s := 'HT ';
       10  : s := 'LF ';
       11  : s := 'VT ';
       12  : s := 'FF ';
       13  : s := 'CR ';
       14  : s := 'SO ';
       15  : s := 'SI ';
       16  : s := 'DLE';
       17  : s := 'DC1';
       18  : s := 'DC2';
       19  : s := 'DC3';
       20  : s := 'DC4';
       21  : s := 'NAK';
       22  : s := 'SYN';
       23  : s := 'ETB';
       24  : s := 'CAN';
       25  : s := 'EM ';
       26  : s := 'SUB';
       27  : s := 'ESC';
       28  : s := 'FS ';
       29  : s := 'GS ';
       30  : s := 'RS ';
       31  : s := 'US ';
       127 : s := 'DEL';
       else  begin
             if b > 31 then s := chr(b) + '  ';
             end;
       end;
    FmtCvtChr := s;
    end;


{section  FmtHMS }
Function  FmtHMS(hr, mn, sc : word) : string;
var s : string[8];
    l : longint;
     begin
     s := '        ';
     l := (hr+100)*tenthousand + mn*onehundred +sc;
     str(l:8,s);
  {   if s[3] = '0' then s[3] := ' '; }
     FmtHMS :=  s[3] + s[4] + ':' + s[5] + s[6] + ':' +  s[7] + s[8];
     end;


{section  FmtKstr }
Function  FmtKstr(l : longint) : string;
var s : string[10];
     begin
     s := '**';
     str((l div $400),s);
     FmtKstr := s + 'k';
     end;


{section  FmtKstrComma }
Function  FmtKstrComma(l : longint) : string;
var s : string;
     begin
     s := '**';
     str((l div $400),s);
     if length(s) > 3 then insert(',',s,length(s)-2);
     FmtKstrComma := s + 'k';
     end;


{section FmtStr }
Function FmtStr(s : string) : string;
var s1 : string;
    i : integer;
     begin
     s1 := '';
     if length(s) > 0 then for i := 1 to length(s) do
          begin
          s1 := s1 + FmtChr(ord(s[i]));
          end;
     fmtStr := s1;
     end;


{section  FmtYMD }
Function  FmtYMD(Yr, Mo, Da : word) : string;
var s : string;
    l : longint;
     begin
     l := yr*tenthousand + mo*onehundred +da;
     str(l:8,s);
     if s[5] = '0' then s[5] := ' ';
     FmtYMD :=  s[5] + s[6] + '/' + s[7] + s[8] + '/' +  s[3] + s[4];
     end;


{section  ForceExt }
Procedure ForceExt(var fname : string; ext : string);
var i : integer;
    begin
    i := pos('.',fname);
    if i > 0 then fname := copy(fname,1,i-1);
    if ext[1] = '.' then fname := fname + ext
    else fname := fname + '.' + ext;
    end;


{section  ForcePath }
Procedure ForcePath(var fname : string; path : string);
var i : integer;
    npath : string;
    begin
    npath := ExtractPath(fname); { take out path and throw away}
    npath := path;
    if path = '' then
         begin
         getdir(0,npath);
         npath := addbackslash(defaultdrivestr)+npath;
         end;
    fname := addbackslash(path) + fname;
    end;



{section ForceRenameFile }
Function ForceRenameFile(fname1,fname2 : string) : boolean;
                  {[FILE] Erases file 2 first. }
     begin
     ForceRenameFile := false;
     EraseFile(fname2);
     if RenameFile(fname1,fname2) then ForceRenameFile := true;
     end;


{section ForceRenameToBak }
Function ForceRenameToBAK(fname : string) : boolean;
var fn1 : string;
     begin
     ForceRenameToBAK := true;
     fn1 := fname;
     ForceExt(fn1,'BAK');
     if not ForceRenameFile(fname,fn1) then
          begin
          ForceRenameToBAK := false;
          writeln('unable to rename [',fname,']  to [',fn1,']');
          end;
     end;


{section  FormatDTime }
Function  FormatDTime : string;
var Yr, Mo, Da, dow   : word;
    Hr, Mn, Sc, sc100 : word;
var temp : string;
    begin
    GetDate(yr,mo,da,dow);
    GetTime(hr,mn,sc,sc100);
    FormatDTime :=  FmtYMD(yr,mo,da) + ' ' + FmtHMS(hr,mn,sc);
    end;


{section GetNumber }
Function GetNumber( var astring : string) : real;
var x       : real;
    bstring : string;
    error   : integer;
    begin
    x := 0;
    bstring := GetString(astring);
    if length(bstring) > 0 then
        begin
        val(bstring,x,error);
        if (error <> 0) then
            writeln(' val conversion error  * ',bstring,' *  ',error);
        end;
    GetNumber := x;
    end;



{section  GetSTring }
Function  GetString ( var s : string) : string;
var s1 : string;
    i,l     : integer;
     begin
     i := pos(',',s);
     if i > 0 then
          begin
          GetString := copy(s,1,i-1);
          delete(s,1,i);
          end
     else begin
          GetString := s;
          s := '';
          end;
     end;



{section  HexAddressToLongInt }
Function  HexAddressToLongInt(s : string) : longint;
var l1,l2,l : longint;
    s1,s2 : string[5];
    i    : integer;
     begin
     i := pos(':',s);
     if i > 0 then
          begin
          s1 := copy(s,1,i-1);
          s2 := copy(s,i+1,length(s)-i);
          end
     else begin
          s1 := '';
          s2 := s;
          end;
     l1 := hextolongint(s1);
     l2 := hextolongint(s2);
   {  writeln('hexaddresstolongint [',s1,'] [',s2,'] ',l1,'  ',l2);}
     HexAddressToLongInt := (l1 * 16) + l2;
     end;


{section  HexToByte }
Function  HexToByte( st : string) : byte;
var  s     : string[3];
     b1,b2     : byte;
     begin
     HexToByte := 0;
     s := st;
     if s[1] = '$' then delete(s,1,1);
     if length(s) < 2 then exit;
     if ord(s[1]) < ord('A') then b1 := ((ord(s[1])-48)and $F)
     else b1 := ((ord(s[1])-55) and $F);
     if ord(s[2]) < ord('A') then b2 := ((ord(s[2])-48)and $F)
     else b2 := ((ord(s[2])-55) and $F);
     HexToByte := (b1 * 16) + b2;
     end;


{section  HexToLongInt }
Function  HexToLongInt(s : string) : longint;
var l1,l : longint;
    ll   : byte;
    s1   : string[6];
    nibble : string;
     begin
     s1 := s;
     ll := length(s1);
     if (ll div 2) * 2 <> ll then s1 := '0' + s1;
     l  := 0;
     while length(s1) > 0 do
          begin
          nibble := s1;
          delete(s1,1,2);
          l1 := hextobyte(nibble);
          l := l * $100 + l1;
          end;
     HexToLongInt := l;
     end;


{section  Int2Real }
Function  Int2Real(i : Integer) : real;
var y     : real;
     begin
     y := i;
     Int2Real := y / 8.0;
     end;


{section IntegerStr }
Function IntegerStr( I : integer; L : integer ) : string;
var S : string;
    begin
    Str(I,S);
    IntegerStr := RightStr(S,L);
    end;


{section LeftStr }
Function LeftStr( St : string; L : integer ) : string;
     begin
     LeftStr := copy(St+conststr(' ',L-length(St)),1,l);
     end;


{section LJStr }
Function LJStr(s : string; w : byte) : string;
           {[STRING] Left justifies a string in a field of specified width }
var NewStr : string;
     begin
     FillChar(NewStr, SizeOf(NewStr), ' ');
     NewStr    := s;
     NewStr[0] := CHR(w);
     LJStr     := NewStr
     end;


{section  LongIntStr }
Function  LongIntStr( I : longint; L : integer ) : string;
var S : string;
    begin
    Str(I,S);
    LongintStr := RightStr(S,L);
    end;



{section MergeStr }
Function MergeStr( s : string; posn : integer; s1 : string) : string;
var i,j,n,p : integer;
    st      : string;
    begin
    st := s;
    p := posn;
    if p < 1 then p := 1;
    if (p > 253) then exit;
    i := length(s1);
    n := p+i-1;
    if n > 253 then i := 253 - n;
    if n > length(st) then st := leftstr(st,n);
    move(s1[1],st[p],i);
    Mergestr := st;
    end;


{section MIN }
Function Min(i1,i2 : integer) : integer;
     begin
     if i1 < i2 then min := i1
     else min := i2;
     end;


{section  MiscDelayNTicks }
Procedure MiscDelayNTicks(n : longint);
      {[DATETIME] A delay of 1 seems to be about 0.05 seconds}
var j : integer;
    t : longint;
     begin
     if n = 0 then exit;
     for j := 1 to n do
          begin
          t := TicksSinceMidnight;
          while TicksSinceMidnight = t do begin end;
          end;
     end;


{section  NumericsOnlyStr }
Function  NumericsOnlyStr(s : string) : string;
var i  : integer;
    s1 : string;
     begin
     s1 := '';
     if length(s) > 0 then
          begin
          for i := 1 to length(s) do
              if s[i] in ['0'..'9','-'] then s1 := s1 + s[i];
          end;
     NumericsOnlyStr := s1;
     end;



{section  PackTimeStr }
Function  PackTimestr(PT : longint) : string;
var d : DateTime;  { DOS }
var temp : string[14];
    begin
    UnPackTime(PT,d);
    temp :=  FmtYMD(d.year,d.month,d.day) + ' ' +
                    FmtHMS(d.hour,d.min,d.sec);
    PackTimestr := temp;
    end;


{section  PatchStr }
Procedure PatchStr(var s : string; ch1,ch2 : char);
var i : integer;
    begin
    i := 1;
    while i <= length(s) do
         begin
         if s[i] = ch1 then s[i] := ch2;
         inc(i);
         end;
    end;


{section PctStr }
Function PctStr(x,y : real; L,D : integer) : string;
var s : string;
    z : real;
     begin
     z := (x/(y+0.00001)) * 100;
     if z > 9999 then z := 9999;
     s := realstr(z,L,D);
     PctStr := s + '%';
     end;


{section ProperName }
Function ProperName(s : string) : string;
{ Converts a string to lower case characters and capitalizes first letter}
var i : integer;
    b : byte;
     begin
     s := DnCaseStr(s);
     s[1] := Upcase(s[1]);
     ProperName := s;
     end;


{section  QT }
Function  QT(s : string) : string;    { makes a string with quotes around it }
     begin
     QT := '''' + s + '''';
     end;


{section Real2Int }
Function Real2Int(x : real) : Integer;
{ pack reals in range -4095 to +4095 to an integer }
{ resolution is to 1/8                             }
var y     : real;
    l     : longint;
     begin
     Real2Int := 0;
     l := abs(trunc(x*8));
     if (l > 32760) then l := 32760;
     if x < 0 then l := -1 * l;
     Real2Int := l;
     end;


{section RealStr }
Function RealStr( R : real; L,D : integer ) : string;
var S : string;
    begin
    Str(R:12:D,S);
    RealStr := RightStr(S,L);
    end;


{section RealZero }
Function RealZero( x : real) : boolean;
     begin
     if abs(x) < 0.01 then RealZero := true
     else RealZero := false;
     end;


{section  RemoveBlanks }
Procedure RemoveBlanks(var astring : string);
var j : integer;
    begin
    j := 1;
    while j <= length(astring) do
        begin
        if (astring[j] = ' ') then delete(astring,j,1)
        else inc(j);
        end;
    end;


{section RemoveBrackets }
Function RemoveBrackets(s : string) : string;
var len : integer;
    s1  : string;
    begin
    len := length(s);
    s1  := trimstr(s);
    if len > 2 then
         begin
         case s1[1] of
             '[' :  begin
                    if s1[len] = ']'   then RemoveEnds(s1);
                    end;
             '{' :  begin
                    if s1[len] = '}'   then RemoveEnds(s1);
                    end;
             '(' :  begin
                    if s1[len] = ')'   then RemoveEnds(s1);
                    end;
             '''' : begin
                    if s1[len] = ''''  then RemoveEnds(s1);
                    end;
             '"'  : begin
                    if s1[len] = '"'   then RemoveEnds(s1);
                    end;
             '<'  : begin
                    if s1[len] = '>'   then RemoveEnds(s1);
                    end;
             else   begin end;
             end;
         end;
     RemoveBrackets := s1;
     end;


{section  RemoveEnds }
Procedure RemoveEnds(var s : string);
     begin
     if length(s) < 2 then exit;
     delete(s,1,1);
     delete(s,length(s),1);
     end;


{section  RemoveExcessBlanks }
Procedure RemoveExcessBlanks(var astring : string);
var prev : char;
    j    : integer;
    begin
    prev := ' ';
    j := length(astring);
    if j > 0 then
        begin
        j := 1;
        repeat
            begin
            if (astring[j] = ' ') and (prev = ' ') then delete(astring,j,1)
            else
                begin
                prev := astring[j];
                j := j + 1;
                end;
            end;
        until j > length(astring);
        end;
    end;


{section  RemoveLeading }
Procedure RemoveLeading(var s : string; ch : CHAR);
var i,l : integer;
{ Remove specified leading characters from string }
     begin
     i := 1;
     l := length(s)+1;
     while (i < l) and (s[i] = ch) do inc(i);
     if i > 1 then delete(s, 1, i-1);
     end;


{section  RemoveLeading }
Procedure RemoveLeadingTUG(var s : string; ch : CHAR);
{ Remove specified leading characters from string }
     begin
     while (length(s) > 0) and (s[1] = ch) do
          delete(s, 1, 1)
     end;


{section  RemoveTrailing }
Procedure RemoveTrailing(var s : string; ch : CHAR);
{ Remove specified trailing characters from string }
     begin
     while (length(s) > 0) and (s[length(s)] = ch) do
            s[0] := chr(ord(s[0]) - 1)
     end;



{section RenameFile }
Function RenameFile(fname1,fname2 : string) : boolean;
                  {[FILE] Returns false if fails. }
var fil : file;
    err : integer;
     begin
     RenameFile := false;
     assign(fil,fname1);
     {$I-} rename(fil,fname2); {$I+}
     err := IOResult;
     if err = 0 then RenameFile := true
     else writeln('RenameFile error ',err);
     {$I-} close(fil); {$I+}
     err := IOResult;  {ignore error on close}
     end;


{section  ReplaceStr }
Procedure ReplaceStr( var Str : string; Offset : integer; S1 : string);
     begin
     Str := Str + conststr(' ',offset-length(Str));
     Delete(Str,Offset,length(S1));
     Insert(S1,Str,Offset);
     end;


{section RightStr }
Function RightStr( St : string; l : integer ) : string;
var S : string;
     begin
     s := conststr(' ',L-length(St))+St;
     RightStr := copy(s,(length(s)-l)+1,l);
     end;


{section RJStr }
Function RJStr(s : string; w : byte) : string;
          {[STRING] Right justifies a string in a field of specified width }
var NewStr : string;
     begin
     NewStr := s;
     while length(NewStr) < w do
          insert(' ', NewStr, 1);
     RJStr := NewStr
     end;



{section  SameFile }
Function  SameFile(fn1,fn2 : string) : boolean;
var same : boolean;
    sr1, sr2 : searchrec;
     begin
     same := false;
     if (fileInfo(fn1,'',sr1) = 0) and
        (fileInfo(fn2,'',sr2) = 0) then
          begin
          if (sr1.size = sr2.size) and
             (sr1.time = sr2.time) and
             (sr1.name = sr2.name) then same := true;
          end;
     SameFile := same;
     end;


{section  SetDateBytes }
Procedure SetDateBytes(var yr,mo,dy : byte);
var year,month,day,doy : word;
     begin
     getdate(year,month,day,doy);
     yr := year-1900;
     mo := month;
     day := dy;
     end;


{section SizeofFile }
Function SizeofFile(fname : string; ext : string) : longint;
var l : longint;
    fn : string;
    SR : searchrec;
     begin
     fn := fname;
     l := 0;
     if ext <> '' then ForceExt(fn,ext);
     FindFirst(fn,anyfile,SR);
     if dosError = 0 then l := SR.size;
     SizeofFile := l;
     end;


{section  StrBool }
Function  StrBool (s : string) : boolean;
var x : boolean;
    s1 : string;
    code : integer;
     begin
     x := true;
     s1 := UpCaseStr(s);
     if (s1 = 'NO') or (s1 = 'OFF') then x := false;
     StrBool := x;
     end;


{section  StrCal }
Procedure StrCal(ds : string; var dd,mm,yy : integer);
var s,ss : string[8];
    i,l : word;
    err,defyear,defmonth,defday : word;
    begin
    s := ds;
    getdate(defyear,defmonth,defday,err);
    defyear := defyear mod 100;
    l := length(s);
    if l = 0 then
         begin
         dd := defday;
         mm := defmonth;
         yy := defyear;
         exit;
         end;
    for i := 1 to l do if s[i] = '-' then s[i] := '/';
    for i := 1 to l do
         if not (s[i] in ['0'..'9','/']) then s[i] := ' ';
    removeblanks(s);
    while length(s) <> 8 do
        begin
        if s[2] = '/' then
             begin
             s := '0' + s;
             l := length(s);
             end;
        case l of
            1..2   :  begin         { d,dd }
                      s := integerstr(defmonth,2) + '/' + s;
                      s := s + '/' + integerstr(defyear,2);
                      removeblanks(s);
                      end;

            3..5   :  begin  {m/d,mm/d,mm/dd - add year}
                      s := s + '/' + integerstr(defyear,2);
                      removeblanks(s);
                      end;

            7      :  begin   {mm/d/yy, mm/dd/y}
                      if      s[5] = '/' then insert('0',s,4)
                      else if s[6] = '/' then insert('0',s,6)
                      else s := '01/01/01';
                      end;
            8       : begin end;

            else s := '01/01/01';
            end;
        l := length(s);
        end;
    ss := copy(s,1,2);
    val(ss,mm,err);
    ss := copy(s,4,2);
    val(ss,dd,err);
    ss := copy(s,7,2);
    val(ss,yy,err);
    end;


{section  StrInt }
Function  StrInt(s : string) : integer;
var  x,err  : integer;
     begin
     x := 0;
     val(s,x,err);
     if err > 1 then val(copy(s,1,err-1),x,err);
     StrInt := x;
     end;


{section  StrLong }
Function  StrLong(s : string) : longint;
var  err  : integer;
     x    : longint;
     begin
     x := 0;
     val(s,x,err);
     if err > 1 then val(copy(s,1,err-1),x,err);
     StrLong := x;
     end;


{section  StrReal }
Function  StrReal(s : string) : real;
var  err  : integer;
     x    : real;
     begin
     x := 0;
     val(s,x,err);
     if err > 1 then val(copy(s,1,err-1),x,err);
     StrReal := x;
     end;


{section  SuggestExt }
Procedure SuggestExt(var fname : string; ext : string);
                        {[FILE] only if EXT not specified}
var i : integer;
    begin
    i := pos('.',fname);
    if (i = 0) or (i = length(fname)) then ForceExt(fname,ext);
    end;


{section TicksSinceMidnight }
Function TicksSinceMidnight : longint;
var hr,mn,sc,sc100 : word;
     begin
     GetTime(hr,mn,sc,sc100);
     TicksSinceMidnight := sc100 + (sc * onehundred) +
                                   (mn * 60 * onehundred) +
                                   (hr * 36 * tenthousand);
     end;


{section TicksToSecs }
Function TicksToSecs ( t : longint ) : real;
     begin
     TicksToSecs := t / 100.0;
     end;


{section TicksToSecsStr }
Function TicksToSecsStr ( t : longint ) : string;
var hr,mn,sc,tk : word;
    tx          : longint;
     begin
     mn := 0;     sc := 0;     tk := 0;
     tx := t;
     hr := word(tx div 360000);
     tx := tx -  (hr * 360000);
     if tx > 0 then
          begin
          mn := word(tx div 6000);
          tx := tx -  (mn * 6000);
          if tx > 0 then
               begin
               sc := word(tx div 100);
               tx := tx -  (sc * 100);
               end;
          tk := word(tx);
          end;
     TicksToSecsStr :=  FmtHMS(hr,mn,sc)+'.'+integerstr(tk+100,2);
     end;


{section  Trim }
Procedure Trim(var s : string);
var i : integer;
     begin
     RemoveTrailing(s,' ');
     RemoveLeading(s,' ');
     end;


{section TrimStr }
Function TrimStr(s : string) : string;
var s1 : string;
     begin
     s1 := s;
     trim(s1);
     TrimStr := s1;
     end;


{section UnCompressStr }
Function UnCompressStr(s : string) : string;
var ls,j,k,rc : integer;
    s2      : string;
    ch      : char;
    begin
    ls := length(s);
    s2 := '';
    j := 1;
    while j <= ls  do
        begin
        if (ord(s[j]) < (160+1)) then s2 := s2 + s[j]
        else
            begin
            ch := s[j-1];
            rc := ord(s[j]) - 160;
            for k := 1 to rc do s2 := s2 + ch;
            end;
        inc(j);
        end;
    UnCompressStr := s2;
    end;


{section  UnQT }
Function  UnQT(s : string) : string;    { removes quotes from around a string }
var s1 : string;
     begin
     s1 := s;
     if s1[1] = '''' then delete(s1,1,1);
     if s1[length(s1)] = '''' then delete(s1,length(s1),1);
     UnQT := s1;
     end;



{section UpCaseStr }
Function UpCaseStr(s : STRING) : string;
{ Converts a string to upper case characters }
var i : integer;
     begin
     for i := 1 to length(s) do
         s[i] := UpCase(s[i]);
     UpCaseStr := s
     end;


{section VolumeLabel }
Function VolumeLabel( drive : string) : string;
var SR : searchrec;
     begin
     FindFirst(drive+'*.*',VolumeID,SR);
     if (DOSError = 0) then
          VolumeLabel := SR.Name
     else VolumeLabel := '';
     end;
