diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 17:44:12 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 17:44:12 +0000 |
commit | 8ccb487c21368a7fdc8c7c72315325bf0aa06147 (patch) | |
tree | b2056fae01d325924508a41731edfbd4c3cddd23 /src/editor/editsearch.c | |
parent | Initial commit. (diff) | |
download | mc-8ccb487c21368a7fdc8c7c72315325bf0aa06147.tar.xz mc-8ccb487c21368a7fdc8c7c72315325bf0aa06147.zip |
Adding upstream version 3:4.8.29.upstream/3%4.8.29upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/editor/editsearch.c')
-rw-r--r-- | src/editor/editsearch.c | 1034 |
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); +} + +/* --------------------------------------------------------------------------------------------- */ |