root/src/editor/editsearch.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. edit_dialog_search_show
  2. edit_dialog_replace_show
  3. edit_dialog_replace_prompt_show
  4. edit_search_get_current_end_line_char
  5. edit_calculate_start_of_next_line
  6. edit_calculate_end_of_previous_line
  7. edit_calculate_start_of_previous_line
  8. edit_calculate_start_of_current_line
  9. edit_search_fix_search_start_if_selection
  10. edit_find
  11. edit_replace_cmd__conv_to_display
  12. edit_replace_cmd__conv_to_input
  13. edit_search_show_error
  14. edit_do_search
  15. edit_search
  16. edit_search_init
  17. edit_search_deinit
  18. edit_search_cmd_callback
  19. edit_search_update_callback
  20. edit_search_status_update_cb
  21. edit_search_cmd
  22. edit_replace_cmd

   1 /*
   2    Search & replace engine of MCEditor.
   3 
   4    Copyright (C) 2021-2025
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Andrew Borodin <aborodin@vmail.ru>, 2021-2022
   9 
  10    This file is part of the Midnight Commander.
  11 
  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.
  16 
  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.
  21 
  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/>.
  24  */
  25 
  26 #include <config.h>
  27 
  28 #include <assert.h>
  29 
  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 */
  39 
  40 #include "src/history.h"        /* MC_HISTORY_SHARED_SEARCH */
  41 #include "src/setup.h"          /* verbose */
  42 
  43 #include "edit-impl.h"
  44 #include "editwidget.h"
  45 
  46 #include "editsearch.h"
  47 
  48 /*** global variables ****************************************************************************/
  49 
  50 /*** file scope macro definitions ****************************************************************/
  51 
  52 #define B_REPLACE_ALL (B_USER+1)
  53 #define B_REPLACE_ONE (B_USER+2)
  54 #define B_SKIP_REPLACE (B_USER+3)
  55 
  56 /*** file scope type declarations ****************************************************************/
  57 
  58 typedef struct edit_search_options_t
  59 {
  60     mc_search_type_t type;
  61     gboolean case_sens;
  62     gboolean backwards;
  63     gboolean only_in_selection;
  64     gboolean whole_words;
  65     gboolean all_codepages;
  66 } edit_search_options_t;
  67 
  68 /*** forward declarations (file scope functions) *************************************************/
  69 
  70 /*** file scope variables ************************************************************************/
  71 
  72 static edit_search_options_t edit_search_options = {
  73     .type = MC_SEARCH_T_NORMAL,
  74     .case_sens = FALSE,
  75     .backwards = FALSE,
  76     .only_in_selection = FALSE,
  77     .whole_words = FALSE,
  78     .all_codepages = FALSE
  79 };
  80 
  81 /* --------------------------------------------------------------------------------------------- */
  82 /*** file scope functions ************************************************************************/
  83 /* --------------------------------------------------------------------------------------------- */
  84 
  85 static gboolean
  86 edit_dialog_search_show (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
  87 {
  88     char *search_text = NULL;
  89     size_t num_of_types = 0;
  90     gchar **list_of_types;
  91     int dialog_result;
  92 
  93     list_of_types = mc_search_get_types_strings_array (&num_of_types);
  94 
  95     {
  96         quick_widget_t quick_widgets[] = {
  97             /* *INDENT-OFF* */
  98             QUICK_LABELED_INPUT (N_("Enter search string:"), input_label_above, INPUT_LAST_TEXT, 
  99                                  MC_HISTORY_SHARED_SEARCH, &search_text, NULL, FALSE, FALSE,
 100                                  INPUT_COMPLETE_NONE),
 101             QUICK_SEPARATOR (TRUE),
 102             QUICK_START_COLUMNS,
 103                 QUICK_RADIO (num_of_types, (const char **) list_of_types,
 104                              (int *) &edit_search_options.type, NULL),
 105             QUICK_NEXT_COLUMN,
 106                 QUICK_CHECKBOX (N_("Cas&e sensitive"), &edit_search_options.case_sens, NULL),
 107                 QUICK_CHECKBOX (N_("&Backwards"), &edit_search_options.backwards, NULL),
 108                 QUICK_CHECKBOX (N_("In se&lection"), &edit_search_options.only_in_selection, NULL),
 109                 QUICK_CHECKBOX (N_("&Whole words"), &edit_search_options.whole_words, NULL),
 110 #ifdef HAVE_CHARSET
 111                 QUICK_CHECKBOX (N_("&All charsets"), &edit_search_options.all_codepages, NULL),
 112 #endif
 113             QUICK_STOP_COLUMNS,
 114             QUICK_START_BUTTONS (TRUE, TRUE),
 115                 QUICK_BUTTON (N_("&OK"), B_ENTER, NULL, NULL),
 116                 QUICK_BUTTON (N_("&Find all"), B_USER, NULL, NULL),
 117                 QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
 118             QUICK_END
 119             /* *INDENT-ON* */
 120         };
 121 
 122         WRect r = { -1, -1, 0, 58 };
 123 
 124         quick_dialog_t qdlg = {
 125             r, N_("Search"), "[Input Line Keys]",
 126             quick_widgets, NULL, NULL
 127         };
 128 
 129         dialog_result = quick_dialog (&qdlg);
 130     }
 131 
 132     g_strfreev (list_of_types);
 133 
 134     if (dialog_result == B_CANCEL || search_text == NULL || search_text[0] == '\0')
 135     {
 136         g_free (search_text);
 137         return FALSE;
 138     }
 139 
 140     if (dialog_result == B_USER)
 141         search_create_bookmark = TRUE;
 142 
 143 #ifdef HAVE_CHARSET
 144     {
 145         GString *tmp;
 146 
 147         tmp = str_convert_to_input (search_text);
 148         g_free (search_text);
 149         if (tmp != NULL)
 150             search_text = g_string_free (tmp, FALSE);
 151         else
 152             search_text = g_strdup ("");
 153     }
 154 #endif
 155 
 156     edit_search_deinit (edit);
 157     edit->last_search_string = search_text;
 158 
 159     return edit_search_init (edit, edit->last_search_string);
 160 }
 161 
 162 /* --------------------------------------------------------------------------------------------- */
 163 
 164 static void
 165 edit_dialog_replace_show (WEdit *edit, const char *search_default, const char *replace_default,
     /* [previous][next][first][last][top][bottom][index][help]  */
 166                           /*@out@ */ char **search_text, /*@out@ */ char **replace_text)
 167 {
 168     size_t num_of_types = 0;
 169     gchar **list_of_types;
 170 
 171     if ((search_default == NULL) || (*search_default == '\0'))
 172         search_default = INPUT_LAST_TEXT;
 173 
 174     list_of_types = mc_search_get_types_strings_array (&num_of_types);
 175 
 176     {
 177         quick_widget_t quick_widgets[] = {
 178             /* *INDENT-OFF* */
 179             QUICK_LABELED_INPUT (N_("Enter search string:"), input_label_above, search_default,
 180                                  MC_HISTORY_SHARED_SEARCH, search_text, NULL, FALSE, FALSE,
 181                                  INPUT_COMPLETE_NONE),
 182             QUICK_LABELED_INPUT (N_("Enter replacement string:"), input_label_above, replace_default,
 183                                  "replace", replace_text, NULL, FALSE, FALSE, INPUT_COMPLETE_NONE),
 184             QUICK_SEPARATOR (TRUE),
 185             QUICK_START_COLUMNS,
 186                 QUICK_RADIO (num_of_types, (const char **) list_of_types,
 187                              (int *) &edit_search_options.type, NULL),
 188             QUICK_NEXT_COLUMN,
 189                 QUICK_CHECKBOX (N_("Cas&e sensitive"), &edit_search_options.case_sens, NULL),
 190                 QUICK_CHECKBOX (N_("&Backwards"), &edit_search_options.backwards, NULL),
 191                 QUICK_CHECKBOX (N_("In se&lection"), &edit_search_options.only_in_selection, NULL),
 192                 QUICK_CHECKBOX (N_("&Whole words"), &edit_search_options.whole_words, NULL),
 193 #ifdef HAVE_CHARSET
 194                 QUICK_CHECKBOX (N_("&All charsets"), &edit_search_options.all_codepages, NULL),
 195 #endif
 196             QUICK_STOP_COLUMNS,
 197             QUICK_BUTTONS_OK_CANCEL,
 198             QUICK_END
 199             /* *INDENT-ON* */
 200         };
 201 
 202         WRect r = { -1, -1, 0, 58 };
 203 
 204         quick_dialog_t qdlg = {
 205             r, N_("Replace"), "[Input Line Keys]",
 206             quick_widgets, NULL, NULL
 207         };
 208 
 209         if (quick_dialog (&qdlg) != B_CANCEL)
 210             edit->replace_mode = 0;
 211         else
 212         {
 213             *replace_text = NULL;
 214             *search_text = NULL;
 215         }
 216     }
 217 
 218     g_strfreev (list_of_types);
 219 }
 220 
 221 /* --------------------------------------------------------------------------------------------- */
 222 
 223 static int
 224 edit_dialog_replace_prompt_show (WEdit *edit, char *from_text, char *to_text, int xpos, int ypos)
     /* [previous][next][first][last][top][bottom][index][help]  */
 225 {
 226     Widget *w = WIDGET (edit);
 227 
 228     /* dialog size */
 229     int dlg_height = 10;
 230     int dlg_width;
 231 
 232     char tmp[BUF_MEDIUM];
 233     char *repl_from, *repl_to;
 234     int retval;
 235 
 236     if (xpos == -1)
 237         xpos = w->rect.x + edit_options.line_state_width + 1;
 238     if (ypos == -1)
 239         ypos = w->rect.y + w->rect.lines / 2;
 240     /* Sometimes menu can hide replaced text. I don't like it */
 241     if ((edit->curs_row >= ypos - 1) && (edit->curs_row <= ypos + dlg_height - 1))
 242         ypos -= dlg_height;
 243 
 244     dlg_width = WIDGET (w->owner)->rect.cols - xpos - 1;
 245 
 246     g_snprintf (tmp, sizeof (tmp), "\"%s\"", from_text);
 247     repl_from = g_strdup (str_trunc (tmp, dlg_width - 7));
 248 
 249     g_snprintf (tmp, sizeof (tmp), "\"%s\"", to_text);
 250     repl_to = g_strdup (str_trunc (tmp, dlg_width - 7));
 251 
 252     {
 253         quick_widget_t quick_widgets[] = {
 254             /* *INDENT-OFF* */
 255             QUICK_LABEL (repl_from, NULL),
 256             QUICK_LABEL (N_("Replace with:"), NULL),
 257             QUICK_LABEL (repl_to, NULL),
 258             QUICK_START_BUTTONS (TRUE, TRUE),
 259                 QUICK_BUTTON (N_("&Replace"), B_ENTER, NULL, NULL),
 260                 QUICK_BUTTON (N_("A&ll"), B_REPLACE_ALL, NULL, NULL),
 261                 QUICK_BUTTON (N_("&Skip"), B_SKIP_REPLACE, NULL, NULL),
 262                 QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
 263             QUICK_END
 264             /* *INDENT-ON* */
 265         };
 266 
 267         WRect r = { ypos, xpos, 0, -1 };
 268 
 269         quick_dialog_t qdlg = {
 270             r, N_("Confirm replace"), NULL,
 271             quick_widgets, NULL, NULL
 272         };
 273 
 274         retval = quick_dialog (&qdlg);
 275     }
 276 
 277     g_free (repl_from);
 278     g_free (repl_to);
 279 
 280     return retval;
 281 }
 282 
 283 /* --------------------------------------------------------------------------------------------- */
 284 
 285 /**
 286  * Get EOL symbol for searching.
 287  *
 288  * @param edit editor object
 289  * @return EOL symbol
 290  */
 291 
 292 static inline char
 293 edit_search_get_current_end_line_char (const WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 294 {
 295     switch (edit->lb)
 296     {
 297     case LB_MAC:
 298         return '\r';
 299     default:
 300         return '\n';
 301     }
 302 }
 303 
 304 /* --------------------------------------------------------------------------------------------- */
 305 /**
 306  * Calculating the start position of next line.
 307  *
 308  * @param buf               editor buffer object
 309  * @param current_pos       current position
 310  * @param max_pos           max position
 311  * @param end_string_symbol end of line symbol
 312  * @return start position of next line
 313  */
 314 
 315 static off_t
 316 edit_calculate_start_of_next_line (const edit_buffer_t *buf, off_t current_pos, off_t max_pos,
     /* [previous][next][first][last][top][bottom][index][help]  */
 317                                    char end_string_symbol)
 318 {
 319     off_t i;
 320 
 321     for (i = current_pos; i < max_pos; i++)
 322     {
 323         current_pos++;
 324         if (edit_buffer_get_byte (buf, i) == end_string_symbol)
 325             break;
 326     }
 327 
 328     return current_pos;
 329 }
 330 
 331 /* --------------------------------------------------------------------------------------------- */
 332 /**
 333  * Calculating the end position of previous line.
 334  *
 335  * @param buf               editor buffer object
 336  * @param current_pos       current position
 337  * @param end_string_symbol end of line symbol
 338  * @return end position of previous line
 339  */
 340 
 341 static off_t
 342 edit_calculate_end_of_previous_line (const edit_buffer_t *buf, off_t current_pos,
     /* [previous][next][first][last][top][bottom][index][help]  */
 343                                      char end_string_symbol)
 344 {
 345     off_t i;
 346 
 347     for (i = current_pos - 1; i >= 0; i--)
 348         if (edit_buffer_get_byte (buf, i) == end_string_symbol)
 349             break;
 350 
 351     return i;
 352 }
 353 
 354 /* --------------------------------------------------------------------------------------------- */
 355 /**
 356  * Calculating the start position of previous line.
 357  *
 358  * @param buf               editor buffer object
 359  * @param current_pos       current position
 360  * @param end_string_symbol end of line symbol
 361  * @return start position of previous line
 362  */
 363 
 364 static inline off_t
 365 edit_calculate_start_of_previous_line (const edit_buffer_t *buf, off_t current_pos,
     /* [previous][next][first][last][top][bottom][index][help]  */
 366                                        char end_string_symbol)
 367 {
 368     current_pos = edit_calculate_end_of_previous_line (buf, current_pos, end_string_symbol);
 369     current_pos = edit_calculate_end_of_previous_line (buf, current_pos, end_string_symbol);
 370 
 371     return (current_pos + 1);
 372 }
 373 
 374 /* --------------------------------------------------------------------------------------------- */
 375 /**
 376  * Calculating the start position of current line.
 377  *
 378  * @param buf               editor buffer object
 379  * @param current_pos       current position
 380  * @param end_string_symbol end of line symbol
 381  * @return start position of current line
 382  */
 383 
 384 static inline off_t
 385 edit_calculate_start_of_current_line (const edit_buffer_t *buf, off_t current_pos,
     /* [previous][next][first][last][top][bottom][index][help]  */
 386                                       char end_string_symbol)
 387 {
 388     current_pos = edit_calculate_end_of_previous_line (buf, current_pos, end_string_symbol);
 389 
 390     return (current_pos + 1);
 391 }
 392 
 393 /* --------------------------------------------------------------------------------------------- */
 394 /**
 395  * Fixing (if needed) search start position if 'only in selection' option present.
 396  *
 397  * @param edit editor object
 398  */
 399 
 400 static void
 401 edit_search_fix_search_start_if_selection (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 402 {
 403     off_t start_mark = 0;
 404     off_t end_mark = 0;
 405 
 406     if (!edit_search_options.only_in_selection)
 407         return;
 408 
 409     if (!eval_marks (edit, &start_mark, &end_mark))
 410         return;
 411 
 412     if (edit_search_options.backwards)
 413     {
 414         if (edit->search_start > end_mark || edit->search_start <= start_mark)
 415             edit->search_start = end_mark;
 416     }
 417     else
 418     {
 419         if (edit->search_start < start_mark || edit->search_start >= end_mark)
 420             edit->search_start = start_mark;
 421     }
 422 }
 423 
 424 /* --------------------------------------------------------------------------------------------- */
 425 
 426 static gboolean
 427 edit_find (edit_search_status_msg_t *esm, gsize *len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 428 {
 429     WEdit *edit = esm->edit;
 430     off_t search_start = edit->search_start;
 431     off_t search_end;
 432     off_t start_mark = 0;
 433     off_t end_mark = edit->buffer.size;
 434     char end_string_symbol;
 435 
 436     end_string_symbol = edit_search_get_current_end_line_char (edit);
 437 
 438     /* prepare for search */
 439     if (edit_search_options.only_in_selection)
 440     {
 441         if (!eval_marks (edit, &start_mark, &end_mark))
 442         {
 443             mc_search_set_error (edit->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
 444             return FALSE;
 445         }
 446 
 447         /* fix the start and the end of search block positions */
 448         if ((edit->search_line_type & MC_SEARCH_LINE_BEGIN) != 0
 449             && (start_mark != 0
 450                 || edit_buffer_get_byte (&edit->buffer, start_mark - 1) != end_string_symbol))
 451             start_mark =
 452                 edit_calculate_start_of_next_line (&edit->buffer, start_mark, edit->buffer.size,
 453                                                    end_string_symbol);
 454 
 455         if ((edit->search_line_type & MC_SEARCH_LINE_END) != 0
 456             && (end_mark - 1 != edit->buffer.size
 457                 || edit_buffer_get_byte (&edit->buffer, end_mark) != end_string_symbol))
 458             end_mark =
 459                 edit_calculate_end_of_previous_line (&edit->buffer, end_mark, end_string_symbol);
 460 
 461         if (start_mark >= end_mark)
 462         {
 463             mc_search_set_error (edit->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
 464             return FALSE;
 465         }
 466     }
 467     else if (edit_search_options.backwards)
 468         end_mark = MAX (1, edit->buffer.curs1) - 1;
 469 
 470     /* search */
 471     if (edit_search_options.backwards)
 472     {
 473         /* backward search */
 474         search_end = end_mark;
 475 
 476         if ((edit->search_line_type & MC_SEARCH_LINE_BEGIN) != 0)
 477             search_start =
 478                 edit_calculate_start_of_current_line (&edit->buffer, search_start,
 479                                                       end_string_symbol);
 480 
 481         while (search_start >= start_mark)
 482         {
 483             gboolean ok;
 484 
 485             if (search_end > (off_t) (search_start + edit->search->original.str->len)
 486                 && mc_search_is_fixed_search_str (edit->search))
 487                 search_end = search_start + edit->search->original.str->len;
 488 
 489             ok = mc_search_run (edit->search, (void *) esm, search_start, search_end, len);
 490 
 491             if (ok && edit->search->normal_offset == search_start)
 492                 return TRUE;
 493 
 494             /* We abort the search in case of a pattern error, or if the user aborts
 495                the search. In other words: in all cases except "string not found". */
 496             if (!ok && edit->search->error != MC_SEARCH_E_NOTFOUND)
 497                 return FALSE;
 498 
 499             if ((edit->search_line_type & MC_SEARCH_LINE_BEGIN) != 0)
 500                 search_start =
 501                     edit_calculate_start_of_previous_line (&edit->buffer, search_start,
 502                                                            end_string_symbol);
 503             else
 504                 search_start--;
 505         }
 506 
 507         mc_search_set_error (edit->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
 508         return FALSE;
 509     }
 510 
 511     /* forward search */
 512     if ((edit->search_line_type & MC_SEARCH_LINE_BEGIN) != 0 && search_start != start_mark)
 513         search_start =
 514             edit_calculate_start_of_next_line (&edit->buffer, search_start, end_mark,
 515                                                end_string_symbol);
 516 
 517     return mc_search_run (edit->search, (void *) esm, search_start, end_mark, len);
 518 }
 519 
 520 /* --------------------------------------------------------------------------------------------- */
 521 
 522 static char *
 523 edit_replace_cmd__conv_to_display (const char *str)
     /* [previous][next][first][last][top][bottom][index][help]  */
 524 {
 525 #ifdef HAVE_CHARSET
 526     GString *tmp;
 527 
 528     tmp = str_convert_to_display (str);
 529     if (tmp != NULL)
 530     {
 531         if (tmp->len != 0)
 532             return g_string_free (tmp, FALSE);
 533         g_string_free (tmp, TRUE);
 534     }
 535 #endif
 536     return g_strdup (str);
 537 }
 538 
 539 /* --------------------------------------------------------------------------------------------- */
 540 
 541 static char *
 542 edit_replace_cmd__conv_to_input (char *str)
     /* [previous][next][first][last][top][bottom][index][help]  */
 543 {
 544 #ifdef HAVE_CHARSET
 545     GString *tmp;
 546 
 547     tmp = str_convert_to_input (str);
 548     if (tmp != NULL)
 549     {
 550         if (tmp->len != 0)
 551             return g_string_free (tmp, FALSE);
 552         g_string_free (tmp, TRUE);
 553     }
 554 #endif
 555     return g_strdup (str);
 556 }
 557 
 558 /* --------------------------------------------------------------------------------------------- */
 559 
 560 static void
 561 edit_search_show_error (const WEdit *edit, const char *title)
     /* [previous][next][first][last][top][bottom][index][help]  */
 562 {
 563     if (edit->search->error == MC_SEARCH_E_NOTFOUND)
 564         message (D_NORMAL, title, "%s", _(STR_E_NOTFOUND));
 565     else if (edit->search->error_str != NULL)
 566         message (D_NORMAL, title, "%s", edit->search->error_str);
 567 }
 568 
 569 /* --------------------------------------------------------------------------------------------- */
 570 
 571 static void
 572 edit_do_search (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 573 {
 574     edit_search_status_msg_t esm;
 575     gsize len = 0;
 576 
 577     /* This shouldn't happen */
 578     assert (edit->search != NULL);
 579 
 580     edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
 581 
 582     esm.first = TRUE;
 583     esm.edit = edit;
 584     esm.offset = edit->search_start;
 585 
 586     status_msg_init (STATUS_MSG (&esm), _("Search"), 1.0, simple_status_msg_init_cb,
 587                      edit_search_status_update_cb, NULL);
 588 
 589     if (search_create_bookmark)
 590     {
 591         gboolean found = FALSE;
 592         long l = 0, l_last = -1;
 593         long q = 0;
 594 
 595         search_create_bookmark = FALSE;
 596         book_mark_flush (edit, -1);
 597 
 598         while (mc_search_run (edit->search, (void *) &esm, q, edit->buffer.size, &len))
 599         {
 600             if (!found)
 601                 edit->search_start = edit->search->normal_offset;
 602             found = TRUE;
 603 
 604             l += edit_buffer_count_lines (&edit->buffer, q, edit->search->normal_offset);
 605             if (l != l_last)
 606                 book_mark_insert (edit, l, BOOK_MARK_FOUND_COLOR);
 607             l_last = l;
 608             q = edit->search->normal_offset + 1;
 609         }
 610 
 611         if (!found)
 612             message (D_NORMAL, _("Search"), "%s", _(STR_E_NOTFOUND));
 613         else
 614             edit_cursor_move (edit, edit->search_start - edit->buffer.curs1);
 615     }
 616     else
 617     {
 618         if (edit_search_options.backwards)
 619         {
 620             if (edit->found_len != 0 && edit->search_start == edit->found_start + 1)
 621                 edit->search_start--;
 622         }
 623         else
 624         {
 625             if (edit->found_len != 0 && edit->search_start == edit->found_start - 1)
 626                 edit->search_start++;
 627         }
 628 
 629         if (edit_find (&esm, &len))
 630         {
 631             edit->found_start = edit->search_start = edit->search->normal_offset;
 632             edit->found_len = len;
 633             edit->over_col = 0;
 634             edit_cursor_move (edit, edit->search_start - edit->buffer.curs1);
 635             edit_scroll_screen_over_cursor (edit);
 636             if (edit_search_options.backwards)
 637                 edit->search_start--;
 638             else
 639                 edit->search_start++;
 640         }
 641         else
 642         {
 643             edit->search_start = edit->buffer.curs1;
 644             edit_search_show_error (edit, _("Search"));
 645         }
 646     }
 647 
 648     status_msg_deinit (STATUS_MSG (&esm));
 649 
 650     edit->force |= REDRAW_COMPLETELY;
 651     edit_scroll_screen_over_cursor (edit);
 652 }
 653 
 654 /* --------------------------------------------------------------------------------------------- */
 655 
 656 static void
 657 edit_search (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 658 {
 659     if (edit_dialog_search_show (edit))
 660         edit_do_search (edit);
 661 }
 662 
 663 /* --------------------------------------------------------------------------------------------- */
 664 /*** public functions ****************************************************************************/
 665 /* --------------------------------------------------------------------------------------------- */
 666 
 667 gboolean
 668 edit_search_init (WEdit *edit, const char *str)
     /* [previous][next][first][last][top][bottom][index][help]  */
 669 {
 670 #ifdef HAVE_CHARSET
 671     edit->search = mc_search_new (str, cp_source);
 672 #else
 673     edit->search = mc_search_new (str, NULL);
 674 #endif
 675 
 676     if (edit->search == NULL)
 677         return FALSE;
 678 
 679     edit->search->search_type = edit_search_options.type;
 680 #ifdef HAVE_CHARSET
 681     edit->search->is_all_charsets = edit_search_options.all_codepages;
 682 #endif
 683     edit->search->is_case_sensitive = edit_search_options.case_sens;
 684     edit->search->whole_words = edit_search_options.whole_words;
 685     edit->search->search_fn = edit_search_cmd_callback;
 686     edit->search->update_fn = edit_search_update_callback;
 687 
 688     edit->search_line_type = mc_search_get_line_type (edit->search);
 689 
 690     edit_search_fix_search_start_if_selection (edit);
 691 
 692     return TRUE;
 693 }
 694 
 695 /* --------------------------------------------------------------------------------------------- */
 696 
 697 void
 698 edit_search_deinit (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 699 {
 700     mc_search_free (edit->search);
 701     g_free (edit->last_search_string);
 702 }
 703 
 704 /* --------------------------------------------------------------------------------------------- */
 705 
 706 mc_search_cbret_t
 707 edit_search_cmd_callback (const void *user_data, off_t char_offset, int *current_char)
     /* [previous][next][first][last][top][bottom][index][help]  */
 708 {
 709     WEdit *edit = ((const edit_search_status_msg_t *) user_data)->edit;
 710 
 711     *current_char = edit_buffer_get_byte (&edit->buffer, char_offset);
 712 
 713     return MC_SEARCH_CB_OK;
 714 }
 715 
 716 /* --------------------------------------------------------------------------------------------- */
 717 
 718 mc_search_cbret_t
 719 edit_search_update_callback (const void *user_data, off_t char_offset)
     /* [previous][next][first][last][top][bottom][index][help]  */
 720 {
 721     status_msg_t *sm = STATUS_MSG (user_data);
 722 
 723     ((edit_search_status_msg_t *) sm)->offset = char_offset;
 724 
 725     return (sm->update (sm) == B_CANCEL ? MC_SEARCH_CB_ABORT : MC_SEARCH_CB_OK);
 726 }
 727 
 728 /* --------------------------------------------------------------------------------------------- */
 729 
 730 int
 731 edit_search_status_update_cb (status_msg_t *sm)
     /* [previous][next][first][last][top][bottom][index][help]  */
 732 {
 733     simple_status_msg_t *ssm = SIMPLE_STATUS_MSG (sm);
 734     edit_search_status_msg_t *esm = (edit_search_status_msg_t *) sm;
 735     Widget *wd = WIDGET (sm->dlg);
 736 
 737     if (verbose)
 738         label_set_textv (ssm->label, _("Searching %s: %3d%%"), esm->edit->last_search_string,
 739                          edit_buffer_calc_percent (&esm->edit->buffer, esm->offset));
 740     else
 741         label_set_textv (ssm->label, _("Searching %s"), esm->edit->last_search_string);
 742 
 743     if (esm->first)
 744     {
 745         Widget *lw = WIDGET (ssm->label);
 746         WRect r;
 747 
 748         r = wd->rect;
 749         r.cols = MAX (r.cols, lw->rect.cols + 6);
 750         widget_set_size_rect (wd, &r);
 751         r = lw->rect;
 752         r.x = wd->rect.x + (wd->rect.cols - r.cols) / 2;
 753         widget_set_size_rect (lw, &r);
 754         esm->first = FALSE;
 755     }
 756 
 757     return status_msg_common_update (sm);
 758 }
 759 
 760 /* --------------------------------------------------------------------------------------------- */
 761 
 762 void
 763 edit_search_cmd (WEdit *edit, gboolean again)
     /* [previous][next][first][last][top][bottom][index][help]  */
 764 {
 765     if (!again)
 766         edit_search (edit);
 767     else if (edit->last_search_string != NULL)
 768         edit_do_search (edit);
 769     else
 770     {
 771         /* find last search string in history */
 772         char *s;
 773 
 774         s = mc_config_history_get_recent_item (MC_HISTORY_SHARED_SEARCH);
 775         if (s != NULL)
 776         {
 777             edit->last_search_string = s;
 778 
 779             if (edit_search_init (edit, edit->last_search_string))
 780             {
 781                 edit_do_search (edit);
 782                 return;
 783             }
 784 
 785             /* found, but cannot init search */
 786             MC_PTR_FREE (edit->last_search_string);
 787         }
 788 
 789         /* if not... then ask for an expression */
 790         edit_search (edit);
 791     }
 792 }
 793 
 794 /* --------------------------------------------------------------------------------------------- */
 795 /** call with edit = 0 before shutdown to close memory leaks */
 796 
 797 void
 798 edit_replace_cmd (WEdit *edit, gboolean again)
     /* [previous][next][first][last][top][bottom][index][help]  */
 799 {
 800     /* 1 = search string, 2 = replace with */
 801     static char *saved1 = NULL; /* saved default[123] */
 802     static char *saved2 = NULL;
 803     char *input1 = NULL;        /* user input from the dialog */
 804     char *input2 = NULL;
 805     GString *input2_str = NULL;
 806     char *disp1 = NULL;
 807     char *disp2 = NULL;
 808     long times_replaced = 0;
 809     gboolean once_found = FALSE;
 810     edit_search_status_msg_t esm;
 811 
 812     if (edit == NULL)
 813     {
 814         MC_PTR_FREE (saved1);
 815         MC_PTR_FREE (saved2);
 816         return;
 817     }
 818 
 819     edit->force |= REDRAW_COMPLETELY;
 820 
 821     if (again && saved1 == NULL && saved2 == NULL)
 822         again = FALSE;
 823 
 824     if (again)
 825     {
 826         input1 = g_strdup (saved1 != NULL ? saved1 : "");
 827         input2 = g_strdup (saved2 != NULL ? saved2 : "");
 828     }
 829     else
 830     {
 831         char *tmp_inp1, *tmp_inp2;
 832 
 833         disp1 = edit_replace_cmd__conv_to_display (saved1 != NULL ? saved1 : "");
 834         disp2 = edit_replace_cmd__conv_to_display (saved2 != NULL ? saved2 : "");
 835 
 836         edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
 837 
 838         edit_dialog_replace_show (edit, disp1, disp2, &input1, &input2);
 839 
 840         g_free (disp1);
 841         g_free (disp2);
 842 
 843         if (input1 == NULL || *input1 == '\0')
 844         {
 845             edit->force = REDRAW_COMPLETELY;
 846             goto cleanup;
 847         }
 848 
 849         tmp_inp1 = input1;
 850         tmp_inp2 = input2;
 851         input1 = edit_replace_cmd__conv_to_input (input1);
 852         input2 = edit_replace_cmd__conv_to_input (input2);
 853         g_free (tmp_inp1);
 854         g_free (tmp_inp2);
 855 
 856         g_free (saved1);
 857         saved1 = g_strdup (input1);
 858         g_free (saved2);
 859         saved2 = g_strdup (input2);
 860 
 861         mc_search_free (edit->search);
 862         edit->search = NULL;
 863     }
 864 
 865     input2_str = g_string_new_take (input2);
 866     input2 = NULL;
 867 
 868     if (edit->search == NULL && !edit_search_init (edit, input1))
 869     {
 870         edit->search_start = edit->buffer.curs1;
 871         goto cleanup;
 872     }
 873 
 874     if (edit_search_options.backwards)
 875     {
 876         if (edit->found_len != 0 && edit->search_start == edit->found_start + 1)
 877             edit->search_start--;
 878     }
 879     else
 880     {
 881         if (edit->found_len != 0 && edit->search_start == edit->found_start - 1)
 882             edit->search_start++;
 883     }
 884 
 885     esm.first = TRUE;
 886     esm.edit = edit;
 887     esm.offset = edit->search_start;
 888 
 889     status_msg_init (STATUS_MSG (&esm), _("Search"), 1.0, simple_status_msg_init_cb,
 890                      edit_search_status_update_cb, NULL);
 891 
 892     do
 893     {
 894         gsize len = 0;
 895 
 896         if (!edit_find (&esm, &len))
 897         {
 898             if (!(edit->search->error == MC_SEARCH_E_OK ||
 899                   (once_found && edit->search->error == MC_SEARCH_E_NOTFOUND)))
 900                 edit_search_show_error (edit, _("Search"));
 901             break;
 902         }
 903 
 904         once_found = TRUE;
 905 
 906         edit->search_start = edit->search->normal_offset;
 907         /* returns negative on not found or error in pattern */
 908 
 909         if (edit->search_start >= 0 && edit->search_start < edit->buffer.size)
 910         {
 911             gsize i;
 912             GString *repl_str;
 913 
 914             edit->found_start = edit->search_start;
 915             edit->found_len = len;
 916 
 917             edit_cursor_move (edit, edit->search_start - edit->buffer.curs1);
 918             edit_scroll_screen_over_cursor (edit);
 919 
 920             if (edit->replace_mode == 0)
 921             {
 922                 long l;
 923                 int prompt;
 924 
 925                 l = edit->curs_row - WIDGET (edit)->rect.lines / 3;
 926                 if (l > 0)
 927                     edit_scroll_downward (edit, l);
 928                 if (l < 0)
 929                     edit_scroll_upward (edit, -l);
 930 
 931                 edit_scroll_screen_over_cursor (edit);
 932                 edit->force |= REDRAW_PAGE;
 933                 edit_render_keypress (edit);
 934 
 935                 /*so that undo stops at each query */
 936                 edit_push_key_press (edit);
 937                 /* and prompt 2/3 down */
 938                 disp1 = edit_replace_cmd__conv_to_display (saved1);
 939                 disp2 = edit_replace_cmd__conv_to_display (saved2);
 940                 prompt = edit_dialog_replace_prompt_show (edit, disp1, disp2, -1, -1);
 941                 g_free (disp1);
 942                 g_free (disp2);
 943 
 944                 if (prompt == B_REPLACE_ALL)
 945                     edit->replace_mode = 1;
 946                 else if (prompt == B_SKIP_REPLACE)
 947                 {
 948                     if (edit_search_options.backwards)
 949                         edit->search_start--;
 950                     else
 951                         edit->search_start++;
 952                     continue;   /* loop */
 953                 }
 954                 else if (prompt == B_CANCEL)
 955                 {
 956                     edit->replace_mode = -1;
 957                     break;      /* loop */
 958                 }
 959             }
 960 
 961             repl_str = mc_search_prepare_replace_str (edit->search, input2_str);
 962 
 963             if (edit->search->error != MC_SEARCH_E_OK)
 964             {
 965                 edit_search_show_error (edit, _("Replace"));
 966                 if (repl_str != NULL)
 967                     g_string_free (repl_str, TRUE);
 968                 break;
 969             }
 970 
 971             /* delete then insert new */
 972             for (i = 0; i < len; i++)
 973                 edit_delete (edit, TRUE);
 974 
 975             for (i = 0; i < repl_str->len; i++)
 976                 edit_insert (edit, repl_str->str[i]);
 977 
 978             edit->found_len = repl_str->len;
 979             g_string_free (repl_str, TRUE);
 980             times_replaced++;
 981 
 982             /* so that we don't find the same string again */
 983             if (edit_search_options.backwards)
 984                 edit->search_start--;
 985             else
 986             {
 987                 edit->search_start += edit->found_len + (len == 0 ? 1 : 0);
 988 
 989                 if (edit->search_start >= edit->buffer.size)
 990                     break;
 991             }
 992 
 993             edit_scroll_screen_over_cursor (edit);
 994         }
 995         else
 996         {
 997             /* try and find from right here for next search */
 998             edit->search_start = edit->buffer.curs1;
 999             edit_update_curs_col (edit);
1000 
1001             edit->force |= REDRAW_PAGE;
1002             edit_render_keypress (edit);
1003 
1004             if (times_replaced == 0)
1005                 query_dialog (_("Replace"), _(STR_E_NOTFOUND), D_NORMAL, 1, _("&OK"));
1006             break;
1007         }
1008     }
1009     while (edit->replace_mode >= 0);
1010 
1011     status_msg_deinit (STATUS_MSG (&esm));
1012     edit_scroll_screen_over_cursor (edit);
1013     edit->force |= REDRAW_COMPLETELY;
1014     edit_render_keypress (edit);
1015 
1016     if (edit->replace_mode == 1 && times_replaced != 0)
1017         message (D_NORMAL, _("Replace"), _("%ld replacements made"), times_replaced);
1018 
1019   cleanup:
1020     g_free (input1);
1021     g_free (input2);
1022     if (input2_str != NULL)
1023         g_string_free (input2_str, TRUE);
1024 }
1025 
1026 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */