summaryrefslogtreecommitdiffstats
path: root/src/viewer/search.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/viewer/search.c')
-rw-r--r--src/viewer/search.c491
1 files changed, 491 insertions, 0 deletions
diff --git a/src/viewer/search.c b/src/viewer/search.c
new file mode 100644
index 0000000..f470a36
--- /dev/null
+++ b/src/viewer/search.c
@@ -0,0 +1,491 @@
+/*
+ Internal file viewer for the Midnight Commander
+ Function for search data
+
+ Copyright (C) 1994-2023
+ Free Software Foundation, Inc.
+
+ Written by:
+ Miguel de Icaza, 1994, 1995, 1998
+ Janne Kukonlehto, 1994, 1995
+ Jakub Jelinek, 1995
+ Joseph M. Hinkle, 1996
+ Norbert Warmuth, 1997
+ Pavel Machek, 1998
+ Roland Illig <roland.illig@gmx.de>, 2004, 2005
+ Slava Zanko <slavazanko@google.com>, 2009
+ Andrew Borodin <aborodin@vmail.ru>, 2009-2022
+ Ilia Maslakov <il.smind@gmail.com>, 2009
+
+ This file is part of the Midnight Commander.
+
+ The Midnight Commander is free software: you can redistribute it
+ and/or modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the License,
+ or (at your option) any later version.
+
+ The Midnight Commander is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "lib/global.h"
+#include "lib/strutil.h"
+#ifdef HAVE_CHARSET
+#include "lib/charsets.h" /* cp_source */
+#endif
+#include "lib/widget.h"
+
+#include "src/setup.h"
+
+#include "internal.h"
+
+/*** global variables ****************************************************************************/
+
+mcview_search_options_t mcview_search_options = {
+ .type = MC_SEARCH_T_NORMAL,
+ .case_sens = FALSE,
+ .backwards = FALSE,
+ .whole_words = FALSE,
+ .all_codepages = FALSE
+};
+
+/*** file scope macro definitions ****************************************************************/
+
+/*** file scope type declarations ****************************************************************/
+
+typedef struct
+{
+ simple_status_msg_t status_msg; /* base class */
+
+ gboolean first;
+ WView *view;
+ off_t offset;
+} mcview_search_status_msg_t;
+
+/*** forward declarations (file scope functions) *************************************************/
+
+/*** file scope variables ************************************************************************/
+
+static int search_cb_char_curr_index = -1;
+static char search_cb_char_buffer[6];
+
+/* --------------------------------------------------------------------------------------------- */
+/*** file scope functions ************************************************************************/
+/* --------------------------------------------------------------------------------------------- */
+
+static int
+mcview_search_status_update_cb (status_msg_t * sm)
+{
+ simple_status_msg_t *ssm = SIMPLE_STATUS_MSG (sm);
+ mcview_search_status_msg_t *vsm = (mcview_search_status_msg_t *) sm;
+ Widget *wd = WIDGET (sm->dlg);
+ int percent = -1;
+
+ if (verbose)
+ percent = mcview_calc_percent (vsm->view, vsm->offset);
+
+ if (percent >= 0)
+ label_set_textv (ssm->label, _("Searching %s: %3d%%"), vsm->view->last_search_string,
+ percent);
+ else
+ label_set_textv (ssm->label, _("Searching %s"), vsm->view->last_search_string);
+
+ if (vsm->first)
+ {
+ Widget *lw = WIDGET (ssm->label);
+ WRect r;
+
+ r = wd->rect;
+ r.cols = MAX (r.cols, lw->rect.cols + 6);
+ widget_set_size_rect (wd, &r);
+ r = lw->rect;
+ r.x = wd->rect.x + (wd->rect.cols - r.cols) / 2;
+ widget_set_size_rect (lw, &r);
+ vsm->first = FALSE;
+ }
+
+ return status_msg_common_update (sm);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+mcview_search_update_steps (WView * view)
+{
+ off_t filesize;
+
+ filesize = mcview_get_filesize (view);
+
+ if (filesize != 0)
+ view->update_steps = filesize / 100;
+ else /* viewing a data stream, not a file */
+ view->update_steps = 40000;
+
+ /* Do not update the percent display but every 20 kb */
+ if (view->update_steps < 20000)
+ view->update_steps = 20000;
+
+ /* Make interrupt more responsive */
+ if (view->update_steps > 40000)
+ view->update_steps = 40000;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static gboolean
+mcview_find (mcview_search_status_msg_t * ssm, off_t search_start, off_t search_end, gsize * len)
+{
+ WView *view = ssm->view;
+
+ view->search_numNeedSkipChar = 0;
+ search_cb_char_curr_index = -1;
+
+ if (mcview_search_options.backwards)
+ {
+ search_end = mcview_get_filesize (view);
+ while (search_start >= 0)
+ {
+ gboolean ok;
+
+ view->search_nroff_seq->index = search_start;
+ mcview_nroff_seq_info (view->search_nroff_seq);
+
+ if (search_end > search_start + (off_t) view->search->original.str->len
+ && mc_search_is_fixed_search_str (view->search))
+ search_end = search_start + view->search->original.str->len;
+
+ ok = mc_search_run (view->search, (void *) ssm, search_start, search_end, len);
+ if (ok && view->search->normal_offset == search_start)
+ {
+ if (view->mode_flags.nroff)
+ view->search->normal_offset++;
+ return TRUE;
+ }
+
+ /* We abort the search in case of a pattern error, or if the user aborts
+ the search. In other words: in all cases except "string not found". */
+ if (!ok && view->search->error != MC_SEARCH_E_NOTFOUND)
+ return FALSE;
+
+ search_start--;
+ }
+
+ mc_search_set_error (view->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
+ return FALSE;
+ }
+ view->search_nroff_seq->index = search_start;
+ mcview_nroff_seq_info (view->search_nroff_seq);
+
+ return mc_search_run (view->search, (void *) ssm, search_start, search_end, len);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+mcview_search_show_result (WView * view, size_t match_len)
+{
+ int nroff_len;
+
+ nroff_len =
+ view->mode_flags.nroff
+ ? mcview__get_nroff_real_len (view, view->search->start_buffer,
+ view->search->normal_offset - view->search->start_buffer) : 0;
+ view->search_start = view->search->normal_offset + nroff_len;
+
+ if (!view->mode_flags.hex)
+ view->search_start++;
+
+ nroff_len =
+ view->mode_flags.nroff ? mcview__get_nroff_real_len (view, view->search_start - 1,
+ match_len) : 0;
+ view->search_end = view->search_start + match_len + nroff_len;
+
+ mcview_moveto_match (view);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/*** public functions ****************************************************************************/
+/* --------------------------------------------------------------------------------------------- */
+
+gboolean
+mcview_search_init (WView * view)
+{
+#ifdef HAVE_CHARSET
+ view->search = mc_search_new (view->last_search_string, cp_source);
+#else
+ view->search = mc_search_new (view->last_search_string, NULL);
+#endif
+
+ view->search_nroff_seq = mcview_nroff_seq_new (view);
+
+ if (view->search == NULL)
+ return FALSE;
+
+ view->search->search_type = mcview_search_options.type;
+#ifdef HAVE_CHARSET
+ view->search->is_all_charsets = mcview_search_options.all_codepages;
+#endif
+ view->search->is_case_sensitive = mcview_search_options.case_sens;
+ view->search->whole_words = mcview_search_options.whole_words;
+ view->search->search_fn = mcview_search_cmd_callback;
+ view->search->update_fn = mcview_search_update_cmd_callback;
+
+ return TRUE;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+mcview_search_deinit (WView * view)
+{
+ mc_search_free (view->search);
+ g_free (view->last_search_string);
+ mcview_nroff_seq_free (&view->search_nroff_seq);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+mc_search_cbret_t
+mcview_search_cmd_callback (const void *user_data, gsize char_offset, int *current_char)
+{
+ WView *view = ((const mcview_search_status_msg_t *) user_data)->view;
+
+ /* view_read_continue (view, &view->search_onechar_info); *//* AB:FIXME */
+ if (!view->mode_flags.nroff)
+ {
+ mcview_get_byte (view, char_offset, current_char);
+ return MC_SEARCH_CB_OK;
+ }
+
+ if (view->search_numNeedSkipChar != 0)
+ {
+ view->search_numNeedSkipChar--;
+ return MC_SEARCH_CB_SKIP;
+ }
+
+ if (search_cb_char_curr_index == -1
+ || search_cb_char_curr_index >= view->search_nroff_seq->char_length)
+ {
+ if (search_cb_char_curr_index != -1)
+ mcview_nroff_seq_next (view->search_nroff_seq);
+
+ search_cb_char_curr_index = 0;
+ if (view->search_nroff_seq->char_length > 1)
+ g_unichar_to_utf8 (view->search_nroff_seq->current_char, search_cb_char_buffer);
+ else
+ search_cb_char_buffer[0] = (char) view->search_nroff_seq->current_char;
+
+ if (view->search_nroff_seq->type != NROFF_TYPE_NONE)
+ {
+ switch (view->search_nroff_seq->type)
+ {
+ case NROFF_TYPE_BOLD:
+ view->search_numNeedSkipChar = 1 + view->search_nroff_seq->char_length; /* real char length and 0x8 */
+ break;
+ case NROFF_TYPE_UNDERLINE:
+ view->search_numNeedSkipChar = 2; /* underline symbol and ox8 */
+ break;
+ default:
+ break;
+ }
+ }
+ return MC_SEARCH_CB_INVALID;
+ }
+
+ *current_char = search_cb_char_buffer[search_cb_char_curr_index];
+ search_cb_char_curr_index++;
+
+ return (*current_char != -1) ? MC_SEARCH_CB_OK : MC_SEARCH_CB_INVALID;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+mc_search_cbret_t
+mcview_search_update_cmd_callback (const void *user_data, gsize char_offset)
+{
+ status_msg_t *sm = STATUS_MSG (user_data);
+ mcview_search_status_msg_t *vsm = (mcview_search_status_msg_t *) user_data;
+ WView *view = vsm->view;
+ gboolean do_update = FALSE;
+ mc_search_cbret_t result = MC_SEARCH_CB_OK;
+
+ vsm->offset = (off_t) char_offset;
+
+ if (mcview_search_options.backwards)
+ {
+ if (vsm->offset <= view->update_activate)
+ {
+ view->update_activate -= view->update_steps;
+
+ do_update = TRUE;
+ }
+ }
+ else
+ {
+ if (vsm->offset >= view->update_activate)
+ {
+ view->update_activate += view->update_steps;
+
+ do_update = TRUE;
+ }
+ }
+
+ if (do_update && sm->update (sm) == B_CANCEL)
+ result = MC_SEARCH_CB_ABORT;
+
+ /* may be in future return from this callback will change current position in searching block. */
+
+ return result;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+mcview_do_search (WView * view, off_t want_search_start)
+{
+ mcview_search_status_msg_t vsm;
+
+ off_t search_start = 0;
+ off_t orig_search_start = view->search_start;
+ gboolean found = FALSE;
+
+ size_t match_len;
+
+ view->search_start = want_search_start;
+ /* for avoid infinite search loop we need to increase or decrease start offset of search */
+
+ if (view->search_start != 0)
+ {
+ if (!view->mode_flags.nroff)
+ search_start = view->search_start + (mcview_search_options.backwards ? -2 : 0);
+ else
+ {
+ if (mcview_search_options.backwards)
+ {
+ mcview_nroff_t *nroff;
+
+ nroff = mcview_nroff_seq_new_num (view, view->search_start);
+ if (mcview_nroff_seq_prev (nroff) != -1)
+ search_start =
+ -(mcview__get_nroff_real_len (view, nroff->index - 1, 2) +
+ nroff->char_length + 1);
+ else
+ search_start = -2;
+
+ mcview_nroff_seq_free (&nroff);
+ }
+ else
+ {
+ search_start = mcview__get_nroff_real_len (view, view->search_start + 1, 2);
+ }
+ search_start += view->search_start;
+ }
+ }
+
+ if (mcview_search_options.backwards && search_start < 0)
+ search_start = 0;
+
+ /* Compute the percent steps */
+ mcview_search_update_steps (view);
+
+ view->update_activate = search_start;
+
+ vsm.first = TRUE;
+ vsm.view = view;
+ vsm.offset = search_start;
+
+ status_msg_init (STATUS_MSG (&vsm), _("Search"), 1.0, simple_status_msg_init_cb,
+ mcview_search_status_update_cb, NULL);
+
+ do
+ {
+ off_t growbufsize;
+
+ if (view->growbuf_in_use)
+ growbufsize = mcview_growbuf_filesize (view);
+ else
+ growbufsize = view->search->original.str->len;
+
+ if (mcview_find (&vsm, search_start, mcview_get_filesize (view), &match_len))
+ {
+ mcview_search_show_result (view, match_len);
+ found = TRUE;
+ break;
+ }
+
+ /* Search error is here.
+ * MC_SEARCH_E_NOTFOUND: continue search
+ * others: stop
+ */
+ if (view->search->error != MC_SEARCH_E_NOTFOUND)
+ break;
+
+ search_start = growbufsize - view->search->original.str->len;
+ }
+ while (search_start > 0 && mcview_may_still_grow (view));
+
+ /* After mcview_may_still_grow (view) == FALSE we have remained last chunk. Search there. */
+ if (view->growbuf_in_use && !found && view->search->error == MC_SEARCH_E_NOTFOUND
+ && !mcview_search_options.backwards
+ && mcview_find (&vsm, search_start, mcview_get_filesize (view), &match_len))
+ {
+ mcview_search_show_result (view, match_len);
+ found = TRUE;
+ }
+
+ status_msg_deinit (STATUS_MSG (&vsm));
+
+ if (orig_search_start != 0 && (!found && view->search->error == MC_SEARCH_E_NOTFOUND)
+ && !mcview_search_options.backwards)
+ {
+ view->search_start = orig_search_start;
+ mcview_update (view);
+
+ if (query_dialog
+ (_("Search done"), _("Continue from beginning?"), D_NORMAL, 2, _("&Yes"),
+ _("&No")) != 0)
+ found = TRUE;
+ else
+ {
+ /* continue search from beginning */
+ view->update_activate = 0;
+
+ vsm.first = TRUE;
+ vsm.view = view;
+ vsm.offset = 0;
+
+ status_msg_init (STATUS_MSG (&vsm), _("Search"), 1.0, simple_status_msg_init_cb,
+ mcview_search_status_update_cb, NULL);
+
+ /* search from file begin up to initial search start position */
+ if (mcview_find (&vsm, 0, orig_search_start, &match_len))
+ {
+ mcview_search_show_result (view, match_len);
+ found = TRUE;
+ }
+
+ status_msg_deinit (STATUS_MSG (&vsm));
+ }
+ }
+
+ if (!found)
+ {
+ view->search_start = orig_search_start;
+ mcview_update (view);
+
+ if (view->search->error == MC_SEARCH_E_NOTFOUND)
+ query_dialog (_("Search"), _(STR_E_NOTFOUND), D_NORMAL, 1, _("&Dismiss"));
+ else if (view->search->error_str != NULL)
+ query_dialog (_("Search"), view->search->error_str, D_NORMAL, 1, _("&Dismiss"));
+ }
+ view->dirty++;
+}
+
+/* --------------------------------------------------------------------------------------------- */