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

    MODUL
	$Id: search.c 1.3 1994/09/09 12:31:30 digulla Exp digulla $

    DESCRIPTION
	Inherits all the search/replace stuff for DME

    HISTORY
	28. May 1992	ada created
	$Log: search.c $
 * Revision 1.3  1994/09/09  12:31:30  digulla
 * added new style Prototypes, DEFCMD and DEFHELP
 *
 * Revision 1.2  1994/08/19  14:07:15  digulla
 * some reformatting
 * fixed some minor bugs in search_operation
 *
 * Revision 1.1  1994/08/14  12:35:51  digulla
 * Initial revision
 *

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

/* 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 */
static 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.

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

/*DEFHELP #cmd searchreplace FINDSTR string - Set the search string pattern */
/*DEFHELP #cmd searchreplace REPSTR string - SET the replace string pattern */

DEFUSERCMD("repstr",  1, CF_VWM, void, do_findstr, (void),;)
DEFUSERCMD("findstr", 1, CF_VWM, 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.

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

/*DEFHELP #cmd searchreplace FINDR s1 s2 - Set find and replace patterns and do one find&replace. */
/*DEFHELP #cmd searchreplace NEXTR - find next occurance and replace */
/*DEFHELP #cmd searchreplace PREVR - find previous occurance and replace */

DEFUSERCMD("findr", 2, 0, void, do_findr, (void),;)
DEFUSERCMD("nextr", 0, 0, void, do_findr, (void),;)
DEFUSERCMD("prevr", 0, 0, 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.

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

/*DEFHELP #cmd searchreplace FIND string - Set the search pattern to @{B}string@{UB} and do a NEXT */
/*DEFHELP #cmd searchreplace NEXT - find next occurance of search pattern */
/*DEFHELP #cmd searchreplace PREV - find previous occurance of search pattern */

DEFUSERCMD("find", 1, CF_VWM, void, do_find, (void),;)
DEFUSERCMD("next", 0, CF_VWM, void, do_find, (void),;)
DEFUSERCMD("prev", 0, CF_VWM, 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.

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

/*DEFHELP #cmd searchreplace REPLACE - replaces the next strlen(findstr) chars with repstr */

DEFUSERCMD("replace", 0, 0, void, do_replace, (void),)
{
    ULONG  rlen;
    ULONG  flen;
    Column col = Ep->column;

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

    text_sync ();

    /* Check whether replace-string does fit into the line */
    if (rlen > flen && (Clen+rlen-flen) > MAXLINELEN-1)
    {
DEFMESSAGE( _FIND_line_2_long, "%s:\nLine too long" )
	error (_FIND_line_2_long, av[0]);
    } 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;

	/* only if we search forward else stay on first char */
	if (Srch_sign > 0)
	    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 */

static void search_operation (void)
{
    UBYTE * ptr;
    WORD    j;	    /* Counter in Search-Pattern */
    Column  col;    /* actual column */
    Line    lin;    /* actual line */
    UWORD   linlen; /* length of actual line */
    UWORD   test;   /* result of case(in)sensitive compare */
    ED	  * ep;     /* actual editor */
    int flen = strlen (Fstr);        /* Length of Search/Replace-String */

    if (!flen)
    {			 /* Error if nothing to look for. */
DEFMESSAGE( _FIND_no_searchstring_defined, "%s:\nNo find pattern" )
	error (_FIND_no_searchstring_defined, av[0]);
	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, sizeof (skip));  /* default: skip full length */

    if (Srch_sign > 0)
    {	 /* Search foreward */

// ADD WC-Search Part DELTA, here!
	for (j=0; j<flen; j ++)
	{
	    if (GETF_IGNORECASE(ep))
	    {
		skip[tolower(Fstr[j])] =
		    skip[toupper(Fstr[j])] = flen-j-1;
	    } else
		skip[Fstr[j]] = flen-j-1;
	}

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

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

		/* Until reached beginning of search-string or EOL */
		while (col < linlen)
		{
		    /* if both are equal (case(in)sensitive) */
		    if (GETF_IGNORECASE(ep))
			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]])
			    j = flen - j;    /* length of match */
			else
			    j = skip[ptr[col]]; /* Adjust to char. */

			if (!j)
			{
printf ("search: infinite loop\n");
			    return;
			}

			col += j;

			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 (!GETF_GLOBALSEARCH(ep))
		break;

	    if (ep = (ED *)GetSucc((struct Node *)ep))
	    {
		lin = 0;
		ptr = GETTEXT(ep,0);      /* Contents and Length */
		linlen = strlen (ptr);
	    }
	} /* for all editors */
    } else
    { /* Search backward */
	for (j=flen-1; j>=0; j--)
	{
	    if (GETF_IGNORECASE(ep))
	    {
		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-flen;
	else
	    col --;	/* don't find it right here if we are already on it */

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

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

		while (col >= 0)
		{ /* Until BOL */
		    /* if both are equal (case(in)sensitive) */
		    if (GETF_IGNORECASE(ep))
			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)
		    {
			j ++;
			col ++; /* Next position */

			if (j == flen)
			{ /* Full Match */
			    col -= flen;
			    goto found;
			}
		    } else
		    {	 /* Determine skip-length */
			/* j += j; */

			if (j < skip[ptr[col]])
			{
			    j = skip[ptr[col]]; /* Adjust to char. */
			}
			else
			    j += flen;

			if (!j)
			{
printf ("search: infinite loop\n");
			    return;
			}

			col -= j;

			j = 0;	 /* Restart j */
		    } /* Compare */

#if DEBUG
{
    char buf[41];

    strncpy (buf, &ptr[col], 40);
    buf[35] = 0;

    printf ("Str '%6s'  Text '%s'  col=%2d j=%+d l=%3d\n", Fstr+j, buf, col, j, lin);
}
#endif
		} /* 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-flen;
	    } /* while in text */

	    if (!GETF_GLOBALSEARCH(ep))
		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 */

DEFMESSAGE( _FIND_pattern_not_found, "%s:\nPattern `%s' not found" )
    warn (_FIND_pattern_not_found, av[0], Fstr);
    SETF_ABORTCOMMAND(ep,1);
    return;

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

	if (GETF_ICONMODE(ep))
	{
	    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
******************************************************************************/

#if 0 // WildCard Search

// WC Search PART DELTA
    if (!FLPttrnBuffer)



// WC Search PART GAMMA
#define my_MatchPattern(ep,pt,str) (GETF_IGNORECASE(ep)? MatchPatternNoCase(pt,str): MatchPattern(pt, str))
		// ---- since Dos.PatternMatch is not RegExr, we have no special
		//	Patterns for SOL/EOL; for that reason, we must do some
		//	additional matching;
		//	imho, it would be better, we could call a subfunction
		//	found = _matchpattern(line, pattern, &start, &len, from_back?, from_sol?, from_eol?);
		//	but that function would need either an array of pattern,
		//	or it needed global access to the different *PttrnBuffer =8-(

		if (FLPttrnBuffer) {
		    int ecol, ichar;
		    // ---- Match w/ prepended and postpended #? (only if 1/last!=^/$)
		    if (my_MatchPattern(ep, FLPttrnBuffer, ptr)) {
			ecol = strlen (ptr);
			if (!PLPttrnBuffer) {
			    // ---- User wanted match of Full Line
			    if (!col) {
				flen = ecol;
				goto found;
			    } /* if */
			} else {
			    while (ptr[col]) {
				// ---- match w/ postpended #? (ony if last!=$)
				if (my_MatchPattern(ep, RLPttrnBuffer, ptr + col)) {
				    if (!PttrnBuffer) {
					// ---- User wanted match up to EOL
					flen = ecol - col;
					goto found;
				    } else {
					while (ecol >= col) {
					    ichar = ptr [ecol];
					    ptr[ecol] = 0;
					    if (my_MatchPattern(ep, PttrnBuffer, ptr + col)) {
						flen = ecol - col;
						ptr[ecol] = ichar;
						// ---- Das hier geht verdammt tief in
						//	die Hose ... wir muessten etwas
						//	machen ala txt_replace(pos,rep,flen);
						//	so wird eindeutig die _falsche_
						//	Laenge ersetzt
						goto found;
					    } /* if */
					    ptr[ecol] = ichar;
					    -- ecol;
					} /* while */
				    } /* if */
				} /* if */
				++ col;
			    } /* while */
			} /* if */
		    } /* if */

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








// WC Search Part Alpha

// start is an _input_and_result_!
// end is a _input_and_result_!

// we _might_ have much _less_ problems, if we used strings, prepended and postpended with "\n"
// that way we could just check for "\n" instead of that starnge way below

BOOL match_pattern (UBYTE *text, char **patterns, int *start, int *end, BOOL reverse, BOOL nocase)
{
#define loc_MatchPattern(ncs,pt,str) (ncs? MatchPatternNoCase(pt,str): MatchPattern(pt, str))
    int    col,
	   ecol;
    UBYTE *fl_pattern;
    UYBTE *rl_pattern;
    UBYTE *___pattern;
    char   ichar;

    fl_pattern = patterns[0];
    rl_pattern = patterns[1];
    ___pattern = patterns[2];
    col        = *start;
    ecol       = *end;

    if (!reverse && col && !patterns[1])
	return FALSE;

    ichar = ptr[ecol];
    ptr[ecol] = 0;
    if (loc_MatchPattern(ep, fl_pattern, ptr)) {
	if (!rl_pattern) {
	    ptr[ecol] = ichar;
	    // ---- User wanted match of Full Line
	    if (reverse ? col : !col) {
		*end   = ecol;
		*start = 0;
		return TRUE;
	    } /* if */
	    return FALSE;
	} else {
	    while (reverse ? (col >= 0) : (ptr[col] != 0)) {
		// ---- match w/ postpended #? (ony if last!=$)
		if (loc_MatchPattern(ep, rl_pattern, ptr + col)) {
		    ptr[ecol] = ichar;
		    if (!___pattern) {
			ptr[ecol] = ichar;
			// ---- User wanted match up to EOL
			if (!ichar) {
			    *end   = ecol;
			    *start =  col;
			    return TRUE;
			} /* if */
			return FALSE;
		    } else {
			while (ecol >= col) {
			    if (loc_MatchPattern(ep, ___pattern, ptr + col)) {
				ptr[ecol] = ichar;
				*end   = ecol;
				*start =  col;
				return TRUE;
			    } /* if */
			    ptr[ecol] = ichar;
			    --ecol;
			    ichar     = ptr[ecol];
			    ptr[ecol] = 0;
			} /* while */
			ptr[ecol] = ichar;
			ecol	  = *end;
			ichar	  = ptr[ecol];
			ptr[ecol] = 0;
		    } /* if */
		} /* if */
		if (reverse)
		    -- col;
		else
		    ++ col;
	    } /* while */
	} /* if */
    } /* if */
    ptr[ecol] = ichar;
    return FALSE;
} /* match_pattern */




#endif
