(edit_get_search_line_type): refactoring.
[midnight-commander.git] / src / editor / editsearch.c
blob86a5e08e1644db7bc3d51c90bed2d406ac31ee66
1 /*
2 Search & replace engine of MCEditor.
4 Copyright (C) 2021-2024
5 Free Software Foundation, Inc.
7 Written by:
8 Andrew Borodin <aborodin@vmail.ru>, 2021-2022
10 This file is part of the Midnight Commander.
12 The Midnight Commander is free software: you can redistribute it
13 and/or modify it under the terms of the GNU General Public License as
14 published by the Free Software Foundation, either version 3 of the License,
15 or (at your option) any later version.
17 The Midnight Commander is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include <config.h>
28 #include <assert.h>
30 #include "lib/global.h"
31 #include "lib/search.h"
32 #include "lib/mcconfig.h" /* mc_config_history_get_recent_item() */
33 #ifdef HAVE_CHARSET
34 #include "lib/charsets.h" /* cp_source */
35 #endif
36 #include "lib/util.h"
37 #include "lib/widget.h"
38 #include "lib/skin.h" /* BOOK_MARK_FOUND_COLOR */
40 #include "src/history.h" /* MC_HISTORY_SHARED_SEARCH */
41 #include "src/setup.h" /* verbose */
43 #include "edit-impl.h"
44 #include "editwidget.h"
46 #include "editsearch.h"
48 /*** global variables ****************************************************************************/
50 edit_search_options_t edit_search_options = {
51 .type = MC_SEARCH_T_NORMAL,
52 .case_sens = FALSE,
53 .backwards = FALSE,
54 .only_in_selection = FALSE,
55 .whole_words = FALSE,
56 .all_codepages = FALSE
59 /*** file scope macro definitions ****************************************************************/
61 #define B_REPLACE_ALL (B_USER+1)
62 #define B_REPLACE_ONE (B_USER+2)
63 #define B_SKIP_REPLACE (B_USER+3)
65 /*** file scope type declarations ****************************************************************/
67 /*** forward declarations (file scope functions) *************************************************/
69 /*** file scope variables ************************************************************************/
71 /* --------------------------------------------------------------------------------------------- */
72 /*** file scope functions ************************************************************************/
73 /* --------------------------------------------------------------------------------------------- */
75 static gboolean
76 edit_dialog_search_show (WEdit * edit)
78 char *search_text;
79 size_t num_of_types = 0;
80 gchar **list_of_types;
81 int dialog_result;
83 list_of_types = mc_search_get_types_strings_array (&num_of_types);
86 quick_widget_t quick_widgets[] = {
87 /* *INDENT-OFF* */
88 QUICK_LABELED_INPUT (N_("Enter search string:"), input_label_above, INPUT_LAST_TEXT,
89 MC_HISTORY_SHARED_SEARCH, &search_text, NULL, FALSE, FALSE,
90 INPUT_COMPLETE_NONE),
91 QUICK_SEPARATOR (TRUE),
92 QUICK_START_COLUMNS,
93 QUICK_RADIO (num_of_types, (const char **) list_of_types,
94 (int *) &edit_search_options.type, NULL),
95 QUICK_NEXT_COLUMN,
96 QUICK_CHECKBOX (N_("Cas&e sensitive"), &edit_search_options.case_sens, NULL),
97 QUICK_CHECKBOX (N_("&Backwards"), &edit_search_options.backwards, NULL),
98 QUICK_CHECKBOX (N_("In se&lection"), &edit_search_options.only_in_selection, NULL),
99 QUICK_CHECKBOX (N_("&Whole words"), &edit_search_options.whole_words, NULL),
100 #ifdef HAVE_CHARSET
101 QUICK_CHECKBOX (N_("&All charsets"), &edit_search_options.all_codepages, NULL),
102 #endif
103 QUICK_STOP_COLUMNS,
104 QUICK_START_BUTTONS (TRUE, TRUE),
105 QUICK_BUTTON (N_("&OK"), B_ENTER, NULL, NULL),
106 QUICK_BUTTON (N_("&Find all"), B_USER, NULL, NULL),
107 QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
108 QUICK_END
109 /* *INDENT-ON* */
112 WRect r = { -1, -1, 0, 58 };
114 quick_dialog_t qdlg = {
115 r, N_("Search"), "[Input Line Keys]",
116 quick_widgets, NULL, NULL
119 dialog_result = quick_dialog (&qdlg);
122 g_strfreev (list_of_types);
124 if (dialog_result == B_CANCEL || search_text[0] == '\0')
126 g_free (search_text);
127 return FALSE;
130 if (dialog_result == B_USER)
131 search_create_bookmark = TRUE;
133 #ifdef HAVE_CHARSET
135 GString *tmp;
137 tmp = str_convert_to_input (search_text);
138 g_free (search_text);
139 if (tmp != NULL)
140 search_text = g_string_free (tmp, FALSE);
141 else
142 search_text = g_strdup ("");
144 #endif
146 edit_search_deinit (edit);
147 edit->last_search_string = search_text;
149 return edit_search_init (edit, edit->last_search_string);
152 /* --------------------------------------------------------------------------------------------- */
154 static void
155 edit_dialog_replace_show (WEdit * edit, const char *search_default, const char *replace_default,
156 /*@out@ */ char **search_text, /*@out@ */ char **replace_text)
158 size_t num_of_types = 0;
159 gchar **list_of_types;
161 if ((search_default == NULL) || (*search_default == '\0'))
162 search_default = INPUT_LAST_TEXT;
164 list_of_types = mc_search_get_types_strings_array (&num_of_types);
167 quick_widget_t quick_widgets[] = {
168 /* *INDENT-OFF* */
169 QUICK_LABELED_INPUT (N_("Enter search string:"), input_label_above, search_default,
170 MC_HISTORY_SHARED_SEARCH, search_text, NULL, FALSE, FALSE,
171 INPUT_COMPLETE_NONE),
172 QUICK_LABELED_INPUT (N_("Enter replacement string:"), input_label_above, replace_default,
173 "replace", replace_text, NULL, FALSE, FALSE, INPUT_COMPLETE_NONE),
174 QUICK_SEPARATOR (TRUE),
175 QUICK_START_COLUMNS,
176 QUICK_RADIO (num_of_types, (const char **) list_of_types,
177 (int *) &edit_search_options.type, NULL),
178 QUICK_NEXT_COLUMN,
179 QUICK_CHECKBOX (N_("Cas&e sensitive"), &edit_search_options.case_sens, NULL),
180 QUICK_CHECKBOX (N_("&Backwards"), &edit_search_options.backwards, NULL),
181 QUICK_CHECKBOX (N_("In se&lection"), &edit_search_options.only_in_selection, NULL),
182 QUICK_CHECKBOX (N_("&Whole words"), &edit_search_options.whole_words, NULL),
183 #ifdef HAVE_CHARSET
184 QUICK_CHECKBOX (N_("&All charsets"), &edit_search_options.all_codepages, NULL),
185 #endif
186 QUICK_STOP_COLUMNS,
187 QUICK_BUTTONS_OK_CANCEL,
188 QUICK_END
189 /* *INDENT-ON* */
192 WRect r = { -1, -1, 0, 58 };
194 quick_dialog_t qdlg = {
195 r, N_("Replace"), "[Input Line Keys]",
196 quick_widgets, NULL, NULL
199 if (quick_dialog (&qdlg) != B_CANCEL)
200 edit->replace_mode = 0;
201 else
203 *replace_text = NULL;
204 *search_text = NULL;
208 g_strfreev (list_of_types);
211 /* --------------------------------------------------------------------------------------------- */
213 static int
214 edit_dialog_replace_prompt_show (WEdit * edit, char *from_text, char *to_text, int xpos, int ypos)
216 Widget *w = WIDGET (edit);
218 /* dialog size */
219 int dlg_height = 10;
220 int dlg_width;
222 char tmp[BUF_MEDIUM];
223 char *repl_from, *repl_to;
224 int retval;
226 if (xpos == -1)
227 xpos = w->rect.x + edit_options.line_state_width + 1;
228 if (ypos == -1)
229 ypos = w->rect.y + w->rect.lines / 2;
230 /* Sometimes menu can hide replaced text. I don't like it */
231 if ((edit->curs_row >= ypos - 1) && (edit->curs_row <= ypos + dlg_height - 1))
232 ypos -= dlg_height;
234 dlg_width = WIDGET (w->owner)->rect.cols - xpos - 1;
236 g_snprintf (tmp, sizeof (tmp), "\"%s\"", from_text);
237 repl_from = g_strdup (str_trunc (tmp, dlg_width - 7));
239 g_snprintf (tmp, sizeof (tmp), "\"%s\"", to_text);
240 repl_to = g_strdup (str_trunc (tmp, dlg_width - 7));
243 quick_widget_t quick_widgets[] = {
244 /* *INDENT-OFF* */
245 QUICK_LABEL (repl_from, NULL),
246 QUICK_LABEL (N_("Replace with:"), NULL),
247 QUICK_LABEL (repl_to, NULL),
248 QUICK_START_BUTTONS (TRUE, TRUE),
249 QUICK_BUTTON (N_("&Replace"), B_ENTER, NULL, NULL),
250 QUICK_BUTTON (N_("A&ll"), B_REPLACE_ALL, NULL, NULL),
251 QUICK_BUTTON (N_("&Skip"), B_SKIP_REPLACE, NULL, NULL),
252 QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
253 QUICK_END
254 /* *INDENT-ON* */
257 WRect r = { ypos, xpos, 0, -1 };
259 quick_dialog_t qdlg = {
260 r, N_("Confirm replace"), NULL,
261 quick_widgets, NULL, NULL
264 retval = quick_dialog (&qdlg);
267 g_free (repl_from);
268 g_free (repl_to);
270 return retval;
273 /* --------------------------------------------------------------------------------------------- */
276 * Get EOL symbol for searching.
278 * @param edit editor object
279 * @return EOL symbol
282 static inline char
283 edit_search_get_current_end_line_char (const WEdit * edit)
285 switch (edit->lb)
287 case LB_MAC:
288 return '\r';
289 default:
290 return '\n';
294 /* --------------------------------------------------------------------------------------------- */
296 * Checking if search condition have BOL(^) or EOL ($) regexp special characters.
298 * @param search search object
299 * @return result of checks.
302 static edit_search_line_t
303 edit_get_search_line_type (const mc_search_t * search)
305 edit_search_line_t search_line_type = 0;
307 if (search->search_type == MC_SEARCH_T_REGEX)
309 if (search->original.str->str[0] == '^')
310 search_line_type |= AT_START_LINE;
312 if (search->original.str->str[search->original.str->len - 1] == '$')
313 search_line_type |= AT_END_LINE;
316 return search_line_type;
319 /* --------------------------------------------------------------------------------------------- */
321 * Calculating the start position of next line.
323 * @param buf editor buffer object
324 * @param current_pos current position
325 * @param max_pos max position
326 * @param end_string_symbol end of line symbol
327 * @return start position of next line
330 static off_t
331 edit_calculate_start_of_next_line (const edit_buffer_t * buf, off_t current_pos, off_t max_pos,
332 char end_string_symbol)
334 off_t i;
336 for (i = current_pos; i < max_pos; i++)
338 current_pos++;
339 if (edit_buffer_get_byte (buf, i) == end_string_symbol)
340 break;
343 return current_pos;
346 /* --------------------------------------------------------------------------------------------- */
348 * Calculating the end position of previous line.
350 * @param buf editor buffer object
351 * @param current_pos current position
352 * @param end_string_symbol end of line symbol
353 * @return end position of previous line
356 static off_t
357 edit_calculate_end_of_previous_line (const edit_buffer_t * buf, off_t current_pos,
358 char end_string_symbol)
360 off_t i;
362 for (i = current_pos - 1; i >= 0; i--)
363 if (edit_buffer_get_byte (buf, i) == end_string_symbol)
364 break;
366 return i;
369 /* --------------------------------------------------------------------------------------------- */
371 * Calculating the start position of previous line.
373 * @param buf editor buffer object
374 * @param current_pos current position
375 * @param end_string_symbol end of line symbol
376 * @return start position of previous line
379 static inline off_t
380 edit_calculate_start_of_previous_line (const edit_buffer_t * buf, off_t current_pos,
381 char end_string_symbol)
383 current_pos = edit_calculate_end_of_previous_line (buf, current_pos, end_string_symbol);
384 current_pos = edit_calculate_end_of_previous_line (buf, current_pos, end_string_symbol);
386 return (current_pos + 1);
389 /* --------------------------------------------------------------------------------------------- */
391 * Calculating the start position of current line.
393 * @param buf editor buffer object
394 * @param current_pos current position
395 * @param end_string_symbol end of line symbol
396 * @return start position of current line
399 static inline off_t
400 edit_calculate_start_of_current_line (const edit_buffer_t * buf, off_t current_pos,
401 char end_string_symbol)
403 current_pos = edit_calculate_end_of_previous_line (buf, current_pos, end_string_symbol);
405 return (current_pos + 1);
408 /* --------------------------------------------------------------------------------------------- */
410 * Fixing (if needed) search start position if 'only in selection' option present.
412 * @param edit editor object
415 static void
416 edit_search_fix_search_start_if_selection (WEdit * edit)
418 off_t start_mark = 0;
419 off_t end_mark = 0;
421 if (!edit_search_options.only_in_selection)
422 return;
424 if (!eval_marks (edit, &start_mark, &end_mark))
425 return;
427 if (edit_search_options.backwards)
429 if (edit->search_start > end_mark || edit->search_start <= start_mark)
430 edit->search_start = end_mark;
432 else
434 if (edit->search_start < start_mark || edit->search_start >= end_mark)
435 edit->search_start = start_mark;
439 /* --------------------------------------------------------------------------------------------- */
441 static gboolean
442 edit_find (edit_search_status_msg_t * esm, gsize * len)
444 WEdit *edit = esm->edit;
445 off_t search_start = edit->search_start;
446 off_t search_end;
447 off_t start_mark = 0;
448 off_t end_mark = edit->buffer.size;
449 char end_string_symbol;
451 end_string_symbol = edit_search_get_current_end_line_char (edit);
453 /* prepare for search */
454 if (edit_search_options.only_in_selection)
456 if (!eval_marks (edit, &start_mark, &end_mark))
458 mc_search_set_error (edit->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
459 return FALSE;
462 /* fix the start and the end of search block positions */
463 if ((edit->search_line_type & AT_START_LINE) != 0
464 && (start_mark != 0
465 || edit_buffer_get_byte (&edit->buffer, start_mark - 1) != end_string_symbol))
466 start_mark =
467 edit_calculate_start_of_next_line (&edit->buffer, start_mark, edit->buffer.size,
468 end_string_symbol);
470 if ((edit->search_line_type & AT_END_LINE) != 0
471 && (end_mark - 1 != edit->buffer.size
472 || edit_buffer_get_byte (&edit->buffer, end_mark) != end_string_symbol))
473 end_mark =
474 edit_calculate_end_of_previous_line (&edit->buffer, end_mark, end_string_symbol);
476 if (start_mark >= end_mark)
478 mc_search_set_error (edit->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
479 return FALSE;
482 else if (edit_search_options.backwards)
483 end_mark = MAX (1, edit->buffer.curs1) - 1;
485 /* search */
486 if (edit_search_options.backwards)
488 /* backward search */
489 search_end = end_mark;
491 if ((edit->search_line_type & AT_START_LINE) != 0)
492 search_start =
493 edit_calculate_start_of_current_line (&edit->buffer, search_start,
494 end_string_symbol);
496 while (search_start >= start_mark)
498 gboolean ok;
500 if (search_end > (off_t) (search_start + edit->search->original.str->len)
501 && mc_search_is_fixed_search_str (edit->search))
502 search_end = search_start + edit->search->original.str->len;
504 ok = mc_search_run (edit->search, (void *) esm, search_start, search_end, len);
506 if (ok && edit->search->normal_offset == search_start)
507 return TRUE;
509 /* We abort the search in case of a pattern error, or if the user aborts
510 the search. In other words: in all cases except "string not found". */
511 if (!ok && edit->search->error != MC_SEARCH_E_NOTFOUND)
512 return FALSE;
514 if ((edit->search_line_type & AT_START_LINE) != 0)
515 search_start =
516 edit_calculate_start_of_previous_line (&edit->buffer, search_start,
517 end_string_symbol);
518 else
519 search_start--;
522 mc_search_set_error (edit->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
523 return FALSE;
526 /* forward search */
527 if ((edit->search_line_type & AT_START_LINE) != 0 && search_start != start_mark)
528 search_start =
529 edit_calculate_start_of_next_line (&edit->buffer, search_start, end_mark,
530 end_string_symbol);
532 return mc_search_run (edit->search, (void *) esm, search_start, end_mark, len);
535 /* --------------------------------------------------------------------------------------------- */
537 static char *
538 edit_replace_cmd__conv_to_display (const char *str)
540 #ifdef HAVE_CHARSET
541 GString *tmp;
543 tmp = str_convert_to_display (str);
544 if (tmp != NULL)
546 if (tmp->len != 0)
547 return g_string_free (tmp, FALSE);
548 g_string_free (tmp, TRUE);
550 #endif
551 return g_strdup (str);
554 /* --------------------------------------------------------------------------------------------- */
556 static char *
557 edit_replace_cmd__conv_to_input (char *str)
559 #ifdef HAVE_CHARSET
560 GString *tmp;
562 tmp = str_convert_to_input (str);
563 if (tmp != NULL)
565 if (tmp->len != 0)
566 return g_string_free (tmp, FALSE);
567 g_string_free (tmp, TRUE);
569 #endif
570 return g_strdup (str);
573 /* --------------------------------------------------------------------------------------------- */
575 static void
576 edit_show_search_error (const WEdit * edit, const char *title)
578 if (edit->search->error == MC_SEARCH_E_NOTFOUND)
579 edit_query_dialog (title, _(STR_E_NOTFOUND));
580 else if (edit->search->error_str != NULL)
581 edit_query_dialog (title, edit->search->error_str);
584 /* --------------------------------------------------------------------------------------------- */
586 static void
587 edit_do_search (WEdit * edit)
589 edit_search_status_msg_t esm;
590 gsize len = 0;
592 /* This shouldn't happen */
593 assert (edit->search != NULL);
595 edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
597 esm.first = TRUE;
598 esm.edit = edit;
599 esm.offset = edit->search_start;
601 status_msg_init (STATUS_MSG (&esm), _("Search"), 1.0, simple_status_msg_init_cb,
602 edit_search_status_update_cb, NULL);
604 if (search_create_bookmark)
606 gboolean found = FALSE;
607 long l = 0, l_last = -1;
608 long q = 0;
610 search_create_bookmark = FALSE;
611 book_mark_flush (edit, -1);
613 while (mc_search_run (edit->search, (void *) &esm, q, edit->buffer.size, &len))
615 if (!found)
616 edit->search_start = edit->search->normal_offset;
617 found = TRUE;
619 l += edit_buffer_count_lines (&edit->buffer, q, edit->search->normal_offset);
620 if (l != l_last)
621 book_mark_insert (edit, l, BOOK_MARK_FOUND_COLOR);
622 l_last = l;
623 q = edit->search->normal_offset + 1;
626 if (!found)
627 edit_error_dialog (_("Search"), _(STR_E_NOTFOUND));
628 else
629 edit_cursor_move (edit, edit->search_start - edit->buffer.curs1);
631 else
633 if (edit->found_len != 0 && edit->search_start == edit->found_start + 1
634 && edit_search_options.backwards)
635 edit->search_start--;
637 if (edit->found_len != 0 && edit->search_start == edit->found_start - 1
638 && !edit_search_options.backwards)
639 edit->search_start++;
641 if (edit_find (&esm, &len))
643 edit->found_start = edit->search_start = edit->search->normal_offset;
644 edit->found_len = len;
645 edit->over_col = 0;
646 edit_cursor_move (edit, edit->search_start - edit->buffer.curs1);
647 edit_scroll_screen_over_cursor (edit);
648 if (edit_search_options.backwards)
649 edit->search_start--;
650 else
651 edit->search_start++;
653 else
655 edit->search_start = edit->buffer.curs1;
656 edit_show_search_error (edit, _("Search"));
660 status_msg_deinit (STATUS_MSG (&esm));
662 edit->force |= REDRAW_COMPLETELY;
663 edit_scroll_screen_over_cursor (edit);
666 /* --------------------------------------------------------------------------------------------- */
668 static void
669 edit_search (WEdit * edit)
671 if (edit_dialog_search_show (edit))
672 edit_do_search (edit);
675 /* --------------------------------------------------------------------------------------------- */
676 /*** public functions ****************************************************************************/
677 /* --------------------------------------------------------------------------------------------- */
679 gboolean
680 edit_search_init (WEdit * edit, const char *str)
682 #ifdef HAVE_CHARSET
683 edit->search = mc_search_new (str, cp_source);
684 #else
685 edit->search = mc_search_new (str, NULL);
686 #endif
688 if (edit->search == NULL)
689 return FALSE;
691 edit->search->search_type = edit_search_options.type;
692 #ifdef HAVE_CHARSET
693 edit->search->is_all_charsets = edit_search_options.all_codepages;
694 #endif
695 edit->search->is_case_sensitive = edit_search_options.case_sens;
696 edit->search->whole_words = edit_search_options.whole_words;
697 edit->search->search_fn = edit_search_cmd_callback;
698 edit->search->update_fn = edit_search_update_callback;
700 edit->search_line_type = edit_get_search_line_type (edit->search);
702 edit_search_fix_search_start_if_selection (edit);
704 return TRUE;
707 /* --------------------------------------------------------------------------------------------- */
709 void
710 edit_search_deinit (WEdit * edit)
712 mc_search_free (edit->search);
713 g_free (edit->last_search_string);
716 /* --------------------------------------------------------------------------------------------- */
718 mc_search_cbret_t
719 edit_search_cmd_callback (const void *user_data, gsize char_offset, int *current_char)
721 WEdit *edit = ((const edit_search_status_msg_t *) user_data)->edit;
723 *current_char = edit_buffer_get_byte (&edit->buffer, (off_t) char_offset);
725 return MC_SEARCH_CB_OK;
728 /* --------------------------------------------------------------------------------------------- */
730 mc_search_cbret_t
731 edit_search_update_callback (const void *user_data, gsize char_offset)
733 status_msg_t *sm = STATUS_MSG (user_data);
735 ((edit_search_status_msg_t *) sm)->offset = (off_t) char_offset;
737 return (sm->update (sm) == B_CANCEL ? MC_SEARCH_CB_ABORT : MC_SEARCH_CB_OK);
740 /* --------------------------------------------------------------------------------------------- */
743 edit_search_status_update_cb (status_msg_t * sm)
745 simple_status_msg_t *ssm = SIMPLE_STATUS_MSG (sm);
746 edit_search_status_msg_t *esm = (edit_search_status_msg_t *) sm;
747 Widget *wd = WIDGET (sm->dlg);
749 if (verbose)
750 label_set_textv (ssm->label, _("Searching %s: %3d%%"), esm->edit->last_search_string,
751 edit_buffer_calc_percent (&esm->edit->buffer, esm->offset));
752 else
753 label_set_textv (ssm->label, _("Searching %s"), esm->edit->last_search_string);
755 if (esm->first)
757 Widget *lw = WIDGET (ssm->label);
758 WRect r;
760 r = wd->rect;
761 r.cols = MAX (r.cols, lw->rect.cols + 6);
762 widget_set_size_rect (wd, &r);
763 r = lw->rect;
764 r.x = wd->rect.x + (wd->rect.cols - r.cols) / 2;
765 widget_set_size_rect (lw, &r);
766 esm->first = FALSE;
769 return status_msg_common_update (sm);
772 /* --------------------------------------------------------------------------------------------- */
774 void
775 edit_search_cmd (WEdit * edit, gboolean again)
777 if (!again)
778 edit_search (edit);
779 else if (edit->last_search_string != NULL)
780 edit_do_search (edit);
781 else
783 /* find last search string in history */
784 char *s;
786 s = mc_config_history_get_recent_item (MC_HISTORY_SHARED_SEARCH);
787 if (s != NULL)
789 edit->last_search_string = s;
791 if (edit_search_init (edit, edit->last_search_string))
793 edit_do_search (edit);
794 return;
797 /* found, but cannot init search */
798 MC_PTR_FREE (edit->last_search_string);
801 /* if not... then ask for an expression */
802 edit_search (edit);
806 /* --------------------------------------------------------------------------------------------- */
807 /** call with edit = 0 before shutdown to close memory leaks */
809 void
810 edit_replace_cmd (WEdit * edit, gboolean again)
812 /* 1 = search string, 2 = replace with */
813 static char *saved1 = NULL; /* saved default[123] */
814 static char *saved2 = NULL;
815 char *input1 = NULL; /* user input from the dialog */
816 char *input2 = NULL;
817 GString *input2_str = NULL;
818 char *disp1 = NULL;
819 char *disp2 = NULL;
820 long times_replaced = 0;
821 gboolean once_found = FALSE;
822 edit_search_status_msg_t esm;
824 if (edit == NULL)
826 MC_PTR_FREE (saved1);
827 MC_PTR_FREE (saved2);
828 return;
831 edit->force |= REDRAW_COMPLETELY;
833 if (again && saved1 == NULL && saved2 == NULL)
834 again = FALSE;
836 if (again)
838 input1 = g_strdup (saved1 != NULL ? saved1 : "");
839 input2 = g_strdup (saved2 != NULL ? saved2 : "");
841 else
843 char *tmp_inp1, *tmp_inp2;
845 disp1 = edit_replace_cmd__conv_to_display (saved1 != NULL ? saved1 : "");
846 disp2 = edit_replace_cmd__conv_to_display (saved2 != NULL ? saved2 : "");
848 edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
850 edit_dialog_replace_show (edit, disp1, disp2, &input1, &input2);
852 g_free (disp1);
853 g_free (disp2);
855 if (input1 == NULL || *input1 == '\0')
857 edit->force = REDRAW_COMPLETELY;
858 goto cleanup;
861 tmp_inp1 = input1;
862 tmp_inp2 = input2;
863 input1 = edit_replace_cmd__conv_to_input (input1);
864 input2 = edit_replace_cmd__conv_to_input (input2);
865 g_free (tmp_inp1);
866 g_free (tmp_inp2);
868 g_free (saved1);
869 saved1 = g_strdup (input1);
870 g_free (saved2);
871 saved2 = g_strdup (input2);
873 mc_search_free (edit->search);
874 edit->search = NULL;
877 input2_str = g_string_new_take (input2);
878 input2 = NULL;
880 if (edit->search == NULL && !edit_search_init (edit, input1))
882 edit->search_start = edit->buffer.curs1;
883 goto cleanup;
886 if (edit->found_len != 0 && edit->search_start == edit->found_start + 1
887 && edit_search_options.backwards)
888 edit->search_start--;
890 if (edit->found_len != 0 && edit->search_start == edit->found_start - 1
891 && !edit_search_options.backwards)
892 edit->search_start++;
894 esm.first = TRUE;
895 esm.edit = edit;
896 esm.offset = edit->search_start;
898 status_msg_init (STATUS_MSG (&esm), _("Search"), 1.0, simple_status_msg_init_cb,
899 edit_search_status_update_cb, NULL);
903 gsize len = 0;
905 if (!edit_find (&esm, &len))
907 if (!(edit->search->error == MC_SEARCH_E_OK ||
908 (once_found && edit->search->error == MC_SEARCH_E_NOTFOUND)))
909 edit_show_search_error (edit, _("Search"));
910 break;
913 once_found = TRUE;
915 edit->search_start = edit->search->normal_offset;
916 /* returns negative on not found or error in pattern */
918 if (edit->search_start >= 0 && edit->search_start < edit->buffer.size)
920 gsize i;
921 GString *repl_str;
923 edit->found_start = edit->search_start;
924 edit->found_len = len;
926 edit_cursor_move (edit, edit->search_start - edit->buffer.curs1);
927 edit_scroll_screen_over_cursor (edit);
929 if (edit->replace_mode == 0)
931 long l;
932 int prompt;
934 l = edit->curs_row - WIDGET (edit)->rect.lines / 3;
935 if (l > 0)
936 edit_scroll_downward (edit, l);
937 if (l < 0)
938 edit_scroll_upward (edit, -l);
940 edit_scroll_screen_over_cursor (edit);
941 edit->force |= REDRAW_PAGE;
942 edit_render_keypress (edit);
944 /*so that undo stops at each query */
945 edit_push_key_press (edit);
946 /* and prompt 2/3 down */
947 disp1 = edit_replace_cmd__conv_to_display (saved1);
948 disp2 = edit_replace_cmd__conv_to_display (saved2);
949 prompt = edit_dialog_replace_prompt_show (edit, disp1, disp2, -1, -1);
950 g_free (disp1);
951 g_free (disp2);
953 if (prompt == B_REPLACE_ALL)
954 edit->replace_mode = 1;
955 else if (prompt == B_SKIP_REPLACE)
957 if (edit_search_options.backwards)
958 edit->search_start--;
959 else
960 edit->search_start++;
961 continue; /* loop */
963 else if (prompt == B_CANCEL)
965 edit->replace_mode = -1;
966 break; /* loop */
970 repl_str = mc_search_prepare_replace_str (edit->search, input2_str);
972 if (edit->search->error != MC_SEARCH_E_OK)
974 edit_show_search_error (edit, _("Replace"));
975 if (repl_str != NULL)
976 g_string_free (repl_str, TRUE);
977 break;
980 /* delete then insert new */
981 for (i = 0; i < len; i++)
982 edit_delete (edit, TRUE);
984 for (i = 0; i < repl_str->len; i++)
985 edit_insert (edit, repl_str->str[i]);
987 edit->found_len = repl_str->len;
988 g_string_free (repl_str, TRUE);
989 times_replaced++;
991 /* so that we don't find the same string again */
992 if (edit_search_options.backwards)
993 edit->search_start--;
994 else
996 edit->search_start += edit->found_len + (len == 0 ? 1 : 0);
998 if (edit->search_start >= edit->buffer.size)
999 break;
1002 edit_scroll_screen_over_cursor (edit);
1004 else
1006 /* try and find from right here for next search */
1007 edit->search_start = edit->buffer.curs1;
1008 edit_update_curs_col (edit);
1010 edit->force |= REDRAW_PAGE;
1011 edit_render_keypress (edit);
1013 if (times_replaced == 0)
1014 query_dialog (_("Replace"), _(STR_E_NOTFOUND), D_NORMAL, 1, _("&OK"));
1015 break;
1018 while (edit->replace_mode >= 0);
1020 status_msg_deinit (STATUS_MSG (&esm));
1021 edit_scroll_screen_over_cursor (edit);
1022 edit->force |= REDRAW_COMPLETELY;
1023 edit_render_keypress (edit);
1025 if (edit->replace_mode == 1 && times_replaced != 0)
1026 message (D_NORMAL, _("Replace"), _("%ld replacements made"), times_replaced);
1028 cleanup:
1029 g_free (input1);
1030 g_free (input2);
1031 if (input2_str != NULL)
1032 g_string_free (input2_str, TRUE);
1035 /* --------------------------------------------------------------------------------------------- */