/* Routinesheader

	Name:		ChangeFunc.c
	Main:		Change
	Versionstring:	$VER: ChangeFunc.c 1.8 (01.01.2000)
	Author:		SDI
	Distribution:	PD
	Description:	all internal texts for Change program

 1.0   23.12.97 : first version
 1.1   26.12.97 : fixed bugs
 1.2   29.12.97 : fixed bugs
 1.3   02.07.98 : fixed bug
 1.5   05.07.98 : fixed bug with OVERWRITE
 1.6   03.12.98 : fixed bug with POSITION and PATTERN
 1.7   31.12.98 : no more errors when overwriting source and
	using patterns
 1.8   01.01.00 : fixed problem with PATH
*/

#include <proto/dos.h>
#include <proto/exec.h>
#include <exec/memory.h>
#include "ChangeTexts.h"
#include "ChangeFunc.h"
#include "GetChar.h"
#include "SDI_ASM_STD_protos.h"
#include "SDI_defines.h"

static ULONG TestSameFile(STRPTR a, STRPTR b)
{
  ULONG l1, l2, ret = 0;
   
  if(!(l1 = Lock(a, SHARED_LOCK)))
    return 0;
  if((l2 = Lock(b, SHARED_LOCK)))
  {
    if(SameLock(l1, l2) == LOCK_SAME)
      ret = 1;
    UnLock(l2);
  }
  UnLock(l1);
  return ret;
}

static void OutputResult(ULONG f, ULONG c)
{
  STRPTR str = TEXT_STATUS_TIMES;

  if(f == 1)
    str = TEXT_STATUS_TIME;
  if(!f)
    Printf(TEXT_STATUS_NOT_FOUND);
  else if(f == c)
    Printf(TEXT_STATUS_FOUND_EQ_CHANGE, f, str);
  else if(!c)
    Printf(TEXT_STATUS_FOUND, f, str);
  else
  {
    Printf(TEXT_STATUS_FOUND_UNEQ_CHANGE, f, str, c,
    c == 1 ? TEXT_STATUS_TIME : TEXT_STATUS_TIMES);
  }
}

static ULONG DoInOut(ULONG infh, STRPTR inbuf, STRPTR *inptr, ULONG insize,
LONG *incount, ULONG allsize,
ULONG outfh, STRPTR outbuf, STRPTR *outptr, ULONG outsize,
ULONG free, ULONG store)
{
  LONG d, e = inbuf+(*incount)-*inptr;

  if(outbuf+outsize <= *outptr && outbuf+store < *outptr)
  {
    if(Write(outfh, outbuf, *outptr-outbuf-store) != *outptr-outbuf-store)
    {
      *incount = -1; return 0;
    }
    else if(store)
      CopyMem(*outptr-store, outbuf, store);
    *outptr = outbuf+store; /* new pointer */
  }

  if(e < free && *incount == insize)
  {
    if(store > *inptr-inbuf)
      store = *inptr-inbuf;
    e += store;

    if(e > 0)
      CopyMem(*inptr-store, inbuf, e);
    else
      e = 0;
    *inptr = inbuf+store;

    if((d = Read(infh, inbuf+e, insize-e)) < 0)
    {
      *incount = -1; return 0;
    }
    *incount = d+e;
    allsize += d;
  }
  else if(e <= 0)
   *incount = 0;
  return allsize;
}

/* return 0, when string is found correct */
static ULONG CheckIt(STRPTR ptr, UWORD *scan, ULONG ssize, ULONG nosize)
{
  if(nosize)
  {
    while(ssize && (*scan == PATTERN_BACK ||
    SDI_tolower((UBYTE) *scan) == SDI_tolower(*ptr)))
    {
      --ssize; ++scan; ++ptr;
    }
  }
  else
  {
    while(ssize && (*scan == PATTERN_BACK || (UBYTE) *scan == *ptr))
    {
      --ssize; ++scan; ++ptr;
    }
  }
  return ssize;
}

