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

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