/**************************************************************
 *
 *	CRISP - Custom Reduced Instruction Set Programmers Editor
 *
 *	(C) Paul Fox, 1989, 1990, 1991
 *
 *    Please See COPYRIGHT notice.
 *
 **************************************************************/
# include	"list.h"
# include	"alt.h"

int	pnf_msg = TRUE;		/* FALSE when we dont want the 'Pattern */
				/* not found.' message printed.		*/
REGEXP	*prog;
extern char *re_code;		/* From regexp.c so we can save the compiled */
extern int re_code_size;	/* re when we go prompting the user.	*/
struct re_state {
	int	saved_magic;
	int	saved_case;
	int	bmatch;		/* Backward match.			*/
	int	line;
	int	last_line;
	int	search_col;
	int	undo_flag;	/* TRUE if we want forwsrch() to save undo. */
				/* FALSE for translate code.		    */
	};

int	trans_string PROTO((struct re_state *, char *, int, int));
int	get_tr_response PROTO((int, int));
int	search_string PROTO((char *, char *, int, int));
int	regexec PROTO((REGEXP *, char *, int));
int	search_start PROTO((struct re_state *, int, char *, int, int, int));
int	forwsrch PROTO((struct re_state *));
void	set_syntax_arg PROTO((int, int));
void	translate_string PROTO((int, char *));
int	repl_string PROTO((char *, char **, int *));

