162 lines
3.7 KiB
C
162 lines
3.7 KiB
C
/*
|
|
* Copyright 2008-2013 Various Authors
|
|
* Copyright 2005 Timo Hirvonen
|
|
*
|
|
* This program 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 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program 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 "search.h"
|
|
#include "editable.h"
|
|
#include "xmalloc.h"
|
|
#include "ui_curses.h"
|
|
#include "convert.h"
|
|
#include "options.h"
|
|
#include "debug.h"
|
|
|
|
struct searchable {
|
|
void *data;
|
|
struct iter head;
|
|
struct searchable_ops ops;
|
|
};
|
|
|
|
static int advance(struct searchable *s, struct iter *iter,
|
|
enum search_direction dir, int *wrapped)
|
|
{
|
|
if (dir == SEARCH_FORWARD) {
|
|
if (!s->ops.get_next(iter)) {
|
|
if (!wrap_search)
|
|
return 0;
|
|
*iter = s->head;
|
|
if (!s->ops.get_next(iter))
|
|
return 0;
|
|
*wrapped += 1;
|
|
}
|
|
} else {
|
|
if (!s->ops.get_prev(iter)) {
|
|
if (!wrap_search)
|
|
return 0;
|
|
*iter = s->head;
|
|
if (!s->ops.get_prev(iter))
|
|
return 0;
|
|
*wrapped += 1;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* returns next matching item or NULL if not found
|
|
* result can be the current item unless skip_current is set */
|
|
static int do_u_search(struct searchable *s, struct iter *iter, const char *text,
|
|
enum search_direction dir, int skip_current)
|
|
{
|
|
struct iter start;
|
|
int wrapped = 0;
|
|
|
|
if (skip_current && !advance(s, iter, dir, &wrapped))
|
|
return 0;
|
|
|
|
start = *iter;
|
|
while (1) {
|
|
if (s->ops.matches(s->data, iter, text)) {
|
|
if (wrapped)
|
|
info_msg(dir == SEARCH_FORWARD ?
|
|
"search hit BOTTOM, continuing at TOP" :
|
|
"search hit TOP, continuing at BOTTOM");
|
|
return 1;
|
|
}
|
|
if (!advance(s, iter, dir, &wrapped) || iters_equal(iter, &start))
|
|
return 0;
|
|
/**
|
|
* workaround for buggy implementations of search_ops where
|
|
* get_next/get_prev never equals the initial get_current
|
|
* (#1332)
|
|
*/
|
|
if (wrapped > 1) {
|
|
d_print("fixme: bailing since search wrapped more than once without a match\n");
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int do_search(struct searchable *s, struct iter *iter, const char *text,
|
|
enum search_direction dir, int skip_current)
|
|
{
|
|
char *u_text = NULL;
|
|
int r;
|
|
|
|
/* search text is always in locale encoding (because cmdline is) */
|
|
if (!using_utf8 && utf8_encode(text, charset, &u_text) == 0)
|
|
text = u_text;
|
|
|
|
r = do_u_search(s, iter, text, dir, skip_current);
|
|
|
|
free(u_text);
|
|
return r;
|
|
}
|
|
|
|
struct searchable *searchable_new(void *data, const struct iter *head, const struct searchable_ops *ops)
|
|
{
|
|
struct searchable *s;
|
|
|
|
s = xnew(struct searchable, 1);
|
|
s->data = data;
|
|
s->head = *head;
|
|
s->ops = *ops;
|
|
return s;
|
|
}
|
|
|
|
void searchable_free(struct searchable *s)
|
|
{
|
|
free(s);
|
|
}
|
|
|
|
void searchable_set_head(struct searchable *s, const struct iter *head)
|
|
{
|
|
s->head = *head;
|
|
}
|
|
|
|
int search(struct searchable *s, const char *text, enum search_direction dir, int beginning)
|
|
{
|
|
struct iter iter;
|
|
int ret;
|
|
|
|
if (beginning) {
|
|
/* first or last item */
|
|
iter = s->head;
|
|
if (dir == SEARCH_FORWARD) {
|
|
ret = s->ops.get_next(&iter);
|
|
} else {
|
|
ret = s->ops.get_prev(&iter);
|
|
}
|
|
} else {
|
|
/* selected item */
|
|
ret = s->ops.get_current(s->data, &iter, dir);
|
|
}
|
|
if (ret)
|
|
ret = do_search(s, &iter, text, dir, 0);
|
|
return ret;
|
|
}
|
|
|
|
int search_next(struct searchable *s, const char *text, enum search_direction dir)
|
|
{
|
|
struct iter iter;
|
|
int ret;
|
|
|
|
if (!s->ops.get_current(s->data, &iter, dir)) {
|
|
return 0;
|
|
}
|
|
ret = do_search(s, &iter, text, dir, 1);
|
|
return ret;
|
|
}
|