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

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