/**********************************************************************/
/*   Following  function  implements  the macro to take a string and  */
/*   quote  it  so  that  it  can  be  used  in a search without the  */
/*   regular expression characters causing problems.		      */
/**********************************************************************/
void
quote_regexp()
{	register char *cp = get_str(1);
	char	buf[BUFSIZ];
	char	*bp = buf;
	char	*rech = "+%@[]<>^$*?{}|";

	while (*cp) {
		if (*cp == '\\') {
			*bp++ = *cp++;
			*bp++ = *cp++;
			continue;
			}
		if (strchr(rech, *cp))
			*bp++ = '\\';
		*bp++ = *cp++;
		}
	*bp = NULL;
	acc_assign_str(buf, bp - buf);
}
void
search_list()
{	struct re_state	rs;
	char	*cp = get_str(2);
	int	atom_no = 0;
	int	i;
	LIST	*lp = get_list(3);
	
	acc_assign_int(-1L);
	argv[6].l_flags = F_NULL;
	if (lp == NULL || search_start(&rs, TRUE, cp, 4, 5, 6) == FALSE)
		return;
	regopts.offset = 0;
	rs.undo_flag = FALSE;
	if (argv[1].l_flags != F_NULL)
		atom_no = argv[1].l_int;

	for (i = atom_no; i-- > 0 && *lp != F_HALT; )
		lp = next_atom(lp);

	for ( ; *lp != F_HALT; lp = next_atom(lp), atom_no++) {
		cp = get_str_ptr(lp);
		if (cp && regexec(prog, cp, strlen(cp))) {
			acc_assign_int((long) atom_no);
			return;
			}
		}
}
void
search(dir)
int	dir;
{	struct re_state	rs;
	char	buf[BUFSIZ];
	char	*cp = get_arg1(dir ? "Search for: " : "Search back: ", buf, sizeof buf);

	if (cp == NULL)
		return;
	if (*cp == NULL) {
		infof("No pattern specified.");
		acc_assign_int(-1L);
		return;
		}
	if (search_start(&rs, dir, cp, 2, 3, 4) == FALSE) {
		acc_assign_int(-1L);
		return;
		}

	infof("Searching...");
	if (forwsrch(&rs) == 0) {
		*cur_col = current_col(rs.search_col);
		infof("Search completed.");
		}
	regopts.case_sense = rs.saved_case;
	regopts.regexp_chars = rs.saved_magic;
}
int
srch_case()
{
	regopts.case_sense = argv[1].l_flags == F_NULL ? !regopts.case_sense : argv[1].l_int;
	infof("Case sensitivity %s.", regopts.case_sense ? "on" : "off");
	return 0;
}
void
srch_string()
{	long	val;
	val = search_string(get_str(1), get_str(2),
		(int) (argv[5].l_flags == F_NULL ? regopts.case_sense : argv[5].l_int),
		(int) (argv[4].l_flags == F_NULL ? regopts.regexp_chars : argv[4].l_int)
		);
	if (val && argv[3].l_flags != F_NULL)
			argv[3].l_sym->s_int = prog->end - prog->start;
	acc_assign_int(val);
}
int
search_string(str1, str2, casef, magicf)
char	*str1, *str2;
int	casef;
int	magicf;
{	int	saved_case = regopts.case_sense;
	int	saved_magic = regopts.regexp_chars;
	int	len2 = strlen(str2);
	int	ret = 0;

	regopts.case_sense = casef;
	regopts.regexp_chars = magicf;

	if ((prog = regcomp(str1)) == NULL) {
		regopts.case_sense = saved_case;
		regopts.regexp_chars = saved_magic;
		return 0;
		}
	regopts.fwd_search = TRUE;
	regopts.offset = 0;
	if (len2 && regexec(prog, str2, len2)) {
		if (prog->setpos)
			prog->start = prog->setpos;
		ret = prog->start - str2 + 1;
		}
	regopts.regexp_chars = saved_magic;
	regopts.case_sense = saved_case;
	return ret;
}
void
hilight_chars(size)
int	size;
{	int	buffer_flags = curbp->b_flag;

	/***********************************************/
	/*   Set flag to turn off undo.		       */
	/***********************************************/
	curbp->b_flag |= BF_NO_UNDO;

	/***********************************************/
	/*   Drop  an  anchor  so  that  we  get  the  */
	/*   start of the hilight.		       */
	/***********************************************/
	argv[1].l_flags = F_NULL;
	drop_anchor();
	
	/***********************************************/
	/*   Now   move   cursor   over  so  that  we  */
	/*   hilight the specified amount.	       */
	/***********************************************/
	argv[1].l_int = size - 1;
	argv[1].l_flags = F_INT;
	if (size)
		next_char();

	/***********************************************/
	/*   Update the window.			       */
	/***********************************************/
	win_modify(WFEDIT);
	update();
	
	/***********************************************/
	/*   Restore  buffer  state  to  what  it was  */
	/*   before we disabled undo.		       */
	/***********************************************/
	curbp->b_flag = buffer_flags;
}
void
undo_hilight(size)
int	size;
{	int	buffer_flags = curbp->b_flag;

	/***********************************************/
	/*   Disable   undo   whilst  we  remove  the  */
	/*   hilight.				       */
	/***********************************************/
	curbp->b_flag |= BF_NO_UNDO;
	
	argv[1].l_int = size - 1;
	argv[1].l_flags = F_INT;
	if (size)
		prev_char();
	raise_anchor();
	
	/***********************************************/
	/*   Restore flags to what they were.	       */
	/***********************************************/
	curbp->b_flag = buffer_flags;
}
void
translate()
{	char	rep_buf[256];
	int	global;
	int	fwd = argv[7].l_flags == NULL || argv[7].l_int != 0;
	int	ntranslations = 0;
	int	print_tmsg = FALSE;
	int	found_one = FALSE;
	int	abort = FALSE;
	int	ch;
	int	beg_match = FALSE;
	char	patbuf[256];
	char	*pat;
	struct re_state rs;

	if (rdonly())
		return;	
	/***********************************************/
	/*   Figure  out  whether  we  need  to  keep  */
	/*   prompting,   or  whether  its  a  global  */
	/*   translate or whether we do it only once.  */
	/***********************************************/
	global = argv[3].l_flags == F_NULL ? -1 :
		 argv[3].l_int ? 1 : 0;

	if ((pat = get_arg1("Pattern: ", patbuf, sizeof patbuf)) == NULL)
		return;
	if (*pat == NULL) {
		errorf("No previous translate pattern.");
		return;
		}

	if (argv[2].l_flags == F_NULL)
		ereply("Replacement: ", rep_buf, sizeof rep_buf);
	else
		strcpy(rep_buf, get_str(2));

	save_position();
	infof("Searching...");
	if (search_start(&rs, fwd, pat, 4, 5, 6) == FALSE)
		return;
	rs.undo_flag = FALSE;
	pnf_msg = FALSE;
	u_dot();

	if (*pat == '^' || (regopts.unix_re == FALSE && *pat == '<'))
		beg_match = TRUE;
	while (rs.line > 0) {
		/***********************************************/
		/*   Look  for  next  occurence. If not found  */
		/*   then we've finished the translation.      */
		/***********************************************/
		if (forwsrch(&rs))
			break;
		ch = 'Y';
		found_one = TRUE;
		if (global < 0) {
			*cur_col = current_col(rs.search_col);
			ch = get_tr_response(prog->end - prog->start, ntranslations);
			switch (ch) {
			  case 'A':
			  	abort = TRUE;
				goto end_of_function;
			  case 'U':
			  	ntranslations--;
				rs.line = *cur_line;
				rs.search_col = current_offset(*cur_col, FALSE);
				continue;
			  case 'E':
				*cur_line = 1;
				*cur_col = 1;
			  	rs.line = 1;
				rs.search_col = 1;
				continue;
			  case 'G':
			  	global = 1;
				print_tmsg = TRUE;
				break;
			  case 'O':
			  	global = 0;
				print_tmsg = TRUE;
				break;
			  case 'N':
			  	rs.search_col += fwd ? 1 : -1;
				continue;
			  }
			}
		if (global > 0)
			percentage((long) *cur_line, 
				(long) curbp->b_numlines, 
				"Global", "translate");
		if (trans_string(&rs, rep_buf, prog->end - prog->start, global < 0) < 0) {
			print_tmsg = FALSE;
			global = 0;
			found_one = FALSE;
			break;
			}
		ntranslations++;
		if (global == 0)
			break;
		/***********************************************/
		/*   If  matching  at  beginning of line then  */
		/*   skip to next line.			       */
		/***********************************************/
		if (beg_match) {
			rs.search_col = 0;
			rs.line++;
			}
		}
end_of_function:
	regopts.case_sense = rs.saved_case;
	regopts.regexp_chars = rs.saved_magic;
	acc_assign_int((long) ntranslations);
	argv[1].l_flags = F_NULL;
	if (abort) {
		argv[1].l_flags = F_INT;
		argv[1].l_int = 0;
		*cur_col = current_col(rs.search_col);
		}
	restore_position();
	win_modify(WFHARD);
	if (print_tmsg || global < 0) {
		if (found_one)
			infof("Translation complete; %d occurrence%s changed.",
				ntranslations, ntranslations == 1 ? "" : "s");
		else
			infof("Pattern not found.");
		}
	pnf_msg = TRUE;
}
/**********************************************************************/
/*   Function  to  handle  a translate replacement. Returns 0 if OK,  */
/*   -1 on error. Adjust the current line/col for the next search.    */
/**********************************************************************/
int
trans_string(rs, rep_buf, size, interactive)
struct re_state	*rs;
char	*rep_buf;
int	size;
int	interactive;
{
	char	*rep_str;
	int	len;
	int	num_lines_inserted;
	int	ret;

	ret = repl_string(rep_buf, &rep_str, &len);
	if (ret < 0)
		return -1;
	/***********************************************/
	/*   Mark  interactive  translates  so we can  */
	/*   easily undo them.			       */
	/***********************************************/
	if (interactive)
		u_soft_start();
	num_lines_inserted = lreplace(rep_str, len, (RSIZE) size, rs->search_col);
	/***********************************************/
	/*   On  a  forward  translate  we wanna skip  */
	/*   the stuff we're inserting.		       */
	/***********************************************/
	if (regopts.fwd_search) {
		extern int global_dot;
		rs->last_line += num_lines_inserted;
		rs->line = *cur_line;
		rs->search_col = global_dot + (size == 0);
		}
	else {
		/***********************************************/
		/*   Back   up   one   character   past   the  */
		/*   replaced   string   so   we  dont  match  */
		/*   against it.			       */
		/***********************************************/
		if (--rs->search_col < 0) {
			LINE *lp;
			if (--rs->line) {
				lp = vm_lock_line(rs->line);
				rs->search_col = llength(lp);
				}
			}
		}
	if (ret)
		chk_free(rep_str);
	return 0;
}
int
get_tr_response(size, ntranslations)
int	size;
int	ntranslations;
{	char	ch_buf[4];
	register int ch;
	REGEXP	saved_prog;
	
	/***********************************************/
	/*   Keep   a  copy  of  the  prog  structure  */
	/*   because   the  command  line  completion  */
	/*   may  get  called  and  cause us problems  */
	/*   with re-entrancy.			       */
	/***********************************************/
	saved_prog = *prog;
	hilight_chars(size);
	while (1) {
		if (ereply("Change [Yes|No|Global|One|Top|Undo|Entire]? ", 
		   ch_buf, 1) == ABORT) {
			undo_hilight(size);
			return 'A';
			}
		*prog = saved_prog;
		ch = ch_buf[0];
		if (ch > 'a' && ch <= 'z')
			ch -= 0x20;
		switch (ch) {
		  case 'T':
		  case ALT_T:
		  case CTRL_T:
			/***********************************************/
			/*   Move current line to near top of window.  */
			/***********************************************/
		  	curwp->w_top_line = curwp->w_line;
			if (curwp->w_top_line < 1)
				curwp->w_top_line = 1;
			win_modify(WFHARD);
			curwp->w_indent = 0;
			update();
		  	break;
		  case 'U':
		  case ALT_U:
		  case CTRL_U:
		  	if (ntranslations) {
				undo_hilight(size);
			  	undo(2);
			  	update();
				return 'U';
				}
			break;
		  case 'E':
		  case 'O':
		  case 'Y':
		  case 'N':
		  case 'G':
			undo_hilight(size);
		  	return ch;
		  }
		}
}
int
search_start(re_state, dir, pat, re, _case, block)
struct re_state *re_state;
int	dir;
char *pat;
int	re;
int	_case;
int	block;
{
	regopts.fwd_search = dir;
	re_state->bmatch = FALSE;
	re_state->line = *cur_line;
	re_state->saved_case = regopts.case_sense;
	re_state->last_line = curbp ? curbp->b_numlines : 0;
	re_state->saved_magic = regopts.regexp_chars;
	re_state->undo_flag = TRUE;
	regopts.regexp_chars = TRUE;

	if (argv[re].l_flags == F_INT)
		regopts.regexp_chars = argv[re].l_int;
	if (argv[_case].l_flags == F_INT)
		regopts.case_sense = argv[_case].l_int;

	if ((prog = regcomp(pat)) == NULL) {
		regopts.regexp_chars = re_state->saved_magic;
		return FALSE;
		}
	prog->exp_level = -1;

	/***********************************************/
	/*   If  block  is  TRUE, then we only search  */
	/*   within marked block.		       */
	/***********************************************/
	if (curbp == NULL)
		re_state->search_col = 0;
	else if (argv[block].l_flags == F_INT && argv[block].l_int && get_marked_areas((WINDOW *) NULL)) {
		extern int start_line, end_line, start_col;
		re_state->line = start_line;
		re_state->last_line = end_line + 1;
		re_state->search_col = current_offset(start_col, FALSE);
		}
	else
		re_state->search_col = current_offset(*cur_col, FALSE);

	if (!regopts.fwd_search) {
		switch (regopts.regexp_chars) {
	  	  case -3: case 3:
			re_state->bmatch = TRUE; break;
	  	  case -2: case 2:
			re_state->bmatch = !regopts.fwd_search; break;
	  	  case -1: case 0: case 1:
			re_state->bmatch = FALSE; break;
	  	  }
		if (pat[0] == '$' || (regopts.unix_re == FALSE && pat[0] == '>')) {
			re_state->line++;
			re_state->search_col = -1;
			}
		}

	return TRUE;

}
int
forwsrch(re_state)
struct re_state *re_state;
{	register LINE   *clp;
	int	success = FALSE;
	int	length;
	int	incr, col;
	
