let query_replace_gen
  ?(mes="")
  command_name
  (fsearch_buffer : search_buffer_function)
  (freplace : searched: string -> found:string -> repl:string -> string)
  (v : sourceview) args =
  let mb = v#minibuffer in
  let len = Array.length args in
  if len <= 0 then
    let f s = Cam_commands.launch_command command_name [| s |] in
    let title = Printf.sprintf "Query-replace%s" mes in
    Ed_misc.input_string ~history: replace_history
      mb ~title "" f
  else
    if len = 1 then
      begin
        let title = Ed_misc.to_utf8
          (Printf.sprintf "Query-replace%s %s with" mes args.(0))
        in
        let f s = Cam_commands.launch_command command_name [| args.(0); s |] in
        Ed_misc.input_string ~history: replace_history
          mb ~title "" f
      end
    else
      begin
        let title = Ed_misc.to_utf8
          (Printf.sprintf "Query-replace%s %s with %s (y/n/!)"
           mes args.(0) args.(1))
        in
        let s1_utf8 = Ed_misc.to_utf8 args.(0) in
        let s2_utf8 = Ed_misc.to_utf8 args.(1) in
        let rec iter interactive =
          let b = v#file#buffer in
          let it = b#get_iter `INSERT in
          let start = it in
          match fsearch_buffer true (*=forward*) b ~start s1_utf8 with
            true, _
          | _, None -> mb#set_active false
          | falseSome (start,stop) ->
              if interactive then
                (
                 v#set_location (location_of_iter start);
                 b#select_range start stop;
                 ignore(v#source_view#scroll_to_iter start);
                 ignore(v#source_view#scroll_to_iter stop)
                );
              let replace () =
                v#place_cursor ~scroll: interactive start;
                let found = b#get_text ~start ~stop () in
                b#delete ~start ~stop;
                (*              prerr_endline
                   (Printf.sprintf "searched=%s, found=%s, repl=%s"
                   s1_utf8 found s2_utf8);
                *)

                let new_text = freplace
                  ~searched: s1_utf8 ~found ~repl: s2_utf8
                in
                b#insert new_text
              in
              if interactive then
                (
                 let f_yes () = replace (); iter true in
                 let f_no () =
                   v#place_cursor stop;
                   iter true
                 in
                 let f_bang () = replace (); iter false in
                 mb#clear;
                 mb#set_more_key_bindings
                   [ [[], GdkKeysyms._y], f_yes ;
                     [[], GdkKeysyms._n], f_no ;
                     [[], GdkKeysyms._exclam], f_bang ;
                   ];
                 mb#set_text ~fixed: title "";
                 if not mb#active then (mb#set_active true; mb#wait);
                )
              else
                (replace (); iter interactive)
        in
        iter true
      end