push
This commit is contained in:
161
search.c
Normal file
161
search.c
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
Reference in New Issue
Block a user