	acc_assign_int(0L);
	if (regopts.fwd_search) {
		incr = 1;
		col = 0;
		}
	else {
		incr = -1;
		col = -1;
		}
	clp = vm_lock_line(re_state->line);
	while (1) {
		if (regopts.fwd_search) {
			if (re_state->line >= re_state->last_line)
				break;
			}
		else {
			if (re_state->line < 1)
				break;
			}
		length = llength(clp);
		if (re_state->search_col < 0)
			re_state->search_col = length;
		if (re_state->bmatch) {
			if (length > re_state->search_col)
				length = re_state->search_col;
			}
		regopts.offset = re_state->search_col;
		success = regexec(prog, (char *) ltext(clp), length);
		vm_unlock(re_state->line);
		if (success)
			break;
		clp = regopts.fwd_search ? lforw(clp) : lback(clp);
		re_state->line += incr;
		re_state->search_col = col;
		}
	if (success != 1) {
		if (pnf_msg)
			infof("Pattern not found.");
		return 1;
		}
	trace_log("Search succeeded\n");

	if (re_state->undo_flag)
		u_dot();
	win_modify(WFMOVE);
	*cur_line = re_state->line;
	if (prog->setpos)
		prog->start = prog->setpos;
	re_state->search_col = prog->start - (char *) ltext(clp);
	acc_assign_int((long) (prog->end - prog->start + 1));
	
