2 Internal file viewer for the Midnight Commander
3 Function for search data
5 Copyright (C) 1994-2024
6 Free Software Foundation, Inc.
9 Miguel de Icaza, 1994, 1995, 1998
10 Janne Kukonlehto, 1994, 1995
12 Joseph M. Hinkle, 1996
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
20 This file is part of the Midnight Commander.
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.
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.
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/>.
38 #include "lib/global.h"
39 #include "lib/strutil.h"
41 #include "lib/charsets.h" /* cp_source */
43 #include "lib/widget.h"
45 #include "src/setup.h"
49 /*** global variables ****************************************************************************/
51 mcview_search_options_t mcview_search_options
= {
52 .type
= MC_SEARCH_T_NORMAL
,
56 .all_codepages
= FALSE
59 /*** file scope macro definitions ****************************************************************/
61 /*** file scope type declarations ****************************************************************/
65 simple_status_msg_t status_msg
; /* base class */
70 } mcview_search_status_msg_t
;
72 /*** forward declarations (file scope functions) *************************************************/
74 /*** file scope variables ************************************************************************/
76 static int search_cb_char_curr_index
= -1;
77 static char search_cb_char_buffer
[6];
79 /* --------------------------------------------------------------------------------------------- */
80 /*** file scope functions ************************************************************************/
81 /* --------------------------------------------------------------------------------------------- */
84 mcview_search_status_update_cb (status_msg_t
*sm
)
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
);
92 percent
= mcview_calc_percent (vsm
->view
, vsm
->offset
);
95 label_set_textv (ssm
->label
, _("Searching %s: %3d%%"), vsm
->view
->last_search_string
,
98 label_set_textv (ssm
->label
, _("Searching %s"), vsm
->view
->last_search_string
);
102 Widget
*lw
= WIDGET (ssm
->label
);
106 r
.cols
= MAX (r
.cols
, lw
->rect
.cols
+ 6);
107 widget_set_size_rect (wd
, &r
);
109 r
.x
= wd
->rect
.x
+ (wd
->rect
.cols
- r
.cols
) / 2;
110 widget_set_size_rect (lw
, &r
);
114 return status_msg_common_update (sm
);
117 /* --------------------------------------------------------------------------------------------- */
120 mcview_search_update_steps (WView
*view
)
124 filesize
= mcview_get_filesize (view
);
127 view
->update_steps
= filesize
/ 100;
128 else /* viewing a data stream, not a file */
129 view
->update_steps
= 40000;
131 /* Do not update the percent display but every 20 kb */
132 if (view
->update_steps
< 20000)
133 view
->update_steps
= 20000;
135 /* Make interrupt more responsive */
136 if (view
->update_steps
> 40000)
137 view
->update_steps
= 40000;
140 /* --------------------------------------------------------------------------------------------- */
143 mcview_find (mcview_search_status_msg_t
*ssm
, off_t search_start
, off_t search_end
, gsize
*len
)
145 WView
*view
= ssm
->view
;
147 view
->search_numNeedSkipChar
= 0;
148 search_cb_char_curr_index
= -1;
150 if (mcview_search_options
.backwards
)
152 search_end
= mcview_get_filesize (view
);
153 while (search_start
>= 0)
157 view
->search_nroff_seq
->index
= search_start
;
158 mcview_nroff_seq_info (view
->search_nroff_seq
);
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
;
164 ok
= mc_search_run (view
->search
, (void *) ssm
, search_start
, search_end
, len
);
165 if (ok
&& view
->search
->normal_offset
== search_start
)
167 if (view
->mode_flags
.nroff
)
168 view
->search
->normal_offset
++;
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
)
180 mc_search_set_error (view
->search
, MC_SEARCH_E_NOTFOUND
, "%s", _(STR_E_NOTFOUND
));
183 view
->search_nroff_seq
->index
= search_start
;
184 mcview_nroff_seq_info (view
->search_nroff_seq
);
186 return mc_search_run (view
->search
, (void *) ssm
, search_start
, search_end
, len
);
189 /* --------------------------------------------------------------------------------------------- */
192 mcview_search_show_result (WView
*view
, size_t match_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
;
202 if (!view
->mode_flags
.hex
)
203 view
->search_start
++;
206 view
->mode_flags
.nroff
? mcview__get_nroff_real_len (view
, view
->search_start
- 1,
208 view
->search_end
= view
->search_start
+ match_len
+ nroff_len
;
210 mcview_moveto_match (view
);
213 /* --------------------------------------------------------------------------------------------- */
214 /*** public functions ****************************************************************************/
215 /* --------------------------------------------------------------------------------------------- */
218 mcview_search_init (WView
*view
)
221 view
->search
= mc_search_new (view
->last_search_string
, cp_source
);
223 view
->search
= mc_search_new (view
->last_search_string
, NULL
);
226 view
->search_nroff_seq
= mcview_nroff_seq_new (view
);
228 if (view
->search
== NULL
)
231 view
->search
->search_type
= mcview_search_options
.type
;
233 view
->search
->is_all_charsets
= mcview_search_options
.all_codepages
;
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
;
243 /* --------------------------------------------------------------------------------------------- */
246 mcview_search_deinit (WView
*view
)
248 mc_search_free (view
->search
);
249 g_free (view
->last_search_string
);
250 mcview_nroff_seq_free (&view
->search_nroff_seq
);
253 /* --------------------------------------------------------------------------------------------- */
256 mcview_search_cmd_callback (const void *user_data
, gsize char_offset
, int *current_char
)
258 WView
*view
= ((const mcview_search_status_msg_t
*) user_data
)->view
;
260 /* view_read_continue (view, &view->search_onechar_info); *//* AB:FIXME */
261 if (!view
->mode_flags
.nroff
)
263 mcview_get_byte (view
, char_offset
, current_char
);
264 return MC_SEARCH_CB_OK
;
267 if (view
->search_numNeedSkipChar
!= 0)
269 view
->search_numNeedSkipChar
--;
270 return MC_SEARCH_CB_SKIP
;
273 if (search_cb_char_curr_index
== -1
274 || search_cb_char_curr_index
>= view
->search_nroff_seq
->char_length
)
276 if (search_cb_char_curr_index
!= -1)
277 mcview_nroff_seq_next (view
->search_nroff_seq
);
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
);
283 search_cb_char_buffer
[0] = (char) view
->search_nroff_seq
->current_char
;
285 if (view
->search_nroff_seq
->type
!= NROFF_TYPE_NONE
)
287 switch (view
->search_nroff_seq
->type
)
289 case NROFF_TYPE_BOLD
:
290 view
->search_numNeedSkipChar
= 1 + view
->search_nroff_seq
->char_length
; /* real char length and 0x8 */
292 case NROFF_TYPE_UNDERLINE
:
293 view
->search_numNeedSkipChar
= 2; /* underline symbol and ox8 */
299 return MC_SEARCH_CB_INVALID
;
302 *current_char
= search_cb_char_buffer
[search_cb_char_curr_index
];
303 search_cb_char_curr_index
++;
305 return (*current_char
!= -1) ? MC_SEARCH_CB_OK
: MC_SEARCH_CB_INVALID
;
308 /* --------------------------------------------------------------------------------------------- */
311 mcview_search_update_cmd_callback (const void *user_data
, gsize char_offset
)
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
;
319 vsm
->offset
= (off_t
) char_offset
;
321 if (mcview_search_options
.backwards
)
323 if (vsm
->offset
<= view
->update_activate
)
325 view
->update_activate
-= view
->update_steps
;
332 if (vsm
->offset
>= view
->update_activate
)
334 view
->update_activate
+= view
->update_steps
;
340 if (do_update
&& sm
->update (sm
) == B_CANCEL
)
341 result
= MC_SEARCH_CB_ABORT
;
343 /* may be in future return from this callback will change current position in searching block. */
348 /* --------------------------------------------------------------------------------------------- */
351 mcview_do_search (WView
*view
, off_t want_search_start
)
353 mcview_search_status_msg_t vsm
;
355 off_t search_start
= 0;
356 off_t orig_search_start
= view
->search_start
;
357 gboolean found
= FALSE
;
361 view
->search_start
= want_search_start
;
362 /* for avoid infinite search loop we need to increase or decrease start offset of search */
364 if (view
->search_start
!= 0)
366 if (!view
->mode_flags
.nroff
)
367 search_start
= view
->search_start
+ (mcview_search_options
.backwards
? -2 : 0);
370 if (mcview_search_options
.backwards
)
372 mcview_nroff_t
*nroff
;
374 nroff
= mcview_nroff_seq_new_num (view
, view
->search_start
);
375 if (mcview_nroff_seq_prev (nroff
) != -1)
377 -(mcview__get_nroff_real_len (view
, nroff
->index
- 1, 2) +
378 nroff
->char_length
+ 1);
382 mcview_nroff_seq_free (&nroff
);
386 search_start
= mcview__get_nroff_real_len (view
, view
->search_start
+ 1, 2);
388 search_start
+= view
->search_start
;
392 if (mcview_search_options
.backwards
&& search_start
< 0)
395 /* Compute the percent steps */
396 mcview_search_update_steps (view
);
398 view
->update_activate
= search_start
;
402 vsm
.offset
= search_start
;
404 status_msg_init (STATUS_MSG (&vsm
), _("Search"), 1.0, simple_status_msg_init_cb
,
405 mcview_search_status_update_cb
, NULL
);
411 if (view
->growbuf_in_use
)
412 growbufsize
= mcview_growbuf_filesize (view
);
414 growbufsize
= view
->search
->original
.str
->len
;
416 if (mcview_find (&vsm
, search_start
, mcview_get_filesize (view
), &match_len
))
418 mcview_search_show_result (view
, match_len
);
423 /* Search error is here.
424 * MC_SEARCH_E_NOTFOUND: continue search
427 if (view
->search
->error
!= MC_SEARCH_E_NOTFOUND
)
430 search_start
= growbufsize
- view
->search
->original
.str
->len
;
432 while (search_start
> 0 && mcview_may_still_grow (view
));
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
))
439 mcview_search_show_result (view
, match_len
);
443 status_msg_deinit (STATUS_MSG (&vsm
));
445 if (orig_search_start
!= 0 && (!found
&& view
->search
->error
== MC_SEARCH_E_NOTFOUND
)
446 && !mcview_search_options
.backwards
)
448 view
->search_start
= orig_search_start
;
449 mcview_update (view
);
452 (_("Search done"), _("Continue from beginning?"), D_NORMAL
, 2, _("&Yes"),
457 /* continue search from beginning */
458 view
->update_activate
= 0;
464 status_msg_init (STATUS_MSG (&vsm
), _("Search"), 1.0, simple_status_msg_init_cb
,
465 mcview_search_status_update_cb
, NULL
);
467 /* search from file begin up to initial search start position */
468 if (mcview_find (&vsm
, 0, orig_search_start
, &match_len
))
470 mcview_search_show_result (view
, match_len
);
474 status_msg_deinit (STATUS_MSG (&vsm
));
480 view
->search_start
= orig_search_start
;
481 mcview_update (view
);
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"));
491 /* --------------------------------------------------------------------------------------------- */