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-2022
   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-2022
  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         Widget *lw = WIDGET (ssm->label);
 101         WRect r;
 102 
 103         r = wd->rect;
 104         r.cols = MAX (r.cols, lw->rect.cols + 6);
 105         widget_set_size_rect (wd, &r);
 106         r = lw->rect;
 107         r.x = wd->rect.x + (wd->rect.cols - r.cols) / 2;
 108         widget_set_size_rect (lw, &r);
 109         vsm->first = FALSE;
 110     }
 111 
 112     return status_msg_common_update (sm);
 113 }
 114 
 115 /* --------------------------------------------------------------------------------------------- */
 116 
 117 static void
 118 mcview_search_update_steps (WView * view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 119 {
 120     off_t filesize;
 121 
 122     filesize = mcview_get_filesize (view);
 123 
 124     if (filesize != 0)
 125         view->update_steps = filesize / 100;
 126     else                        /* viewing a data stream, not a file */
 127         view->update_steps = 40000;
 128 
 129     /* Do not update the percent display but every 20 kb */
 130     if (view->update_steps < 20000)
 131         view->update_steps = 20000;
 132 
 133     /* Make interrupt more responsive */
 134     if (view->update_steps > 40000)
 135         view->update_steps = 40000;
 136 }
 137 
 138 /* --------------------------------------------------------------------------------------------- */
 139 
 140 static gboolean
 141 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]  */
 142 {
 143     WView *view = ssm->view;
 144 
 145     view->search_numNeedSkipChar = 0;
 146     search_cb_char_curr_index = -1;
 147 
 148     if (mcview_search_options.backwards)
 149     {
 150         search_end = mcview_get_filesize (view);
 151         while (search_start >= 0)
 152         {
 153             gboolean ok;
 154 
 155             view->search_nroff_seq->index = search_start;
 156             mcview_nroff_seq_info (view->search_nroff_seq);
 157 
 158             if (search_end > search_start + (off_t) view->search->original_len
 159                 && mc_search_is_fixed_search_str (view->search))
 160                 search_end = search_start + view->search->original_len;
 161 
 162             ok = mc_search_run (view->search, (void *) ssm, search_start, search_end, len);
 163             if (ok && view->search->normal_offset == search_start)
 164             {
 165                 if (view->mode_flags.nroff)
 166                     view->search->normal_offset++;
 167                 return TRUE;
 168             }
 169 
 170             /* We abort the search in case of a pattern error, or if the user aborts
 171                the search. In other words: in all cases except "string not found". */
 172             if (!ok && view->search->error != MC_SEARCH_E_NOTFOUND)
 173                 return FALSE;
 174 
 175             search_start--;
 176         }
 177 
 178         mc_search_set_error (view->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
 179         return FALSE;
 180     }
 181     view->search_nroff_seq->index = search_start;
 182     mcview_nroff_seq_info (view->search_nroff_seq);
 183 
 184     return mc_search_run (view->search, (void *) ssm, search_start, search_end, len);
 185 }
 186 
 187 /* --------------------------------------------------------------------------------------------- */
 188 
 189 static void
 190 mcview_search_show_result (WView * view, size_t match_len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 191 {
 192     int nroff_len;
 193 
 194     nroff_len =
 195         view->mode_flags.nroff
 196         ? mcview__get_nroff_real_len (view, view->search->start_buffer,
 197                                       view->search->normal_offset - view->search->start_buffer) : 0;
 198     view->search_start = view->search->normal_offset + nroff_len;
 199 
 200     if (!view->mode_flags.hex)
 201         view->search_start++;
 202 
 203     nroff_len =
 204         view->mode_flags.nroff ? mcview__get_nroff_real_len (view, view->search_start - 1,
 205                                                              match_len) : 0;
 206     view->search_end = view->search_start + match_len + nroff_len;
 207 
 208     mcview_moveto_match (view);
 209 }
 210 
 211 /* --------------------------------------------------------------------------------------------- */
 212 /*** public functions ****************************************************************************/
 213 /* --------------------------------------------------------------------------------------------- */
 214 
 215 gboolean
 216 mcview_search_init (WView * view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 217 {
 218 #ifdef HAVE_CHARSET
 219     view->search = mc_search_new (view->last_search_string, cp_source);
 220 #else
 221     view->search = mc_search_new (view->last_search_string, NULL);
 222 #endif
 223 
 224     view->search_nroff_seq = mcview_nroff_seq_new (view);
 225 
 226     if (view->search == NULL)
 227         return FALSE;
 228 
 229     view->search->search_type = mcview_search_options.type;
 230 #ifdef HAVE_CHARSET
 231     view->search->is_all_charsets = mcview_search_options.all_codepages;
 232 #endif
 233     view->search->is_case_sensitive = mcview_search_options.case_sens;
 234     view->search->whole_words = mcview_search_options.whole_words;
 235     view->search->search_fn = mcview_search_cmd_callback;
 236     view->search->update_fn = mcview_search_update_cmd_callback;
 237 
 238     return TRUE;
 239 }
 240 
 241 /* --------------------------------------------------------------------------------------------- */
 242 
 243 void
 244 mcview_search_deinit (WView * view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 245 {
 246     mc_search_free (view->search);
 247     g_free (view->last_search_string);
 248     mcview_nroff_seq_free (&view->search_nroff_seq);
 249 }
 250 
 251 /* --------------------------------------------------------------------------------------------- */
 252 
 253 mc_search_cbret_t
 254 mcview_search_cmd_callback (const void *user_data, gsize char_offset, int *current_char)
     /* [previous][next][first][last][top][bottom][index][help]  */
 255 {
 256     WView *view = ((const mcview_search_status_msg_t *) user_data)->view;
 257 
 258     /*    view_read_continue (view, &view->search_onechar_info); *//* AB:FIXME */
 259     if (!view->mode_flags.nroff)
 260     {
 261         mcview_get_byte (view, char_offset, current_char);
 262         return MC_SEARCH_CB_OK;
 263     }
 264 
 265     if (view->search_numNeedSkipChar != 0)
 266     {
 267         view->search_numNeedSkipChar--;
 268         return MC_SEARCH_CB_SKIP;
 269     }
 270 
 271     if (search_cb_char_curr_index == -1
 272         || search_cb_char_curr_index >= view->search_nroff_seq->char_length)
 273     {
 274         if (search_cb_char_curr_index != -1)
 275             mcview_nroff_seq_next (view->search_nroff_seq);
 276 
 277         search_cb_char_curr_index = 0;
 278         if (view->search_nroff_seq->char_length > 1)
 279             g_unichar_to_utf8 (view->search_nroff_seq->current_char, search_cb_char_buffer);
 280         else
 281             search_cb_char_buffer[0] = (char) view->search_nroff_seq->current_char;
 282 
 283         if (view->search_nroff_seq->type != NROFF_TYPE_NONE)
 284         {
 285             switch (view->search_nroff_seq->type)
 286             {
 287             case NROFF_TYPE_BOLD:
 288                 view->search_numNeedSkipChar = 1 + view->search_nroff_seq->char_length; /* real char length and 0x8 */
 289                 break;
 290             case NROFF_TYPE_UNDERLINE:
 291                 view->search_numNeedSkipChar = 2;       /* underline symbol and ox8 */
 292                 break;
 293             default:
 294                 break;
 295             }
 296         }
 297         return MC_SEARCH_CB_INVALID;
 298     }
 299 
 300     *current_char = search_cb_char_buffer[search_cb_char_curr_index];
 301     search_cb_char_curr_index++;
 302 
 303     return (*current_char != -1) ? MC_SEARCH_CB_OK : MC_SEARCH_CB_INVALID;
 304 }
 305 
 306 /* --------------------------------------------------------------------------------------------- */
 307 
 308 mc_search_cbret_t
 309 mcview_search_update_cmd_callback (const void *user_data, gsize char_offset)
     /* [previous][next][first][last][top][bottom][index][help]  */
 310 {
 311     status_msg_t *sm = STATUS_MSG (user_data);
 312     mcview_search_status_msg_t *vsm = (mcview_search_status_msg_t *) user_data;
 313     WView *view = vsm->view;
 314     gboolean do_update = FALSE;
 315     mc_search_cbret_t result = MC_SEARCH_CB_OK;
 316 
 317     vsm->offset = (off_t) char_offset;
 318 
 319     if (mcview_search_options.backwards)
 320     {
 321         if (vsm->offset <= view->update_activate)
 322         {
 323             view->update_activate -= view->update_steps;
 324 
 325             do_update = TRUE;
 326         }
 327     }
 328     else
 329     {
 330         if (vsm->offset >= view->update_activate)
 331         {
 332             view->update_activate += view->update_steps;
 333 
 334             do_update = TRUE;
 335         }
 336     }
 337 
 338     if (do_update && sm->update (sm) == B_CANCEL)
 339         result = MC_SEARCH_CB_ABORT;
 340 
 341     /* may be in future return from this callback will change current position in searching block. */
 342 
 343     return result;
 344 }
 345 
 346 /* --------------------------------------------------------------------------------------------- */
 347 
 348 void
 349 mcview_do_search (WView * view, off_t want_search_start)
     /* [previous][next][first][last][top][bottom][index][help]  */
 350 {
 351     mcview_search_status_msg_t vsm;
 352 
 353     off_t search_start = 0;
 354     off_t orig_search_start = view->search_start;
 355     gboolean found = FALSE;
 356 
 357     size_t match_len;
 358 
 359     view->search_start = want_search_start;
 360     /* for avoid infinite search loop we need to increase or decrease start offset of search */
 361 
 362     if (view->search_start != 0)
 363     {
 364         if (!view->mode_flags.nroff)
 365             search_start = view->search_start + (mcview_search_options.backwards ? -2 : 0);
 366         else
 367         {
 368             if (mcview_search_options.backwards)
 369             {
 370                 mcview_nroff_t *nroff;
 371 
 372                 nroff = mcview_nroff_seq_new_num (view, view->search_start);
 373                 if (mcview_nroff_seq_prev (nroff) != -1)
 374                     search_start =
 375                         -(mcview__get_nroff_real_len (view, nroff->index - 1, 2) +
 376                           nroff->char_length + 1);
 377                 else
 378                     search_start = -2;
 379 
 380                 mcview_nroff_seq_free (&nroff);
 381             }
 382             else
 383             {
 384                 search_start = mcview__get_nroff_real_len (view, view->search_start + 1, 2);
 385             }
 386             search_start += view->search_start;
 387         }
 388     }
 389 
 390     if (mcview_search_options.backwards && search_start < 0)
 391         search_start = 0;
 392 
 393     /* Compute the percent steps */
 394     mcview_search_update_steps (view);
 395 
 396     view->update_activate = search_start;
 397 
 398     vsm.first = TRUE;
 399     vsm.view = view;
 400     vsm.offset = search_start;
 401 
 402     status_msg_init (STATUS_MSG (&vsm), _("Search"), 1.0, simple_status_msg_init_cb,
 403                      mcview_search_status_update_cb, NULL);
 404 
 405     do
 406     {
 407         off_t growbufsize;
 408 
 409         if (view->growbuf_in_use)
 410             growbufsize = mcview_growbuf_filesize (view);
 411         else
 412             growbufsize = view->search->original_len;
 413 
 414         if (mcview_find (&vsm, search_start, mcview_get_filesize (view), &match_len))
 415         {
 416             mcview_search_show_result (view, match_len);
 417             found = TRUE;
 418             break;
 419         }
 420 
 421         /* Search error is here.
 422          * MC_SEARCH_E_NOTFOUND: continue search
 423          * others: stop
 424          */
 425         if (view->search->error != MC_SEARCH_E_NOTFOUND)
 426             break;
 427 
 428         search_start = growbufsize - view->search->original_len;
 429     }
 430     while (search_start > 0 && mcview_may_still_grow (view));
 431 
 432     /* After mcview_may_still_grow (view) == FALSE we have remained last chunk. Search there. */
 433     if (view->growbuf_in_use && !found && view->search->error == MC_SEARCH_E_NOTFOUND
 434         && !mcview_search_options.backwards
 435         && mcview_find (&vsm, search_start, mcview_get_filesize (view), &match_len))
 436     {
 437         mcview_search_show_result (view, match_len);
 438         found = TRUE;
 439     }
 440 
 441     status_msg_deinit (STATUS_MSG (&vsm));
 442 
 443     if (orig_search_start != 0 && (!found && view->search->error == MC_SEARCH_E_NOTFOUND)
 444         && !mcview_search_options.backwards)
 445     {
 446         view->search_start = orig_search_start;
 447         mcview_update (view);
 448 
 449         if (query_dialog
 450             (_("Search done"), _("Continue from beginning?"), D_NORMAL, 2, _("&Yes"),
 451              _("&No")) != 0)
 452             found = TRUE;
 453         else
 454         {
 455             /* continue search from beginning */
 456             view->update_activate = 0;
 457 
 458             vsm.first = TRUE;
 459             vsm.view = view;
 460             vsm.offset = 0;
 461 
 462             status_msg_init (STATUS_MSG (&vsm), _("Search"), 1.0, simple_status_msg_init_cb,
 463                              mcview_search_status_update_cb, NULL);
 464 
 465             /* search from file begin up to initial search start position */
 466             if (mcview_find (&vsm, 0, orig_search_start, &match_len))
 467             {
 468                 mcview_search_show_result (view, match_len);
 469                 found = TRUE;
 470             }
 471 
 472             status_msg_deinit (STATUS_MSG (&vsm));
 473         }
 474     }
 475 
 476     if (!found)
 477     {
 478         view->search_start = orig_search_start;
 479         mcview_update (view);
 480 
 481         if (view->search->error == MC_SEARCH_E_NOTFOUND)
 482             query_dialog (_("Search"), _(STR_E_NOTFOUND), D_NORMAL, 1, _("&Dismiss"));
 483         else if (view->search->error_str != NULL)
 484             query_dialog (_("Search"), view->search->error_str, D_NORMAL, 1, _("&Dismiss"));
 485     }
 486     view->dirty++;
 487 }
 488 
 489 /* --------------------------------------------------------------------------------------------- */

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