	win_modify(WFMOVE);
	return 0;
}
void
regerror(msg)
char	*msg;
{
	errorf("%s", msg);
}
/**********************************************************************/
/*   Function  to  set  the  regular expression mode for re_search()  */
/*   and  re_translate().  In  Unix  syntax  mode, we go for maximal  */
/*   matching always.						      */
/**********************************************************************/
void
set_syntax_arg(n, flag)
int	n;
int	flag;
{
	argv[n].l_flags = F_INT;
	if (flag & SF_NOT_REGEXP) {
		argv[n].l_int = 0;
		return;
		}
	/***********************************************/
	/*   Check  for  unix  syntax  and set regexp  */
	/*   mode.				       */
	/***********************************************/
	if (regopts.unix_re || flag & SF_MAXIMAL) {
		argv[n].l_int = -2; /* Maximal mode. */
		}
	else
		argv[n].l_flags = F_NULL;
}
/**********************************************************************/
/*   Function   to   implement   the   re_search()  primitive.  This  */
/*   primitive  combines  the  functionality  of  the  other  search  */
/*   primitives  but  presents a consistent interface with hopefully  */
/*   fewer parameters to remember.				      */
/**********************************************************************/
void
re_search()
{	int	current_syntax;
	int	flags = argv[1].l_flags == F_NULL ? 0 : (int) argv[1].l_int;
	
	/***********************************************/
	/*   Set the regular expression syntax first.  */
	/***********************************************/
	current_syntax = set_re_syntax(flags & SF_UNIX);

	switch (argv[3].l_flags) {
	  case F_NULL:
		/***********************************************/
		/*   If  object  to search is NULL, then this  */
		/*   means search current buffer.	       */
		/***********************************************/
		argv[1] = argv[2];
		set_syntax_arg(2, flags);
		argv[3].l_flags = F_INT;
		argv[3].l_int = !(flags & SF_IGNORE_CASE);
		argv[4].l_flags = F_INT;
		argv[4].l_int = flags & SF_BLOCK;
		argv[5].l_flags = F_INT;
		argv[5].l_int = flags & SF_LENGTH;
		search(!(flags & SF_BACKWARDS));
		break;
	  case F_RLIST:
	  case F_LIST:
	  	argv[1] = argv[4];
		set_syntax_arg(4, flags);
		argv[5].l_flags = F_INT;
		argv[5].l_int = !(flags & SF_IGNORE_CASE);
		search_list();
	  	break;
	  case F_LIT:
	  case F_RSTR:
	  case F_STR:
		argv[1] = argv[2];
		argv[2] = argv[3];
		argv[3] = argv[5];
		set_syntax_arg(4, flags);
		if (argv[4].l_flags == F_NULL) {
			argv[4].l_int = 1;
			argv[4].l_flags = F_INT;
			}
		argv[5].l_flags = F_INT;
		argv[5].l_int = !(flags & SF_IGNORE_CASE);
		srch_string();
	  	break;
	  default:
	  	acc_assign_int(-1L);
		break;
	  }
	  
	/***********************************************/
	/*   Put the syntax back to what it was.       */
	/***********************************************/
	set_re_syntax(current_syntax);
}
/**********************************************************************/
/*   Function  similar  to  translate()  primitive  but  allows  the  */
/*   syntax  to  be  specified at the time of the translate and is a  */
/*   bit more compact.						      */
/**********************************************************************/
void
re_translate()
{	int	current_syntax;
	int	flags = argv[1].l_flags == F_NULL ? 0 : (int) argv[1].l_int;
	char	*str = NULL;
	
	/***********************************************/
	/*   Set the regular expression syntax first.  */
	/***********************************************/
	current_syntax = set_re_syntax(flags & SF_UNIX);

	argv[1] = argv[2];
	argv[2] = argv[3];
	if (flags & SF_GLOBAL) {
		argv[3].l_flags = F_INT;
		argv[3].l_int = 1;
		}
	else if (flags & SF_PROMPT) {
		argv[3].l_flags = F_NULL;
		argv[3].l_int = 0;
		}
	else {
		argv[3].l_flags = F_INT;
		argv[3].l_int = 0;
		}
		
	/***********************************************/
	/*   If  4th  argument  supplied,  then we're  */
	/*   gonna do a string translate.	       */
	/***********************************************/
	if (argv[4].l_flags != F_NULL)
		str = get_str(4);

	set_syntax_arg(4, flags);
	argv[5].l_flags = F_INT;
	argv[5].l_int = !(flags & SF_IGNORE_CASE);
	argv[6].l_flags = F_INT;
	argv[6].l_int = flags & SF_BLOCK;
	argv[7].l_flags = F_INT;
	argv[7].l_int = (flags & SF_BACKWARDS) == 0;

	/***********************************************/
	/*   If  we  got a string, then we need to do  */
	/*   a  string  translate.  Otherwise operate  */
	/*   on the current buffer.		       */
	/***********************************************/
	if (str == NULL)
		translate();
	else {
		translate_string(flags, str);
		}

	/***********************************************/
	/*   Put the syntax back to what it was.       */
	/***********************************************/
	set_re_syntax(current_syntax);
}
/**********************************************************************/
/*   Translate  a  string  and return value to accumulator. Bit like  */
/*   awks sub and gsub.						      */
/**********************************************************************/
void
translate_string(flags, src_str)
int	flags;
char	*src_str;
{	char	*pattern;
	char	*repl;
	int	src_len;
	char	*dst_str = NULL;
	char	*rep_str;
	int	dst_size;
	int	dst_len;
	int	len;
	int	ret;
	struct regopts	saved_regopts;
	struct re_state rs;

	/***********************************************/
	/*   Set   up   environment  for  the  regexp  */
	/*   matching.				       */
	/***********************************************/
	saved_regopts = regopts;
 
	regopts.fwd_search = TRUE;
	regopts.offset = 0;
	pattern = get_str(1);
	repl = get_str(2);
	src_len = strlen(src_str);
	if (search_start(&rs, TRUE, pattern, 4, 5, 6) == FALSE) {
		acc_assign_str("", 1);
		goto end_of_function;
		}
	
	/***********************************************/
	/*   Start  off  with  an initial string size  */
	/*   so   we've   room   to   fill   in   the  */
	/*   destination.			       */
	/***********************************************/
	dst_size = src_len + 16;
	dst_len = 0;
	dst_str = (char *) chk_alloc(dst_size);
	/***********************************************/
	/*   Now  we  need  to  keep  on matching and  */
	/*   replacing.				       */
	/***********************************************/
	while (1) {
		/***********************************************/
		/*   If  we  don't  find  another  match then  */
		/*   give up.				       */
		/***********************************************/
		if (src_len <= 0 || regexec(prog, src_str, src_len) == FALSE)
			break;
		/***********************************************/
		/*   We  got  a  match.  Copy  over the stuff  */
		/*   before the match.			       */
		/***********************************************/
		if (prog->start != src_str) {
			len = prog->start - src_str;
			if (len > dst_size - dst_len) {
				dst_size += len + 1;
				dst_str = (char *) chk_realloc(dst_str, dst_size);
				}
			memcpy(dst_str + dst_len, src_str, len);
			dst_len += len;
			}
		/***********************************************/
		/*   Now fill in the replacement.	       */
		/***********************************************/
		ret = repl_string(repl, &rep_str, &len);
		if (ret < 0) {
			acc_assign_str("", 1);
			goto end_of_function;
			}
		if (dst_size - dst_len < len) {
			dst_size += len + 1;
			dst_str = (char *) chk_realloc(dst_str, dst_size);
			}
		memcpy(dst_str + dst_len, rep_str, len);
		if (ret)
			chk_free(rep_str);
		dst_len += len;
		/***********************************************/
		/*   Now skip over matched part of string.     */
		/***********************************************/
		src_str = prog->end;
		src_len = strlen(src_str);
		if ((flags & SF_GLOBAL) == 0)
			break;
		}
	/***********************************************/
	/*   Now copy over rest of source string.      */
	/***********************************************/
	if (dst_size - dst_len < src_len + 1) {
		dst_size += src_len + 1;
		dst_str = (char *) chk_realloc(dst_str, dst_size);
		}
	memcpy(dst_str + dst_len, src_str, src_len);
	dst_len += src_len;
	dst_str[dst_len] = NULL;
	acc_assign_str(dst_str, dst_len + 1);
	/***********************************************/
	/*   Restore the regexp matching.	       */
	/***********************************************/
end_of_function:
	regopts = saved_regopts;
	if (dst_str)
		chk_free(dst_str);
}
/**********************************************************************/
/*   Function  to  perform  a  replacement  translation.  Returns  a  */
/*   string  which  with  all  \n  patterns replaced. Return 0 if we  */
/*   can  use  string as is because there were no \n's). Return 1 if  */
/*   we  allocated memory  and therefore it needs to be freed. -1 on  */
/*   an error.							      */
/**********************************************************************/
int
repl_string(rep_str, rpp, lenp)
register char	*rep_str;
char	**rpp;
int	*lenp;
{	register char *cp;
	register char	*dst;
	register int	dst_len;
	register int	dst_size;
	int	group, len;
	char	*cp1;
	char	*start, *end;
	int	ampersand = regopts.unix_re ? '&' : 0x100;

	/***********************************************/
	/*   If  no  \n  groups  we can use string as  */
	/*   it  is.  If Unix mode in effect, we need  */
	/*   to check for '&' as well.		       */
	/***********************************************/
	cp = strchr(rep_str, '\\');
	if (regopts.unix_re)
		cp1 = strchr(rep_str, '&');
	else
		cp1 = NULL;
	if (cp == NULL && cp1 == NULL) {
		*lenp = strlen(rep_str);
		*rpp = rep_str;
		return 0;
		}
	if ((cp == NULL || cp1 < cp) && cp1)
		cp = cp1;
	/***********************************************/
	/*   Copy  over  everything before first '\\'  */
	/*   group.				       */
	/***********************************************/
	if (cp == rep_str) {
		dst = (char *) chk_alloc(16);
		dst_size = 16;
		dst_len = 0;
		}
	else {
		cp--;
		dst_len = cp - rep_str;
		dst_size = dst_len + 16;
		dst = chk_alloc(dst_size);
		memcpy(dst, rep_str, dst_len);
		rep_str += dst_len;
		}
	while (*rep_str) {
		if (*rep_str == ampersand) {
			start = prog->start;
			end = prog->end;
			rep_str++;
			}
		else {
			if (*rep_str != '\\') {
copy_char:
				if (dst_size - dst_len < 2) {
					dst_size += dst_size;
					dst = chk_realloc(dst, dst_size);
					}
				dst[dst_len++] = *rep_str++;
				continue;
				}
			if (!isdigit(rep_str[1])) {
				rep_str++;
				goto copy_char;
				}
			group = rep_str[1] - '0' - (regopts.unix_re ? 1 : 0);
			if (group < 0) {
				rep_str++;
				goto copy_char;
				}
			rep_str += 2;
			start = prog->startp[group];
			end = prog->endp[group];
			if (start == NULL || group > prog->exp_level) {
				errorf("No such group '\\%d' in pattern.", group);
				chk_free(dst);
				return -1;
				}
			}
		len = end - start;
		if (dst_size - dst_len < len) {
			dst_size += len + 2;
			dst = chk_realloc(dst, dst_size);
			}
		memcpy(dst + dst_len, start, len);
		dst_len += len;
		}
	dst[dst_len] = NULL;
	*lenp = dst_len;
	*rpp = dst;
	return 1;
}
