1 #include <stdio.h>
2 #include <string.h>
3 #include <ctype.h>
4 #include <pcre.h>
5 #include "mlbuf.h"
6
7 typedef char* (*mark_find_match_fn)(char *haystack, bint_t haystack_len, bint_t look_offset, bint_t max_offset, void *u1, void *u2, bint_t *ret_needle_len);
8 static int mark_find_match(mark_t *self, mark_find_match_fn matchfn, void *u1, void *u2, int reverse, bline_t **ret_line, bint_t *ret_col, bint_t *ret_num_chars);
9 static int mark_find_re(mark_t *self, char *re, bint_t re_len, int reverse, bline_t **ret_line, bint_t *ret_col, bint_t *ret_num_chars);
10 static char *mark_find_match_prev(char *haystack, bint_t haystack_len, bint_t look_offset, bint_t max_offset, mark_find_match_fn matchfn, void *u1, void *u2);
11 static char *mark_find_next_str_matchfn(char *haystack, bint_t haystack_len, bint_t look_offset, bint_t max_offset, void *needle, void *needle_len, bint_t *ret_needle_len);
12 static char *mark_find_prev_str_matchfn(char *haystack, bint_t haystack_len, bint_t look_offset, bint_t max_offset, void *needle, void *needle_len, bint_t *ret_needle_len);
13 static char *mark_find_next_cre_matchfn(char *haystack, bint_t haystack_len, bint_t look_offset, bint_t max_offset, void *cre, void *unused, bint_t *ret_needle_len);
14 static char *mark_find_prev_cre_matchfn(char *haystack, bint_t haystack_len, bint_t look_offset, bint_t max_offset, void *cre, void *unused, bint_t *ret_needle_len);
15
16 static int *pcre_ovector = NULL;
17 static int pcre_ovector_size = 0;
18 static int *pcre_rc;
19 static char bracket_pairs[8] = {
20 '[', ']',
21 '(', ')',
22 '{', '}'
23 };
24
25 // Return a clone (same position) of an existing mark
mark_clone(mark_t * self,mark_t ** ret_mark)26 int mark_clone(mark_t *self, mark_t **ret_mark) {
27 *ret_mark = buffer_add_mark(self->bline->buffer, self->bline, self->col);
28 return MLBUF_OK;
29 }
30
31 // Return a lettered clone (same position) of an existing mark
mark_clone_w_letter(mark_t * self,char letter,mark_t ** ret_mark)32 int mark_clone_w_letter(mark_t *self, char letter, mark_t **ret_mark) {
33 *ret_mark = buffer_add_mark_ex(self->bline->buffer, letter, self->bline, self->col);
34 return MLBUF_OK;
35 }
36
37 // Insert data before mark
mark_insert_before(mark_t * self,char * data,bint_t data_len)38 int mark_insert_before(mark_t *self, char *data, bint_t data_len) {
39 return bline_insert(self->bline, self->col, data, data_len, NULL);
40 }
41
42 // Insert data after mark
mark_insert_after(mark_t * self,char * data,bint_t data_len)43 int mark_insert_after(mark_t *self, char *data, bint_t data_len) {
44 int rc;
45 bint_t num_chars;
46 if ((rc = bline_insert(self->bline, self->col, data, data_len, &num_chars)) == MLBUF_OK) {
47 rc = mark_move_by(self, -1 * num_chars);
48 }
49 return rc;
50 }
51
52 // Delete data after mark
mark_delete_after(mark_t * self,bint_t num_chars)53 int mark_delete_after(mark_t *self, bint_t num_chars) {
54 return bline_delete(self->bline, self->col, num_chars);
55 }
56
57 // Delete data before mark
mark_delete_before(mark_t * self,bint_t num_chars)58 int mark_delete_before(mark_t *self, bint_t num_chars) {
59 int rc;
60 if ((rc = mark_move_by(self, -1 * num_chars)) == MLBUF_OK) {
61 rc = mark_delete_after(self, num_chars);
62 }
63 return rc;
64 }
65
66 // Replace data
mark_replace(mark_t * self,bint_t num_chars,char * data,bint_t data_len)67 int mark_replace(mark_t *self, bint_t num_chars, char *data, bint_t data_len) {
68 return bline_replace(self->bline, self->col, num_chars, data, data_len);
69 }
70
71 // Replace data between marks
mark_replace_between_mark(mark_t * self,mark_t * other,char * data,bint_t data_len)72 int mark_replace_between_mark(mark_t *self, mark_t *other, char *data, bint_t data_len) {
73 bint_t offset_a;
74 bint_t offset_b;
75 buffer_get_offset(self->bline->buffer, self->bline, self->col, &offset_a);
76 buffer_get_offset(other->bline->buffer, other->bline, other->col, &offset_b);
77 if (offset_a < offset_b) {
78 return buffer_replace(self->bline->buffer, offset_a, offset_b - offset_a, data, data_len);
79 }
80 return buffer_replace(self->bline->buffer, offset_b, offset_a - offset_b, data, data_len);
81 }
82
83 // Move mark to bline:col
mark_move_to_w_bline(mark_t * self,bline_t * bline,bint_t col)84 int mark_move_to_w_bline(mark_t *self, bline_t *bline, bint_t col) {
85 _mark_mark_move_inner(self, bline, col, 1, 1);
86 return MLBUF_OK;
87 }
88
89 // Move mark to line_index:col
mark_move_to(mark_t * self,bint_t line_index,bint_t col)90 int mark_move_to(mark_t *self, bint_t line_index, bint_t col) {
91 bline_t *bline;
92 buffer_get_bline(self->bline->buffer, line_index, &bline);
93 _mark_mark_move_inner(self, bline, col, 1, 1);
94 return MLBUF_OK;
95 }
96
97 // Move mark by a character delta
mark_move_by(mark_t * self,bint_t char_delta)98 int mark_move_by(mark_t *self, bint_t char_delta) {
99 bint_t offset;
100 buffer_get_offset(self->bline->buffer, self->bline, self->col, &offset);
101 return mark_move_offset(self, offset + char_delta);
102 }
103
104 // Get mark offset
mark_get_offset(mark_t * self,bint_t * ret_offset)105 int mark_get_offset(mark_t *self, bint_t *ret_offset) {
106 return buffer_get_offset(self->bline->buffer, self->bline, self->col, ret_offset);
107 }
108
109 // Move mark by line delta
mark_move_vert(mark_t * self,bint_t line_delta)110 int mark_move_vert(mark_t *self, bint_t line_delta) {
111 bline_t *cur_line;
112 bline_t *tmp_line;
113 cur_line = self->bline;
114 while (line_delta != 0) {
115 tmp_line = line_delta > 0 ? cur_line->next : cur_line->prev;
116 if (!tmp_line) {
117 break;
118 }
119 cur_line = tmp_line;
120 line_delta = line_delta + (line_delta > 0 ? -1 : 1);
121 }
122 if (cur_line == self->bline) {
123 return MLBUF_OK;
124 }
125 _mark_mark_move_inner(self, cur_line, self->target_col, 0, 1);
126 return MLBUF_OK;
127 }
128
129 // Move mark to beginning of line
mark_move_bol(mark_t * self)130 int mark_move_bol(mark_t *self) {
131 _mark_mark_move_inner(self, self->bline, 0, 1, 1);
132 return MLBUF_OK;
133 }
134
135 // Move mark to end of line
mark_move_eol(mark_t * self)136 int mark_move_eol(mark_t *self) {
137 MLBUF_BLINE_ENSURE_CHARS(self->bline);
138 _mark_mark_move_inner(self, self->bline, self->bline->char_count, 1, 1);
139 return MLBUF_OK;
140 }
141
142 // Move mark to a column on the current line
mark_move_col(mark_t * self,bint_t col)143 int mark_move_col(mark_t *self, bint_t col) {
144 _mark_mark_move_inner(self, self->bline, col, 1, 1);
145 return MLBUF_OK;
146 }
147
148 // Move mark to beginning of buffer
mark_move_beginning(mark_t * self)149 int mark_move_beginning(mark_t *self) {
150 _mark_mark_move_inner(self, self->bline->buffer->first_line, 0, 1, 1);
151 return MLBUF_OK;
152 }
153
154 // Move mark to end of buffer
mark_move_end(mark_t * self)155 int mark_move_end(mark_t *self) {
156 MLBUF_BLINE_ENSURE_CHARS(self->bline->buffer->last_line);
157 _mark_mark_move_inner(self, self->bline->buffer->last_line, self->bline->buffer->last_line->char_count, 1, 1);
158 return MLBUF_OK;
159 }
160
161 // Move mark to a particular offset
mark_move_offset(mark_t * self,bint_t offset)162 int mark_move_offset(mark_t *self, bint_t offset) {
163 bline_t *dest_line;
164 bint_t dest_col;
165 buffer_get_bline_col(self->bline->buffer, offset, &dest_line, &dest_col);
166 _mark_mark_move_inner(self, dest_line, dest_col, 1, 1);
167 return MLBUF_OK;
168 }
169
170 // Find next occurrence of string from mark
mark_find_next_str(mark_t * self,char * str,bint_t str_len,bline_t ** ret_line,bint_t * ret_col,bint_t * ret_num_chars)171 int mark_find_next_str(mark_t *self, char *str, bint_t str_len, bline_t **ret_line, bint_t *ret_col, bint_t *ret_num_chars) {
172 return mark_find_match(self, mark_find_next_str_matchfn, (void*)str, (void*)&str_len, 0, ret_line, ret_col, ret_num_chars);
173 }
174
175 // Find prev occurrence of string from mark
mark_find_prev_str(mark_t * self,char * str,bint_t str_len,bline_t ** ret_line,bint_t * ret_col,bint_t * ret_num_chars)176 int mark_find_prev_str(mark_t *self, char *str, bint_t str_len, bline_t **ret_line, bint_t *ret_col, bint_t *ret_num_chars) {
177 return mark_find_match(self, mark_find_prev_str_matchfn, (void*)str, (void*)&str_len, 1, ret_line, ret_col, ret_num_chars);
178 }
179
180 // Find next occurence of regex from mark
mark_find_next_cre(mark_t * self,pcre * cre,bline_t ** ret_line,bint_t * ret_col,bint_t * ret_num_chars)181 int mark_find_next_cre(mark_t *self, pcre *cre, bline_t **ret_line, bint_t *ret_col, bint_t *ret_num_chars) {
182 return mark_find_match(self, mark_find_next_cre_matchfn, (void*)cre, NULL, 0, ret_line, ret_col, ret_num_chars);
183 }
184
185 // Find prev occurence of regex from mark
mark_find_prev_cre(mark_t * self,pcre * cre,bline_t ** ret_line,bint_t * ret_col,bint_t * ret_num_chars)186 int mark_find_prev_cre(mark_t *self, pcre *cre, bline_t **ret_line, bint_t *ret_col, bint_t *ret_num_chars) {
187 return mark_find_match(self, mark_find_prev_cre_matchfn, (void*)cre, NULL, 1, ret_line, ret_col, ret_num_chars);
188 }
189
190 // Find next occurence of uncompiled regex str from mark
mark_find_next_re(mark_t * self,char * re,bint_t re_len,bline_t ** ret_line,bint_t * ret_col,bint_t * ret_num_chars)191 int mark_find_next_re(mark_t *self, char *re, bint_t re_len, bline_t **ret_line, bint_t *ret_col, bint_t *ret_num_chars) {
192 return mark_find_re(self, re, re_len, 0, ret_line, ret_col, ret_num_chars);
193 }
194
195 // Find prev occurence of uncompiled regex str from mark
mark_find_prev_re(mark_t * self,char * re,bint_t re_len,bline_t ** ret_line,bint_t * ret_col,bint_t * ret_num_chars)196 int mark_find_prev_re(mark_t *self, char *re, bint_t re_len, bline_t **ret_line, bint_t *ret_col, bint_t *ret_num_chars) {
197 return mark_find_re(self, re, re_len, 1, ret_line, ret_col, ret_num_chars);
198 }
199
200 // Return 1 if self is before other, otherwise return 0
mark_is_lt(mark_t * self,mark_t * other)201 int mark_is_lt(mark_t *self, mark_t *other) {
202 if (self->bline->line_index == other->bline->line_index) {
203 return self->col < other->col ? 1 : 0;
204 } else if (self->bline->line_index < other->bline->line_index) {
205 return 1;
206 }
207 return 0;
208 }
209
210 // Return 1 if self is past other, otherwise return 0
mark_is_gt(mark_t * self,mark_t * other)211 int mark_is_gt(mark_t *self, mark_t *other) {
212 if (self->bline->line_index == other->bline->line_index) {
213 return self->col > other->col ? 1 : 0;
214 } else if (self->bline->line_index > other->bline->line_index) {
215 return 1;
216 }
217 return 0;
218 }
219
220 // Return 1 if self is at same position as other, otherwise return 0
mark_is_eq(mark_t * self,mark_t * other)221 int mark_is_eq(mark_t *self, mark_t *other) {
222 if (self->bline->line_index == other->bline->line_index) {
223 return self->col == other->col ? 1 : 0;
224 }
225 return 0;
226 }
227
228 // Return 1 if self >= other
mark_is_gte(mark_t * self,mark_t * other)229 int mark_is_gte(mark_t *self, mark_t *other) {
230 return !mark_is_lt(self, other);
231 }
232
233 // Return 1 if self <= other
mark_is_lte(mark_t * self,mark_t * other)234 int mark_is_lte(mark_t *self, mark_t *other) {
235 return !mark_is_gt(self, other);
236 }
237
238 // Find top-level bracket to the left examining no more than max_chars
mark_find_bracket_top(mark_t * self,bint_t max_chars,bline_t ** ret_line,bint_t * ret_col,bint_t * ret_brkt)239 int mark_find_bracket_top(mark_t *self, bint_t max_chars, bline_t **ret_line, bint_t *ret_col, bint_t *ret_brkt) {
240 bline_t *cur_line;
241 bint_t col;
242 int *stacks;
243 int found;
244 int i;
245 int i_left;
246 cur_line = self->bline;
247 col = self->col;
248 stacks = calloc(128, sizeof(int));
249 found = 0;
250 while (!found && max_chars > 0 && cur_line) {
251 MLBUF_BLINE_ENSURE_CHARS(cur_line);
252 col -= 1;
253 if (col < 0) {
254 cur_line = cur_line->prev;
255 if (cur_line) col = cur_line->char_count;
256 max_chars -= 1;
257 continue;
258 }
259 for (i = 0; i < 8; i++) {
260 i_left = (i % 2 == 0 ? i : i - 1);
261 if (cur_line->chars[col].ch == (uint32_t)bracket_pairs[i]) {
262 stacks[(int)bracket_pairs[i_left]] += (i % 2 == 0 ? -1 : 1);
263 if (stacks[(int)bracket_pairs[i_left]] <= -1) {
264 *ret_line = cur_line;
265 *ret_col = col;
266 *ret_brkt = bracket_pairs[i];
267 found = 1;
268 }
269 break;
270 }
271 }
272 }
273 free(stacks);
274 return found ? MLBUF_OK : MLBUF_ERR;
275 }
276
277 // Find the matching bracket character under the mark, examining no more than
278 // max_chars.
mark_find_bracket_pair(mark_t * self,bint_t max_chars,bline_t ** ret_line,bint_t * ret_col,bint_t * ret_brkt)279 int mark_find_bracket_pair(mark_t *self, bint_t max_chars, bline_t **ret_line, bint_t *ret_col, bint_t *ret_brkt) {
280 char brkt;
281 char targ;
282 char cur;
283 int dir;
284 int i;
285 int nest;
286 bint_t col;
287 bint_t nchars;
288 bline_t *cur_line;
289 MLBUF_BLINE_ENSURE_CHARS(self->bline);
290
291 // If we're at eol, there's nothing to match
292 if (self->col >= self->bline->char_count) {
293 return MLBUF_ERR;
294 }
295 // Set brkt to char under mark
296 brkt = *(self->bline->data + self->bline->chars[self->col].index);
297 // Find targ matching bracket char
298 targ = 0;
299 for (i = 0; i < 8; i++) {
300 if (bracket_pairs[i] == brkt) {
301 if (i % 2 == 0) {
302 targ = bracket_pairs[i + 1];
303 dir = 1;
304 } else {
305 targ = bracket_pairs[i - 1];
306 dir = -1;
307 }
308 break;
309 }
310 }
311 // If targ is not set, brkt was not a bracket char
312 if (!targ) {
313 return MLBUF_ERR;
314 }
315 // Now look for targ, keeping track of nesting
316 // Break if we look at more than max_chars
317 nest = -1;
318 cur_line = self->bline;
319 col = self->col;
320 nchars = 0;
321 while (cur_line) {
322 MLBUF_BLINE_ENSURE_CHARS(cur_line);
323 for (; col >= 0 && col < cur_line->char_count; col += dir) {
324 cur = *(cur_line->data + cur_line->chars[col].index);
325 if (cur == targ) {
326 if (nest == 0) {
327 // Match!
328 *ret_line = cur_line;
329 *ret_col = col;
330 *ret_brkt = targ;
331 return MLBUF_OK;
332 } else {
333 nest -= 1;
334 }
335 } else if (cur == brkt) {
336 nest += 1;
337 }
338 nchars += 1;
339 if (nchars >= max_chars) {
340 return MLBUF_ERR;
341 }
342 }
343 if (dir > 0) {
344 cur_line = cur_line->next;
345 if (cur_line) col = 0;
346 } else {
347 cur_line = cur_line->prev;
348 if (cur_line) col = MLBUF_MAX(1, cur_line->char_count) - 1;
349 }
350 }
351 // If we got here, targ was not found, or nesting was off
352 return MLBUF_ERR;
353 }
354
355 // Delete data between self and other
mark_delete_between_mark(mark_t * self,mark_t * other)356 int mark_delete_between_mark(mark_t *self, mark_t *other) {
357 bint_t offset_a;
358 bint_t offset_b;
359 buffer_get_offset(self->bline->buffer, self->bline, self->col, &offset_a);
360 buffer_get_offset(other->bline->buffer, other->bline, other->col, &offset_b);
361 if (offset_a == offset_b) {
362 return MLBUF_OK;
363 } else if (offset_a > offset_b) {
364 return buffer_delete(self->bline->buffer, offset_b, offset_a - offset_b);
365 }
366 return buffer_delete(self->bline->buffer, offset_a, offset_b - offset_a);
367 }
368
369 // Return data between self and other
mark_get_between_mark(mark_t * self,mark_t * other,char ** ret_str,bint_t * ret_str_len)370 int mark_get_between_mark(mark_t *self, mark_t *other, char **ret_str, bint_t *ret_str_len) {
371 bint_t ig;
372 if (mark_is_gt(self, other)) {
373 return buffer_substr(
374 self->bline->buffer,
375 other->bline, other->col,
376 self->bline, self->col,
377 ret_str, ret_str_len, &ig
378 );
379 } else if (mark_is_gt(other, self)) {
380 return buffer_substr(
381 self->bline->buffer,
382 self->bline, self->col,
383 other->bline, other->col,
384 ret_str, ret_str_len, &ig
385 );
386 }
387 *ret_str = strdup("");
388 *ret_str_len = 0;
389 return MLBUF_OK;
390 }
391
392 // Move self to other
mark_join(mark_t * self,mark_t * other)393 int mark_join(mark_t *self, mark_t *other) {
394 _mark_mark_move_inner(self, other->bline, other->col, 1, 1);
395 return MLBUF_OK;
396 }
397
398 // Swap positions of self and other
mark_swap_with_mark(mark_t * self,mark_t * other)399 int mark_swap_with_mark(mark_t *self, mark_t *other) {
400 mark_t tmp_mark;
401 tmp_mark.bline = other->bline;
402 tmp_mark.col = other->col;
403 _mark_mark_move_inner(other, self->bline, self->col, 1, 1);
404 _mark_mark_move_inner(self, tmp_mark.bline, tmp_mark.col, 1, 1);
405 return MLBUF_OK;
406 }
407
408 // Return 1 if mark is at eol, else return 0
mark_is_at_eol(mark_t * self)409 int mark_is_at_eol(mark_t *self) {
410 MLBUF_BLINE_ENSURE_CHARS(self->bline);
411 return self->col >= self->bline->char_count ? 1 : 0;
412 }
413
414 // Return 1 if mark is at bol, else return 0
mark_is_at_bol(mark_t * self)415 int mark_is_at_bol(mark_t *self) {
416 return self->col <= 0;
417 }
418
419 // Destroy a mark
mark_destroy(mark_t * self)420 int mark_destroy(mark_t *self) {
421 return buffer_destroy_mark(self->bline->buffer, self);
422 }
423
424 #define MLBUF_MARK_IMPLEMENT_MOVE_VIA_FIND_EX(mark, findfn, ...) do { \
425 int rc; \
426 bline_t *line = NULL; \
427 bint_t col = 0; \
428 bint_t char_count = 0; \
429 if ((rc = (findfn)((mark), __VA_ARGS__, &line, &col, &char_count)) == MLBUF_OK) { \
430 _mark_mark_move_inner((mark), line, col, 1, 1); \
431 if (optret_line) *optret_line = line; \
432 if (optret_col) *optret_col = col; \
433 if (optret_char_count) *optret_char_count = char_count; \
434 return MLBUF_OK; \
435 } \
436 return rc; \
437 } while(0)
438
439 #define MLBUF_MARK_IMPLEMENT_MOVE_VIA_FIND(mark, findfn, ...) do { \
440 int rc; \
441 bline_t *line = NULL; \
442 bint_t col = 0; \
443 bint_t char_count = 0; \
444 if ((rc = (findfn)((mark), __VA_ARGS__, &line, &col, &char_count)) == MLBUF_OK) { \
445 _mark_mark_move_inner((mark), line, col, 1, 1); \
446 } \
447 return rc; \
448 } while(0)
449
450 #define MLBUF_MARK_IMPLEMENT_NUDGE_VIA_FIND(mark, findfn, ...) do { \
451 int rc; \
452 bline_t *line = NULL; \
453 bint_t col = 0; \
454 bint_t char_count = 0; \
455 mark_t *tmark = NULL; \
456 mark_clone((mark), &tmark); \
457 mark_move_by(tmark, 1); \
458 if ((rc = (findfn)(tmark, __VA_ARGS__, &line, &col, &char_count)) == MLBUF_OK) { \
459 _mark_mark_move_inner((mark), line, col, 1, 1); \
460 } \
461 mark_destroy(tmark); \
462 return rc; \
463 } while(0)
464
mark_move_next_str(mark_t * self,char * str,bint_t str_len)465 int mark_move_next_str(mark_t *self, char *str, bint_t str_len) {
466 MLBUF_MARK_IMPLEMENT_MOVE_VIA_FIND(self, mark_find_next_str, str, str_len);
467 }
468
mark_move_prev_str(mark_t * self,char * str,bint_t str_len)469 int mark_move_prev_str(mark_t *self, char *str, bint_t str_len) {
470 MLBUF_MARK_IMPLEMENT_MOVE_VIA_FIND(self, mark_find_prev_str, str, str_len);
471 }
472
mark_move_next_cre(mark_t * self,pcre * cre)473 int mark_move_next_cre(mark_t *self, pcre *cre) {
474 MLBUF_MARK_IMPLEMENT_MOVE_VIA_FIND(self, mark_find_next_cre, cre);
475 }
476
mark_move_prev_cre(mark_t * self,pcre * cre)477 int mark_move_prev_cre(mark_t *self, pcre *cre) {
478 MLBUF_MARK_IMPLEMENT_MOVE_VIA_FIND(self, mark_find_prev_cre, cre);
479 }
480
mark_move_next_re(mark_t * self,char * re,bint_t re_len)481 int mark_move_next_re(mark_t *self, char *re, bint_t re_len) {
482 MLBUF_MARK_IMPLEMENT_MOVE_VIA_FIND(self, mark_find_next_re, re, re_len);
483 }
484
mark_move_prev_re(mark_t * self,char * re,bint_t re_len)485 int mark_move_prev_re(mark_t *self, char *re, bint_t re_len) {
486 MLBUF_MARK_IMPLEMENT_MOVE_VIA_FIND(self, mark_find_prev_re, re, re_len);
487 }
488
mark_move_next_str_nudge(mark_t * self,char * str,bint_t str_len)489 int mark_move_next_str_nudge(mark_t *self, char *str, bint_t str_len) {
490 MLBUF_MARK_IMPLEMENT_NUDGE_VIA_FIND(self, mark_find_next_str, str, str_len);
491 }
492
mark_move_next_cre_nudge(mark_t * self,pcre * cre)493 int mark_move_next_cre_nudge(mark_t *self, pcre *cre) {
494 MLBUF_MARK_IMPLEMENT_NUDGE_VIA_FIND(self, mark_find_next_cre, cre);
495 }
496
mark_move_next_re_nudge(mark_t * self,char * re,bint_t re_len)497 int mark_move_next_re_nudge(mark_t *self, char *re, bint_t re_len) {
498 MLBUF_MARK_IMPLEMENT_NUDGE_VIA_FIND(self, mark_find_next_re, re, re_len);
499 }
500
mark_move_bracket_pair(mark_t * self,bint_t max_chars)501 int mark_move_bracket_pair(mark_t *self, bint_t max_chars) {
502 MLBUF_MARK_IMPLEMENT_MOVE_VIA_FIND(self, mark_find_bracket_pair, max_chars);
503 }
504
mark_move_bracket_top(mark_t * self,bint_t max_chars)505 int mark_move_bracket_top(mark_t *self, bint_t max_chars) {
506 MLBUF_MARK_IMPLEMENT_MOVE_VIA_FIND(self, mark_find_bracket_top, max_chars);
507 }
508
mark_move_next_str_ex(mark_t * self,char * str,bint_t str_len,bline_t ** optret_line,bint_t * optret_col,bint_t * optret_char_count)509 int mark_move_next_str_ex(mark_t *self, char *str, bint_t str_len, bline_t **optret_line, bint_t *optret_col, bint_t *optret_char_count) {
510 MLBUF_MARK_IMPLEMENT_MOVE_VIA_FIND_EX(self, mark_find_next_str, str, str_len);
511 }
512
mark_move_prev_str_ex(mark_t * self,char * str,bint_t str_len,bline_t ** optret_line,bint_t * optret_col,bint_t * optret_char_count)513 int mark_move_prev_str_ex(mark_t *self, char *str, bint_t str_len, bline_t **optret_line, bint_t *optret_col, bint_t *optret_char_count) {
514 MLBUF_MARK_IMPLEMENT_MOVE_VIA_FIND_EX(self, mark_find_prev_str, str, str_len);
515 }
516
mark_move_next_cre_ex(mark_t * self,pcre * cre,bline_t ** optret_line,bint_t * optret_col,bint_t * optret_char_count)517 int mark_move_next_cre_ex(mark_t *self, pcre *cre, bline_t **optret_line, bint_t *optret_col, bint_t *optret_char_count) {
518 MLBUF_MARK_IMPLEMENT_MOVE_VIA_FIND_EX(self, mark_find_next_cre, cre);
519 }
520
mark_move_prev_cre_ex(mark_t * self,pcre * cre,bline_t ** optret_line,bint_t * optret_col,bint_t * optret_char_count)521 int mark_move_prev_cre_ex(mark_t *self, pcre *cre, bline_t **optret_line, bint_t *optret_col, bint_t *optret_char_count) {
522 MLBUF_MARK_IMPLEMENT_MOVE_VIA_FIND_EX(self, mark_find_prev_cre, cre);
523 }
524
mark_move_next_re_ex(mark_t * self,char * re,bint_t re_len,bline_t ** optret_line,bint_t * optret_col,bint_t * optret_char_count)525 int mark_move_next_re_ex(mark_t *self, char *re, bint_t re_len, bline_t **optret_line, bint_t *optret_col, bint_t *optret_char_count) {
526 MLBUF_MARK_IMPLEMENT_MOVE_VIA_FIND_EX(self, mark_find_next_re, re, re_len);
527 }
528
mark_move_prev_re_ex(mark_t * self,char * re,bint_t re_len,bline_t ** optret_line,bint_t * optret_col,bint_t * optret_char_count)529 int mark_move_prev_re_ex(mark_t *self, char *re, bint_t re_len, bline_t **optret_line, bint_t *optret_col, bint_t *optret_char_count) {
530 MLBUF_MARK_IMPLEMENT_MOVE_VIA_FIND_EX(self, mark_find_prev_re, re, re_len);
531 }
532
mark_move_bracket_pair_ex(mark_t * self,bint_t max_chars,bline_t ** optret_line,bint_t * optret_col,bint_t * optret_char_count)533 int mark_move_bracket_pair_ex(mark_t *self, bint_t max_chars, bline_t **optret_line, bint_t *optret_col, bint_t *optret_char_count) {
534 MLBUF_MARK_IMPLEMENT_MOVE_VIA_FIND_EX(self, mark_find_bracket_pair, max_chars);
535 }
536
mark_move_bracket_top_ex(mark_t * self,bint_t max_chars,bline_t ** optret_line,bint_t * optret_col,bint_t * optret_char_count)537 int mark_move_bracket_top_ex(mark_t *self, bint_t max_chars, bline_t **optret_line, bint_t *optret_col, bint_t *optret_char_count) {
538 MLBUF_MARK_IMPLEMENT_MOVE_VIA_FIND_EX(self, mark_find_bracket_top, max_chars);
539 }
540
541 // Return 1 if mark is at a word boundary. If side <= -1, return 1 only for
542 // left word boundary (i.e., \W\w). If side >= 1, return 1 only for right word
543 // boundary (i.e., \w\W). If side == 0, return 1 for either case.
mark_is_at_word_bound(mark_t * self,int side)544 int mark_is_at_word_bound(mark_t *self, int side) {
545 uint32_t before, after;
546 MLBUF_BLINE_ENSURE_CHARS(self->bline);
547 before = self->col > 0 && self->col - 1 < self->bline->char_count ? self->bline->chars[self->col - 1].ch : 0;
548 after = self->col < self->bline->char_count ? self->bline->chars[self->col].ch : 0;
549 if (side <= -1 || side == 0) {
550 // If before is bol or non-word, and after is word
551 if ((before == 0 || !(isalnum(before) || before == '_'))
552 && (isalnum(after) || after == '_')
553 ) {
554 return 1;
555 }
556 }
557 if (side >= 1 || side == 0) {
558 // If after is eol or non-word, and before is word
559 if ((after == 0 || !(isalnum(after) || after == '_'))
560 && (isalnum(before) || before == '_')
561 ) {
562 return 1;
563 }
564 }
565 return 0;
566 }
567
568 // Set ovector for capturing substrs
mark_set_pcre_capture(int * rc,int * ovector,int ovector_size)569 int mark_set_pcre_capture(int *rc, int *ovector, int ovector_size) {
570 if (rc == NULL || ovector == NULL || ovector_size == 0) {
571 rc = NULL;
572 pcre_ovector = NULL;
573 pcre_ovector_size = 0;
574 return MLBUF_OK;
575 } else if (rc != NULL && ovector != NULL && ovector_size >= 3 && ovector_size % 3 == 0) {
576 pcre_rc = rc;
577 pcre_ovector = ovector;
578 pcre_ovector_size = ovector_size;
579 return MLBUF_OK;
580 }
581 pcre_rc = NULL;
582 pcre_ovector = NULL;
583 pcre_ovector_size = 0;
584 return MLBUF_ERR;
585 }
586
587 // Return char after mark, or 0 if at eol.
mark_get_char_after(mark_t * self,uint32_t * ret_char)588 int mark_get_char_after(mark_t *self, uint32_t *ret_char) {
589 if (mark_is_at_eol(self)) {
590 *ret_char = 0;
591 } else {
592 MLBUF_BLINE_ENSURE_CHARS(self->bline);
593 *ret_char = self->bline->chars[self->col].ch;
594 }
595 return MLBUF_OK;
596 }
597
598 // Return char before mark, or 0 if at bol.
mark_get_char_before(mark_t * self,uint32_t * ret_char)599 int mark_get_char_before(mark_t *self, uint32_t *ret_char) {
600 if (mark_is_at_bol(self)) {
601 *ret_char = 0;
602 } else {
603 MLBUF_BLINE_ENSURE_CHARS(self->bline);
604 *ret_char = self->bline->chars[self->col - 1].ch;
605 }
606 return MLBUF_OK;
607 }
608
609 // Return 1 if mark is after col, else 0. Lefty marks are considered 'after' col
610 // if `mark->col > col`. Righty marks (the default) are considered 'after' col
611 // if `mark->col >= col`.
mark_is_after_col_minus_lefties(mark_t * self,bint_t col)612 int mark_is_after_col_minus_lefties(mark_t *self, bint_t col) {
613 if (self->lefty) {
614 return self->col > col ? 1 : 0;
615 }
616 return self->col >= col ? 1 : 0;
617 }
618
619 // Find first occurrence of match according to matchfn. Search backwards if
620 // reverse is truthy.
mark_find_match(mark_t * self,mark_find_match_fn matchfn,void * u1,void * u2,int reverse,bline_t ** ret_line,bint_t * ret_col,bint_t * ret_num_chars)621 static int mark_find_match(mark_t *self, mark_find_match_fn matchfn, void *u1, void *u2, int reverse, bline_t **ret_line, bint_t *ret_col, bint_t *ret_num_chars) {
622 bline_t *search_line = NULL;
623 char *match = NULL;
624 bint_t look_offset = 0;
625 bint_t match_col = 0;
626 bint_t match_col_end = 0;
627 bint_t max_offset = 0;
628 bint_t match_len = 0;
629 search_line = self->bline;
630 *ret_line = NULL;
631 if (reverse) {
632 if (self->col <= 0) {
633 // At bol, so look on prev line
634 search_line = search_line->prev;
635 if (!search_line) return MLBUF_ERR;
636 look_offset = 0;
637 max_offset = search_line->data_len;
638 } else {
639 look_offset = 0;
640 MLBUF_BLINE_ENSURE_CHARS(search_line);
641 max_offset = search_line->chars[self->col - 1].index;
642 }
643 } else {
644 MLBUF_BLINE_ENSURE_CHARS(search_line);
645 if (self->col >= search_line->char_count) {
646 // At eol, so look on next line
647 search_line = search_line->next;
648 if (!search_line) return MLBUF_ERR;
649 look_offset = 0;
650 max_offset = search_line->data_len;
651 } else {
652 look_offset = self->col < search_line->char_count ? search_line->chars[self->col].index : search_line->data_len;
653 max_offset = search_line->data_len;
654 }
655 }
656 while (search_line) {
657 match = matchfn(search_line->data, search_line->data_len, look_offset, max_offset, u1, u2, &match_len);
658 if (match != NULL) {
659 bline_get_col(search_line, (bint_t)(match - search_line->data), &match_col);
660 bline_get_col(search_line, (bint_t)((match + match_len) - search_line->data), &match_col_end);
661 *ret_line = search_line;
662 *ret_col = match_col;
663 *ret_num_chars = match_col_end - match_col;
664 return MLBUF_OK;
665 }
666 search_line = reverse ? search_line->prev : search_line->next;
667 if (search_line) {
668 look_offset = 0;
669 max_offset = search_line->data_len;
670 }
671 }
672 return MLBUF_ERR;
673 }
674
675 // Move mark to target:col, setting target_col if do_set_target is truthy,
676 // restyling if do_style is truthy
_mark_mark_move_inner(mark_t * mark,bline_t * bline_target,bint_t col,int do_set_target,int do_style)677 void _mark_mark_move_inner(mark_t *mark, bline_t *bline_target, bint_t col, int do_set_target, int do_style) {
678 bline_t *bline_orig;
679 int is_changing_line;
680 bline_t *bline_restyle;
681 bint_t min_restylelines;
682 do_style = do_style && mark->range_srule ? 1 : 0;
683 is_changing_line = mark->bline != bline_target ? 1 : 0;
684 if (do_style) {
685 bline_orig = mark->bline;
686 }
687 if (is_changing_line) {
688 DL_DELETE(mark->bline->marks, mark);
689 mark->bline = bline_target;
690 }
691 MLBUF_BLINE_ENSURE_CHARS(mark->bline);
692 mark->col = MLBUF_MIN(mark->bline->char_count, MLBUF_MAX(0, col));
693 if (do_set_target) {
694 mark->target_col = mark->col;
695 }
696 if (is_changing_line) {
697 DL_APPEND(bline_target->marks, mark);
698 }
699 if (do_style) {
700 if (bline_target->line_index > bline_orig->line_index) {
701 bline_restyle = bline_orig;
702 min_restylelines = (bline_target->line_index - bline_orig->line_index) + 1;
703 } else {
704 bline_restyle = bline_target;
705 min_restylelines = (bline_orig->line_index - bline_target->line_index) + 1;
706 }
707 buffer_apply_styles(
708 bline_restyle->buffer,
709 bline_restyle,
710 min_restylelines
711 );
712 }
713 }
714
715 // Return the last occurrence of a match given a forward-searching matchfn
mark_find_match_prev(char * haystack,bint_t haystack_len,bint_t look_offset,bint_t max_offset,mark_find_match_fn matchfn,void * u1,void * u2)716 static char *mark_find_match_prev(char *haystack, bint_t haystack_len, bint_t look_offset, bint_t max_offset, mark_find_match_fn matchfn, void *u1, void *u2) {
717 char *match;
718 char *last_match;
719 bint_t match_len;
720 last_match = NULL;
721 while (1) {
722 match = matchfn(haystack, haystack_len, look_offset, max_offset, u1, u2, &match_len);
723 if (match == NULL) {
724 return last_match;
725 }
726 if (match - haystack > max_offset) {
727 return last_match;
728 }
729 // Override match_len to 1. Reasoning: If we have a haystack like
730 // 'banana' and our re is 'ana', using the actual match_len for the
731 // next search offset would skip the 2nd 'ana' match.
732 match_len = 1;
733 look_offset = (bint_t)(match - haystack) + match_len;
734 if (look_offset + match_len > haystack_len) {
735 return match;
736 }
737 last_match = match;
738 }
739 }
740
741 // Find uncompiled regex from mark. Search backwards if reverse is truthy.
mark_find_re(mark_t * self,char * re,bint_t re_len,int reverse,bline_t ** ret_line,bint_t * ret_col,bint_t * ret_num_chars)742 static int mark_find_re(mark_t *self, char *re, bint_t re_len, int reverse, bline_t **ret_line, bint_t *ret_col, bint_t *ret_num_chars) {
743 int rc;
744 char *regex;
745 pcre *cre;
746 const char *error;
747 int erroffset;
748 MLBUF_MAKE_GT_EQ0(re_len);
749 regex = malloc(re_len + 1);
750 snprintf(regex, re_len + 1, "%s", re);
751 cre = pcre_compile((const char*)regex, PCRE_CASELESS, &error, &erroffset, NULL);
752 if (cre == NULL) {
753 // TODO log error
754 free(regex);
755 return MLBUF_ERR;
756 }
757 if (reverse) {
758 rc = mark_find_prev_cre(self, cre, ret_line, ret_col, ret_num_chars);
759 } else {
760 rc = mark_find_next_cre(self, cre, ret_line, ret_col, ret_num_chars);
761 }
762 pcre_free(cre);
763 free(regex);
764 return rc;
765 }
766
mark_find_next_str_matchfn(char * haystack,bint_t haystack_len,bint_t look_offset,bint_t max_offset,void * needle,void * needle_len,bint_t * ret_needle_len)767 static char *mark_find_next_str_matchfn(char *haystack, bint_t haystack_len, bint_t look_offset, bint_t max_offset, void *needle, void *needle_len, bint_t *ret_needle_len) {
768 if (ret_needle_len) *ret_needle_len = *((bint_t*)needle_len);
769 if (look_offset >= haystack_len) return NULL;
770 return memmem(haystack + look_offset, haystack_len - look_offset, needle, *((bint_t*)needle_len));
771 }
772
mark_find_prev_str_matchfn(char * haystack,bint_t haystack_len,bint_t look_offset,bint_t max_offset,void * needle,void * needle_len,bint_t * ret_needle_len)773 static char *mark_find_prev_str_matchfn(char *haystack, bint_t haystack_len, bint_t look_offset, bint_t max_offset, void *needle, void *needle_len, bint_t *ret_needle_len) {
774 return mark_find_match_prev(haystack, haystack_len, look_offset, max_offset, mark_find_next_str_matchfn, needle, needle_len);
775 }
776
mark_find_next_cre_matchfn(char * haystack,bint_t haystack_len,bint_t look_offset,bint_t max_offset,void * cre,void * unused,bint_t * ret_needle_len)777 static char *mark_find_next_cre_matchfn(char *haystack, bint_t haystack_len, bint_t look_offset, bint_t max_offset, void *cre, void *unused, bint_t *ret_needle_len) {
778 int rc;
779 int substrs[3];
780 int *use_rc;
781 int *use_substrs;
782 int use_substrs_size;
783 if (!haystack || haystack_len == 0) {
784 haystack = "";
785 haystack_len = 0;
786 }
787 if (pcre_ovector) {
788 use_substrs = pcre_ovector;
789 use_substrs_size = pcre_ovector_size;
790 use_rc = pcre_rc;
791 } else {
792 use_substrs = substrs;
793 use_substrs_size = 3;
794 use_rc = &rc;
795 }
796 MLBUF_INIT_PCRE_EXTRA(pcre_extra);
797 if ((*use_rc = pcre_exec((pcre*)cre, &pcre_extra, haystack, haystack_len, look_offset, 0, use_substrs, use_substrs_size)) >= 0) {
798 if (ret_needle_len) *ret_needle_len = (bint_t)(use_substrs[1] - use_substrs[0]);
799 return haystack + use_substrs[0];
800 }
801 return NULL;
802 }
803
mark_find_prev_cre_matchfn(char * haystack,bint_t haystack_len,bint_t look_offset,bint_t max_offset,void * cre,void * unused,bint_t * ret_needle_len)804 static char *mark_find_prev_cre_matchfn(char *haystack, bint_t haystack_len, bint_t look_offset, bint_t max_offset, void *cre, void *unused, bint_t *ret_needle_len) {
805 return mark_find_match_prev(haystack, haystack_len, look_offset, max_offset, mark_find_next_cre_matchfn, cre, unused);
806 }
807