search.c - ledit - Text editor (WIP)
HTML git clone git://lumidify.org/ledit.git (fast, but not encrypted)
HTML git clone https://lumidify.org/ledit.git (encrypted, but very slow)
HTML git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ledit.git (over tor)
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
search.c (5173B)
---
1 #include <string.h>
2
3 #include "view.h"
4 #include "buffer.h"
5 #include "search.h"
6 #include "memory.h"
7
8 /* FIXME: make sure only whole utf8 chars are matched
9 -> actually, isn't this always the case as long as the
10 pattern is valid utf8 because of the structure of utf8? */
11 static char *last_search = NULL;
12 static size_t last_search_len = 0;
13 enum {
14 FORWARD,
15 BACKWARD
16 } last_dir = FORWARD;
17
18 void
19 search_cleanup(void) {
20 free(last_search);
21 last_search = NULL;
22 last_search_len = 0;
23 }
24
25 void
26 set_search_forward(char *pattern) {
27 last_dir = FORWARD;
28 free(last_search);
29 last_search = ledit_strdup(pattern);
30 last_search_len = strlen(last_search);
31 }
32
33 void
34 set_search_backward(char *pattern) {
35 last_dir = BACKWARD;
36 free(last_search);
37 last_search = ledit_strdup(pattern);
38 last_search_len = strlen(last_search);
39 }
40
41 static search_state
42 search_forward(ledit_view *view, size_t *line_ret, size_t *byte_ret, size_t *len_ret) {
43 *line_ret = view->cur_line;
44 *byte_ret = view->cur_index;
45 *len_ret = last_search_len;
46 /* if last_search is empty, strstr will find the ending '\0' */
47 if (last_search == NULL || last_search[0] == '\0')
48 return SEARCH_NO_PATTERN;
49 size_t line = view->cur_line;
50 /* start one byte later so it doesn't get stuck on a match
51 note: in certain cases, this may not be a valid index */
52 size_t byte = view->cur_index + 1;
53 char *res;
54 ledit_line *lline = buffer_get_line(view->buffer, line);
55 buffer_normalize_line(lline);
56 if (byte < lline->len && (res = strstr(lline->text + byte, last_search)) != NULL) {
57 *line_ret = line;
58 *byte_ret = (size_t)(res - lline->text);
59 return SEARCH_NORMAL;
60 }
61 for (size_t i = line + 1; i < view->lines_num; i++) {
62 lline = buffer_get_line(view->buffer, i);
63 buffer_normalize_line(lline);
64 if ((res = strstr(lline->text, last_search)) != NULL) {
65 *line_ret = i;
66 *byte_ret = (size_t)(res - lline->text);
67 return SEARCH_NORMAL;
68 }
69 }
70 for (size_t i = 0; i < line; i++) {
71 lline = buffer_get_line(view->buffer, i);
72 buffer_normalize_line(lline);
73 if ((res = strstr(lline->text, last_search)) != NULL) {
74 *line_ret = i;
75 *byte_ret = (size_t)(res - lline->text);
76 return SEARCH_WRAPPED;
77 }
78 }
79 lline = buffer_get_line(view->buffer, line);
80 buffer_normalize_line(lline);
81 if ((res = strstr(lline->text, last_search)) != NULL) {
82 *line_ret = line;
83 *byte_ret = (size_t)(res - lline->text);
84 return SEARCH_WRAPPED;
85 }
86 return SEARCH_NOT_FOUND;
87 }
88
89 /* FIXME: this is insanely inefficient */
90 /* FIXME: just go backwards char-by-char and compare or knuth-morris-pratt */
91 static search_state
92 search_backward(ledit_view *view, size_t *line_ret, size_t *byte_ret, size_t *len_ret) {
93 *line_ret = view->cur_line;
94 *byte_ret = view->cur_index;
95 *len_ret = last_search_len;
96 if (last_search == NULL || last_search[0] == '\0')
97 return SEARCH_NO_PATTERN;
98 size_t line = view->cur_line;
99 size_t byte = view->cur_index;
100 ledit_line *lline = buffer_get_line(view->buffer, line);
101 buffer_normalize_line(lline);
102 char *last = NULL, *res = lline->text;
103 while ((res = strstr(res, last_search)) != NULL && res < lline->text + byte) {
104 last = res;
105 res++;
106 }
107 if (last != NULL) {
108 *line_ret = line;
109 /* FIXME: check if this is safe */
110 *byte_ret = (size_t)(last - lline->text);
111 return SEARCH_NORMAL;
112 }
113 for (size_t i = line; i > 0; i--) {
114 lline = buffer_get_line(view->buffer, i-1);
115 buffer_normalize_line(lline);
116 res = lline->text;
117 while ((res = strstr(res, last_search)) != NULL) {
118 last = res;
119 res++;
120 }
121 if (last != NULL) {
122 *line_ret = i-1;
123 *byte_ret = (size_t)(last - lline->text);
124 return SEARCH_NORMAL;
125 }
126 }
127 for (size_t i = view->lines_num - 1; i > line; i--) {
128 lline = buffer_get_line(view->buffer, i);
129 buffer_normalize_line(lline);
130 res = lline->text;
131 while ((res = strstr(res, last_search)) != NULL) {
132 last = res;
133 res++;
134 }
135 if (last != NULL) {
136 *line_ret = i;
137 *byte_ret = (size_t)(last - lline->text);
138 return SEARCH_WRAPPED;
139 }
140 }
141 lline = buffer_get_line(view->buffer, line);
142 buffer_normalize_line(lline);
143 res = lline->text + byte;
144 while ((res = strstr(res, last_search)) != NULL) {
145 last = res;
146 res++;
147 }
148 if (last != NULL) {
149 *line_ret = line;
150 *byte_ret = (size_t)(last - lline->text);
151 return SEARCH_WRAPPED;
152 }
153 return SEARCH_NOT_FOUND;
154 }
155
156 search_state
157 ledit_search_next(ledit_view *view, size_t *line_ret, size_t *byte_ret, size_t *len_ret) {
158 if (last_dir == FORWARD)
159 return search_forward(view, line_ret, byte_ret, len_ret);
160 else
161 return search_backward(view, line_ret, byte_ret, len_ret);
162 }
163
164 search_state
165 ledit_search_prev(ledit_view *view, size_t *line_ret, size_t *byte_ret, size_t *len_ret) {
166 if (last_dir == FORWARD)
167 return search_backward(view, line_ret, byte_ret, len_ret);
168 else
169 return search_forward(view, line_ret, byte_ret, len_ret);
170 }
171
172 char *
173 search_state_to_str(search_state s) {
174 switch (s) {
175 case SEARCH_NORMAL:
176 return "Found match";
177 case SEARCH_WRAPPED:
178 return "Search wrapped";
179 case SEARCH_NOT_FOUND:
180 return "Pattern not found";
181 case SEARCH_NO_PATTERN:
182 return "No previous search pattern";
183 default:
184 return "This message should not be shown. "
185 "Please bug lumidify about it.";
186 }
187 }