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