/* Search & replace engine of MCEditor. Copyright (C) 2021-2022 Free Software Foundation, Inc. Written by: Andrew Borodin , 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 . */ #include #include #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); } /* --------------------------------------------------------------------------------------------- */