/* ============================== DoChange ============================= */
/* == Replaces string in source file by replace string in destination == */

LONG DoChange(STRPTR inname, UWORD * sstr, ULONG ssize, STRPTR outname,
UWORD * rstr, ULONG rsize, ULONG flags, ULONG pos)
{
 struct FileInfoBlock *fib;
 ULONG infh;
 LONG ret = RETURN_FAIL;

 if(ssize == rsize)	/* size correcting stuff is useless */
  flags &= ~(CHANGEFLAG_OVERWRITE|CHANGEFLAG_LENGTH|CHANGEFLAG_PATH);
 if(flags & CHANGEFLAG_PATH) /* only one allowed */
  flags &= ~CHANGEFLAG_OVERWRITE;

 if(!ssize)
   return 0;

 if(!outname || !*(outname) || TestSameFile(inname, outname))
 {
   outname = inname; flags |= CHANGEFLAG_SAMEFILE;
 }

 if(!(fib = (struct FileInfoBlock *) AllocDosObject(DOS_FIB, 0)))
  return ret;

 if((infh = Open(inname, MODE_OLDFILE)))
 {
  if(ExamineFH(infh, fib))
  {
   ULONG insize;
   LONG incount;
   STRPTR inbuf;

   if(((insize = AvailMem(MEMF_LARGEST)) > fib->fib_Size) ||
   (flags & CHANGEFLAG_SAMEFILE))
     insize = fib->fib_Size + ssize;

   if((inbuf = (STRPTR) AllocMem(insize, MEMF_ANY)))
   {
    if((incount = Read(infh, inbuf, insize)) > 0)
    {
     ULONG outfh;

     if(insize == fib->fib_Size + ssize)
     {
      Close(infh); infh = 0;
     }
     if((outfh = Open(outname, MODE_READWRITE)))
     {
      if(ChangeMode(CHANGE_FH, outfh, EXCLUSIVE_LOCK))
      {
       if(!SetFileSize(outfh, 0, OFFSET_BEGINNING))
       {
        ULONG outsize;
        STRPTR outbuf;
     
        if(((outsize = AvailMem(MEMF_LARGEST)) > fib->fib_Size))
          outsize = fib->fib_Size;
        if(outsize < rsize + ssize + MINOUTSIZE)
          outsize = rsize + ssize + MINOUTSIZE;

        if((outbuf = (STRPTR) AllocMem(outsize, MEMF_ANY)))
        {
/************************* real program start ***************************/
         STRPTR inptr = inbuf, outptr = outbuf;
         ULONG ff = 0;			/* How much PATTERN at start ? */
         ULONG filepos = 0, found = 0, changed = 0, scanpos = 0;
         ULONG delay = 0; /* When ff==ssize, the last found is skipped else */

         while(ff < ssize && *sstr == PATTERN_BACK)
         {
          ++ff; ++sstr;
         }
         ssize -= ff;

         do
         {
          LONG a, b, c;

          if(ssize)
          {
	   STRPTR pos;

           do
           {
	    pos = inptr;
            a = inbuf+incount-inptr-ssize;
            if((c = b = outbuf+outsize-outptr-ssize-rsize) > a)
             c = a;
		  if(c <= 0)
             c = 1;

            if(flags & CHANGEFLAG_NOSIZE) /* These are the 2 main loops */
            {
             UBYTE st = SDI_tolower((UBYTE) *sstr);

             while(c && SDI_tolower(*inptr) != st)
             {
              *(outptr++) = *(inptr++); c--;
             }
            }
            else
            {
             while(c && *inptr != (UBYTE) *sstr)
             {
              *(outptr++) = *(inptr++); c--;
             }
            }
           
		  scanpos += inptr-pos;
            filepos = DoInOut(infh, inbuf, &inptr, insize, &incount, filepos,
            outfh, outbuf, &outptr, outsize-rsize-ssize, ssize+rsize, ff+1);
           } while(!c && incount > 0 && !CTRL_C);
          } /* if ssize */

          if((delay || incount > 0) && !CTRL_C)
          {
           delay = 0;
           if(scanpos >= ff && inbuf+incount-inptr >= ssize &&
           (!ssize || !CheckIt(inptr, sstr, ssize, flags & CHANGEFLAG_NOSIZE)))
           {
            ULONG actpos = filepos + (inptr - inbuf) - ff;
	    /* We found a string */
	    if(flags & CHANGEFLAG_FOUND)
	    {
             if(!found)
              PutStr(TEXT_STRING_FOUND_AT);
             Printf(" %lx", actpos);
	    }
	    ++found;
	    if(!(flags & CHANGEFLAG_POSITION) || actpos == pos)
	    {
	     scanpos = 0;
	     inptr -= ff; outptr -= ff;
	     ++changed;
	     if((flags & CHANGEFLAG_LENGTH) && outptr > outbuf)
	      *(outptr-1) += rsize - ssize;
	     for(a = 0; a < rsize; ++a)		/* insert replace string */
	     {
              if(rstr[a] != PATTERN_BACK)
               *(outptr++) = (UBYTE) rstr[a];
              else if(inbuf+incount-inptr > a)
               *(outptr++) = inptr[a];
		    else
               *(outptr++) = ' ';
             }
             inptr += ssize + ff;
             a = rsize - (ssize + ff);
             if(flags & CHANGEFLAG_PATH)	        /* if necessary use PATH */
             {
              while(*inptr)
              {
               *(outptr++) += *(inptr++);
               filepos = DoInOut(infh, inbuf, &inptr, insize, &incount, filepos,
               outfh, outbuf, &outptr, outsize-rsize-ssize, ssize+rsize, ff+1);
              }
              if(a > 0)
               inptr += a+1;
              else
              {
               while(++a)
                *(outptr++) = 0;
              }
              *(outptr++) = 0;
             }
             else if(flags & CHANGEFLAG_OVERWRITE)  /* overwrite with text or \0 */
             {
              if(a > 0)
               inptr += a;
              else
               while(a++)
                *(outptr++) = 0;
             }
	    } /* change it */
            else
            {
             if(!incount)
               ++inptr;
             else
               *(outptr++) = *(inptr++);
            }
           } /* CheckIt */
           else
           {
            *(outptr++) = *(inptr++); ++scanpos;
           }
          } /* if delay || incount > 0 */

          filepos = DoInOut(infh, inbuf, &inptr, insize, &incount, filepos,
          outfh, outbuf, &outptr, outsize-rsize-ssize, ssize+rsize, ff+1);

	  if(!incount && !ssize && ff == scanpos &&
	  inptr <= inbuf+insize-scanpos)
	   delay = 1;
         } while((incount > 0 || delay) && !CTRL_C);

         if(!incount && !CTRL_C)
          ret = 0;

         if(outptr > outbuf)
         {
          if(Write(outfh, outbuf, outptr-outbuf) != outptr-outbuf)
           ret = RETURN_FAIL;
         }

         if(!ret)
         {
          if(flags & CHANGEFLAG_FOUND)
           PutStr("\n");
          OutputResult(found, changed);
         }

/************************************************************************/
          FreeMem(outbuf, outsize);
        } /* AllocMem outbuf */
       } /* SetFileSize */
      } /* ChangeMode */

      Close(outfh);
      if(ret)
      {
       DeleteFile(outname);
       if(flags & CHANGEFLAG_SAMEFILE)
       {
	if((outfh = Open(outname, MODE_NEWFILE)))
	{
          Write(outfh, inbuf, insize); Close(outfh);
        }
       }
      }
     } /* Open outfh */
    } /* Read inbuf */

    FreeMem(inbuf, insize);
   } /* AllocMem inbuf */
  } /* Examine */

  if(infh) /* May be closed already */
   Close(infh);
 } /* Open infh */

 FreeDosObject(DOS_FIB, fib);
 return ret;
}

