1 /*
2 * Copyright 2008-2013 Various Authors
3 * Copyright 2005 Timo Hirvonen
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "search.h"
20 #include "editable.h"
21 #include "xmalloc.h"
22 #include "ui_curses.h"
23 #include "convert.h"
24 #include "options.h"
25
26 struct searchable {
27 void *data;
28 struct iter head;
29 struct searchable_ops ops;
30 };
31
advance(struct searchable * s,struct iter * iter,enum search_direction dir,int * wrapped)32 static int advance(struct searchable *s, struct iter *iter,
33 enum search_direction dir, int *wrapped)
34 {
35 if (dir == SEARCH_FORWARD) {
36 if (!s->ops.get_next(iter)) {
37 if (!wrap_search)
38 return 0;
39 *iter = s->head;
40 if (!s->ops.get_next(iter))
41 return 0;
42 *wrapped = 1;
43 }
44 } else {
45 if (!s->ops.get_prev(iter)) {
46 if (!wrap_search)
47 return 0;
48 *iter = s->head;
49 if (!s->ops.get_prev(iter))
50 return 0;
51 *wrapped = 1;
52 }
53 }
54 return 1;
55 }
56
57 /* returns next matching item or NULL if not found
58 * result can be the current item unless skip_current is set */
do_u_search(struct searchable * s,struct iter * iter,const char * text,enum search_direction dir,int skip_current)59 static int do_u_search(struct searchable *s, struct iter *iter, const char *text,
60 enum search_direction dir, int skip_current)
61 {
62 struct iter start;
63 int wrapped = 0;
64
65 if (skip_current && !advance(s, iter, dir, &wrapped))
66 return 0;
67
68 start = *iter;
69 while (1) {
70 if (s->ops.matches(s->data, iter, text)) {
71 if (wrapped)
72 info_msg(dir == SEARCH_FORWARD ?
73 "search hit BOTTOM, continuing at TOP" :
74 "search hit TOP, continuing at BOTTOM");
75 return 1;
76 }
77 if (!advance(s, iter, dir, &wrapped) || iters_equal(iter, &start))
78 return 0;
79 }
80 }
81
do_search(struct searchable * s,struct iter * iter,const char * text,enum search_direction dir,int skip_current)82 static int do_search(struct searchable *s, struct iter *iter, const char *text,
83 enum search_direction dir, int skip_current)
84 {
85 char *u_text = NULL;
86 int r;
87
88 /* search text is always in locale encoding (because cmdline is) */
89 if (!using_utf8 && utf8_encode(text, charset, &u_text) == 0)
90 text = u_text;
91
92 r = do_u_search(s, iter, text, dir, skip_current);
93
94 free(u_text);
95 return r;
96 }
97
searchable_new(void * data,const struct iter * head,const struct searchable_ops * ops)98 struct searchable *searchable_new(void *data, const struct iter *head, const struct searchable_ops *ops)
99 {
100 struct searchable *s;
101
102 s = xnew(struct searchable, 1);
103 s->data = data;
104 s->head = *head;
105 s->ops = *ops;
106 return s;
107 }
108
searchable_free(struct searchable * s)109 void searchable_free(struct searchable *s)
110 {
111 free(s);
112 }
113
searchable_set_head(struct searchable * s,const struct iter * head)114 void searchable_set_head(struct searchable *s, const struct iter *head)
115 {
116 s->head = *head;
117 }
118
search(struct searchable * s,const char * text,enum search_direction dir,int beginning)119 int search(struct searchable *s, const char *text, enum search_direction dir, int beginning)
120 {
121 struct iter iter;
122 int ret;
123
124 if (beginning) {
125 /* first or last item */
126 iter = s->head;
127 if (dir == SEARCH_FORWARD) {
128 ret = s->ops.get_next(&iter);
129 } else {
130 ret = s->ops.get_prev(&iter);
131 }
132 } else {
133 /* selected item */
134 ret = s->ops.get_current(s->data, &iter);
135 }
136 if (ret)
137 ret = do_search(s, &iter, text, dir, 0);
138 return ret;
139 }
140
search_next(struct searchable * s,const char * text,enum search_direction dir)141 int search_next(struct searchable *s, const char *text, enum search_direction dir)
142 {
143 struct iter iter;
144 int ret;
145
146 if (!s->ops.get_current(s->data, &iter)) {
147 return 0;
148 }
149 ret = do_search(s, &iter, text, dir, 1);
150 return ret;
151 }
152