%  These routines are common to both regular expression searches and ordinary
%  searches.

define mark_next_nchars (n, dir)
{
   variable h;
   ERROR_BLOCK 
     {
	set_line_hidden (h); pop_mark_0 ();
     }

   h = is_line_hidden ();
   set_line_hidden (0);
   push_visible_mark ();
   go_right (n);
   if (dir < 0) exchange_point_and_mark ();
   update(1);
   ungetkey(getkey());
   EXECUTE_ERROR_BLOCK;
}



% The search function is to return: 0 if non-match found or the length of the 
% item matched.
% search_fun takes the pattern to search for and returns the length of the 
% pattern matched.  If no match occurs, return -1.
% rep_fun returns the length of characters replaced.

define replace_with_query (search_fun, pat, rep, query, rep_fun)
{
   variable n, prompt, doit, err, ch, pat_len;
   variable undo_stack_type = struct
     {
	rep_len,
	prev_string,
	user_mark,
	next
     };
   variable undo_stack = NULL;
   variable tmp;
	
   prompt =  sprintf ("Replace '%s' with '%s'? (y/n/!/+/q/h)", pat, rep);
   
   while (pat_len = @search_fun (pat), pat_len >= 0)
     {
	!if (query)
	  {
	     tmp = create_user_mark ();
	     () = @rep_fun (rep, pat_len);
	     if (tmp == create_user_mark ())
	       go_right_1 ();
	     continue;
	  }

	do 
	  {
	     message(prompt);
	     mark_next_nchars (pat_len, -1);
	     
	     ch = getkey ();
	     if (ch == 'r')
	       {
		  recenter (window_info('r') / 2);
	       }
	     
	  } while (ch == 'r');
	
	switch(ch)
	  { case 'u' and (undo_stack != NULL) :
	     goto_user_mark (undo_stack.user_mark);
	     push_spot ();
	     () = @rep_fun (undo_stack.prev_string, undo_stack.rep_len);
	     pop_spot ();
	     undo_stack = undo_stack.next;
	  }   
	  { case 'y' :
	     tmp = @undo_stack_type; 
	     tmp.next = undo_stack;
	     undo_stack = tmp;

	     push_spot(); push_mark ();
	     go_right (pat_len); undo_stack.prev_string = bufsubstr ();
	     pop_spot (); 
	     undo_stack.user_mark = create_user_mark ();
	     undo_stack.rep_len  = @rep_fun (rep, pat_len);
	  }
	  { case 'n' : go_right_1 ();}
	  { case '+' : () = @rep_fun (rep, pat_len); 
	               break;
	  }
	  { case '!' :
	     query = 0;
	  }
          { case 'q' : break; }
          {
	     flush ("y:replace, n:skip, !:replace all, u: undo last, +:replace then quit, q:quit");
	     () = input_pending (30); 
	  }
     }
}


define search_maybe_again (fun, str, dir, match_ok_fun)
{
   variable ch, len;
   
   while (len = @fun (str, dir), len >= 0)
     {	
	if (@match_ok_fun ())
	  {
	     if (EXECUTING_MACRO or DEFINING_MACRO) return 1;
	     message ("Press RET to continue searching.");
	     mark_next_nchars (len, -1);
	     ch = getkey ();
	     if (ch != '\r')
	       {
		  ungetkey (ch);
		  return 1;
	       }
	  }
	if (dir > 0) go_right_1 ();
     }
   return 0;
}
