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

    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"

/* Globale Variable */

/* Interne Defines & Strukturen */

/* Interne Variable */
static ubyte Fstr[MAXLINELEN];
static ubyte Rstr[MAXLINELEN];
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 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 ();
}



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

    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;
    short   j;	    /* Counter in Search-Pattern */
    short   col;    /* actual column */
    short   lin;    /* actual line */
    ushort  linlen; /* length of actual line */
    ushort  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->IgnoreCase) {
		skip[tolower(*ptr)] = j;
		skip[toupper(*ptr)] = j;
	    } else
		skip[*ptr] = j;
	}

	ptr = ep->List[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->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;	/* Full length */
			else
			    col += skip[ptr[col]]; /* Adjust to char. */

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

		lin ++;

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

		ptr = ep->List[lin];	  /* Contents and Length */
		linlen = strlen (ptr);

		col = 0;
	    } /* while in text */

	    if (!global_search)
		break;

	    if (ep = (ED *)GetSucc((struct Node *)ep)) {
		lin = 0;
		ptr = ep->List[0];
		linlen = strlen (ptr);
	    }
	} /* 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->IgnoreCase) {
		skip[tolower(Fstr[j])] = j;
		skip[toupper(Fstr[j])] = j;
	    } else {
		skip[Fstr[j]] = j;
	    }
	}

	ptr = ep->List[lin];		/* Get line contents ... */
	linlen = strlen (ptr);          /* ... and its length */

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

	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->IgnoreCase)
			test = (tolower (ptr[col]) == tolower (Fstr[j]));
		    else
			test = (ptr[col] == Fstr[j]);

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

			col ++; /* Next position */
			j ++;
		    } else {	/* Determine skip-length */
			if (j+j >= skip[ptr[col]]) {
			    col -= j+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 --;

		ptr = ep->List[lin];	  /* Contents and Length */
		linlen = strlen (ptr);

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

	    if (!global_search)
		break;

	    if (ep = (ED *)GetPred((struct Node *)ep)) {
		lin = ep->Lines - 1;
		ptr = ep->List[lin];
		col = linlen = strlen (ptr);
	    }
	} /* for all editors */
    } /* if foreward/backward */

    warn ("Pattern `%s' Not Found", Fstr);
    return;

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

	text_load ();

	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) {    /* Replace ? */
	/* Check whether replace-string does fit into the line */
	if (rlen > flen && rlen-flen+strlen(ptr) > 254) {
	    error ("replace:\nLine Too Long");
	    return;
	}

	/* This if checks whether the search-pattern does fit into
	   the current line at the current position !?!?! */
	if (Clen-col-flen >= 0) {
	    /* Move rest of line. Make enough place for replace-string */
	    movmem(Current+col+flen, Current+col+rlen, Clen-col-flen+1);

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

	    /* correct line-length and position */
	    Clen += rlen-flen;
	    ep->Column += rlen;
	}

	text_sync();                /* Update Display */
	text_redisplaycurrline();
    } else {
	text_sync();                /* Update Display */
    }
} /* search_operation */


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