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_get_search_line_type
  6. edit_calculate_start_of_next_line
  7. edit_calculate_end_of_previous_line
  8. edit_calculate_start_of_previous_line
  9. edit_calculate_start_of_current_line
  10. edit_search_fix_search_start_if_selection
  11. edit_find
  12. edit_replace_cmd__conv_to_display
  13. edit_replace_cmd__conv_to_input
  14. edit_show_search_error
  15. edit_do_search
  16. edit_search
  17. edit_search_init
  18. edit_search_deinit
  19. edit_search_cmd_callback
  20. edit_search_update_callback
  21. edit_search_status_update_cb
  22. edit_search_cmd
  23. 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  * Checking if search condition have BOL(^) or EOL ($) regexp special characters.
 297  *
 298  * @param search search object
 299  * @return result of checks.
 300  */
 301 
 302 static edit_search_line_t
 303 edit_get_search_line_type (mc_search_t * search)
     /* [previous][next][first][last][top][bottom][index][help]  */
 304 {
 305     edit_search_line_t search_line_type = 0;
 306 
 307     if (search->search_type != MC_SEARCH_T_REGEX)
 308         return search_line_type;
 309 
 310     if (search->original.str->str[0] == '^')
 311         search_line_type |= AT_START_LINE;
 312 
 313     if (search->original.str->str[search->original.str->len - 1] == '$')
 314         search_line_type |= AT_END_LINE;
 315     return search_line_type;
 316 }
 317 
 318 /* --------------------------------------------------------------------------------------------- */
 319 /**
 320  * Calculating the start position of next line.
 321  *
 322  * @param buf               editor buffer object
 323  * @param current_pos       current position
 324  * @param max_pos           max position
 325  * @param end_string_symbol end of line symbol
 326  * @return start position of next line
 327  */
 328 
 329 static off_t
 330 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]  */
 331                                    char end_string_symbol)
 332 {
 333     off_t i;
 334 
 335     for (i = current_pos; i < max_pos; i++)
 336     {
 337         current_pos++;
 338         if (edit_buffer_get_byte (buf, i) == end_string_symbol)
 339             break;
 340     }
 341 
 342     return current_pos;
 343 }
 344 
 345 /* --------------------------------------------------------------------------------------------- */
 346 /**
 347  * Calculating the end position of previous line.
 348  *
 349  * @param buf               editor buffer object
 350  * @param current_pos       current position
 351  * @param end_string_symbol end of line symbol
 352  * @return end position of previous line
 353  */
 354 
 355 static off_t
 356 edit_calculate_end_of_previous_line (const edit_buffer_t * buf, off_t current_pos,
     /* [previous][next][first][last][top][bottom][index][help]  */
 357                                      char end_string_symbol)
 358 {
 359     off_t i;
 360 
 361     for (i = current_pos - 1; i >= 0; i--)
 362         if (edit_buffer_get_byte (buf, i) == end_string_symbol)
 363             break;
 364 
 365     return i;
 366 }
 367 
 368 /* --------------------------------------------------------------------------------------------- */
 369 /**
 370  * Calculating the start position of previous line.
 371  *
 372  * @param buf               editor buffer object
 373  * @param current_pos       current position
 374  * @param end_string_symbol end of line symbol
 375  * @return start position of previous line
 376  */
 377 
 378 static inline off_t
 379 edit_calculate_start_of_previous_line (const edit_buffer_t * buf, off_t current_pos,
     /* [previous][next][first][last][top][bottom][index][help]  */
 380                                        char end_string_symbol)
 381 {
 382     current_pos = edit_calculate_end_of_previous_line (buf, current_pos, end_string_symbol);
 383     current_pos = edit_calculate_end_of_previous_line (buf, current_pos, end_string_symbol);
 384 
 385     return (current_pos + 1);
 386 }
 387 
 388 /* --------------------------------------------------------------------------------------------- */
 389 /**
 390  * Calculating the start position of current line.
 391  *
 392  * @param buf               editor buffer object
 393  * @param current_pos       current position
 394  * @param end_string_symbol end of line symbol
 395  * @return start position of current line
 396  */
 397 
 398 static inline off_t
 399 edit_calculate_start_of_current_line (const edit_buffer_t * buf, off_t current_pos,
     /* [previous][next][first][last][top][bottom][index][help]  */
 400                                       char end_string_symbol)
 401 {
 402     current_pos = edit_calculate_end_of_previous_line (buf, current_pos, end_string_symbol);
 403 
 404     return (current_pos + 1);
 405 }
 406 
 407 /* --------------------------------------------------------------------------------------------- */
 408 /**
 409  * Fixing (if needed) search start position if 'only in selection' option present.
 410  *
 411  * @param edit editor object
 412  */
 413 
 414 static void
 415 edit_search_fix_search_start_if_selection (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 416 {
 417     off_t start_mark = 0;
 418     off_t end_mark = 0;
 419 
 420     if (!edit_search_options.only_in_selection)
 421         return;
 422 
 423     if (!eval_marks (edit, &start_mark, &end_mark))
 424         return;
 425 
 426     if (edit_search_options.backwards)
 427     {
 428         if (edit->search_start > end_mark || edit->search_start <= start_mark)
 429             edit->search_start = end_mark;
 430     }
 431     else
 432     {
 433         if (edit->search_start < start_mark || edit->search_start >= end_mark)
 434             edit->search_start = start_mark;
 435     }
 436 }
 437 
 438 /* --------------------------------------------------------------------------------------------- */
 439 
 440 static gboolean
 441 edit_find (edit_search_status_msg_t * esm, gsize * len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 442 {
 443     WEdit *edit = esm->edit;
 444     off_t search_start = edit->search_start;
 445     off_t search_end;
 446     off_t start_mark = 0;
 447     off_t end_mark = edit->buffer.size;
 448     char end_string_symbol;
 449 
 450     end_string_symbol = edit_search_get_current_end_line_char (edit);
 451 
 452     /* prepare for search */
 453     if (edit_search_options.only_in_selection)
 454     {
 455         if (!eval_marks (edit, &start_mark, &end_mark))
 456         {
 457             mc_search_set_error (edit->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
 458             return FALSE;
 459         }
 460 
 461         /* fix the start and the end of search block positions */
 462         if ((edit->search_line_type & AT_START_LINE) != 0
 463             && (start_mark != 0
 464                 || edit_buffer_get_byte (&edit->buffer, start_mark - 1) != end_string_symbol))
 465             start_mark =
 466                 edit_calculate_start_of_next_line (&edit->buffer, start_mark, edit->buffer.size,
 467                                                    end_string_symbol);
 468 
 469         if ((edit->search_line_type & AT_END_LINE) != 0
 470             && (end_mark - 1 != edit->buffer.size
 471                 || edit_buffer_get_byte (&edit->buffer, end_mark) != end_string_symbol))
 472             end_mark =
 473                 edit_calculate_end_of_previous_line (&edit->buffer, end_mark, end_string_symbol);
 474 
 475         if (start_mark >= end_mark)
 476         {
 477             mc_search_set_error (edit->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
 478             return FALSE;
 479         }
 480     }
 481     else if (edit_search_options.backwards)
 482         end_mark = MAX (1, edit->buffer.curs1) - 1;
 483 
 484     /* search */
 485     if (edit_search_options.backwards)
 486     {
 487         /* backward search */
 488         search_end = end_mark;
 489 
 490         if ((edit->search_line_type & AT_START_LINE) != 0)
 491             search_start =
 492                 edit_calculate_start_of_current_line (&edit->buffer, search_start,
 493                                                       end_string_symbol);
 494 
 495         while (search_start >= start_mark)
 496         {
 497             gboolean ok;
 498 
 499             if (search_end > (off_t) (search_start + edit->search->original.str->len)
 500                 && mc_search_is_fixed_search_str (edit->search))
 501                 search_end = search_start + edit->search->original.str->len;
 502 
 503             ok = mc_search_run (edit->search, (void *) esm, search_start, search_end, len);
 504 
 505             if (ok && edit->search->normal_offset == search_start)
 506                 return TRUE;
 507 
 508             /* We abort the search in case of a pattern error, or if the user aborts
 509                the search. In other words: in all cases except "string not found". */
 510             if (!ok && edit->search->error != MC_SEARCH_E_NOTFOUND)
 511                 return FALSE;
 512 
 513             if ((edit->search_line_type & AT_START_LINE) != 0)
 514                 search_start =
 515                     edit_calculate_start_of_previous_line (&edit->buffer, search_start,
 516                                                            end_string_symbol);
 517             else
 518                 search_start--;
 519         }
 520 
 521         mc_search_set_error (edit->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
 522         return FALSE;
 523     }
 524 
 525     /* forward search */
 526     if ((edit->search_line_type & AT_START_LINE) != 0 && search_start != start_mark)
 527         search_start =
 528             edit_calculate_start_of_next_line (&edit->buffer, search_start, end_mark,
 529                                                end_string_symbol);
 530 
 531     return mc_search_run (edit->search, (void *) esm, search_start, end_mark, len);
 532 }
 533 
 534 /* --------------------------------------------------------------------------------------------- */
 535 
 536 static char *
 537 edit_replace_cmd__conv_to_display (const char *str)
     /* [previous][next][first][last][top][bottom][index][help]  */
 538 {
 539 #ifdef HAVE_CHARSET
 540     GString *tmp;
 541 
 542     tmp = str_convert_to_display (str);
 543     if (tmp != NULL)
 544     {
 545         if (tmp->len != 0)
 546             return g_string_free (tmp, FALSE);
 547         g_string_free (tmp, TRUE);
 548     }
 549 #endif
 550     return g_strdup (str);
 551 }
 552 
 553 /* --------------------------------------------------------------------------------------------- */
 554 
 555 static char *
 556 edit_replace_cmd__conv_to_input (char *str)
     /* [previous][next][first][last][top][bottom][index][help]  */
 557 {
 558 #ifdef HAVE_CHARSET
 559     GString *tmp;
 560 
 561     tmp = str_convert_to_input (str);
 562     if (tmp != NULL)
 563     {
 564         if (tmp->len != 0)
 565             return g_string_free (tmp, FALSE);
 566         g_string_free (tmp, TRUE);
 567     }
 568 #endif
 569     return g_strdup (str);
 570 }
 571 
 572 /* --------------------------------------------------------------------------------------------- */
 573 
 574 static void
 575 edit_show_search_error (const WEdit * edit, const char *title)
     /* [previous][next][first][last][top][bottom][index][help]  */
 576 {
 577     if (edit->search->error == MC_SEARCH_E_NOTFOUND)
 578         edit_query_dialog (title, _(STR_E_NOTFOUND));
 579     else if (edit->search->error_str != NULL)
 580         edit_query_dialog (title, edit->search->error_str);
 581 }
 582 
 583 /* --------------------------------------------------------------------------------------------- */
 584 
 585 static void
 586 edit_do_search (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 587 {
 588     edit_search_status_msg_t esm;
 589     gsize len = 0;
 590 
 591     /* This shouldn't happen */
 592     assert (edit->search != NULL);
 593 
 594     edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
 595 
 596     esm.first = TRUE;
 597     esm.edit = edit;
 598     esm.offset = edit->search_start;
 599 
 600     status_msg_init (STATUS_MSG (&esm), _("Search"), 1.0, simple_status_msg_init_cb,
 601                      edit_search_status_update_cb, NULL);
 602 
 603     if (search_create_bookmark)
 604     {
 605         gboolean found = FALSE;
 606         long l = 0, l_last = -1;
 607         long q = 0;
 608 
 609         search_create_bookmark = FALSE;
 610         book_mark_flush (edit, -1);
 611 
 612         while (mc_search_run (edit->search, (void *) &esm, q, edit->buffer.size, &len))
 613         {
 614             if (!found)
 615                 edit->search_start = edit->search->normal_offset;
 616             found = TRUE;
 617 
 618             l += edit_buffer_count_lines (&edit->buffer, q, edit->search->normal_offset);
 619             if (l != l_last)
 620                 book_mark_insert (edit, l, BOOK_MARK_FOUND_COLOR);
 621             l_last = l;
 622             q = edit->search->normal_offset + 1;
 623         }
 624 
 625         if (!found)
 626             edit_error_dialog (_("Search"), _(STR_E_NOTFOUND));
 627         else
 628             edit_cursor_move (edit, edit->search_start - edit->buffer.curs1);
 629     }
 630     else
 631     {
 632         if (edit->found_len != 0 && edit->search_start == edit->found_start + 1
 633             && edit_search_options.backwards)
 634             edit->search_start--;
 635 
 636         if (edit->found_len != 0 && edit->search_start == edit->found_start - 1
 637             && !edit_search_options.backwards)
 638             edit->search_start++;
 639 
 640         if (edit_find (&esm, &len))
 641         {
 642             edit->found_start = edit->search_start = edit->search->normal_offset;
 643             edit->found_len = len;
 644             edit->over_col = 0;
 645             edit_cursor_move (edit, edit->search_start - edit->buffer.curs1);
 646             edit_scroll_screen_over_cursor (edit);
 647             if (edit_search_options.backwards)
 648                 edit->search_start--;
 649             else
 650                 edit->search_start++;
 651         }
 652         else
 653         {
 654             edit->search_start = edit->buffer.curs1;
 655             edit_show_search_error (edit, _("Search"));
 656         }
 657     }
 658 
 659     status_msg_deinit (STATUS_MSG (&esm));
 660 
 661     edit->force |= REDRAW_COMPLETELY;
 662     edit_scroll_screen_over_cursor (edit);
 663 }
 664 
 665 /* --------------------------------------------------------------------------------------------- */
 666 
 667 static void
 668 edit_search (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 669 {
 670     if (edit_dialog_search_show (edit))
 671         edit_do_search (edit);
 672 }
 673 
 674 /* --------------------------------------------------------------------------------------------- */
 675 /*** public functions ****************************************************************************/
 676 /* --------------------------------------------------------------------------------------------- */
 677 
 678 gboolean
 679 edit_search_init (WEdit * edit, const char *str)
     /* [previous][next][first][last][top][bottom][index][help]  */
 680 {
 681 #ifdef HAVE_CHARSET
 682     edit->search = mc_search_new (str, cp_source);
 683 #else
 684     edit->search = mc_search_new (str, NULL);
 685 #endif
 686 
 687     if (edit->search == NULL)
 688         return FALSE;
 689 
 690     edit->search->search_type = edit_search_options.type;
 691 #ifdef HAVE_CHARSET
 692     edit->search->is_all_charsets = edit_search_options.all_codepages;
 693 #endif
 694     edit->search->is_case_sensitive = edit_search_options.case_sens;
 695     edit->search->whole_words = edit_search_options.whole_words;
 696     edit->search->search_fn = edit_search_cmd_callback;
 697     edit->search->update_fn = edit_search_update_callback;
 698 
 699     edit->search_line_type = edit_get_search_line_type (edit->search);
 700 
 701     edit_search_fix_search_start_if_selection (edit);
 702 
 703     return TRUE;
 704 }
 705 
 706 /* --------------------------------------------------------------------------------------------- */
 707 
 708 void
 709 edit_search_deinit (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 710 {
 711     mc_search_free (edit->search);
 712     g_free (edit->last_search_string);
 713 }
 714 
 715 /* --------------------------------------------------------------------------------------------- */
 716 
 717 mc_search_cbret_t
 718 edit_search_cmd_callback (const void *user_data, gsize char_offset, int *current_char)
     /* [previous][next][first][last][top][bottom][index][help]  */
 719 {
 720     WEdit *edit = ((const edit_search_status_msg_t *) user_data)->edit;
 721 
 722     *current_char = edit_buffer_get_byte (&edit->buffer, (off_t) char_offset);
 723 
 724     return MC_SEARCH_CB_OK;
 725 }
 726 
 727 /* --------------------------------------------------------------------------------------------- */
 728 
 729 mc_search_cbret_t
 730 edit_search_update_callback (const void *user_data, gsize char_offset)
     /* [previous][next][first][last][top][bottom][index][help]  */
 731 {
 732     status_msg_t *sm = STATUS_MSG (user_data);
 733 
 734     ((edit_search_status_msg_t *) sm)->offset = (off_t) char_offset;
 735 
 736     return (sm->update (sm) == B_CANCEL ? MC_SEARCH_CB_ABORT : MC_SEARCH_CB_OK);
 737 }
 738 
 739 /* --------------------------------------------------------------------------------------------- */
 740 
 741 int
 742 edit_search_status_update_cb (status_msg_t * sm)
     /* [previous][next][first][last][top][bottom][index][help]  */
 743 {
 744     simple_status_msg_t *ssm = SIMPLE_STATUS_MSG (sm);
 745     edit_search_status_msg_t *esm = (edit_search_status_msg_t *) sm;
 746     Widget *wd = WIDGET (sm->dlg);
 747 
 748     if (verbose)
 749         label_set_textv (ssm->label, _("Searching %s: %3d%%"), esm->edit->last_search_string,
 750                          edit_buffer_calc_percent (&esm->edit->buffer, esm->offset));
 751     else
 752         label_set_textv (ssm->label, _("Searching %s"), esm->edit->last_search_string);
 753 
 754     if (esm->first)
 755     {
 756         Widget *lw = WIDGET (ssm->label);
 757         WRect r;
 758 
 759         r = wd->rect;
 760         r.cols = MAX (r.cols, lw->rect.cols + 6);
 761         widget_set_size_rect (wd, &r);
 762         r = lw->rect;
 763         r.x = wd->rect.x + (wd->rect.cols - r.cols) / 2;
 764         widget_set_size_rect (lw, &r);
 765         esm->first = FALSE;
 766     }
 767 
 768     return status_msg_common_update (sm);
 769 }
 770 
 771 /* --------------------------------------------------------------------------------------------- */
 772 
 773 void
 774 edit_search_cmd (WEdit * edit, gboolean again)
     /* [previous][next][first][last][top][bottom][index][help]  */
 775 {
 776     if (!again)
 777         edit_search (edit);
 778     else if (edit->last_search_string != NULL)
 779         edit_do_search (edit);
 780     else
 781     {
 782         /* find last search string in history */
 783         char *s;
 784 
 785         s = mc_config_history_get_recent_item (MC_HISTORY_SHARED_SEARCH);
 786         if (s != NULL)
 787         {
 788             edit->last_search_string = s;
 789 
 790             if (edit_search_init (edit, edit->last_search_string))
 791             {
 792                 edit_do_search (edit);
 793                 return;
 794             }
 795 
 796             /* found, but cannot init search */
 797             MC_PTR_FREE (edit->last_search_string);
 798         }
 799 
 800         /* if not... then ask for an expression */
 801         edit_search (edit);
 802     }
 803 }
 804 
 805 /* --------------------------------------------------------------------------------------------- */
 806 /** call with edit = 0 before shutdown to close memory leaks */
 807 
 808 void
 809 edit_replace_cmd (WEdit * edit, gboolean again)
     /* [previous][next][first][last][top][bottom][index][help]  */
 810 {
 811     /* 1 = search string, 2 = replace with */
 812     static char *saved1 = NULL; /* saved default[123] */
 813     static char *saved2 = NULL;
 814     char *input1 = NULL;        /* user input from the dialog */
 815     char *input2 = NULL;
 816     GString *input2_str = NULL;
 817     char *disp1 = NULL;
 818     char *disp2 = NULL;
 819     long times_replaced = 0;
 820     gboolean once_found = FALSE;
 821     edit_search_status_msg_t esm;
 822 
 823     if (edit == NULL)
 824     {
 825         MC_PTR_FREE (saved1);
 826         MC_PTR_FREE (saved2);
 827         return;
 828     }
 829 
 830     edit->force |= REDRAW_COMPLETELY;
 831 
 832     if (again && saved1 == NULL && saved2 == NULL)
 833         again = FALSE;
 834 
 835     if (again)
 836     {
 837         input1 = g_strdup (saved1 != NULL ? saved1 : "");
 838         input2 = g_strdup (saved2 != NULL ? saved2 : "");
 839     }
 840     else
 841     {
 842         char *tmp_inp1, *tmp_inp2;
 843 
 844         disp1 = edit_replace_cmd__conv_to_display (saved1 != NULL ? saved1 : "");
 845         disp2 = edit_replace_cmd__conv_to_display (saved2 != NULL ? saved2 : "");
 846 
 847         edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
 848 
 849         edit_dialog_replace_show (edit, disp1, disp2, &input1, &input2);
 850 
 851         g_free (disp1);
 852         g_free (disp2);
 853 
 854         if (input1 == NULL || *input1 == '\0')
 855         {
 856             edit->force = REDRAW_COMPLETELY;
 857             goto cleanup;
 858         }
 859 
 860         tmp_inp1 = input1;
 861         tmp_inp2 = input2;
 862         input1 = edit_replace_cmd__conv_to_input (input1);
 863         input2 = edit_replace_cmd__conv_to_input (input2);
 864         g_free (tmp_inp1);
 865         g_free (tmp_inp2);
 866 
 867         g_free (saved1);
 868         saved1 = g_strdup (input1);
 869         g_free (saved2);
 870         saved2 = g_strdup (input2);
 871 
 872         mc_search_free (edit->search);
 873         edit->search = NULL;
 874     }
 875 
 876     input2_str = g_string_new_take (input2);
 877     input2 = NULL;
 878 
 879     if (edit->search == NULL && !edit_search_init (edit, input1))
 880     {
 881         edit->search_start = edit->buffer.curs1;
 882         goto cleanup;
 883     }
 884 
 885     if (edit->found_len != 0 && edit->search_start == edit->found_start + 1
 886         && edit_search_options.backwards)
 887         edit->search_start--;
 888 
 889     if (edit->found_len != 0 && edit->search_start == edit->found_start - 1
 890         && !edit_search_options.backwards)
 891         edit->search_start++;
 892 
 893     esm.first = TRUE;
 894     esm.edit = edit;
 895     esm.offset = edit->search_start;
 896 
 897     status_msg_init (STATUS_MSG (&esm), _("Search"), 1.0, simple_status_msg_init_cb,
 898                      edit_search_status_update_cb, NULL);
 899 
 900     do
 901     {
 902         gsize len = 0;
 903 
 904         if (!edit_find (&esm, &len))
 905         {
 906             if (!(edit->search->error == MC_SEARCH_E_OK ||
 907                   (once_found && edit->search->error == MC_SEARCH_E_NOTFOUND)))
 908                 edit_show_search_error (edit, _("Search"));
 909             break;
 910         }
 911 
 912         once_found = TRUE;
 913 
 914         edit->search_start = edit->search->normal_offset;
 915         /* returns negative on not found or error in pattern */
 916 
 917         if (edit->search_start >= 0 && edit->search_start < edit->buffer.size)
 918         {
 919             gsize i;
 920             GString *repl_str;
 921 
 922             edit->found_start = edit->search_start;
 923             edit->found_len = len;
 924 
 925             edit_cursor_move (edit, edit->search_start - edit->buffer.curs1);
 926             edit_scroll_screen_over_cursor (edit);
 927 
 928             if (edit->replace_mode == 0)
 929             {
 930                 long l;
 931                 int prompt;
 932 
 933                 l = edit->curs_row - WIDGET (edit)->rect.lines / 3;
 934                 if (l > 0)
 935                     edit_scroll_downward (edit, l);
 936                 if (l < 0)
 937                     edit_scroll_upward (edit, -l);
 938 
 939                 edit_scroll_screen_over_cursor (edit);
 940                 edit->force |= REDRAW_PAGE;
 941                 edit_render_keypress (edit);
 942 
 943                 /*so that undo stops at each query */
 944                 edit_push_key_press (edit);
 945                 /* and prompt 2/3 down */
 946                 disp1 = edit_replace_cmd__conv_to_display (saved1);
 947                 disp2 = edit_replace_cmd__conv_to_display (saved2);
 948                 prompt = edit_dialog_replace_prompt_show (edit, disp1, disp2, -1, -1);
 949                 g_free (disp1);
 950                 g_free (disp2);
 951 
 952                 if (prompt == B_REPLACE_ALL)
 953                     edit->replace_mode = 1;
 954                 else if (prompt == B_SKIP_REPLACE)
 955                 {
 956                     if (edit_search_options.backwards)
 957                         edit->search_start--;
 958                     else
 959                         edit->search_start++;
 960                     continue;   /* loop */
 961                 }
 962                 else if (prompt == B_CANCEL)
 963                 {
 964                     edit->replace_mode = -1;
 965                     break;      /* loop */
 966                 }
 967             }
 968 
 969             repl_str = mc_search_prepare_replace_str (edit->search, input2_str);
 970 
 971             if (edit->search->error != MC_SEARCH_E_OK)
 972             {
 973                 edit_show_search_error (edit, _("Replace"));
 974                 if (repl_str != NULL)
 975                     g_string_free (repl_str, TRUE);
 976                 break;
 977             }
 978 
 979             /* delete then insert new */
 980             for (i = 0; i < len; i++)
 981                 edit_delete (edit, TRUE);
 982 
 983             for (i = 0; i < repl_str->len; i++)
 984                 edit_insert (edit, repl_str->str[i]);
 985 
 986             edit->found_len = repl_str->len;
 987             g_string_free (repl_str, TRUE);
 988             times_replaced++;
 989 
 990             /* so that we don't find the same string again */
 991             if (edit_search_options.backwards)
 992                 edit->search_start--;
 993             else
 994             {
 995                 edit->search_start += edit->found_len + (len == 0 ? 1 : 0);
 996 
 997                 if (edit->search_start >= edit->buffer.size)
 998                     break;
 999             }
1000 
1001             edit_scroll_screen_over_cursor (edit);
1002         }
1003         else
1004         {
1005             /* try and find from right here for next search */
1006             edit->search_start = edit->buffer.curs1;
1007             edit_update_curs_col (edit);
1008 
1009             edit->force |= REDRAW_PAGE;
1010             edit_render_keypress (edit);
1011 
1012             if (times_replaced == 0)
1013                 query_dialog (_("Replace"), _(STR_E_NOTFOUND), D_NORMAL, 1, _("&OK"));
1014             break;
1015         }
1016     }
1017     while (edit->replace_mode >= 0);
1018 
1019     status_msg_deinit (STATUS_MSG (&esm));
1020     edit_scroll_screen_over_cursor (edit);
1021     edit->force |= REDRAW_COMPLETELY;
1022     edit_render_keypress (edit);
1023 
1024     if (edit->replace_mode == 1 && times_replaced != 0)
1025         message (D_NORMAL, _("Replace"), _("%ld replacements made"), times_replaced);
1026 
1027   cleanup:
1028     g_free (input1);
1029     g_free (input2);
1030     if (input2_str != NULL)
1031         g_string_free (input2_str, TRUE);
1032 }
1033 
1034 /* --------------------------------------------------------------------------------------------- */

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