/* ============================== DoSearch ============================= */
/* == Replaces string in source file by replace string in destination == */
/* ========= This function is called with option NOCHANGE ============== */

LONG DoSearch(STRPTR inname, UWORD * sstr, LONG ssize, ULONG flags)
{
 struct FileInfoBlock *fib;
 LONG ret = RETURN_FAIL;
 ULONG infh;

 if(!ssize)
   return 0;

 if(!(fib = (struct FileInfoBlock *) AllocDosObject(DOS_FIB, 0)))
  return ret;

 if((infh = Open(inname, MODE_OLDFILE)))
 {
  if(ExamineFH(infh, fib))
  {
   ULONG insize;
   LONG incount;
   STRPTR inbuf;

   if(((insize = AvailMem(MEMF_LARGEST)) > fib->fib_Size) ||
   (flags & CHANGEFLAG_SAMEFILE))
     insize = fib->fib_Size + ssize;

   if((inbuf = (STRPTR) AllocMem(insize, MEMF_ANY)))
   {
    if((incount = Read(infh, inbuf, insize)) > 0)
    {
/************************* real program start ***************************/
     STRPTR inptr = inbuf;
     ULONG ff = 0;			/* How much PATTERN at start ? */
     ULONG filepos = 0, found = 0, scanpos = 0;

     while(ff < ssize && sstr[ff] == PATTERN_BACK)
     {
      ++ff; ++sstr; --ssize;
     }

     do
     {
      LONG c;

      if(ssize)
      {
       STRPTR pos;

       pos = inptr;
       c = inbuf+incount-inptr-ssize;

       if(flags & CHANGEFLAG_NOSIZE) /* These are the 2 main loops */
       {
        UBYTE st = SDI_tolower((UBYTE) *sstr);

        while(c > 0 && SDI_tolower(*inptr) != st)
        {
         inptr++; c--;
        }
       }
       else
       {
        while(c > 0 && *inptr != (UBYTE) *sstr)
        {
         inptr++; c--;
        }
       }

       scanpos += inptr-pos;
      }

      if(inbuf+incount-inptr >= ssize)
      {
       if(scanpos >= ff && (!ssize ||
       !CheckIt(inptr, sstr, ssize, flags & CHANGEFLAG_NOSIZE)))
       {
        ULONG actpos = filepos + (inptr - inbuf) - ff;
        ++found;
	++inptr;
        /* We found a string */
        if(flags & CHANGEFLAG_FOUND)
        {
         if(!found)
          PutStr(TEXT_STRING_FOUND_AT);
         Printf(" %lx", actpos);
        }
       }
       else
       {
         ++inptr; ++scanpos;
       }
      }
      else
      {
       LONG d, e = inbuf+incount-inptr;
       if(e < ssize && incount == insize)
       {
        if(e > 0)
         CopyMem(inptr, inbuf, e);
        else
         e = 0;
        inptr = inbuf;

        if((d = Read(infh, inbuf+e, insize-e)) < 0)
         incount = -1;
        else
        {
         incount = d+e; filepos += d;
        }
       }
       else if(e < ssize)
        incount = 0;
      }

     } while(incount > 0 && !CTRL_C);

     if(!incount && !CTRL_C)
      ret = 0;

     if(!ret)
     {
      if(flags & CHANGEFLAG_FOUND)
       PutStr("\n");
      OutputResult(found, 0);
     }

/************************************************************************/
    } /* Read inbuf */

    FreeMem(inbuf, insize);
   } /* AllocMem inbuf */
  } /* Examine */

  if(infh) /* May be closed already */
   Close(infh);
 } /* Open infh */

 FreeDosObject(DOS_FIB, fib);
 return ret;
}

