/******************************************************************************

    MODUL
	search.c

    DESCRIPTION
	Inherits all the search/replace stuff for DME

    NOTES

    BUGS

    TODO

    EXAMPLES

    SEE ALSO

    INDEX

    HISTORY
	28. May 1992	ada created

******************************************************************************/

/* Includes */
#include "defs.h"
#define MYDEBUG     0
#include <debug.h>

/* Globale Variable */

/* Interne Defines & Strukturen */

/* Interne Variable */
/*static*/ UBYTE Fstr[MAXLINELEN]; /* PATCH_NULL - commented out for spcvars */
/*static*/ UBYTE Rstr[MAXLINELEN]; /* PATCH_NULL - commented out for spcvars */
static char Srch_sign;
static char Doreplace;

/* Interne Prototypes */
Prototype void do_findstr	(void);
Prototype void do_findr 	(void);
Prototype void do_find		(void);
Prototype void do_replace	(void);
Prototype void search_operation (void);


/*****************************************************************************

    NAME
	do_findstr

    PARAMETER
	av[0]	    "find" or "replace"
	av[1]	    string to find or replace-string

    DESCRIPTION
	Copies the search/replace-string to the internal buffer.

******************************************************************************/

void do_findstr (void)
{
    if (av[0][0] == 'f')    /* Check command name */
	strcpy(Fstr, av[1]);    /* "find" */
    else
	strcpy(Rstr, av[1]);    /* "replace" */
} /* do_findstr */


/*****************************************************************************

    NAME
	do_findr

    PARAMETER
	av[0]	"findr", "nextr" or "prevr"
	av[1]	find-string
	av[2]	replace-string

    DESCRIPTION
	Starts one find/replace-operation with both search- and replace-string
	as arguments.

******************************************************************************/

void do_findr (void)
{
    Doreplace = 1;  /* Really replace */
    Srch_sign = 1;  /* Search to EOF */

    switch (*av[0])
    {
	case 'f':
	    strcpy(Fstr, av[1]);    /* Set strings for "findr" */
	    strcpy(Rstr, av[2]);
	    break;

	case 'p':
	    Srch_sign = -1;	    /* Search reverse */
	    break;
    }

    search_operation(); /* Start search */
} /* do_findr */


/*****************************************************************************

    NAME
	do_find

    PARAMETER
	av[0]	"find", "next" or "prev"
	av[1]	find-string if av[0] == find

    DESCRIPTION
	Looks for a new string or searches forwards/backwars for the old one.

******************************************************************************/

void do_find (void)
{
    Doreplace = 0;  /* Don't replace */
    Srch_sign = 1;

    switch(av[0][0])
    {
	case 'f':   /* Find: new string */
	    strcpy(Fstr, av[1]);
	break;

	case 'p':   /* Search reverse */
	    Srch_sign = -1;
	break;
    }

    search_operation ();
} /* do_find */


/*****************************************************************************

    NAME
	do_replace

    PARAMETER
	av[0]	"replace"

    DESCRIPTION
	Substitutes the next strlen(search-string) chars with the rep-string.
	This command does no checking ! It always removes strlen(search-
	string) chars and the rep-string is always inserted.

******************************************************************************/

void do_replace (void)
{
    ULONG  rlen;
    ULONG  flen;
    Column col;

    rlen = strlen (Rstr);
    flen = strlen (Fstr);

    text_sync ();

    col = Ep->column;

    /* Check whether replace-string does fit into the line */
    if (rlen > flen && (Clen+rlen-flen) > MAXLINELEN-1)
    {
	error ("replace:\nLine Too Long");
    } else
    /* This checks whether the search-pattern does fit into
	the current line at the current position !?!?! */
	   if (Clen >= col+flen)
    {
	/* Move rest of line. Make enough place for replace-string */
	movmem (Current+col+flen, Current+col+rlen, Clen-col-flen+1);

	/* copy replace-string */
	if (rlen)
	    movmem (Rstr, Current+col, rlen);

	/* correct line-length and position after symbol */
	Clen	   += rlen-flen;
	Ep->column += rlen -1; /* one position left since we start
				  at Ep->column+1 with searching */

	if (Clen < 0)
	    Clen = 0;

	if (Ep->column < 0)
	    Ep->column = 0;
    }

    /* Check display, since we are not sure, whether the actual
       position is visible on the screen */
    if (!text_adjust (FALSE))
    {
	text_sync ();
	text_redisplaycurrline ();
    }

} /* do_replace */


/******************************************************************************

    NAME
	search_operation

    PARAMETER
	Fstr	    String to look for
	Rstr	    String to replace Fsrt with (if Doreplace != 0)
	Doreplace   Replace Fstr with Rstr ?
	Ep	    Different fields in this structure

    DESCRIPTION
	Searches in the text for Fstr and replaces it with Rstr if Doreplace is
	true. If Ep->ignorecase is true, the string-search is case-insensitive
	else not.

	For speed, I did replace the old function with this new one. The
	routine works like Boyer-Moore search but I didn't find a good
	explanation for it in books. You will find some for searching from the
	beginning of a string but none if you try reverse. Sigh.

******************************************************************************/

//#define DEBUG
static UBYTE skip[256]; 	    /* Distance for Boyer-Moore */

