2 Internal file viewer for the Midnight Commander
3 Function for search data
5 Copyright (C) 1994-2022
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 /*** file scope variables ************************************************************************/
74 static int search_cb_char_curr_index
= -1;
75 static char search_cb_char_buffer
[6];
77 /* --------------------------------------------------------------------------------------------- */
78 /*** file scope functions ************************************************************************/
79 /* --------------------------------------------------------------------------------------------- */
82 mcview_search_status_update_cb (status_msg_t
* sm
)
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
);
90 percent
= mcview_calc_percent (vsm
->view
, vsm
->offset
);
93 label_set_textv (ssm
->label
, _("Searching %s: %3d%%"), vsm
->view
->last_search_string
,
96 label_set_textv (ssm
->label
, _("Searching %s"), vsm
->view
->last_search_string
);
100 Widget
*lw
= WIDGET (ssm
->label
);
104 r
.cols
= MAX (r
.cols
, lw
->rect
.cols
+ 6);
105 widget_set_size_rect (wd
, &r
);
107 r
.x
= wd
->rect
.x
+ (wd
->rect
.cols
- r
.cols
) / 2;
108 widget_set_size_rect (lw
, &r
);
112 return status_msg_common_update (sm
);
115 /* --------------------------------------------------------------------------------------------- */
118 mcview_search_update_steps (WView
* view
)
122 filesize
= mcview_get_filesize (view
);
125 view
->update_steps
= filesize
/ 100;
126 else /* viewing a data stream, not a file */
127 view
->update_steps
= 40000;
129 /* Do not update the percent display but every 20 kb */
130 if (view
->update_steps
< 20000)
131 view
->update_steps
= 20000;
133 /* Make interrupt more responsive */
134 if (view
->update_steps
> 40000)
135 view
->update_steps
= 40000;
138 /* --------------------------------------------------------------------------------------------- */
141 mcview_find (mcview_search_status_msg_t
* ssm
, off_t search_start
, off_t search_end
, gsize
* len
)
143 WView
*view
= ssm
->view
;
145 view
->search_numNeedSkipChar
= 0;
146 search_cb_char_curr_index
= -1;
148 if (mcview_search_options
.backwards
)
150 search_end
= mcview_get_filesize (view
);
151 while (search_start
>= 0)
155 view
->search_nroff_seq
->index
= search_start
;
156 mcview_nroff_seq_info (view
->search_nroff_seq
);
158 if (search_end
> search_start
+ (off_t
) view
->search
->original
.str
->len
159 && mc_search_is_fixed_search_str (view
->search
))
160 search_end
= search_start
+ view
->search
->original
.str
->len
;
162 ok
= mc_search_run (view
->search
, (void *) ssm
, search_start
, search_end
, len
);
163 if (ok
&& view
->search
->normal_offset
== search_start
)
165 if (view
->mode_flags
.nroff
)
166 view
->search
->normal_offset
++;
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
)
178 mc_search_set_error (view
->search
, MC_SEARCH_E_NOTFOUND
, "%s", _(STR_E_NOTFOUND
));
181 view
->search_nroff_seq
->index
= search_start
;
182 mcview_nroff_seq_info (view
->search_nroff_seq
);
184 return mc_search_run (view
->search
, (void *) ssm
, search_start
, search_end
, len
);
187 /* --------------------------------------------------------------------------------------------- */
190 mcview_search_show_result (WView
* view
, size_t match_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
;
200 if (!view
->mode_flags
.hex
)
201 view
->search_start
++;
204 view
->mode_flags
.nroff
? mcview__get_nroff_real_len (view
, view
->search_start
- 1,
206 view
->search_end
= view
->search_start
+ match_len
+ nroff_len
;
208 mcview_moveto_match (view
);
211 /* --------------------------------------------------------------------------------------------- */
212 /*** public functions ****************************************************************************/
213 /* --------------------------------------------------------------------------------------------- */
216 mcview_search_init (WView
* view
)
219 view
->search
= mc_search_new (view
->last_search_string
, cp_source
);
221 view
->search
= mc_search_new (view
->last_search_string
, NULL
);
224 view
->search_nroff_seq
= mcview_nroff_seq_new (view
);
226 if (view
->search
== NULL
)
229 view
->search
->search_type
= mcview_search_options
.type
;
231 view
->search
->is_all_charsets
= mcview_search_options
.all_codepages
;
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
;
241 /* --------------------------------------------------------------------------------------------- */
244 mcview_search_deinit (WView
* view
)
246 mc_search_free (view
->search
);
247 g_free (view
->last_search_string
);
248 mcview_nroff_seq_free (&view
->search_nroff_seq
);
251 /* --------------------------------------------------------------------------------------------- */
254 mcview_search_cmd_callback (const void *user_data
, gsize char_offset
, int *current_char
)
256 WView
*view
= ((const mcview_search_status_msg_t
*) user_data
)->view
;
258 /* view_read_continue (view, &view->search_onechar_info); *//* AB:FIXME */
259 if (!view
->mode_flags
.nroff
)
261 mcview_get_byte (view
, char_offset
, current_char
);
262 return MC_SEARCH_CB_OK
;
265 if (view
->search_numNeedSkipChar
!= 0)
267 view
->search_numNeedSkipChar
--;
268 return MC_SEARCH_CB_SKIP
;
271 if (search_cb_char_curr_index
== -1
272 || search_cb_char_curr_index
>= view
->search_nroff_seq
->char_length
)
274 if (search_cb_char_curr_index
!= -1)
275 mcview_nroff_seq_next (view
->search_nroff_seq
);
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
);
281 search_cb_char_buffer
[0] = (char) view
->search_nroff_seq
->current_char
;
283 if (view
->search_nroff_seq
->type
!= NROFF_TYPE_NONE
)
285 switch (view
->search_nroff_seq
->type
)
287 case NROFF_TYPE_BOLD
:
288 view
->search_numNeedSkipChar
= 1 + view
->search_nroff_seq
->char_length
; /* real char length and 0x8 */
290 case NROFF_TYPE_UNDERLINE
:
291 view
->search_numNeedSkipChar
= 2; /* underline symbol and ox8 */
297 return MC_SEARCH_CB_INVALID
;
300 *current_char
= search_cb_char_buffer
[search_cb_char_curr_index
];
301 search_cb_char_curr_index
++;
303 return (*current_char
!= -1) ? MC_SEARCH_CB_OK
: MC_SEARCH_CB_INVALID
;
306 /* --------------------------------------------------------------------------------------------- */
309 mcview_search_update_cmd_callback (const void *user_data
, gsize char_offset
)
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
;
317 vsm
->offset
= (off_t
) char_offset
;
319 if (mcview_search_options
.backwards
)
321 if (vsm
->offset
<= view
->update_activate
)
323 view
->update_activate
-= view
->update_steps
;
330 if (vsm
->offset
>= view
->update_activate
)
332 view
->update_activate
+= view
->update_steps
;
338 if (do_update
&& sm
->update (sm
) == B_CANCEL
)
339 result
= MC_SEARCH_CB_ABORT
;
341 /* may be in future return from this callback will change current position in searching block. */
346 /* --------------------------------------------------------------------------------------------- */
349 mcview_do_search (WView
* view
, off_t want_search_start
)
351 mcview_search_status_msg_t vsm
;
353 off_t search_start
= 0;
354 off_t orig_search_start
= view
->search_start
;
355 gboolean found
= FALSE
;
359 view
->search_start
= want_search_start
;
360 /* for avoid infinite search loop we need to increase or decrease start offset of search */
362 if (view
->search_start
!= 0)
364 if (!view
->mode_flags
.nroff
)
365 search_start
= view
->search_start
+ (mcview_search_options
.backwards
? -2 : 0);
368 if (mcview_search_options
.backwards
)
370 mcview_nroff_t
*nroff
;
372 nroff
= mcview_nroff_seq_new_num (view
, view
->search_start
);
373 if (mcview_nroff_seq_prev (nroff
) != -1)
375 -(mcview__get_nroff_real_len (view
, nroff
->index
- 1, 2) +
376 nroff
->char_length
+ 1);
380 mcview_nroff_seq_free (&nroff
);
384 search_start
= mcview__get_nroff_real_len (view
, view
->search_start
+ 1, 2);
386 search_start
+= view
->search_start
;
390 if (mcview_search_options
.backwards
&& search_start
< 0)
393 /* Compute the percent steps */
394 mcview_search_update_steps (view
);
396 view
->update_activate
= search_start
;
400 vsm
.offset
= search_start
;
402 status_msg_init (STATUS_MSG (&vsm
), _("Search"), 1.0, simple_status_msg_init_cb
,
403 mcview_search_status_update_cb
, NULL
);
409 if (view
->growbuf_in_use
)
410 growbufsize
= mcview_growbuf_filesize (view
);
412 growbufsize
= view
->search
->original
.str
->len
;
414 if (mcview_find (&vsm
, search_start
, mcview_get_filesize (view
), &match_len
))
416 mcview_search_show_result (view
, match_len
);
421 /* Search error is here.
422 * MC_SEARCH_E_NOTFOUND: continue search
425 if (view
->search
->error
!= MC_SEARCH_E_NOTFOUND
)
428 search_start
= growbufsize
- view
->search
->original
.str
->len
;
430 while (search_start
> 0 && mcview_may_still_grow (view
));
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
))
437 mcview_search_show_result (view
, match_len
);
441 status_msg_deinit (STATUS_MSG (&vsm
));
443 if (orig_search_start
!= 0 && (!found
&& view
->search
->error
== MC_SEARCH_E_NOTFOUND
)
444 && !mcview_search_options
.backwards
)
446 view
->search_start
= orig_search_start
;
447 mcview_update (view
);
450 (_("Search done"), _("Continue from beginning?"), D_NORMAL
, 2, _("&Yes"),
455 /* continue search from beginning */
456 view
->update_activate
= 0;
462 status_msg_init (STATUS_MSG (&vsm
), _("Search"), 1.0, simple_status_msg_init_cb
,
463 mcview_search_status_update_cb
, NULL
);
465 /* search from file begin up to initial search start position */
466 if (mcview_find (&vsm
, 0, orig_search_start
, &match_len
))
468 mcview_search_show_result (view
, match_len
);
472 status_msg_deinit (STATUS_MSG (&vsm
));
478 view
->search_start
= orig_search_start
;
479 mcview_update (view
);
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"));
489 /* --------------------------------------------------------------------------------------------- */