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