/* Routinesheader

	Name:		ChangeFunc.c
	Main:		Change
	Versionstring:	$VER: ChangeFunc.c 1.2 (29.12.1997)
	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
*/

#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_NEWFILE)))
     {
      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[ff] == PATTERN_BACK)
       {
        ++ff; ++sstr; --ssize;
       }

       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)
        {
         if(scanpos >= ff && inbuf+incount-inptr >= ssize && (!ssize ||
         !CheckIt(inptr, sstr, ssize, flags & CHANGEFLAG_NOSIZE)))
         {
          ULONG actpos = filepos + (inptr - inbuf) - ff;
          delay = 0;
	 /* 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
           *(outptr++) = *(inptr++);
         } /* CheckIt */
         else
         {
          *(outptr++) = *(inptr++); ++scanpos;
         }
        } /* if c && 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)
          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 */

      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;
}

