summaryrefslogtreecommitdiffstats
path: root/src/editor/editsearch.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/editor/editsearch.c')
-rw-r--r--src/editor/editsearch.c1034
1 files changed, 1034 insertions, 0 deletions
diff --git a/src/editor/editsearch.c b/src/editor/editsearch.c
new file mode 100644
index 0000000..ba91d86
--- /dev/null
+++ b/src/editor/editsearch.c
@@ -0,0 +1,1034 @@
+/*
+ Search & replace engine of MCEditor.
+
+ Copyright (C) 2021-2022
+ Free Software Foundation, Inc.
+
+ Written by:
+ Andrew Borodin <aborodin@vmail.ru>, 2021-2022
+
+ This file is part of the Midnight Commander.
+
+ The Midnight Commander is free software: you can redistribute it
+ and/or modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the License,
+ or (at your option) any later version.
+
+ The Midnight Commander is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <assert.h>
+
+#include "lib/global.h"
+#include "lib/search.h"
+#include "lib/mcconfig.h" /* mc_config_history_get */
+#ifdef HAVE_CHARSET
+#include "lib/charsets.h" /* cp_source */
+#endif
+#include "lib/util.h"
+#include "lib/widget.h"
+#include "lib/skin.h" /* BOOK_MARK_FOUND_COLOR */
+
+#include "src/history.h" /* MC_HISTORY_SHARED_SEARCH */
+#include "src/setup.h" /* verbose */
+
+#include "edit-impl.h"
+#include "editwidget.h"
+
+#include "editsearch.h"
+
+/*** global variables ****************************************************************************/
+
+edit_search_options_t edit_search_options = {
+ .type = MC_SEARCH_T_NORMAL,
+ .case_sens = FALSE,
+ .backwards = FALSE,
+ .only_in_selection = FALSE,
+ .whole_words = FALSE,
+ .all_codepages = FALSE
+};
+
+/*** file scope macro definitions ****************************************************************/
+
+#define B_REPLACE_ALL (B_USER+1)
+#define B_REPLACE_ONE (B_USER+2)
+#define B_SKIP_REPLACE (B_USER+3)
+
+/*** file scope type declarations ****************************************************************/
+
+/*** file scope variables ************************************************************************/
+
+/* --------------------------------------------------------------------------------------------- */
+/*** file scope functions ************************************************************************/
+/* --------------------------------------------------------------------------------------------- */
+
+static gboolean
+edit_dialog_search_show (WEdit * edit)
+{
+ char *search_text;
+ size_t num_of_types = 0;
+ gchar **list_of_types;
+ int dialog_result;
+
+ list_of_types = mc_search_get_types_strings_array (&num_of_types);
+
+ {
+ quick_widget_t quick_widgets[] = {
+ /* *INDENT-OFF* */
+ QUICK_LABELED_INPUT (N_("Enter search string:"), input_label_above, INPUT_LAST_TEXT,
+ MC_HISTORY_SHARED_SEARCH, &search_text, NULL, FALSE, FALSE,
+ INPUT_COMPLETE_NONE),
+ QUICK_SEPARATOR (TRUE),
+ QUICK_START_COLUMNS,
+ QUICK_RADIO (num_of_types, (const char **) list_of_types,
+ (int *) &edit_search_options.type, NULL),
+ QUICK_NEXT_COLUMN,
+ QUICK_CHECKBOX (N_("Cas&e sensitive"), &edit_search_options.case_sens, NULL),
+ QUICK_CHECKBOX (N_("&Backwards"), &edit_search_options.backwards, NULL),
+ QUICK_CHECKBOX (N_("In se&lection"), &edit_search_options.only_in_selection, NULL),
+ QUICK_CHECKBOX (N_("&Whole words"), &edit_search_options.whole_words, NULL),
+#ifdef HAVE_CHARSET
+ QUICK_CHECKBOX (N_("&All charsets"), &edit_search_options.all_codepages, NULL),
+#endif
+ QUICK_STOP_COLUMNS,
+ QUICK_START_BUTTONS (TRUE, TRUE),
+ QUICK_BUTTON (N_("&OK"), B_ENTER, NULL, NULL),
+ QUICK_BUTTON (N_("&Find all"), B_USER, NULL, NULL),
+ QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
+ QUICK_END
+ /* *INDENT-ON* */
+ };
+
+ WRect r = { -1, -1, 0, 58 };
+
+ quick_dialog_t qdlg = {
+ r, N_("Search"), "[Input Line Keys]",
+ quick_widgets, NULL, NULL
+ };
+
+ dialog_result = quick_dialog (&qdlg);
+ }
+
+ g_strfreev (list_of_types);
+
+ if (dialog_result == B_CANCEL || search_text[0] == '\0')
+ {
+ g_free (search_text);
+ return FALSE;
+ }
+
+ if (dialog_result == B_USER)
+ search_create_bookmark = TRUE;
+
+#ifdef HAVE_CHARSET
+ {
+ GString *tmp;
+
+ tmp = str_convert_to_input (search_text);
+ g_free (search_text);
+ search_text = g_string_free (tmp, FALSE);
+ }
+#endif
+
+ edit_search_deinit (edit);
+ edit->last_search_string = search_text;
+
+ return edit_search_init (edit, edit->last_search_string);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+edit_dialog_replace_show (WEdit * edit, const char *search_default, const char *replace_default,
+ /*@out@ */ char **search_text, /*@out@ */ char **replace_text)
+{
+ size_t num_of_types = 0;
+ gchar **list_of_types;
+
+ if ((search_default == NULL) || (*search_default == '\0'))
+ search_default = INPUT_LAST_TEXT;
+
+ list_of_types = mc_search_get_types_strings_array (&num_of_types);
+
+ {
+ quick_widget_t quick_widgets[] = {
+ /* *INDENT-OFF* */
+ QUICK_LABELED_INPUT (N_("Enter search string:"), input_label_above, search_default,
+ MC_HISTORY_SHARED_SEARCH, search_text, NULL, FALSE, FALSE,
+ INPUT_COMPLETE_NONE),
+ QUICK_LABELED_INPUT (N_("Enter replacement string:"), input_label_above, replace_default,
+ "replace", replace_text, NULL, FALSE, FALSE, INPUT_COMPLETE_NONE),
+ QUICK_SEPARATOR (TRUE),
+ QUICK_START_COLUMNS,
+ QUICK_RADIO (num_of_types, (const char **) list_of_types,
+ (int *) &edit_search_options.type, NULL),
+ QUICK_NEXT_COLUMN,
+ QUICK_CHECKBOX (N_("Cas&e sensitive"), &edit_search_options.case_sens, NULL),
+ QUICK_CHECKBOX (N_("&Backwards"), &edit_search_options.backwards, NULL),
+ QUICK_CHECKBOX (N_("In se&lection"), &edit_search_options.only_in_selection, NULL),
+ QUICK_CHECKBOX (N_("&Whole words"), &edit_search_options.whole_words, NULL),
+#ifdef HAVE_CHARSET
+ QUICK_CHECKBOX (N_("&All charsets"), &edit_search_options.all_codepages, NULL),
+#endif
+ QUICK_STOP_COLUMNS,
+ QUICK_BUTTONS_OK_CANCEL,
+ QUICK_END
+ /* *INDENT-ON* */
+ };
+
+ WRect r = { -1, -1, 0, 58 };
+
+ quick_dialog_t qdlg = {
+ r, N_("Replace"), "[Input Line Keys]",
+ quick_widgets, NULL, NULL
+ };
+
+ if (quick_dialog (&qdlg) != B_CANCEL)
+ edit->replace_mode = 0;
+ else
+ {
+ *replace_text = NULL;
+ *search_text = NULL;
+ }
+ }
+
+ g_strfreev (list_of_types);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static int
+edit_dialog_replace_prompt_show (WEdit * edit, char *from_text, char *to_text, int xpos, int ypos)
+{
+ Widget *w = WIDGET (edit);
+
+ /* dialog size */
+ int dlg_height = 10;
+ int dlg_width;
+
+ char tmp[BUF_MEDIUM];
+ char *repl_from, *repl_to;
+ int retval;
+
+ if (xpos == -1)
+ xpos = w->rect.x + option_line_state_width + 1;
+ if (ypos == -1)
+ ypos = w->rect.y + w->rect.lines / 2;
+ /* Sometimes menu can hide replaced text. I don't like it */
+ if ((edit->curs_row >= ypos - 1) && (edit->curs_row <= ypos + dlg_height - 1))
+ ypos -= dlg_height;
+
+ dlg_width = WIDGET (w->owner)->rect.cols - xpos - 1;
+
+ g_snprintf (tmp, sizeof (tmp), "\"%s\"", from_text);
+ repl_from = g_strdup (str_trunc (tmp, dlg_width - 7));
+
+ g_snprintf (tmp, sizeof (tmp), "\"%s\"", to_text);
+ repl_to = g_strdup (str_trunc (tmp, dlg_width - 7));
+
+ {
+ quick_widget_t quick_widgets[] = {
+ /* *INDENT-OFF* */
+ QUICK_LABEL (repl_from, NULL),
+ QUICK_LABEL (N_("Replace with:"), NULL),
+ QUICK_LABEL (repl_to, NULL),
+ QUICK_START_BUTTONS (TRUE, TRUE),
+ QUICK_BUTTON (N_("&Replace"), B_ENTER, NULL, NULL),
+ QUICK_BUTTON (N_("A&ll"), B_REPLACE_ALL, NULL, NULL),
+ QUICK_BUTTON (N_("&Skip"), B_SKIP_REPLACE, NULL, NULL),
+ QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
+ QUICK_END
+ /* *INDENT-ON* */
+ };
+
+ WRect r = { ypos, xpos, 0, -1 };
+
+ quick_dialog_t qdlg = {
+ r, N_("Confirm replace"), NULL,
+ quick_widgets, NULL, NULL
+ };
+
+ retval = quick_dialog (&qdlg);
+ }
+
+ g_free (repl_from);
+ g_free (repl_to);
+
+ return retval;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+/**
+ * Get EOL symbol for searching.
+ *
+ * @param edit editor object
+ * @return EOL symbol
+ */
+
+static inline char
+edit_search_get_current_end_line_char (const WEdit * edit)
+{
+ switch (edit->lb)
+ {
+ case LB_MAC:
+ return '\r';
+ default:
+ return '\n';
+ }
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Checking if search condition have BOL(^) or EOL ($) regexp special characters.
+ *
+ * @param search search object
+ * @return result of checks.
+ */
+
+static edit_search_line_t
+edit_get_search_line_type (mc_search_t * search)
+{
+ edit_search_line_t search_line_type = 0;
+
+ if (search->search_type != MC_SEARCH_T_REGEX)
+ return search_line_type;
+
+ if (search->original.str->str[0] == '^')
+ search_line_type |= AT_START_LINE;
+
+ if (search->original.str->str[search->original.str->len - 1] == '$')
+ search_line_type |= AT_END_LINE;
+ return search_line_type;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Calculating the start position of next line.
+ *
+ * @param buf editor buffer object
+ * @param current_pos current position
+ * @param max_pos max position
+ * @param end_string_symbol end of line symbol
+ * @return start position of next line
+ */
+
+static off_t
+edit_calculate_start_of_next_line (const edit_buffer_t * buf, off_t current_pos, off_t max_pos,
+ char end_string_symbol)
+{
+ off_t i;
+
+ for (i = current_pos; i < max_pos; i++)
+ {
+ current_pos++;
+ if (edit_buffer_get_byte (buf, i) == end_string_symbol)
+ break;
+ }
+
+ return current_pos;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Calculating the end position of previous line.
+ *
+ * @param buf editor buffer object
+ * @param current_pos current position
+ * @param end_string_symbol end of line symbol
+ * @return end position of previous line
+ */
+
+static off_t
+edit_calculate_end_of_previous_line (const edit_buffer_t * buf, off_t current_pos,
+ char end_string_symbol)
+{
+ off_t i;
+
+ for (i = current_pos - 1; i >= 0; i--)
+ if (edit_buffer_get_byte (buf, i) == end_string_symbol)
+ break;
+
+ return i;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Calculating the start position of previous line.
+ *
+ * @param buf editor buffer object
+ * @param current_pos current position
+ * @param end_string_symbol end of line symbol
+ * @return start position of previous line
+ */
+
+static inline off_t
+edit_calculate_start_of_previous_line (const edit_buffer_t * buf, off_t current_pos,
+ char end_string_symbol)
+{
+ current_pos = edit_calculate_end_of_previous_line (buf, current_pos, end_string_symbol);
+ current_pos = edit_calculate_end_of_previous_line (buf, current_pos, end_string_symbol);
+
+ return (current_pos + 1);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Calculating the start position of current line.
+ *
+ * @param buf editor buffer object
+ * @param current_pos current position
+ * @param end_string_symbol end of line symbol
+ * @return start position of current line
+ */
+
+static inline off_t
+edit_calculate_start_of_current_line (const edit_buffer_t * buf, off_t current_pos,
+ char end_string_symbol)
+{
+ current_pos = edit_calculate_end_of_previous_line (buf, current_pos, end_string_symbol);
+
+ return (current_pos + 1);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Fixing (if needed) search start position if 'only in selection' option present.
+ *
+ * @param edit editor object
+ */
+
+static void
+edit_search_fix_search_start_if_selection (WEdit * edit)
+{
+ off_t start_mark = 0;
+ off_t end_mark = 0;
+
+ if (!edit_search_options.only_in_selection)
+ return;
+
+ if (!eval_marks (edit, &start_mark, &end_mark))
+ return;
+
+ if (edit_search_options.backwards)
+ {
+ if (edit->search_start > end_mark || edit->search_start <= start_mark)
+ edit->search_start = end_mark;
+ }
+ else
+ {
+ if (edit->search_start < start_mark || edit->search_start >= end_mark)
+ edit->search_start = start_mark;
+ }
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static gboolean
+edit_find (edit_search_status_msg_t * esm, gsize * len)
+{
+ WEdit *edit = esm->edit;
+ off_t search_start = edit->search_start;
+ off_t search_end;
+ off_t start_mark = 0;
+ off_t end_mark = edit->buffer.size;
+ char end_string_symbol;
+
+ end_string_symbol = edit_search_get_current_end_line_char (edit);
+
+ /* prepare for search */
+ if (edit_search_options.only_in_selection)
+ {
+ if (!eval_marks (edit, &start_mark, &end_mark))
+ {
+ mc_search_set_error (edit->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
+ return FALSE;
+ }
+
+ /* fix the start and the end of search block positions */
+ if ((edit->search_line_type & AT_START_LINE) != 0
+ && (start_mark != 0
+ || edit_buffer_get_byte (&edit->buffer, start_mark - 1) != end_string_symbol))
+ start_mark =
+ edit_calculate_start_of_next_line (&edit->buffer, start_mark, edit->buffer.size,
+ end_string_symbol);
+
+ if ((edit->search_line_type & AT_END_LINE) != 0
+ && (end_mark - 1 != edit->buffer.size
+ || edit_buffer_get_byte (&edit->buffer, end_mark) != end_string_symbol))
+ end_mark =
+ edit_calculate_end_of_previous_line (&edit->buffer, end_mark, end_string_symbol);
+
+ if (start_mark >= end_mark)
+ {
+ mc_search_set_error (edit->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
+ return FALSE;
+ }
+ }
+ else if (edit_search_options.backwards)
+ end_mark = MAX (1, edit->buffer.curs1) - 1;
+
+ /* search */
+ if (edit_search_options.backwards)
+ {
+ /* backward search */
+ search_end = end_mark;
+
+ if ((edit->search_line_type & AT_START_LINE) != 0)
+ search_start =
+ edit_calculate_start_of_current_line (&edit->buffer, search_start,
+ end_string_symbol);
+
+ while (search_start >= start_mark)
+ {
+ gboolean ok;
+
+ if (search_end > (off_t) (search_start + edit->search->original.str->len)
+ && mc_search_is_fixed_search_str (edit->search))
+ search_end = search_start + edit->search->original.str->len;
+
+ ok = mc_search_run (edit->search, (void *) esm, search_start, search_end, len);
+
+ if (ok && edit->search->normal_offset == search_start)
+ return TRUE;
+
+ /* We abort the search in case of a pattern error, or if the user aborts
+ the search. In other words: in all cases except "string not found". */
+ if (!ok && edit->search->error != MC_SEARCH_E_NOTFOUND)
+ return FALSE;
+
+ if ((edit->search_line_type & AT_START_LINE) != 0)
+ search_start =
+ edit_calculate_start_of_previous_line (&edit->buffer, search_start,
+ end_string_symbol);
+ else
+ search_start--;
+ }
+
+ mc_search_set_error (edit->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
+ return FALSE;
+ }
+
+ /* forward search */
+ if ((edit->search_line_type & AT_START_LINE) != 0 && search_start != start_mark)
+ search_start =
+ edit_calculate_start_of_next_line (&edit->buffer, search_start, end_mark,
+ end_string_symbol);
+
+ return mc_search_run (edit->search, (void *) esm, search_start, end_mark, len);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static char *
+edit_replace_cmd__conv_to_display (const char *str)
+{
+#ifdef HAVE_CHARSET
+ GString *tmp;
+
+ tmp = str_convert_to_display (str);
+ if (tmp != NULL)
+ {
+ if (tmp->len != 0)
+ return g_string_free (tmp, FALSE);
+ g_string_free (tmp, TRUE);
+ }
+#endif
+ return g_strdup (str);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static char *
+edit_replace_cmd__conv_to_input (char *str)
+{
+#ifdef HAVE_CHARSET
+ GString *tmp;
+
+ tmp = str_convert_to_input (str);
+ if (tmp->len != 0)
+ return g_string_free (tmp, FALSE);
+ g_string_free (tmp, TRUE);
+#endif
+ return g_strdup (str);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+edit_show_search_error (const WEdit * edit, const char *title)
+{
+ if (edit->search->error == MC_SEARCH_E_NOTFOUND)
+ edit_query_dialog (title, _(STR_E_NOTFOUND));
+ else if (edit->search->error_str != NULL)
+ edit_query_dialog (title, edit->search->error_str);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+edit_do_search (WEdit * edit)
+{
+ edit_search_status_msg_t esm;
+ gsize len = 0;
+
+ /* This shouldn't happen */
+ assert (edit->search != NULL);
+
+ edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
+
+ esm.first = TRUE;
+ esm.edit = edit;
+ esm.offset = edit->search_start;
+
+ status_msg_init (STATUS_MSG (&esm), _("Search"), 1.0, simple_status_msg_init_cb,
+ edit_search_status_update_cb, NULL);
+
+ if (search_create_bookmark)
+ {
+ gboolean found = FALSE;
+ long l = 0, l_last = -1;
+ long q = 0;
+
+ search_create_bookmark = FALSE;
+ book_mark_flush (edit, -1);
+
+ while (mc_search_run (edit->search, (void *) &esm, q, edit->buffer.size, &len))
+ {
+ if (!found)
+ edit->search_start = edit->search->normal_offset;
+ found = TRUE;
+
+ l += edit_buffer_count_lines (&edit->buffer, q, edit->search->normal_offset);
+ if (l != l_last)
+ book_mark_insert (edit, l, BOOK_MARK_FOUND_COLOR);
+ l_last = l;
+ q = edit->search->normal_offset + 1;
+ }
+
+ if (!found)
+ edit_error_dialog (_("Search"), _(STR_E_NOTFOUND));
+ else
+ edit_cursor_move (edit, edit->search_start - edit->buffer.curs1);
+ }
+ else
+ {
+ if (edit->found_len != 0 && edit->search_start == edit->found_start + 1
+ && edit_search_options.backwards)
+ edit->search_start--;
+
+ if (edit->found_len != 0 && edit->search_start == edit->found_start - 1
+ && !edit_search_options.backwards)
+ edit->search_start++;
+
+ if (edit_find (&esm, &len))
+ {
+ edit->found_start = edit->search_start = edit->search->normal_offset;
+ edit->found_len = len;
+ edit->over_col = 0;
+ edit_cursor_move (edit, edit->search_start - edit->buffer.curs1);
+ edit_scroll_screen_over_cursor (edit);
+ if (edit_search_options.backwards)
+ edit->search_start--;
+ else
+ edit->search_start++;
+ }
+ else
+ {
+ edit->search_start = edit->buffer.curs1;
+ edit_show_search_error (edit, _("Search"));
+ }
+ }
+
+ status_msg_deinit (STATUS_MSG (&esm));
+
+ edit->force |= REDRAW_COMPLETELY;
+ edit_scroll_screen_over_cursor (edit);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+edit_search (WEdit * edit)
+{
+ if (edit_dialog_search_show (edit))
+ {
+ edit->search_line_type = edit_get_search_line_type (edit->search);
+ edit_search_fix_search_start_if_selection (edit);
+ edit_do_search (edit);
+ }
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/*** public functions ****************************************************************************/
+/* --------------------------------------------------------------------------------------------- */
+
+gboolean
+edit_search_init (WEdit * edit, const char *str)
+{
+#ifdef HAVE_CHARSET
+ edit->search = mc_search_new (str, cp_source);
+#else
+ edit->search = mc_search_new (str, NULL);
+#endif
+
+ if (edit->search == NULL)
+ return FALSE;
+
+ edit->search->search_type = edit_search_options.type;
+#ifdef HAVE_CHARSET
+ edit->search->is_all_charsets = edit_search_options.all_codepages;
+#endif
+ edit->search->is_case_sensitive = edit_search_options.case_sens;
+ edit->search->whole_words = edit_search_options.whole_words;
+ edit->search->search_fn = edit_search_cmd_callback;
+ edit->search->update_fn = edit_search_update_callback;
+
+ return TRUE;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+edit_search_deinit (WEdit * edit)
+{
+ mc_search_free (edit->search);
+ g_free (edit->last_search_string);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+mc_search_cbret_t
+edit_search_cmd_callback (const void *user_data, gsize char_offset, int *current_char)
+{
+ WEdit *edit = ((const edit_search_status_msg_t *) user_data)->edit;
+
+ *current_char = edit_buffer_get_byte (&edit->buffer, (off_t) char_offset);
+
+ return MC_SEARCH_CB_OK;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+mc_search_cbret_t
+edit_search_update_callback (const void *user_data, gsize char_offset)
+{
+ status_msg_t *sm = STATUS_MSG (user_data);
+
+ ((edit_search_status_msg_t *) sm)->offset = (off_t) char_offset;
+
+ return (sm->update (sm) == B_CANCEL ? MC_SEARCH_CB_ABORT : MC_SEARCH_CB_OK);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+int
+edit_search_status_update_cb (status_msg_t * sm)
+{
+ simple_status_msg_t *ssm = SIMPLE_STATUS_MSG (sm);
+ edit_search_status_msg_t *esm = (edit_search_status_msg_t *) sm;
+ Widget *wd = WIDGET (sm->dlg);
+
+ if (verbose)
+ label_set_textv (ssm->label, _("Searching %s: %3d%%"), esm->edit->last_search_string,
+ edit_buffer_calc_percent (&esm->edit->buffer, esm->offset));
+ else
+ label_set_textv (ssm->label, _("Searching %s"), esm->edit->last_search_string);
+
+ if (esm->first)
+ {
+ Widget *lw = WIDGET (ssm->label);
+ WRect r;
+
+ r = wd->rect;
+ r.cols = MAX (r.cols, lw->rect.cols + 6);
+ widget_set_size_rect (wd, &r);
+ r = lw->rect;
+ r.x = wd->rect.x + (wd->rect.cols - r.cols) / 2;
+ widget_set_size_rect (lw, &r);
+ esm->first = FALSE;
+ }
+
+ return status_msg_common_update (sm);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+edit_search_cmd (WEdit * edit, gboolean again)
+{
+ if (!again)
+ edit_search (edit);
+ else if (edit->last_search_string != NULL)
+ edit_do_search (edit);
+ else
+ {
+ /* find last search string in history */
+ GList *history;
+
+ history = mc_config_history_get (MC_HISTORY_SHARED_SEARCH);
+ if (history != NULL)
+ {
+ /* FIXME: is it possible that history->data == NULL? */
+ edit->last_search_string = (char *) history->data;
+ history->data = NULL;
+ history = g_list_first (history);
+ g_list_free_full (history, g_free);
+
+ if (edit_search_init (edit, edit->last_search_string))
+ {
+ edit_do_search (edit);
+ return;
+ }
+
+ /* found, but cannot init search */
+ MC_PTR_FREE (edit->last_search_string);
+ }
+
+ /* if not... then ask for an expression */
+ edit_search (edit);
+ }
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/** call with edit = 0 before shutdown to close memory leaks */
+
+void
+edit_replace_cmd (WEdit * edit, gboolean again)
+{
+ /* 1 = search string, 2 = replace with */
+ static char *saved1 = NULL; /* saved default[123] */
+ static char *saved2 = NULL;
+ char *input1 = NULL; /* user input from the dialog */
+ char *input2 = NULL;
+ GString *input2_str = NULL;
+ char *disp1 = NULL;
+ char *disp2 = NULL;
+ long times_replaced = 0;
+ gboolean once_found = FALSE;
+ edit_search_status_msg_t esm;
+
+ if (edit == NULL)
+ {
+ MC_PTR_FREE (saved1);
+ MC_PTR_FREE (saved2);
+ return;
+ }
+
+ edit->force |= REDRAW_COMPLETELY;
+
+ if (again && saved1 == NULL && saved2 == NULL)
+ again = FALSE;
+
+ if (again)
+ {
+ input1 = g_strdup (saved1 != NULL ? saved1 : "");
+ input2 = g_strdup (saved2 != NULL ? saved2 : "");
+ }
+ else
+ {
+ char *tmp_inp1, *tmp_inp2;
+
+ disp1 = edit_replace_cmd__conv_to_display (saved1 != NULL ? saved1 : "");
+ disp2 = edit_replace_cmd__conv_to_display (saved2 != NULL ? saved2 : "");
+
+ edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
+
+ edit_dialog_replace_show (edit, disp1, disp2, &input1, &input2);
+
+ g_free (disp1);
+ g_free (disp2);
+
+ if (input1 == NULL || *input1 == '\0')
+ {
+ edit->force = REDRAW_COMPLETELY;
+ goto cleanup;
+ }
+
+ tmp_inp1 = input1;
+ tmp_inp2 = input2;
+ input1 = edit_replace_cmd__conv_to_input (input1);
+ input2 = edit_replace_cmd__conv_to_input (input2);
+ g_free (tmp_inp1);
+ g_free (tmp_inp2);
+
+ g_free (saved1);
+ saved1 = g_strdup (input1);
+ g_free (saved2);
+ saved2 = g_strdup (input2);
+
+ mc_search_free (edit->search);
+ edit->search = NULL;
+ }
+
+ input2_str = g_string_new (input2);
+
+ if (edit->search == NULL)
+ {
+ if (edit_search_init (edit, input1))
+ edit_search_fix_search_start_if_selection (edit);
+ else
+ {
+ edit->search_start = edit->buffer.curs1;
+ goto cleanup;
+ }
+ }
+
+ if (edit->found_len != 0 && edit->search_start == edit->found_start + 1
+ && edit_search_options.backwards)
+ edit->search_start--;
+
+ if (edit->found_len != 0 && edit->search_start == edit->found_start - 1
+ && !edit_search_options.backwards)
+ edit->search_start++;
+
+ esm.first = TRUE;
+ esm.edit = edit;
+ esm.offset = edit->search_start;
+
+ status_msg_init (STATUS_MSG (&esm), _("Search"), 1.0, simple_status_msg_init_cb,
+ edit_search_status_update_cb, NULL);
+
+ do
+ {
+ gsize len = 0;
+
+ if (!edit_find (&esm, &len))
+ {
+ if (!(edit->search->error == MC_SEARCH_E_OK ||
+ (once_found && edit->search->error == MC_SEARCH_E_NOTFOUND)))
+ edit_show_search_error (edit, _("Search"));
+ break;
+ }
+
+ once_found = TRUE;
+
+ edit->search_start = edit->search->normal_offset;
+ /* returns negative on not found or error in pattern */
+
+ if (edit->search_start >= 0 && edit->search_start < edit->buffer.size)
+ {
+ gsize i;
+ GString *repl_str;
+
+ edit->found_start = edit->search_start;
+ edit->found_len = len;
+
+ edit_cursor_move (edit, edit->search_start - edit->buffer.curs1);
+ edit_scroll_screen_over_cursor (edit);
+
+ if (edit->replace_mode == 0)
+ {
+ long l;
+ int prompt;
+
+ l = edit->curs_row - WIDGET (edit)->rect.lines / 3;
+ if (l > 0)
+ edit_scroll_downward (edit, l);
+ if (l < 0)
+ edit_scroll_upward (edit, -l);
+
+ edit_scroll_screen_over_cursor (edit);
+ edit->force |= REDRAW_PAGE;
+ edit_render_keypress (edit);
+
+ /*so that undo stops at each query */
+ edit_push_key_press (edit);
+ /* and prompt 2/3 down */
+ disp1 = edit_replace_cmd__conv_to_display (saved1);
+ disp2 = edit_replace_cmd__conv_to_display (saved2);
+ prompt = edit_dialog_replace_prompt_show (edit, disp1, disp2, -1, -1);
+ g_free (disp1);
+ g_free (disp2);
+
+ if (prompt == B_REPLACE_ALL)
+ edit->replace_mode = 1;
+ else if (prompt == B_SKIP_REPLACE)
+ {
+ if (edit_search_options.backwards)
+ edit->search_start--;
+ else
+ edit->search_start++;
+ continue; /* loop */
+ }
+ else if (prompt == B_CANCEL)
+ {
+ edit->replace_mode = -1;
+ break; /* loop */
+ }
+ }
+
+ repl_str = mc_search_prepare_replace_str (edit->search, input2_str);
+
+ if (edit->search->error != MC_SEARCH_E_OK)
+ {
+ edit_show_search_error (edit, _("Replace"));
+ if (repl_str != NULL)
+ g_string_free (repl_str, TRUE);
+ break;
+ }
+
+ /* delete then insert new */
+ for (i = 0; i < len; i++)
+ edit_delete (edit, TRUE);
+
+ for (i = 0; i < repl_str->len; i++)
+ edit_insert (edit, repl_str->str[i]);
+
+ edit->found_len = repl_str->len;
+ g_string_free (repl_str, TRUE);
+ times_replaced++;
+
+ /* so that we don't find the same string again */
+ if (edit_search_options.backwards)
+ edit->search_start--;
+ else
+ {
+ edit->search_start += edit->found_len + (len == 0 ? 1 : 0);
+
+ if (edit->search_start >= edit->buffer.size)
+ break;
+ }
+
+ edit_scroll_screen_over_cursor (edit);
+ }
+ else
+ {
+ /* try and find from right here for next search */
+ edit->search_start = edit->buffer.curs1;
+ edit_update_curs_col (edit);
+
+ edit->force |= REDRAW_PAGE;
+ edit_render_keypress (edit);
+
+ if (times_replaced == 0)
+ query_dialog (_("Replace"), _(STR_E_NOTFOUND), D_NORMAL, 1, _("&OK"));
+ break;
+ }
+ }
+ while (edit->replace_mode >= 0);
+
+ status_msg_deinit (STATUS_MSG (&esm));
+ edit_scroll_screen_over_cursor (edit);
+ edit->force |= REDRAW_COMPLETELY;
+ edit_render_keypress (edit);
+
+ if (edit->replace_mode == 1 && times_replaced != 0)
+ message (D_NORMAL, _("Replace"), _("%ld replacements made"), times_replaced);
+
+ cleanup:
+ g_free (input1);
+ g_free (input2);
+ if (input2_str != NULL)
+ g_string_free (input2_str, TRUE);
+}
+
+/* --------------------------------------------------------------------------------------------- */