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

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