1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 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"
struct searchable {
void *data;
struct iter head;
struct searchable_ops ops;
};
static void search_lock(void)
{
editable_lock();
}
static void search_unlock(void)
{
editable_unlock();
}
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;
}
}
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);
}
int search(struct searchable *s, const char *text, enum search_direction dir, int beginning)
{
struct iter iter;
int ret;
search_lock();
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);
}
if (ret)
ret = do_search(s, &iter, text, dir, 0);
search_unlock();
return ret;
}
int search_next(struct searchable *s, const char *text, enum search_direction dir)
{
struct iter iter;
int ret;
search_lock();
if (!s->ops.get_current(s->data, &iter)) {
search_unlock();
return 0;
}
ret = do_search(s, &iter, text, dir, 1);
search_unlock();
return ret;
}
|