void search_operation (void)
{
    int flen = strlen(Fstr);        /* Length of Search/Replace-String */
//    int rlen = strlen(Rstr);

    UBYTE * ptr;
    WORD    j;	    /* Counter in Search-Pattern */
    WORD    col;    /* actual column */
    WORD    lin;    /* actual line */
    UWORD   linlen; /* length of actual line */
    UWORD   test;   /* result of case(in)sensitive compare */
    ED	  * ep;     /* actual editor */

    if (!flen)
    {			 /* Error if nothing to look for. */
	error ("search:\nNo find pattern");
	return;
    }

    /* Note that we do not check for nothing to replace, since the user may
       want to erase the full pattern */

    text_sync ();                    /* Save Current */

    ep = Ep;

    col = ep->column;		    /* Actual column ... */
    lin = ep->line;		    /* ... and line */

    /* initskip() */
    memset (skip, flen, 256);       /* Normally : Skip full length */

    if (Srch_sign > 0)
    {	 /* Search foreward */
	ptr = Fstr;

	/* Special: Skip until characters match. NOTE: Don't calc.
	   skip for the LAST character in Fstr */
	for (j=flen-1; j>0; ptr ++, j--)
	{
	    if (ep->config.ignorecase)
	    {
		skip[tolower(*ptr)] = j;
		skip[toupper(*ptr)] = j;
	    } else
		skip[*ptr] = j;
	}

	ptr = GETTEXT(ep,lin);          /* Get line contents ... */
	linlen = strlen (ptr);          /* ... and its length */
	col ++; /* Don't find it if we're already on it */

	for (; ep; )
	{
	    for (;;)
	    {
		/* This is the search-loop. Always remeber Boyer-Moore
		begins comparing at the end and scans forward ! */
		j = flen - 1;
		col += flen - 1;

		/* Until reached beginning of search-string or EOL */
		while (col < linlen)
		{
		    /* if both are equal (case(in)sensitive) */
		    if (ep->config.ignorecase)
			test = (tolower (ptr[col]) == tolower (Fstr[j]));
		    else
			test = (ptr[col] == Fstr[j]);

		    if (test)
		    {
			if (!j) /* Full Match */
			    goto found;

			col --; /* Next position */
			j --;
		    } else
		    {	 /* Determine skip-length */
			if (flen-j >= skip[ptr[col]])
			    col += flen - j;	/* length of match */
			else
			    col += skip[ptr[col]]; /* Adjust to char. */

			j = flen - 1;	/* Restart j */
		    } /* Compare */
		} /* while in line */

		lin ++;

		if (lin >= ep->lines)
		    break;

		ptr = GETTEXT(ep,lin);      /* Contents and Length */
		linlen = strlen (ptr);
		col = 0;
	    } /* while in text */

	    if (!globalflags.global_search)
		break;

	    if (ep = (ED *)GetSucc((struct Node *)ep))
	    {
		lin = 0;
		ptr = GETTEXT(ep,0);      /* Contents and Length */
		linlen = strlen (ptr);
		col = 0;
	    }
	} /* for all editors */
    } else
    { /* Search backward */
	/* Special: Skip until characters match. Also don't process last char */
	for (j=flen-1; j>0; j--)
	{
	    if (ep->config.ignorecase)
	    {
		skip[tolower(Fstr[j])] = j;
		skip[toupper(Fstr[j])] = j;
	    } else
	    {
		skip[Fstr[j]] = j;
	    }
	}

	ptr = GETTEXT(ep,lin);          /* Get line contents ... */
	linlen = strlen (ptr);          /* ... and its length */

	if (col >= linlen)  /* Beyound End-Of-Line ? */
	    col = linlen;

D(bug("new search\n"));

	for (; ep; )
	{
	    for (;;)
	    {
		/* Same as above but reverse */

		j = 0;
		col -= flen;

		while (col >= 0)
		{ /* Until BOL */
		    /* if both are equal (case(in)sensitive) */
		    if (ep->config.ignorecase)
			test = (tolower (ptr[col]) == tolower (Fstr[j]));
		    else
			test = (ptr[col] == Fstr[j]);

D(bug("[%s]\n[%s]\ncol %2ld j %2ld skip %2ld\n", ptr+col, Fstr+j, col, j,
		skip[ptr[col]]));

		    if (test)
		    {
			if (j == flen-1)
			{ /* Full Match */
			    col -= flen - 1;
			    goto found;
			}

			col ++; /* Next position */
			j ++;
		    } else
		    {	 /* Determine skip-length */
			j += j;

			if (j >= skip[ptr[col]])
			{
			    col -= j;	 /* Skip back j chars to beginning of
					    search and j chars to next start */
			} else
			{
			    col -= skip[ptr[col]]; /* Adjust to char. */
			}

			j = 0;	 /* Restart j */
		    } /* Compare */
		} /* while in line */

		/* Prev. line */
		if (!lin)   /* Stop on 1. line */
		    break;

		lin --;
D(bug("new line\n"));

		ptr = GETTEXT(ep,lin);      /* Contents and Length */
		linlen = strlen (ptr);

		/* Last char. */
		col = linlen;
	    } /* while in text */

	    if (!globalflags.global_search)
		break;

	    if (ep = (ED *)GetPred((struct Node *)ep))
	    {
		lin = ep->lines - 1;
		ptr = GETTEXT(ep,lin);
		col = linlen = strlen (ptr);
	    }
	} /* for all editors */
    } /* if foreward/backward */

    warn ("Pattern `%s' Not Found", Fstr);
    globalflags.Abortcommand = 1;
    return;

found:
    if (ep != Ep)
    {
	switch_ed (ep);

	if (ep->iconmode)
	{
	    uniconify ();
	} else
	{
	    /* Make window active */
	    WindowToFront (ep->win);
	    ActivateWindow (ep->win);

	    set_window_params ();
	    window_title ();
	}
    }

    ep->line = lin;	  /* Set Position */
    ep->column = col;

    text_load ();        /* Copy Line into Current */

    if (Doreplace)
	do_replace ();
    else
	text_adjust (FALSE);
} /* search_operation */


/******************************************************************************
*****  ENDE search.c
******************************************************************************/
