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_cmd_callback
  6. mcview_search_update_cmd_callback
  7. mcview_do_search

   1 /*
   2    Internal file viewer for the Midnight Commander
   3    Function for search data
   4 
   5    Copyright (C) 1994-2019
   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 #include "lib/widget.h"
  41 
  42 #include "src/setup.h"
  43 
  44 #include "internal.h"
  45 
  46 /*** global variables ****************************************************************************/
  47 
  48 /*** file scope macro definitions ****************************************************************/
  49 
  50 /*** file scope type declarations ****************************************************************/
  51 
  52 typedef struct
  53 {
  54     simple_status_msg_t status_msg;     /* base class */
  55 
  56     gboolean first;
  57     WView *view;
  58     off_t offset;
  59 } mcview_search_status_msg_t;
  60 
  61 /*** file scope variables ************************************************************************/
  62 
  63 static int search_cb_char_curr_index = -1;
  64 static char search_cb_char_buffer[6];
  65 
  66 /* --------------------------------------------------------------------------------------------- */
  67 /*** file scope functions ************************************************************************/
  68 /* --------------------------------------------------------------------------------------------- */
  69 
  70 static int
  71 mcview_search_status_update_cb (status_msg_t * sm)
     /* [previous][next][first][last][top][bottom][index][help]  */
  72 {
  73     simple_status_msg_t *ssm = SIMPLE_STATUS_MSG (sm);
  74     mcview_search_status_msg_t *vsm = (mcview_search_status_msg_t *) sm;
  75     Widget *wd = WIDGET (sm->dlg);
  76     int percent = -1;
  77 
  78     if (verbose)
  79         percent = mcview_calc_percent (vsm->view, vsm->offset);
  80 
  81     if (percent >= 0)
  82         label_set_textv (ssm->label, _("Searching %s: %3d%%"), vsm->view->last_search_string,
  83                          percent);
  84     else
  85         label_set_textv (ssm->label, _("Searching %s"), vsm->view->last_search_string);
  86 
  87     if (vsm->first)
  88     {
  89         int wd_width;
  90         Widget *lw = WIDGET (ssm->label);
  91 
  92         wd_width = MAX (wd->cols, lw->cols + 6);
  93         widget_set_size (wd, wd->y, wd->x, wd->lines, wd_width);
  94         widget_set_size (lw, lw->y, wd->x + (wd->cols - lw->cols) / 2, lw->lines, lw->cols);
  95         vsm->first = FALSE;
  96     }
  97 
  98     return status_msg_common_update (sm);
  99 }
 100 
 101 /* --------------------------------------------------------------------------------------------- */
 102 
 103 static void
 104 mcview_search_update_steps (WView * view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 105 {
 106     off_t filesize;
 107 
 108     filesize = mcview_get_filesize (view);
 109 
 110     if (filesize != 0)
 111         view->update_steps = filesize / 100;
 112     else                        /* viewing a data stream, not a file */
 113         view->update_steps = 40000;
 114 
 115     /* Do not update the percent display but every 20 kb */
 116     if (view->update_steps < 20000)
 117         view->update_steps = 20000;
 118 
 119     /* Make interrupt more responsive */
 120     if (view->update_steps > 40000)
 121         view->update_steps = 40000;
 122 }
 123 
 124 /* --------------------------------------------------------------------------------------------- */
 125 
 126 static gboolean
 127 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]  */
 128 {
 129     WView *view = ssm->view;
 130 
 131     view->search_numNeedSkipChar = 0;
 132     search_cb_char_curr_index = -1;
 133 
 134     if (mcview_search_options.backwards)
 135     {
 136         search_end = mcview_get_filesize (view);
 137         while (search_start >= 0)
 138         {
 139             gboolean ok;
 140 
 141             view->search_nroff_seq->index = search_start;
 142             mcview_nroff_seq_info (view->search_nroff_seq);
 143 
 144             if (search_end > search_start + (off_t) view->search->original_len
 145                 && mc_search_is_fixed_search_str (view->search))
 146                 search_end = search_start + view->search->original_len;
 147 
 148             ok = mc_search_run (view->search, (void *) ssm, search_start, search_end, len);
 149             if (ok && view->search->normal_offset == search_start)
 150             {
 151                 if (view->mode_flags.nroff)
 152                     view->search->normal_offset++;
 153                 return TRUE;
 154             }
 155 
 156             /* We abort the search in case of a pattern error, or if the user aborts
 157                the search. In other words: in all cases except "string not found". */
 158             if (!ok && view->search->error != MC_SEARCH_E_NOTFOUND)
 159                 return FALSE;
 160 
 161             search_start--;
 162         }
 163 
 164         mc_search_set_error (view->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
 165         return FALSE;
 166     }
 167     view->search_nroff_seq->index = search_start;
 168     mcview_nroff_seq_info (view->search_nroff_seq);
 169 
 170     return mc_search_run (view->search, (void *) ssm, search_start, search_end, len);
 171 }
 172 
 173 /* --------------------------------------------------------------------------------------------- */
 174 
 175 static void
 176 mcview_search_show_result (WView * view, size_t match_len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 177 {
 178     int nroff_len;
 179 
 180     nroff_len =
 181         view->mode_flags.nroff
 182         ? mcview__get_nroff_real_len (view, view->search->start_buffer,
 183                                       view->search->normal_offset - view->search->start_buffer) : 0;
 184     view->search_start = view->search->normal_offset + nroff_len;
 185 
 186     if (!view->mode_flags.hex)
 187         view->search_start++;
 188 
 189     nroff_len =
 190         view->mode_flags.nroff ? mcview__get_nroff_real_len (view, view->search_start - 1,
 191                                                              match_len) : 0;
 192     view->search_end = view->search_start + match_len + nroff_len;
 193 
 194     mcview_moveto_match (view);
 195 }
 196 
 197 /* --------------------------------------------------------------------------------------------- */
 198 /*** public functions ****************************************************************************/
 199 /* --------------------------------------------------------------------------------------------- */
 200 
 201 mc_search_cbret_t
 202 mcview_search_cmd_callback (const void *user_data, gsize char_offset, int *current_char)
     /* [previous][next][first][last][top][bottom][index][help]  */
 203 {
 204     WView *view = ((const mcview_search_status_msg_t *) user_data)->view;
 205 
 206     /*    view_read_continue (view, &view->search_onechar_info); *//* AB:FIXME */
 207     if (!view->mode_flags.nroff)
 208     {
 209         mcview_get_byte (view, char_offset, current_char);
 210         return MC_SEARCH_CB_OK;
 211     }
 212 
 213     if (view->search_numNeedSkipChar != 0)
 214     {
 215         view->search_numNeedSkipChar--;
 216         return MC_SEARCH_CB_SKIP;
 217     }
 218 
 219     if (search_cb_char_curr_index == -1
 220         || search_cb_char_curr_index >= view->search_nroff_seq->char_length)
 221     {
 222         if (search_cb_char_curr_index != -1)
 223             mcview_nroff_seq_next (view->search_nroff_seq);
 224 
 225         search_cb_char_curr_index = 0;
 226         if (view->search_nroff_seq->char_length > 1)
 227             g_unichar_to_utf8 (view->search_nroff_seq->current_char, search_cb_char_buffer);
 228         else
 229             search_cb_char_buffer[0] = (char) view->search_nroff_seq->current_char;
 230 
 231         if (view->search_nroff_seq->type != NROFF_TYPE_NONE)
 232         {
 233             switch (view->search_nroff_seq->type)
 234             {
 235             case NROFF_TYPE_BOLD:
 236                 view->search_numNeedSkipChar = 1 + view->search_nroff_seq->char_length; /* real char length and 0x8 */
 237                 break;
 238             case NROFF_TYPE_UNDERLINE:
 239                 view->search_numNeedSkipChar = 2;       /* underline symbol and ox8 */
 240                 break;
 241             default:
 242                 break;
 243             }
 244         }
 245         return MC_SEARCH_CB_INVALID;
 246     }
 247 
 248     *current_char = search_cb_char_buffer[search_cb_char_curr_index];
 249     search_cb_char_curr_index++;
 250 
 251     return (*current_char != -1) ? MC_SEARCH_CB_OK : MC_SEARCH_CB_INVALID;
 252 }
 253 
 254 /* --------------------------------------------------------------------------------------------- */
 255 
 256 mc_search_cbret_t
 257 mcview_search_update_cmd_callback (const void *user_data, gsize char_offset)
     /* [previous][next][first][last][top][bottom][index][help]  */
 258 {
 259     status_msg_t *sm = STATUS_MSG (user_data);
 260     mcview_search_status_msg_t *vsm = (mcview_search_status_msg_t *) user_data;
 261     WView *view = vsm->view;
 262     gboolean do_update = FALSE;
 263     mc_search_cbret_t result = MC_SEARCH_CB_OK;
 264 
 265     vsm->offset = (off_t) char_offset;
 266 
 267     if (mcview_search_options.backwards)
 268     {
 269         if (vsm->offset <= view->update_activate)
 270         {
 271             view->update_activate -= view->update_steps;
 272 
 273             do_update = TRUE;
 274         }
 275     }
 276     else
 277     {
 278         if (vsm->offset >= view->update_activate)
 279         {
 280             view->update_activate += view->update_steps;
 281 
 282             do_update = TRUE;
 283         }
 284     }
 285 
 286     if (do_update && sm->update (sm) == B_CANCEL)
 287         result = MC_SEARCH_CB_ABORT;
 288 
 289     /* may be in future return from this callback will change current position in searching block. */
 290 
 291     return result;
 292 }
 293 
 294 /* --------------------------------------------------------------------------------------------- */
 295 
 296 void
 297 mcview_do_search (WView * view, off_t want_search_start)
     /* [previous][next][first][last][top][bottom][index][help]  */
 298 {
 299     mcview_search_status_msg_t vsm;
 300 
 301     off_t search_start = 0;
 302     off_t orig_search_start = view->search_start;
 303     gboolean found = FALSE;
 304 
 305     size_t match_len;
 306 
 307     view->search_start = want_search_start;
 308     /* for avoid infinite search loop we need to increase or decrease start offset of search */
 309 
 310     if (view->search_start != 0)
 311     {
 312         if (!view->mode_flags.nroff)
 313             search_start = view->search_start + (mcview_search_options.backwards ? -2 : 0);
 314         else
 315         {
 316             if (mcview_search_options.backwards)
 317             {
 318                 mcview_nroff_t *nroff;
 319 
 320                 nroff = mcview_nroff_seq_new_num (view, view->search_start);
 321                 if (mcview_nroff_seq_prev (nroff) != -1)
 322                     search_start =
 323                         -(mcview__get_nroff_real_len (view, nroff->index - 1, 2) +
 324                           nroff->char_length + 1);
 325                 else
 326                     search_start = -2;
 327 
 328                 mcview_nroff_seq_free (&nroff);
 329             }
 330             else
 331             {
 332                 search_start = mcview__get_nroff_real_len (view, view->search_start + 1, 2);
 333             }
 334             search_start += view->search_start;
 335         }
 336     }
 337 
 338     if (mcview_search_options.backwards && search_start < 0)
 339         search_start = 0;
 340 
 341     /* Compute the percent steps */
 342     mcview_search_update_steps (view);
 343 
 344     view->update_activate = search_start;
 345 
 346     vsm.first = TRUE;
 347     vsm.view = view;
 348     vsm.offset = search_start;
 349 
 350     status_msg_init (STATUS_MSG (&vsm), _("Search"), 1.0, simple_status_msg_init_cb,
 351                      mcview_search_status_update_cb, NULL);
 352 
 353     do
 354     {
 355         off_t growbufsize;
 356 
 357         if (view->growbuf_in_use)
 358             growbufsize = mcview_growbuf_filesize (view);
 359         else
 360             growbufsize = view->search->original_len;
 361 
 362         if (mcview_find (&vsm, search_start, mcview_get_filesize (view), &match_len))
 363         {
 364             mcview_search_show_result (view, match_len);
 365             found = TRUE;
 366             break;
 367         }
 368 
 369         /* Search error is here.
 370          * MC_SEARCH_E_NOTFOUND: continue search
 371          * others: stop
 372          */
 373         if (view->search->error != MC_SEARCH_E_NOTFOUND)
 374             break;
 375 
 376         search_start = growbufsize - view->search->original_len;
 377     }
 378     while (search_start > 0 && mcview_may_still_grow (view));
 379 
 380     /* After mcview_may_still_grow (view) == FALSE we have remained last chunk. Search there. */
 381     if (view->growbuf_in_use && !found && view->search->error == MC_SEARCH_E_NOTFOUND
 382         && !mcview_search_options.backwards
 383         && mcview_find (&vsm, search_start, mcview_get_filesize (view), &match_len))
 384     {
 385         mcview_search_show_result (view, match_len);
 386         found = TRUE;
 387     }
 388 
 389     status_msg_deinit (STATUS_MSG (&vsm));
 390 
 391     if (orig_search_start != 0 && (!found && view->search->error == MC_SEARCH_E_NOTFOUND)
 392         && !mcview_search_options.backwards)
 393     {
 394         view->search_start = orig_search_start;
 395         mcview_update (view);
 396 
 397         if (query_dialog
 398             (_("Search done"), _("Continue from beginning?"), D_NORMAL, 2, _("&Yes"),
 399              _("&No")) != 0)
 400             found = TRUE;
 401         else
 402         {
 403             /* continue search from beginning */
 404             view->update_activate = 0;
 405 
 406             vsm.first = TRUE;
 407             vsm.view = view;
 408             vsm.offset = 0;
 409 
 410             status_msg_init (STATUS_MSG (&vsm), _("Search"), 1.0, simple_status_msg_init_cb,
 411                              mcview_search_status_update_cb, NULL);
 412 
 413             /* search from file begin up to initial search start position */
 414             if (mcview_find (&vsm, 0, orig_search_start, &match_len))
 415             {
 416                 mcview_search_show_result (view, match_len);
 417                 found = TRUE;
 418             }
 419 
 420             status_msg_deinit (STATUS_MSG (&vsm));
 421         }
 422     }
 423 
 424     if (!found)
 425     {
 426         view->search_start = orig_search_start;
 427         mcview_update (view);
 428 
 429         if (view->search->error == MC_SEARCH_E_NOTFOUND)
 430             query_dialog (_("Search"), _(STR_E_NOTFOUND), D_NORMAL, 1, _("&Dismiss"));
 431         else if (view->search->error_str != NULL)
 432             query_dialog (_("Search"), view->search->error_str, D_NORMAL, 1, _("&Dismiss"));
 433     }
 434     view->dirty++;
 435 }
 436 
 437 /* --------------------------------------------------------------------------------------------- */

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