static char rcsid[] = "$Id: search.c,v 1.1 1992/09/05 01:13:32 mike Exp $";

/* $Log: search.c,v $
 * Revision 1.1  1992/09/05  01:13:32  mike
 * Initial revision
 *
 */

/*
 *
 *	SEARCH.C: search and replace stuff
 *
 * The functions in this file implement commands that search in the forward
 *   and backward directions.  Query replace is also done here.  Regular
 *   expression stuff is in regexp.c
 *
 * Revision history:
 *
 * ? Steve Wilhite, 1-Dec-85 - massive cleanup on code.
 *   Craig Durland, March 86: more cleanup and reorganization
 */

/* Copyright 1990, 1991, 1992 Craig Durland
 *   Distributed under the terms of the GNU General Public License.
 *   Distributed "as is", without warranties of any kind, but comments,
 *     suggestions and bug reports are welcome.
 */

#include <char.h>
#include "me2.h"

static int lookfor(), submatch();

int case_fold = TRUE;


/*
 * Search forward beginning at dot, for the string.  If found, reset the dot
 *   to be just after the match string, and [perhaps] repaint the display.
 * Returns:  TRUE if found, else FALSE.
 */
search_forward(pattern) char *pattern;
{
  return lookfor(pattern);
}

    /* Search the current buffer for pattern.  Start at the dot and search
     *   towards the end of the buffer.
     * Returns:
     *   TRUE:  Found a match for pattern.  Dot is after the last matched
     *     character.
     *   FALSE: No match found.  Dot not moved.
     */
static int lookfor(pattern) char *pattern;
{
  unsigned char c;
  register Line *clp;
  register int cbo;

  clp = the_dot->line; cbo = the_dot->offset;		/* start at cursor */
  if (clp == BUFFER_LAST_LINE(curbp)) return FALSE;	/* ??? needed? */
  while (TRUE)
  {
    if (cbo >= llength(clp))	/* at end of line */
    {
      if ((clp = lforw(clp)) == BUFFER_LAST_LINE(curbp)) return FALSE;
      cbo = 0; c = '\n';
    }
    else c = lgetc(clp,cbo++);
    if (ceq(c,*pattern) && submatch(clp,cbo,pattern+1)) return TRUE;
  }
  /* never gets here */
}

	/* point to next char to be matched */
static int submatch(tlp,tbo,pattern)
  Line *tlp; int tbo; char *pattern;
{
  unsigned char c;

  while (*pattern != '\0')
  {
    if (tlp == BUFFER_LAST_LINE(curbp)) return FALSE;
    if (tbo >= llength(tlp)) { tlp = lforw(tlp); tbo = 0; c = '\n'; }
    else c = lgetc(tlp, tbo++);
    if (!ceq(c,*pattern++)) return FALSE;
  }

  the_dot->line = tlp; the_dot->offset = tbo;
  dot_moved();

  return TRUE;
}

/*
 * Reverse search.  Search, starting at dot and proceeding toward the front
 *   of the buffer for pattern.  If found, dot is left pointing at the first
 *   character of the pattern [the last character that was matched].
 * Returns:  TRUE if found, else FALSE.
 */
search_reverse(pattern) char *pattern;
{
  register Line *clp, *dlp;
  register int cbo, dbo;
  unsigned char c;

  clp = dlp = the_dot->line;  cbo = dbo = the_dot->offset;
/*  for (; n>0; n--)*/
    while (TRUE)
    {
      if (cbo == 0)	/* at start of line, goto end of previous line */
      {
	clp = lback(clp);
	if (clp == BUFFER_LAST_LINE(curbp)) return FALSE;
	cbo = llength(clp) +1;
      }
  
      if (--cbo == llength(clp)) c = '\n';  else c = lgetc(clp,cbo);
  
      if (ceq(c,*pattern) && submatch(dlp,dbo,pattern +1))
      {
	the_dot->line = clp; the_dot->offset = cbo;
	break;
      }
      dlp = clp; dbo = cbo;
    }
  return TRUE;
}

    /* Search/Replace.
     * Returns:
     *   TRUE : Did at least one replace.
     *   FALSE: No replacements
     *   ABORT: Something bad happened.
     */
search_replace(search_for,replace_with) char *search_for, *replace_with;
{
  int match_len, got_a_match;

  set_mark(THE_MARK);		/* save excursion */

  match_len = strlen(search_for);

  got_a_match = FALSE;
  while (TRUE)
  {
    if (!lookfor(search_for)) break;	/* no more matches */
	/* got a match, replace it */
    got_a_match = TRUE;
    if (!replace_occ(match_len,replace_with)) return ABORT;
  } /* while */

		/* restore excursion & set mark at end of replace */
  swap_marks(THE_DOT,THE_MARK);

  return got_a_match;
}

#if 0	/* old code that might be interesting */
search_replace(pattern,replace_with) char *pattern, *replace_with;
{
  return qr(lookfor,pattern,replace_with,FALSE);
}

   /* A helper routines to actually perform the search and replace.
    * Returns:  TRUE if everything went as expected, ABORT if something bad
    *   happened.
    */
qr(search, search_pattern,replace_pattern, regexp)
  int (*search)(), regexp;
  char *search_pattern, *replace_pattern;
{
  extern char *eopat[], *bopat[];

  char line[260];
  int s, match_len;

  set_mark(THE_MARK);		/* save excursion */

  if (!regexp) match_len = strlen(search_pattern);
  while (TRUE)
  {
	 /* Note:  relookfor() has no params */
    if ((s = (*search)(search_pattern)) == ABORT) return ABORT;
    if (s == FALSE) break;
	/* got a match, replace it */
/*    repl_cnt++;*/
    if (regexp)
    {
      if (!re_subs(replace_pattern,line))
	{ mlwrite("Invalid replace string"); return ABORT; }
      s = replace_occ(eopat[0]-bopat[0],line);
    }
    else s = replace_occ(match_len,replace_pattern);
    if (!s) return ABORT;	/* something bad happened */
  } /* while */

		/* restore excursion & set mark at end of replace */
  swap_marks(THE_DOT,THE_MARK);

/* ??? return false if nothing replaced?  
 how about return the replace count? */

  return TRUE;
}
#endif


/*  
 * This routine is called by the Search/Replace routines.
 * This routine backs up the dot, deletes the old string and replaces it
 *   with the new string.
 * After replacement cursor points to end of new string.
 * Don't save deleted text in the cut buffer.
 * Returns:
 *   TRUE:  text replaced
 *   FALSE: memory problems.
 */
replace_occ(old_length,new_str) int old_length; char *new_str;
{
  if (!delete_characters(-old_length, FALSE)) return FALSE;
  return insert_text(new_str,strlen(new_str));
}
