root/src/viewer/search.c

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

DEFINITIONS

This source file includes following definitions.
  1. mcview_search_status_update_cb
  2. mcview_search_update_steps
  3. mcview_find
  4. mcview_search_show_result
  5. mcview_search_init
  6. mcview_search_deinit
  7. mcview_search_cmd_callback
  8. mcview_search_update_cmd_callback
  9. mcview_do_search

   1 /*
   2    Internal file viewer for the Midnight Commander
   3    Function for search data
   4 
   5    Copyright (C) 1994-2021
   6    Free Software Foundation, Inc.
   7 
   8    Written by:
   9    Miguel de Icaza, 1994, 1995, 1998
  10    Janne Kukonlehto, 1994, 1995
  11    Jakub Jelinek, 1995
  12    Joseph M. Hinkle, 1996
  13    Norbert Warmuth, 1997
  14    Pavel Machek, 1998
  15    Roland Illig <roland.illig@gmx.de>, 2004, 2005
  16    Slava Zanko <slavazanko@google.com>, 2009
  17    Andrew Borodin <aborodin@vmail.ru>, 2009, 2013
  18    Ilia Maslakov <il.smind@gmail.com>, 2009
  19 
  20    This file is part of the Midnight Commander.
  21 
  22    The Midnight Commander is free software: you can redistribute it
  23    and/or modify it under the terms of the GNU General Public License as
  24    published by the Free Software Foundation, either version 3 of the License,
  25    or (at your option) any later version.
  26 
  27    The Midnight Commander is distributed in the hope that it will be useful,
  28    but WITHOUT ANY WARRANTY; without even the implied warranty of
  29    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  30    GNU General Public License for more details.
  31 
  32    You should have received a copy of the GNU General Public License
  33    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  34  */
  35 
  36 #include <config.h>
  37 
  38 #include "lib/global.h"
  39 #include "lib/strutil.h"
  40 #ifdef HAVE_CHARSET
  41 #include "lib/charsets.h"       /* cp_source */
  42 #endif
  43 #include "lib/widget.h"
  44 
  45 #include "src/setup.h"
  46 
  47 #include "internal.h"
  48 
  49 /*** global variables ****************************************************************************/
  50 
  51 mcview_search_options_t mcview_search_options = {
  52     .type = MC_SEARCH_T_NORMAL,
  53     .case_sens = FALSE,
  54     .backwards = FALSE,
  55     .whole_words = FALSE,
  56     .all_codepages = FALSE
  57 };
  58 
  59 /*** file scope macro definitions ****************************************************************/
  60 
  61 /*** file scope type declarations ****************************************************************/
  62 
  63 typedef struct
  64 {
  65     simple_status_msg_t status_msg;     /* base class */
  66 
  67     gboolean first;
  68     WView *view;
  69     off_t offset;
  70 } mcview_search_status_msg_t;
  71 
  72 /*** file scope variables ************************************************************************/
  73 
  74 static int search_cb_char_curr_index = -1;
  75 static char search_cb_char_buffer[6];
  76 
  77 /* --------------------------------------------------------------------------------------------- */
  78 /*** file scope functions ************************************************************************/
  79 /* --------------------------------------------------------------------------------------------- */
  80 
  81 static int
  82 mcview_search_status_update_cb (status_msg_t * sm)
     /* [previous][next][first][last][top][bottom][index][help]  */
  83 {
  84     simple_status_msg_t *ssm = SIMPLE_STATUS_MSG (sm);
  85     mcview_search_status_msg_t *vsm = (mcview_search_status_msg_t *) sm;
  86     Widget *wd = WIDGET (sm->dlg);
  87     int percent = -1;
  88 
  89     if (verbose)
  90         percent = mcview_calc_percent (vsm->view, vsm->offset);
  91 
  92     if (percent >= 0)
  93         label_set_textv (ssm->label, _("Searching %s: %3d%%"), vsm->view->last_search_string,
  94                          percent);
  95     else
  96         label_set_textv (ssm->label, _("Searching %s"), vsm->view->last_search_string);
  97 
  98     if (vsm->first)
  99     {
 100         int wd_width;
 101         Widget *lw = WIDGET (ssm->label);
 102 
 103         wd_width = MAX (wd->cols, lw->cols + 6);
 104         widget_set_size (wd, wd->y, wd->x, wd->lines, wd_width);
 105         widget_set_size (lw, lw->y, wd->x + (wd->cols - lw->cols) / 2, lw->lines, lw->cols);
 106         vsm->first = FALSE;
 107     }
 108 
 109     return status_msg_common_update (sm);
 110 }
 111 
 112 /* --------------------------------------------------------------------------------------------- */
 113 
 114 static void
 115 mcview_search_update_steps (WView * view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 116 {
 117     off_t filesize;
 118 
 119     filesize = mcview_get_filesize (view);
 120 
 121     if (filesize != 0)
 122         view->update_steps = filesize / 100;
 123     else                        /* viewing a data stream, not a file */
 124         view->update_steps = 40000;
 125 
 126     /* Do not update the percent display but every 20 kb */
 127     if (view->update_steps < 20000)
 128         view->update_steps = 20000;
 129 
 130     /* Make interrupt more responsive */
 131     if (view->update_steps > 40000)
 132         view->update_steps = 40000;
 133 }
 134 
 135 /* --------------------------------------------------------------------------------------------- */
 136 
 137 static gboolean
 138 mcview_find (mcview_search_status_msg_t * ssm, off_t search_start, off_t search_end, gsize * len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 139 {
 140     WView *view = ssm->view;
 141 
 142     view->search_numNeedSkipChar = 0;
 143     search_cb_char_curr_index = -1;
 144 
 145     if (mcview_search_options.backwards)
 146     {
 147         search_end = mcview_get_filesize (view);
 148         while (search_start >= 0)
 149         {
 150             gboolean ok;
 151 
 152             view->search_nroff_seq->index = search_start;
 153             mcview_nroff_seq_info (view->search_nroff_seq);
 154 
 155             if (search_end > search_start + (off_t) view->search->original_len
 156                 && mc_search_is_fixed_search_str (view->search))
 157                 search_end = search_start + view->search->original_len;
 158 
 159             ok = mc_search_run (view->search, (void *) ssm, search_start, search_end, len);
 160             if (ok && view->search->normal_offset == search_start)
 161             {
 162                 if (view->mode_flags.nroff)
 163                     view->search->normal_offset++;
 164                 return TRUE;
 165             }
 166 
 167             /* We abort the search in case of a pattern error, or if the user aborts
 168                the search. In other words: in all cases except "string not found". */
 169             if (!ok && view->search->error != MC_SEARCH_E_NOTFOUND)
 170                 return FALSE;
 171 
 172             search_start--;
 173         }
 174 
 175         mc_search_set_error (view->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
 176         return FALSE;
 177     }
 178     view->search_nroff_seq->index = search_start;
 179     mcview_nroff_seq_info (view->search_nroff_seq);
 180 
 181     return mc_search_run (view->search, (void *) ssm, search_start, search_end, len);
 182 }
 183 
 184 /* --------------------------------------------------------------------------------------------- */
 185 
 186 static void
 187 mcview_search_show_result (WView * view, size_t match_len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 188 {
 189     int nroff_len;
 190 
 191     nroff_len =
 192         view->mode_flags.nroff
 193         ? mcview__get_nroff_real_len (view, view->search->start_buffer,
 194                                       view->search->normal_offset - view->search->start_buffer) : 0;
 195     view->search_start = view->search->normal_offset + nroff_len;
 196 
 197     if (!view->mode_flags.hex)
 198         view->search_start++;
 199 
 200     nroff_len =
 201         view->mode_flags.nroff ? mcview__get_nroff_real_len (view, view->search_start - 1,
 202                                                              match_len) : 0;
 203     view->search_end = view->search_start + match_len + nroff_len;
 204 
 205     mcview_moveto_match (view);
 206 }
 207 
 208 /* --------------------------------------------------------------------------------------------- */
 209 /*** public functions ****************************************************************************/
 210 /* --------------------------------------------------------------------------------------------- */
 211 
 212 gboolean
 213 mcview_search_init (WView * view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 214 {
 215 #ifdef HAVE_CHARSET
 216     view->search = mc_search_new (view->last_search_string, cp_source);
 217 #else
 218     view->search = mc_search_new (view->last_search_string, NULL);
 219 #endif
 220 
 221     view->search_nroff_seq = mcview_nroff_seq_new (view);
 222 
 223     if (view->search == NULL)
 224         return FALSE;
 225 
 226     view->search->search_type = mcview_search_options.type;
 227 #ifdef HAVE_CHARSET
 228     view->search->is_all_charsets = mcview_search_options.all_codepages;
 229 #endif
 230     view->search->is_case_sensitive = mcview_search_options.case_sens;
 231     view->search->whole_words = mcview_search_options.whole_words;
 232     view->search->search_fn = mcview_search_cmd_callback;
 233     view->search->update_fn = mcview_search_update_cmd_callback;
 234 
 235     return TRUE;
 236 }
 237 
 238 /* --------------------------------------------------------------------------------------------- */
 239 
 240 void
 241 mcview_search_deinit (WView * view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 242 {
 243     mc_search_free (view->search);
 244     g_free (view->last_search_string);
 245     mcview_nroff_seq_free (&view->search_nroff_seq);
 246 }
 247 
 248 /* --------------------------------------------------------------------------------------------- */
 249 
 250 mc_search_cbret_t
 251 mcview_search_cmd_callback (const void *user_data, gsize char_offset, int *current_char)
     /* [previous][next][first][last][top][bottom][index][help]  */
 252 {
 253     WView *view = ((const mcview_search_status_msg_t *) user_data)->view;
 254 
 255     /*    view_read_continue (view, &view->search_onechar_info); *//* AB:FIXME */
 256     if (!view->mode_flags.nroff)
 257     {
 258         mcview_get_byte (view, char_offset, current_char);
 259         return MC_SEARCH_CB_OK;
 260     }
 261 
 262     if (view->search_numNeedSkipChar != 0)
 263     {
 264         view->search_numNeedSkipChar--;
 265         return MC_SEARCH_CB_SKIP;
 266     }
 267 
 268     if (search_cb_char_curr_index == -1
 269         || search_cb_char_curr_index >= view->search_nroff_seq->char_length)
 270     {
 271         if (search_cb_char_curr_index != -1)
 272             mcview_nroff_seq_next (view->search_nroff_seq);
 273 
 274         search_cb_char_curr_index = 0;
 275         if (view->search_nroff_seq->char_length > 1)
 276             g_unichar_to_utf8 (view->search_nroff_seq->current_char, search_cb_char_buffer);
 277         else
 278             search_cb_char_buffer[0] = (char) view->search_nroff_seq->current_char;
 279 
 280         if (view->search_nroff_seq->type != NROFF_TYPE_NONE)
 281         {
 282             switch (view->search_nroff_seq->type)
 283             {
 284             case NROFF_TYPE_BOLD:
 285                 view->search_numNeedSkipChar = 1 + view->search_nroff_seq->char_length; /* real char length and 0x8 */
 286                 break;
 287             case NROFF_TYPE_UNDERLINE:
 288                 view->search_numNeedSkipChar = 2;       /* underline symbol and ox8 */
 289                 break;
 290             default:
 291                 break;
 292             }
 293         }
 294         return MC_SEARCH_CB_INVALID;
 295     }
 296 
 297     *current_char = search_cb_char_buffer[search_cb_char_curr_index];
 298     search_cb_char_curr_index++;
 299 
 300     return (*current_char != -1) ? MC_SEARCH_CB_OK : MC_SEARCH_CB_INVALID;
 301 }
 302 
 303 /* --------------------------------------------------------------------------------------------- */
 304 
 305 mc_search_cbret_t
 306 mcview_search_update_cmd_callback (const void *user_data, gsize char_offset)
     /* [previous][next][first][last][top][bottom][index][help]  */
 307 {
 308     status_msg_t *sm = STATUS_MSG (user_data);
 309     mcview_search_status_msg_t *vsm = (mcview_search_status_msg_t *) user_data;
 310     WView *view = vsm->view;
 311     gboolean do_update = FALSE;
 312     mc_search_cbret_t result = MC_SEARCH_CB_OK;
 313 
 314     vsm->offset = (off_t) char_offset;
 315 
 316     if (mcview_search_options.backwards)
 317     {
 318         if (vsm->offset <= view->update_activate)
 319         {
 320             view->update_activate -= view->update_steps;
 321 
 322             do_update = TRUE;
 323         }
 324     }
 325     else
 326     {
 327         if (vsm->offset >= view->update_activate)
 328         {
 329             view->update_activate += view->update_steps;
 330 
 331             do_update = TRUE;
 332         }
 333     }
 334 
 335     if (do_update && sm->update (sm) == B_CANCEL)
 336         result = MC_SEARCH_CB_ABORT;
 337 
 338     /* may be in future return from this callback will change current position in searching block. */
 339 
 340     return result;
 341 }
 342 
 343 /* --------------------------------------------------------------------------------------------- */
 344 
 345 void
 346 mcview_do_search (WView * view, off_t want_search_start)
     /* [previous][next][first][last][top][bottom][index][help]  */
 347 {
 348     mcview_search_status_msg_t vsm;
 349 
 350     off_t search_start = 0;
 351     off_t orig_search_start = view->search_start;
 352     gboolean found = FALSE;
 353 
 354     size_t match_len;
 355 
 356     view->search_start = want_search_start;
 357     /* for avoid infinite search loop we need to increase or decrease start offset of search */
 358 
 359     if (view->search_start != 0)
 360     {
 361         if (!view->mode_flags.nroff)
 362             search_start = view->search_start + (mcview_search_options.backwards ? -2 : 0);
 363         else
 364         {
 365             if (mcview_search_options.backwards)
 366             {
 367                 mcview_nroff_t *nroff;
 368 
 369                 nroff = mcview_nroff_seq_new_num (view, view->search_start);
 370                 if (mcview_nroff_seq_prev (nroff) != -1)
 371                     search_start =
 372                         -(mcview__get_nroff_real_len (view, nroff->index - 1, 2) +
 373                           nroff->char_length + 1);
 374                 else
 375                     search_start = -2;
 376 
 377                 mcview_nroff_seq_free (&nroff);
 378             }
 379             else
 380             {
 381                 search_start = mcview__get_nroff_real_len (view, view->search_start + 1, 2);
 382             }
 383             search_start += view->search_start;
 384         }
 385     }
 386 
 387     if (mcview_search_options.backwards && search_start < 0)
 388         search_start = 0;
 389 
 390     /* Compute the percent steps */
 391     mcview_search_update_steps (view);
 392 
 393     view->update_activate = search_start;
 394 
 395     vsm.first = TRUE;
 396     vsm.view = view;
 397     vsm.offset = search_start;
 398 
 399     status_msg_init (STATUS_MSG (&vsm), _("Search"), 1.0, simple_status_msg_init_cb,
 400                      mcview_search_status_update_cb, NULL);
 401 
 402     do
 403     {
 404         off_t growbufsize;
 405 
 406         if (view->growbuf_in_use)
 407             growbufsize = mcview_growbuf_filesize (view);
 408         else
 409             growbufsize = view->search->original_len;
 410 
 411         if (mcview_find (&vsm, search_start, mcview_get_filesize (view), &match_len))
 412         {
 413             mcview_search_show_result (view, match_len);
 414             found = TRUE;
 415             break;
 416         }
 417 
 418         /* Search error is here.
 419          * MC_SEARCH_E_NOTFOUND: continue search
 420          * others: stop
 421          */
 422         if (view->search->error != MC_SEARCH_E_NOTFOUND)
 423             break;
 424 
 425         search_start = growbufsize - view->search->original_len;
 426     }
 427     while (search_start > 0 && mcview_may_still_grow (view));
 428 
 429     /* After mcview_may_still_grow (view) == FALSE we have remained last chunk. Search there. */
 430     if (view->growbuf_in_use && !found && view->search->error == MC_SEARCH_E_NOTFOUND
 431         && !mcview_search_options.backwards
 432         && mcview_find (&vsm, search_start, mcview_get_filesize (view), &match_len))
 433     {
 434         mcview_search_show_result (view, match_len);
 435         found = TRUE;
 436     }
 437 
 438     status_msg_deinit (STATUS_MSG (&vsm));
 439 
 440     if (orig_search_start != 0 && (!found && view->search->error == MC_SEARCH_E_NOTFOUND)
 441         && !mcview_search_options.backwards)
 442     {
 443         view->search_start = orig_search_start;
 444         mcview_update (view);
 445 
 446         if (query_dialog
 447             (_("Search done"), _("Continue from beginning?"), D_NORMAL, 2, _("&Yes"),
 448              _("&No")) != 0)
 449             found = TRUE;
 450         else
 451         {
 452             /* continue search from beginning */
 453             view->update_activate = 0;
 454 
 455             vsm.first = TRUE;
 456             vsm.view = view;
 457             vsm.offset = 0;
 458 
 459             status_msg_init (STATUS_MSG (&vsm), _("Search"), 1.0, simple_status_msg_init_cb,
 460                              mcview_search_status_update_cb, NULL);
 461 
 462             /* search from file begin up to initial search start position */
 463             if (mcview_find (&vsm, 0, orig_search_start, &match_len))
 464             {
 465                 mcview_search_show_result (view, match_len);
 466                 found = TRUE;
 467             }
 468 
 469             status_msg_deinit (STATUS_MSG (&vsm));
 470         }
 471     }
 472 
 473     if (!found)
 474     {
 475         view->search_start = orig_search_start;
 476         mcview_update (view);
 477 
 478         if (view->search->error == MC_SEARCH_E_NOTFOUND)
 479             query_dialog (_("Search"), _(STR_E_NOTFOUND), D_NORMAL, 1, _("&Dismiss"));
 480         else if (view->search->error_str != NULL)
 481             query_dialog (_("Search"), view->search->error_str, D_NORMAL, 1, _("&Dismiss"));
 482     }
 483     view->dirty++;
 484 }
 485 
 486 /* --------------------------------------------------------------------------------------------- */

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