maint: remove dead hint files
[midnight-commander.git] / src / viewer / search.c
blobfae26cf49dec65315e177e6953f7a2b57eb52b1f
1 /*
2 Internal file viewer for the Midnight Commander
3 Function for search data
5 Copyright (C) 1994-2022
6 Free Software Foundation, Inc.
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
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/>.
36 #include <config.h>
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"
45 #include "src/setup.h"
47 #include "internal.h"
49 /*** global variables ****************************************************************************/
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
59 /*** file scope macro definitions ****************************************************************/
61 /*** file scope type declarations ****************************************************************/
63 typedef struct
65 simple_status_msg_t status_msg; /* base class */
67 gboolean first;
68 WView *view;
69 off_t offset;
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 /* --------------------------------------------------------------------------------------------- */
81 static int
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);
87 int percent = -1;
89 if (verbose)
90 percent = mcview_calc_percent (vsm->view, vsm->offset);
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);
98 if (vsm->first)
100 Widget *lw = WIDGET (ssm->label);
101 WRect r;
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;
112 return status_msg_common_update (sm);
115 /* --------------------------------------------------------------------------------------------- */
117 static void
118 mcview_search_update_steps (WView * view)
120 off_t filesize;
122 filesize = mcview_get_filesize (view);
124 if (filesize != 0)
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 /* --------------------------------------------------------------------------------------------- */
140 static gboolean
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)
153 gboolean ok;
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++;
167 return TRUE;
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;
175 search_start--;
178 mc_search_set_error (view->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
179 return FALSE;
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 /* --------------------------------------------------------------------------------------------- */
189 static void
190 mcview_search_show_result (WView * view, size_t match_len)
192 int nroff_len;
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;
200 if (!view->mode_flags.hex)
201 view->search_start++;
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;
208 mcview_moveto_match (view);
211 /* --------------------------------------------------------------------------------------------- */
212 /*** public functions ****************************************************************************/
213 /* --------------------------------------------------------------------------------------------- */
215 gboolean
216 mcview_search_init (WView * view)
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
224 view->search_nroff_seq = mcview_nroff_seq_new (view);
226 if (view->search == NULL)
227 return FALSE;
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;
238 return TRUE;
241 /* --------------------------------------------------------------------------------------------- */
243 void
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 /* --------------------------------------------------------------------------------------------- */
253 mc_search_cbret_t
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);
280 else
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 */
289 break;
290 case NROFF_TYPE_UNDERLINE:
291 view->search_numNeedSkipChar = 2; /* underline symbol and ox8 */
292 break;
293 default:
294 break;
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 /* --------------------------------------------------------------------------------------------- */
308 mc_search_cbret_t
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;
325 do_update = TRUE;
328 else
330 if (vsm->offset >= view->update_activate)
332 view->update_activate += view->update_steps;
334 do_update = TRUE;
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. */
343 return result;
346 /* --------------------------------------------------------------------------------------------- */
348 void
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;
357 size_t match_len;
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);
366 else
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)
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;
380 mcview_nroff_seq_free (&nroff);
382 else
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)
391 search_start = 0;
393 /* Compute the percent steps */
394 mcview_search_update_steps (view);
396 view->update_activate = search_start;
398 vsm.first = TRUE;
399 vsm.view = view;
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);
407 off_t growbufsize;
409 if (view->growbuf_in_use)
410 growbufsize = mcview_growbuf_filesize (view);
411 else
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);
417 found = TRUE;
418 break;
421 /* Search error is here.
422 * MC_SEARCH_E_NOTFOUND: continue search
423 * others: stop
425 if (view->search->error != MC_SEARCH_E_NOTFOUND)
426 break;
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);
438 found = TRUE;
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);
449 if (query_dialog
450 (_("Search done"), _("Continue from beginning?"), D_NORMAL, 2, _("&Yes"),
451 _("&No")) != 0)
452 found = TRUE;
453 else
455 /* continue search from beginning */
456 view->update_activate = 0;
458 vsm.first = TRUE;
459 vsm.view = view;
460 vsm.offset = 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);
469 found = TRUE;
472 status_msg_deinit (STATUS_MSG (&vsm));
476 if (!found)
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"));
486 view->dirty++;
489 /* --------------------------------------------------------------------------------------------- */