static char rcsid[] = "$Id: regexp.c,v 1.1 1992/09/05 01:13:32 mike Exp $";

/* $Log: regexp.c,v $
 * Revision 1.1  1992/09/05  01:13:32  mike
 * Initial revision
 *
 */

/*
 * REGEXP.C	regular expression search and replace
 */

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 * Since the RE routines expect null terminated lines and ME lines don't
 *   have nulls, I copy the line, add a null and then call the RE routine.
 *   This stinks.  What I should do is rewrite the RE routines to take a
 *   pointer and length, or pad the line and tack on a null.
 * But remember, the RE routines maintain backpointers into the line (for
 *   substitutions like query-replace) so if the line changes (ie is edited)
 *   the pointers could be messed up.
 * Also, since I have to restrict the line length (since the hidden line is
 *   fixed length), I might get false matches or misses.  Blech.
 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/* 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 <const.h>
#include "me2.h"

extern char *eopat[], *bopat[], *re_comp();	/* in regex.c */
static int relookfor();

	/* Hidden storage for relookfor(), (get-matched) & (looking-at)
	 *   needs to stick around because re_subs() has ptrs into it.
	 */
#define HMAX FLINE
static char hline[HMAX + 1];

re_search_forward(pattern) char *pattern;
{
  char *ptr;

  if (ptr = re_comp(pattern)) { mlwrite("%s: %s",ptr,pattern); return ABORT; }
  return relookfor();
}

re_search_reverse(pattern) char *pattern;
{
  char *ptr;
  register Line *clp;
  register int cbo;
  int j, s;

  if (ptr = re_comp(pattern)) { mlwrite("%s: %s",ptr,pattern); return ABORT; }

  clp = the_dot->line; cbo = the_dot->offset;
  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;
    }
    j = imin(llength(clp),HMAX); blkmov(hline,clp->l_text,j); hline[j] = '\0';
    if (cbo) j = cbo;
    for (j--; j >= 0; j--)
    {
      if ((s = re_exec(&hline[j], j == 0, FALSE)) == ABORT) return ABORT;
      if (s == TRUE)			/* match */
      {
	the_dot->line = clp; the_dot->offset = j;
	dot_moved();
	/*if (--n<=0)*/ return TRUE;
      }
    }
    cbo = 0;
  }
  /* never gets here */  
}

    /* Search/Replace with regular expressions.
     * Almost the same as search_replace() but just different enough to make
     *   packing the code into one routine not worth it.
     * Returns:
     *   TRUE : Did at least one replace.
     *   FALSE: No replacements
     *   ABORT: Something bad happened.
     */
re_search_replace(search_pattern,replace_pattern)
  char *search_pattern, *replace_pattern;
{
  char *ptr, line[260];
  int s, got_a_match;

  if (ptr = re_comp(search_pattern))
	{ mlwrite("%s: %s",ptr,search_pattern); return ABORT; }

  set_mark(THE_MARK);		/* save excursion */

  got_a_match = FALSE;
  while (TRUE)
  {
    if ((s = relookfor()) == ABORT) return ABORT;
    if (s == FALSE) break;	/* no more matches */

	/* got a match, replace it */
    got_a_match = TRUE;
    if (!re_subs(replace_pattern,line))
      { mlwrite("Invalid replace string"); return ABORT; }
    if (!replace_occ(eopat[0]-bopat[0],line))	/* something bad happened */
	return ABORT;
  } /* while */

		/* restore excursion & set mark at end of replace */
  swap_marks(THE_DOT,THE_MARK);

  return got_a_match;
}

   /* 
    * Returns:
    *   TRUE  if found
    *   FALSE if not found
    *   ABORT if something bad happens like bad search pattern.
    */
static int relookfor()	/* search_pattern has already been compiled */
{
  register Line *clp;
  int cbo, s;

  clp = the_dot->line; cbo = the_dot->offset;		/* start at cursor */
  if (clp == BUFFER_LAST_LINE(curbp)) return FALSE;	/* ??? needed? */
  while (TRUE)
  {
    s = imin(llength(clp),HMAX); blkmov(hline,clp->l_text,s); hline[s] = '\0';
    if ((s = re_exec(&hline[cbo], cbo == 0, TRUE)) == ABORT) return ABORT;
    if (s == TRUE)	/* match */
    {
      the_dot->line = clp; the_dot->offset = eopat[0] -(char *)hline;
      dot_moved();
      return TRUE;
    }
    if ((clp = lforw(clp)) == BUFFER_LAST_LINE(curbp)) return FALSE;
    cbo = 0;
  }
  /* never gets here */
}

/* 
 * re_fail: internal error handler for re_exec.
 */ 
void re_fail(s,c) char *s, c;
{
  extern re_errorcode;

  mlputs(s);
  re_errorcode = ABORT;
}


    /* Apply RE to current line in current buffer.
     * Notes:
     *   I do the strango code to try and minimize the amount of text
     *     copied.  I don't think it gains anything.
     *   If the dot is not at the start of the line, I have to pass at least
     *     one character before the dot to re_exec() because it might look
     *     at it.
     */
looking_at(pattern) char *pattern;
{
  char *ptr;
  int cbo, len, i, f;
  Line *clp;

  if (ptr = re_comp(pattern)) { mlwrite("%s: %s",ptr,pattern); return ABORT; }
  clp = the_dot->line; cbo = the_dot->offset;		/* start at cursor */

  f = (cbo != 0);		/* 0 if cbo == 0, else 1 */
  i = cbo - f;
  len = imin(llength(clp) -i, HMAX);
  blkmov(hline, &clp->l_text[i], len); hline[len] = '\0';
  return re_exec(hline + f, !f, FALSE);
}

	/* apply RE to string */
REstring(pattern,str) char *pattern, *str;
{
  char *ptr;

  if (ptr = re_comp(pattern)) { mlwrite("%s: %s",ptr,pattern); return ABORT; }
  strcpy(hline,str);
  return re_exec(hline,TRUE,FALSE);
}
