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-2023
   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 */
  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 /*** file scope variables ************************************************************************/
  68 
  69 /* --------------------------------------------------------------------------------------------- */
  70 /*** file scope functions ************************************************************************/
  71 /* --------------------------------------------------------------------------------------------- */
  72 
  73 static gboolean
  74 edit_dialog_search_show (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
  75 {
  76     char *search_text;
  77     size_t num_of_types = 0;
  78     gchar **list_of_types;
  79     int dialog_result;
  80 
  81     list_of_types = mc_search_get_types_strings_array (&num_of_types);
  82 
  83     {
  84         quick_widget_t quick_widgets[] = {
  85             /* *INDENT-OFF* */
  86             QUICK_LABELED_INPUT (N_("Enter search string:"), input_label_above, INPUT_LAST_TEXT, 
  87                                  MC_HISTORY_SHARED_SEARCH, &search_text, NULL, FALSE, FALSE,
  88                                  INPUT_COMPLETE_NONE),
  89             QUICK_SEPARATOR (TRUE),
  90             QUICK_START_COLUMNS,
  91                 QUICK_RADIO (num_of_types, (const char **) list_of_types,
  92                              (int *) &edit_search_options.type, NULL),
  93             QUICK_NEXT_COLUMN,
  94                 QUICK_CHECKBOX (N_("Cas&e sensitive"), &edit_search_options.case_sens, NULL),
  95                 QUICK_CHECKBOX (N_("&Backwards"), &edit_search_options.backwards, NULL),
  96                 QUICK_CHECKBOX (N_("In se&lection"), &edit_search_options.only_in_selection, NULL),
  97                 QUICK_CHECKBOX (N_("&Whole words"), &edit_search_options.whole_words, NULL),
  98 #ifdef HAVE_CHARSET
  99                 QUICK_CHECKBOX (N_("&All charsets"), &edit_search_options.all_codepages, NULL),
 100 #endif
 101             QUICK_STOP_COLUMNS,
 102             QUICK_START_BUTTONS (TRUE, TRUE),
 103                 QUICK_BUTTON (N_("&OK"), B_ENTER, NULL, NULL),
 104                 QUICK_BUTTON (N_("&Find all"), B_USER, NULL, NULL),
 105                 QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
 106             QUICK_END
 107             /* *INDENT-ON* */
 108         };
 109 
 110         WRect r = { -1, -1, 0, 58 };
 111 
 112         quick_dialog_t qdlg = {
 113             r, N_("Search"), "[Input Line Keys]",
 114             quick_widgets, NULL, NULL
 115         };
 116 
 117         dialog_result = quick_dialog (&qdlg);
 118     }
 119 
 120     g_strfreev (list_of_types);
 121 
 122     if (dialog_result == B_CANCEL || search_text[0] == '\0')
 123     {
 124         g_free (search_text);
 125         return FALSE;
 126     }
 127 
 128     if (dialog_result == B_USER)
 129         search_create_bookmark = TRUE;
 130 
 131 #ifdef HAVE_CHARSET
 132     {
 133         GString *tmp;
 134 
 135         tmp = str_convert_to_input (search_text);
 136         g_free (search_text);
 137         search_text = g_string_free (tmp, FALSE);
 138     }
 139 #endif
 140 
 141     edit_search_deinit (edit);
 142     edit->last_search_string = search_text;
 143 
 144     return edit_search_init (edit, edit->last_search_string);
 145 }
 146 
 147 /* --------------------------------------------------------------------------------------------- */
 148 
 149 static void
 150 edit_dialog_replace_show (WEdit * edit, const char *search_default, const char *replace_default,
     /* [previous][next][first][last][top][bottom][index][help]  */
 151                           /*@out@ */ char **search_text, /*@out@ */ char **replace_text)
 152 {
 153     size_t num_of_types = 0;
 154     gchar **list_of_types;
 155 
 156     if ((search_default == NULL) || (*search_default == '\0'))
 157         search_default = INPUT_LAST_TEXT;
 158 
 159     list_of_types = mc_search_get_types_strings_array (&num_of_types);
 160 
 161     {
 162         quick_widget_t quick_widgets[] = {
 163             /* *INDENT-OFF* */
 164             QUICK_LABELED_INPUT (N_("Enter search string:"), input_label_above, search_default,
 165                                  MC_HISTORY_SHARED_SEARCH, search_text, NULL, FALSE, FALSE,
 166                                  INPUT_COMPLETE_NONE),
 167             QUICK_LABELED_INPUT (N_("Enter replacement string:"), input_label_above, replace_default,
 168                                  "replace", replace_text, NULL, FALSE, FALSE, INPUT_COMPLETE_NONE),
 169             QUICK_SEPARATOR (TRUE),
 170             QUICK_START_COLUMNS,
 171                 QUICK_RADIO (num_of_types, (const char **) list_of_types,
 172                              (int *) &edit_search_options.type, NULL),
 173             QUICK_NEXT_COLUMN,
 174                 QUICK_CHECKBOX (N_("Cas&e sensitive"), &edit_search_options.case_sens, NULL),
 175                 QUICK_CHECKBOX (N_("&Backwards"), &edit_search_options.backwards, NULL),
 176                 QUICK_CHECKBOX (N_("In se&lection"), &edit_search_options.only_in_selection, NULL),
 177                 QUICK_CHECKBOX (N_("&Whole words"), &edit_search_options.whole_words, NULL),
 178 #ifdef HAVE_CHARSET
 179                 QUICK_CHECKBOX (N_("&All charsets"), &edit_search_options.all_codepages, NULL),
 180 #endif
 181             QUICK_STOP_COLUMNS,
 182             QUICK_BUTTONS_OK_CANCEL,
 183             QUICK_END
 184             /* *INDENT-ON* */
 185         };
 186 
 187         WRect r = { -1, -1, 0, 58 };
 188 
 189         quick_dialog_t qdlg = {
 190             r, N_("Replace"), "[Input Line Keys]",
 191             quick_widgets, NULL, NULL
 192         };
 193 
 194         if (quick_dialog (&qdlg) != B_CANCEL)
 195             edit->replace_mode = 0;
 196         else
 197         {
 198             *replace_text = NULL;
 199             *search_text = NULL;
 200         }
 201     }
 202 
 203     g_strfreev (list_of_types);
 204 }
 205 
 206 /* --------------------------------------------------------------------------------------------- */
 207 
 208 static int
 209 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]  */
 210 {
 211     Widget *w = WIDGET (edit);
 212 
 213     /* dialog size */
 214     int dlg_height = 10;
 215     int dlg_width;
 216 
 217     char tmp[BUF_MEDIUM];
 218     char *repl_from, *repl_to;
 219     int retval;
 220 
 221     if (xpos == -1)
 222         xpos = w->rect.x + option_line_state_width + 1;
 223     if (ypos == -1)
 224         ypos = w->rect.y + w->rect.lines / 2;
 225     /* Sometimes menu can hide replaced text. I don't like it */
 226     if ((edit->curs_row >= ypos - 1) && (edit->curs_row <= ypos + dlg_height - 1))
 227         ypos -= dlg_height;
 228 
 229     dlg_width = WIDGET (w->owner)->rect.cols - xpos - 1;
 230 
 231     g_snprintf (tmp, sizeof (tmp), "\"%s\"", from_text);
 232     repl_from = g_strdup (str_trunc (tmp, dlg_width - 7));
 233 
 234     g_snprintf (tmp, sizeof (tmp), "\"%s\"", to_text);
 235     repl_to = g_strdup (str_trunc (tmp, dlg_width - 7));
 236 
 237     {
 238         quick_widget_t quick_widgets[] = {
 239             /* *INDENT-OFF* */
 240             QUICK_LABEL (repl_from, NULL),
 241             QUICK_LABEL (N_("Replace with:"), NULL),
 242             QUICK_LABEL (repl_to, NULL),
 243             QUICK_START_BUTTONS (TRUE, TRUE),
 244                 QUICK_BUTTON (N_("&Replace"), B_ENTER, NULL, NULL),
 245                 QUICK_BUTTON (N_("A&ll"), B_REPLACE_ALL, NULL, NULL),
 246                 QUICK_BUTTON (N_("&Skip"), B_SKIP_REPLACE, NULL, NULL),
 247                 QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
 248             QUICK_END
 249             /* *INDENT-ON* */
 250         };
 251 
 252         WRect r = { ypos, xpos, 0, -1 };
 253 
 254         quick_dialog_t qdlg = {
 255             r, N_("Confirm replace"), NULL,
 256             quick_widgets, NULL, NULL
 257         };
 258 
 259         retval = quick_dialog (&qdlg);
 260     }
 261 
 262     g_free (repl_from);
 263     g_free (repl_to);
 264 
 265     return retval;
 266 }
 267 
 268 /* --------------------------------------------------------------------------------------------- */
 269 
 270 /**
 271  * Get EOL symbol for searching.
 272  *
 273  * @param edit editor object
 274  * @return EOL symbol
 275  */
 276 
 277 static inline char
 278 edit_search_get_current_end_line_char (const WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 279 {
 280     switch (edit->lb)
 281     {
 282     case LB_MAC:
 283         return '\r';
 284     default:
 285         return '\n';
 286     }
 287 }
 288 
 289 /* --------------------------------------------------------------------------------------------- */
 290 /**
 291  * Checking if search condition have BOL(^) or EOL ($) regexp special characters.
 292  *
 293  * @param search search object
 294  * @return result of checks.
 295  */
 296 
 297 static edit_search_line_t
 298 edit_get_search_line_type (mc_search_t * search)
     /* [previous][next][first][last][top][bottom][index][help]  */
 299 {
 300     edit_search_line_t search_line_type = 0;
 301 
 302     if (search->search_type != MC_SEARCH_T_REGEX)
 303         return search_line_type;
 304 
 305     if (search->original.str->str[0] == '^')
 306         search_line_type |= AT_START_LINE;
 307 
 308     if (search->original.str->str[search->original.str->len - 1] == '$')
 309         search_line_type |= AT_END_LINE;
 310     return search_line_type;
 311 }
 312 
 313 /* --------------------------------------------------------------------------------------------- */
 314 /**
 315  * Calculating the start position of next line.
 316  *
 317  * @param buf               editor buffer object
 318  * @param current_pos       current position
 319  * @param max_pos           max position
 320  * @param end_string_symbol end of line symbol
 321  * @return start position of next line
 322  */
 323 
 324 static off_t
 325 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]  */
 326                                    char end_string_symbol)
 327 {
 328     off_t i;
 329 
 330     for (i = current_pos; i < max_pos; i++)
 331     {
 332         current_pos++;
 333         if (edit_buffer_get_byte (buf, i) == end_string_symbol)
 334             break;
 335     }
 336 
 337     return current_pos;
 338 }
 339 
 340 /* --------------------------------------------------------------------------------------------- */
 341 /**
 342  * Calculating the end position of previous line.
 343  *
 344  * @param buf               editor buffer object
 345  * @param current_pos       current position
 346  * @param end_string_symbol end of line symbol
 347  * @return end position of previous line
 348  */
 349 
 350 static off_t
 351 edit_calculate_end_of_previous_line (const edit_buffer_t * buf, off_t current_pos,
     /* [previous][next][first][last][top][bottom][index][help]  */
 352                                      char end_string_symbol)
 353 {
 354     off_t i;
 355 
 356     for (i = current_pos - 1; i >= 0; i--)
 357         if (edit_buffer_get_byte (buf, i) == end_string_symbol)
 358             break;
 359 
 360     return i;
 361 }
 362 
 363 /* --------------------------------------------------------------------------------------------- */
 364 /**
 365  * Calculating the start position of previous line.
 366  *
 367  * @param buf               editor buffer object
 368  * @param current_pos       current position
 369  * @param end_string_symbol end of line symbol
 370  * @return start position of previous line
 371  */
 372 
 373 static inline off_t
 374 edit_calculate_start_of_previous_line (const edit_buffer_t * buf, off_t current_pos,
     /* [previous][next][first][last][top][bottom][index][help]  */
 375                                        char end_string_symbol)
 376 {
 377     current_pos = edit_calculate_end_of_previous_line (buf, current_pos, end_string_symbol);
 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  * Calculating the start position of current line.
 386  *
 387  * @param buf               editor buffer object
 388  * @param current_pos       current position
 389  * @param end_string_symbol end of line symbol
 390  * @return start position of current line
 391  */
 392 
 393 static inline off_t
 394 edit_calculate_start_of_current_line (const edit_buffer_t * buf, off_t current_pos,
     /* [previous][next][first][last][top][bottom][index][help]  */
 395                                       char end_string_symbol)
 396 {
 397     current_pos = edit_calculate_end_of_previous_line (buf, current_pos, end_string_symbol);
 398 
 399     return (current_pos + 1);
 400 }
 401 
 402 /* --------------------------------------------------------------------------------------------- */
 403 /**
 404  * Fixing (if needed) search start position if 'only in selection' option present.
 405  *
 406  * @param edit editor object
 407  */
 408 
 409 static void
 410 edit_search_fix_search_start_if_selection (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 411 {
 412     off_t start_mark = 0;
 413     off_t end_mark = 0;
 414 
 415     if (!edit_search_options.only_in_selection)
 416         return;
 417 
 418     if (!eval_marks (edit, &start_mark, &end_mark))
 419         return;
 420 
 421     if (edit_search_options.backwards)
 422     {
 423         if (edit->search_start > end_mark || edit->search_start <= start_mark)
 424             edit->search_start = end_mark;
 425     }
 426     else
 427     {
 428         if (edit->search_start < start_mark || edit->search_start >= end_mark)
 429             edit->search_start = start_mark;
 430     }
 431 }
 432 
 433 /* --------------------------------------------------------------------------------------------- */
 434 
 435 static gboolean
 436 edit_find (edit_search_status_msg_t * esm, gsize * len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 437 {
 438     WEdit *edit = esm->edit;
 439     off_t search_start = edit->search_start;
 440     off_t search_end;
 441     off_t start_mark = 0;
 442     off_t end_mark = edit->buffer.size;
 443     char end_string_symbol;
 444 
 445     end_string_symbol = edit_search_get_current_end_line_char (edit);
 446 
 447     /* prepare for search */
 448     if (edit_search_options.only_in_selection)
 449     {
 450         if (!eval_marks (edit, &start_mark, &end_mark))
 451         {
 452             mc_search_set_error (edit->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
 453             return FALSE;
 454         }
 455 
 456         /* fix the start and the end of search block positions */
 457         if ((edit->search_line_type & AT_START_LINE) != 0
 458             && (start_mark != 0
 459                 || edit_buffer_get_byte (&edit->buffer, start_mark - 1) != end_string_symbol))
 460             start_mark =
 461                 edit_calculate_start_of_next_line (&edit->buffer, start_mark, edit->buffer.size,
 462                                                    end_string_symbol);
 463 
 464         if ((edit->search_line_type & AT_END_LINE) != 0
 465             && (end_mark - 1 != edit->buffer.size
 466                 || edit_buffer_get_byte (&edit->buffer, end_mark) != end_string_symbol))
 467             end_mark =
 468                 edit_calculate_end_of_previous_line (&edit->buffer, end_mark, end_string_symbol);
 469 
 470         if (start_mark >= end_mark)
 471         {
 472             mc_search_set_error (edit->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
 473             return FALSE;
 474         }
 475     }
 476     else if (edit_search_options.backwards)
 477         end_mark = MAX (1, edit->buffer.curs1) - 1;
 478 
 479     /* search */
 480     if (edit_search_options.backwards)
 481     {
 482         /* backward search */
 483         search_end = end_mark;
 484 
 485         if ((edit->search_line_type & AT_START_LINE) != 0)
 486             search_start =
 487                 edit_calculate_start_of_current_line (&edit->buffer, search_start,
 488                                                       end_string_symbol);
 489 
 490         while (search_start >= start_mark)
 491         {
 492             gboolean ok;
 493 
 494             if (search_end > (off_t) (search_start + edit->search->original.str->len)
 495                 && mc_search_is_fixed_search_str (edit->search))
 496                 search_end = search_start + edit->search->original.str->len;
 497 
 498             ok = mc_search_run (edit->search, (void *) esm, search_start, search_end, len);
 499 
 500             if (ok && edit->search->normal_offset == search_start)
 501                 return TRUE;
 502 
 503             /* We abort the search in case of a pattern error, or if the user aborts
 504                the search. In other words: in all cases except "string not found". */
 505             if (!ok && edit->search->error != MC_SEARCH_E_NOTFOUND)
 506                 return FALSE;
 507 
 508             if ((edit->search_line_type & AT_START_LINE) != 0)
 509                 search_start =
 510                     edit_calculate_start_of_previous_line (&edit->buffer, search_start,
 511                                                            end_string_symbol);
 512             else
 513                 search_start--;
 514         }
 515 
 516         mc_search_set_error (edit->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
 517         return FALSE;
 518     }
 519 
 520     /* forward search */
 521     if ((edit->search_line_type & AT_START_LINE) != 0 && search_start != start_mark)
 522         search_start =
 523             edit_calculate_start_of_next_line (&edit->buffer, search_start, end_mark,
 524                                                end_string_symbol);
 525 
 526     return mc_search_run (edit->search, (void *) esm, search_start, end_mark, len);
 527 }
 528 
 529 /* --------------------------------------------------------------------------------------------- */
 530 
 531 static char *
 532 edit_replace_cmd__conv_to_display (const char *str)
     /* [previous][next][first][last][top][bottom][index][help]  */
 533 {
 534 #ifdef HAVE_CHARSET
 535     GString *tmp;
 536 
 537     tmp = str_convert_to_display (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 char *
 551 edit_replace_cmd__conv_to_input (char *str)
     /* [previous][next][first][last][top][bottom][index][help]  */
 552 {
 553 #ifdef HAVE_CHARSET
 554     GString *tmp;
 555 
 556     tmp = str_convert_to_input (str);
 557     if (tmp->len != 0)
 558         return g_string_free (tmp, FALSE);
 559     g_string_free (tmp, TRUE);
 560 #endif
 561     return g_strdup (str);
 562 }
 563 
 564 /* --------------------------------------------------------------------------------------------- */
 565 
 566 static void
 567 edit_show_search_error (const WEdit * edit, const char *title)
     /* [previous][next][first][last][top][bottom][index][help]  */
 568 {
 569     if (edit->search->error == MC_SEARCH_E_NOTFOUND)
 570         edit_query_dialog (title, _(STR_E_NOTFOUND));
 571     else if (edit->search->error_str != NULL)
 572         edit_query_dialog (title, edit->search->error_str);
 573 }
 574 
 575 /* --------------------------------------------------------------------------------------------- */
 576 
 577 static void
 578 edit_do_search (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 579 {
 580     edit_search_status_msg_t esm;
 581     gsize len = 0;
 582 
 583     /* This shouldn't happen */
 584     assert (edit->search != NULL);
 585 
 586     edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
 587 
 588     esm.first = TRUE;
 589     esm.edit = edit;
 590     esm.offset = edit->search_start;
 591 
 592     status_msg_init (STATUS_MSG (&esm), _("Search"), 1.0, simple_status_msg_init_cb,
 593                      edit_search_status_update_cb, NULL);
 594 
 595     if (search_create_bookmark)
 596     {
 597         gboolean found = FALSE;
 598         long l = 0, l_last = -1;
 599         long q = 0;
 600 
 601         search_create_bookmark = FALSE;
 602         book_mark_flush (edit, -1);
 603 
 604         while (mc_search_run (edit->search, (void *) &esm, q, edit->buffer.size, &len))
 605         {
 606             if (!found)
 607                 edit->search_start = edit->search->normal_offset;
 608             found = TRUE;
 609 
 610             l += edit_buffer_count_lines (&edit->buffer, q, edit->search->normal_offset);
 611             if (l != l_last)
 612                 book_mark_insert (edit, l, BOOK_MARK_FOUND_COLOR);
 613             l_last = l;
 614             q = edit->search->normal_offset + 1;
 615         }
 616 
 617         if (!found)
 618             edit_error_dialog (_("Search"), _(STR_E_NOTFOUND));
 619         else
 620             edit_cursor_move (edit, edit->search_start - edit->buffer.curs1);
 621     }
 622     else
 623     {
 624         if (edit->found_len != 0 && edit->search_start == edit->found_start + 1
 625             && edit_search_options.backwards)
 626             edit->search_start--;
 627 
 628         if (edit->found_len != 0 && edit->search_start == edit->found_start - 1
 629             && !edit_search_options.backwards)
 630             edit->search_start++;
 631 
 632         if (edit_find (&esm, &len))
 633         {
 634             edit->found_start = edit->search_start = edit->search->normal_offset;
 635             edit->found_len = len;
 636             edit->over_col = 0;
 637             edit_cursor_move (edit, edit->search_start - edit->buffer.curs1);
 638             edit_scroll_screen_over_cursor (edit);
 639             if (edit_search_options.backwards)
 640                 edit->search_start--;
 641             else
 642                 edit->search_start++;
 643         }
 644         else
 645         {
 646             edit->search_start = edit->buffer.curs1;
 647             edit_show_search_error (edit, _("Search"));
 648         }
 649     }
 650 
 651     status_msg_deinit (STATUS_MSG (&esm));
 652 
 653     edit->force |= REDRAW_COMPLETELY;
 654     edit_scroll_screen_over_cursor (edit);
 655 }
 656 
 657 /* --------------------------------------------------------------------------------------------- */
 658 
 659 static void
 660 edit_search (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 661 {
 662     if (edit_dialog_search_show (edit))
 663     {
 664         edit->search_line_type = edit_get_search_line_type (edit->search);
 665         edit_search_fix_search_start_if_selection (edit);
 666         edit_do_search (edit);
 667     }
 668 }
 669 
 670 /* --------------------------------------------------------------------------------------------- */
 671 /*** public functions ****************************************************************************/
 672 /* --------------------------------------------------------------------------------------------- */
 673 
 674 gboolean
 675 edit_search_init (WEdit * edit, const char *str)
     /* [previous][next][first][last][top][bottom][index][help]  */
 676 {
 677 #ifdef HAVE_CHARSET
 678     edit->search = mc_search_new (str, cp_source);
 679 #else
 680     edit->search = mc_search_new (str, NULL);
 681 #endif
 682 
 683     if (edit->search == NULL)
 684         return FALSE;
 685 
 686     edit->search->search_type = edit_search_options.type;
 687 #ifdef HAVE_CHARSET
 688     edit->search->is_all_charsets = edit_search_options.all_codepages;
 689 #endif
 690     edit->search->is_case_sensitive = edit_search_options.case_sens;
 691     edit->search->whole_words = edit_search_options.whole_words;
 692     edit->search->search_fn = edit_search_cmd_callback;
 693     edit->search->update_fn = edit_search_update_callback;
 694 
 695     return TRUE;
 696 }
 697 
 698 /* --------------------------------------------------------------------------------------------- */
 699 
 700 void
 701 edit_search_deinit (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 702 {
 703     mc_search_free (edit->search);
 704     g_free (edit->last_search_string);
 705 }
 706 
 707 /* --------------------------------------------------------------------------------------------- */
 708 
 709 mc_search_cbret_t
 710 edit_search_cmd_callback (const void *user_data, gsize char_offset, int *current_char)
     /* [previous][next][first][last][top][bottom][index][help]  */
 711 {
 712     WEdit *edit = ((const edit_search_status_msg_t *) user_data)->edit;
 713 
 714     *current_char = edit_buffer_get_byte (&edit->buffer, (off_t) char_offset);
 715 
 716     return MC_SEARCH_CB_OK;
 717 }
 718 
 719 /* --------------------------------------------------------------------------------------------- */
 720 
 721 mc_search_cbret_t
 722 edit_search_update_callback (const void *user_data, gsize char_offset)
     /* [previous][next][first][last][top][bottom][index][help]  */
 723 {
 724     status_msg_t *sm = STATUS_MSG (user_data);
 725 
 726     ((edit_search_status_msg_t *) sm)->offset = (off_t) char_offset;
 727 
 728     return (sm->update (sm) == B_CANCEL ? MC_SEARCH_CB_ABORT : MC_SEARCH_CB_OK);
 729 }
 730 
 731 /* --------------------------------------------------------------------------------------------- */
 732 
 733 int
 734 edit_search_status_update_cb (status_msg_t * sm)
     /* [previous][next][first][last][top][bottom][index][help]  */
 735 {
 736     simple_status_msg_t *ssm = SIMPLE_STATUS_MSG (sm);
 737     edit_search_status_msg_t *esm = (edit_search_status_msg_t *) sm;
 738     Widget *wd = WIDGET (sm->dlg);
 739 
 740     if (verbose)
 741         label_set_textv (ssm->label, _("Searching %s: %3d%%"), esm->edit->last_search_string,
 742                          edit_buffer_calc_percent (&esm->edit->buffer, esm->offset));
 743     else
 744         label_set_textv (ssm->label, _("Searching %s"), esm->edit->last_search_string);
 745 
 746     if (esm->first)
 747     {
 748         Widget *lw = WIDGET (ssm->label);
 749         WRect r;
 750 
 751         r = wd->rect;
 752         r.cols = MAX (r.cols, lw->rect.cols + 6);
 753         widget_set_size_rect (wd, &r);
 754         r = lw->rect;
 755         r.x = wd->rect.x + (wd->rect.cols - r.cols) / 2;
 756         widget_set_size_rect (lw, &r);
 757         esm->first = FALSE;
 758     }
 759 
 760     return status_msg_common_update (sm);
 761 }
 762 
 763 /* --------------------------------------------------------------------------------------------- */
 764 
 765 void
 766 edit_search_cmd (WEdit * edit, gboolean again)
     /* [previous][next][first][last][top][bottom][index][help]  */
 767 {
 768     if (!again)
 769         edit_search (edit);
 770     else if (edit->last_search_string != NULL)
 771         edit_do_search (edit);
 772     else
 773     {
 774         /* find last search string in history */
 775         GList *history;
 776 
 777         history = mc_config_history_get (MC_HISTORY_SHARED_SEARCH);
 778         if (history != NULL)
 779         {
 780             /* FIXME: is it possible that history->data == NULL? */
 781             edit->last_search_string = (char *) history->data;
 782             history->data = NULL;
 783             history = g_list_first (history);
 784             g_list_free_full (history, g_free);
 785 
 786             if (edit_search_init (edit, edit->last_search_string))
 787             {
 788                 edit_do_search (edit);
 789                 return;
 790             }
 791 
 792             /* found, but cannot init search */
 793             MC_PTR_FREE (edit->last_search_string);
 794         }
 795 
 796         /* if not... then ask for an expression */
 797         edit_search (edit);
 798     }
 799 }
 800 
 801 /* --------------------------------------------------------------------------------------------- */
 802 /** call with edit = 0 before shutdown to close memory leaks */
 803 
 804 void
 805 edit_replace_cmd (WEdit * edit, gboolean again)
     /* [previous][next][first][last][top][bottom][index][help]  */
 806 {
 807     /* 1 = search string, 2 = replace with */
 808     static char *saved1 = NULL; /* saved default[123] */
 809     static char *saved2 = NULL;
 810     char *input1 = NULL;        /* user input from the dialog */
 811     char *input2 = NULL;
 812     GString *input2_str = NULL;
 813     char *disp1 = NULL;
 814     char *disp2 = NULL;
 815     long times_replaced = 0;
 816     gboolean once_found = FALSE;
 817     edit_search_status_msg_t esm;
 818 
 819     if (edit == NULL)
 820     {
 821         MC_PTR_FREE (saved1);
 822         MC_PTR_FREE (saved2);
 823         return;
 824     }
 825 
 826     edit->force |= REDRAW_COMPLETELY;
 827 
 828     if (again && saved1 == NULL && saved2 == NULL)
 829         again = FALSE;
 830 
 831     if (again)
 832     {
 833         input1 = g_strdup (saved1 != NULL ? saved1 : "");
 834         input2 = g_strdup (saved2 != NULL ? saved2 : "");
 835     }
 836     else
 837     {
 838         char *tmp_inp1, *tmp_inp2;
 839 
 840         disp1 = edit_replace_cmd__conv_to_display (saved1 != NULL ? saved1 : "");
 841         disp2 = edit_replace_cmd__conv_to_display (saved2 != NULL ? saved2 : "");
 842 
 843         edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
 844 
 845         edit_dialog_replace_show (edit, disp1, disp2, &input1, &input2);
 846 
 847         g_free (disp1);
 848         g_free (disp2);
 849 
 850         if (input1 == NULL || *input1 == '\0')
 851         {
 852             edit->force = REDRAW_COMPLETELY;
 853             goto cleanup;
 854         }
 855 
 856         tmp_inp1 = input1;
 857         tmp_inp2 = input2;
 858         input1 = edit_replace_cmd__conv_to_input (input1);
 859         input2 = edit_replace_cmd__conv_to_input (input2);
 860         g_free (tmp_inp1);
 861         g_free (tmp_inp2);
 862 
 863         g_free (saved1);
 864         saved1 = g_strdup (input1);
 865         g_free (saved2);
 866         saved2 = g_strdup (input2);
 867 
 868         mc_search_free (edit->search);
 869         edit->search = NULL;
 870     }
 871 
 872     input2_str = g_string_new (input2);
 873 
 874     if (edit->search == NULL)
 875     {
 876         if (edit_search_init (edit, input1))
 877             edit_search_fix_search_start_if_selection (edit);
 878         else
 879         {
 880             edit->search_start = edit->buffer.curs1;
 881             goto cleanup;
 882         }
 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]  */