1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <time.h>
5 #include <sys/mman.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <unistd.h>
9 #include <fcntl.h>
10 #include <utlist.h>
11 #include <inttypes.h>
12 #include "mlbuf.h"
13
14 static int _buffer_open_mmap(buffer_t *self, int fd, size_t size);
15 static int _buffer_open_read(buffer_t *self, int fd, size_t size);
16 static int _buffer_bline_unslab(bline_t *self);
17 static void _buffer_stat(buffer_t *self);
18 static int _buffer_baction_do(buffer_t *self, bline_t *bline, baction_t *action, int is_redo, bint_t *opt_repeat_offset);
19 static int _buffer_update(buffer_t *self, baction_t *action);
20 static int _buffer_truncate_undo_stack(buffer_t *self, baction_t *action_from);
21 static int _buffer_add_to_undo_stack(buffer_t *self, baction_t *action);
22 static int _buffer_apply_styles_singles(bline_t *start_line, bint_t min_nlines);
23 static int _buffer_apply_styles_multis(bline_t *start_line, bint_t min_nlines, int srule_type);
24 static int _buffer_bline_apply_style_single(srule_t *srule, bline_t *bline);
25 static int _buffer_bline_apply_style_multi(srule_t *srule, bline_t *bline, srule_t **open_rule, bint_t *look_offset);
26 static bline_t *_buffer_bline_new(buffer_t *self);
27 static int _buffer_bline_free(bline_t *bline, bline_t *maybe_mark_line, bint_t col_delta);
28 static bline_t *_buffer_bline_break(bline_t *bline, bint_t col);
29 static void _buffer_find_end_pos(bline_t *start_line, bint_t start_col, bint_t num_chars, bline_t **ret_end_line, bint_t *ret_end_col, bint_t *ret_safe_num_chars);
30 static void _buffer_bline_replace(bline_t *bline, bint_t start_col, char *data, bint_t data_len, str_t *del_data);
31 static bint_t _buffer_bline_insert(bline_t *bline, bint_t col, char *data, bint_t data_len, int move_marks);
32 static bint_t _buffer_bline_delete(bline_t *bline, bint_t col, bint_t num_chars);
33 static bint_t _buffer_bline_col_to_index(bline_t *bline, bint_t col);
34 static bint_t _buffer_bline_index_to_col(bline_t *bline, bint_t index);
35 static int _buffer_munmap(buffer_t *self);
36 static int _srule_multi_find(srule_t *rule, int find_end, bline_t *bline, bint_t start_offset, bint_t *ret_start, bint_t *ret_stop);
37 static int _srule_multi_find_start(srule_t *rule, bline_t *bline, bint_t start_offset, bint_t *ret_start, bint_t *ret_stop);
38 static int _srule_multi_find_end(srule_t *rule, bline_t *bline, bint_t start_offset, bint_t *ret_stop);
39 static int _baction_destroy(baction_t *action);
40
41 // Make a new buffer and return it
buffer_new()42 buffer_t *buffer_new() {
43 buffer_t *buffer;
44 bline_t *bline;
45 buffer = calloc(1, sizeof(buffer_t));
46 buffer->tab_width = 4;
47 bline = _buffer_bline_new(buffer);
48 buffer->first_line = bline;
49 buffer->last_line = bline;
50 buffer->line_count = 1;
51 buffer->mmap_fd = -1;
52 return buffer;
53 }
54
55 // Wrapper for buffer_new + buffer_open
buffer_new_open(char * path)56 buffer_t *buffer_new_open(char *path) {
57 buffer_t *self;
58 int rc;
59 self = buffer_new();
60 if ((rc = buffer_open(self, path)) != MLBUF_OK) {
61 buffer_destroy(self);
62 return NULL;
63 }
64 return self;
65 }
66
67 // Read buffer from path
buffer_open(buffer_t * self,char * path)68 int buffer_open(buffer_t *self, char *path) {
69 int rc;
70 struct stat st;
71 int fd;
72 fd = -1;
73
74 rc = MLBUF_OK;
75 do {
76 // Exit early if path is empty
77 if (!path || strlen(path) < 1) {
78 rc = MLBUF_ERR;
79 break;
80 }
81
82 // Open file for reading
83 if ((fd = open(path, O_RDONLY)) < 0) {
84 rc = MLBUF_ERR;
85 break;
86 }
87
88 // Stat file
89 if (fstat(fd, &st) < 0) {
90 rc = MLBUF_ERR;
91 break;
92 }
93
94 // Read or mmap file into buffer
95 self->is_in_open = 1;
96 if (st.st_size >= MLBUF_LARGE_FILE_SIZE) {
97 if (_buffer_open_mmap(self, fd, st.st_size) != MLBUF_OK) {
98 // mmap failed so fallback to normal read
99 if (_buffer_open_read(self, fd, st.st_size) != MLBUF_OK) {
100 rc = MLBUF_ERR;
101 break;
102 }
103 }
104 } else {
105 if (_buffer_open_read(self, fd, st.st_size) != MLBUF_OK) {
106 rc = MLBUF_ERR;
107 break;
108 }
109 }
110 self->is_in_open = 0;
111 } while(0);
112
113 if (fd >= 0) close(fd);
114
115 if (rc == MLBUF_ERR) return rc;
116
117 // Set path
118 if (self->path) free(self->path);
119 self->path = strdup(path);
120 self->is_unsaved = 0;
121
122 // Remember stat
123 _buffer_stat(self);
124
125 return MLBUF_OK;
126 }
127
128 // Write buffer to path
buffer_save(buffer_t * self)129 int buffer_save(buffer_t *self) {
130 return buffer_save_as(self, self->path, NULL);
131 }
132
133 // Write buffer to specified path
buffer_save_as(buffer_t * self,char * path,bint_t * optret_nbytes)134 int buffer_save_as(buffer_t *self, char *path, bint_t *optret_nbytes) {
135 FILE *fp;
136 size_t nbytes;
137
138 if (optret_nbytes) *optret_nbytes = 0;
139
140 // Exit early if path is empty
141 if (!path || strlen(path) < 1) {
142 return MLBUF_ERR;
143 }
144
145 // Open file for writing
146 if (!(fp = fopen(path, "wb"))) {
147 return MLBUF_ERR;
148 }
149
150 // Write data to file
151 nbytes = 0;
152 buffer_write_to_file(self, fp, &nbytes);
153 fclose(fp);
154 if (optret_nbytes) *optret_nbytes = (bint_t)nbytes;
155 if ((bint_t)nbytes != self->byte_count) return MLBUF_ERR;
156
157 // Set path
158 if (self->path != path) {
159 if (self->path) free(self->path);
160 self->path = strdup(path);
161 }
162 self->is_unsaved = 0;
163
164 // Remember stat
165 _buffer_stat(self);
166
167 return MLBUF_OK;
168 }
169
170 // Write buffer data to FILE*
buffer_write_to_file(buffer_t * self,FILE * fp,size_t * optret_nbytes)171 int buffer_write_to_file(buffer_t *self, FILE *fp, size_t *optret_nbytes) {
172 return buffer_write_to_fd(self, fileno(fp), optret_nbytes);
173 }
174
175 // Write buffer data to file descriptor
buffer_write_to_fd(buffer_t * self,int fd,size_t * optret_nbytes)176 int buffer_write_to_fd(buffer_t *self, int fd, size_t *optret_nbytes) {
177 bline_t *bline;
178 size_t nbytes;
179 ssize_t write_rc;
180 nbytes = 0;
181 #define MLBUF_BUFFER_WRITE_CHECK(pd, pl) do { \
182 write_rc = write(fd, (pd), (pl)); \
183 if (write_rc < (pl)) { \
184 return MLBUF_ERR; \
185 } \
186 nbytes += write_rc; \
187 } while(0)
188 for (bline = self->first_line; bline; bline = bline->next) {
189 if (bline->data_len > 0) MLBUF_BUFFER_WRITE_CHECK(bline->data, bline->data_len);
190 if (bline->next) MLBUF_BUFFER_WRITE_CHECK("\n", 1);
191 }
192 if (optret_nbytes) *optret_nbytes = nbytes;
193 return MLBUF_OK;
194 }
195
196 // Free a buffer
buffer_destroy(buffer_t * self)197 int buffer_destroy(buffer_t *self) {
198 bline_t *line;
199 bline_t *line_tmp;
200 baction_t *action;
201 baction_t *action_tmp;
202 char c;
203 for (line = self->last_line; line; ) {
204 line_tmp = line->prev;
205 _buffer_bline_free(line, NULL, 0);
206 line = line_tmp;
207 }
208 if (self->data) free(self->data);
209 if (self->path) free(self->path);
210 DL_FOREACH_SAFE(self->actions, action, action_tmp) {
211 DL_DELETE(self->actions, action);
212 _baction_destroy(action);
213 }
214 for (c = 'a'; c <= 'z'; c++) buffer_register_clear(self, c);
215 _buffer_munmap(self);
216 if (self->slabbed_blines) free(self->slabbed_blines);
217 if (self->slabbed_chars) free(self->slabbed_chars);
218 free(self);
219 return MLBUF_OK;
220 }
221
222 // Add a mark to this buffer and return it
buffer_add_mark(buffer_t * self,bline_t * maybe_line,bint_t maybe_col)223 mark_t *buffer_add_mark(buffer_t *self, bline_t *maybe_line, bint_t maybe_col) {
224 return buffer_add_mark_ex(self, '\0', maybe_line, maybe_col);
225 }
226
227 // If letter is [a-z], add a lettered mark and return it.
228 // If letter is \0, add a non-lettered mark and return it.
229 // Otherwise do nothing and return NULL.
buffer_add_mark_ex(buffer_t * self,char letter,bline_t * maybe_line,bint_t maybe_col)230 mark_t *buffer_add_mark_ex(buffer_t *self, char letter, bline_t *maybe_line, bint_t maybe_col) {
231 mark_t *mark;
232 mark_t *mark_tmp;
233 if (!((letter >= 'a' && letter <= 'z') || letter == '\0')) {
234 return NULL;
235 }
236 mark = calloc(1, sizeof(mark_t));
237 mark->letter = letter;
238 MLBUF_MAKE_GT_EQ0(maybe_col);
239 if (maybe_line != NULL) {
240 MLBUF_BLINE_ENSURE_CHARS(maybe_line);
241 mark->bline = maybe_line;
242 mark->col = maybe_col < 0 ? 0 : MLBUF_MIN(maybe_line->char_count, maybe_col);
243 } else {
244 mark->bline = self->first_line;
245 mark->col = 0;
246 }
247 DL_APPEND(mark->bline->marks, mark);
248 if (mark->letter) {
249 if ((mark_tmp = MLBUF_LETT_MARK(self, mark->letter)) != NULL) {
250 buffer_destroy_mark(self, mark_tmp);
251 }
252 MLBUF_LETT_MARK(self, mark->letter) = mark;
253 }
254 return mark;
255 }
256
257 // Return lettered mark or NULL if it does not exist
buffer_get_lettered_mark(buffer_t * self,char letter,mark_t ** ret_mark)258 int buffer_get_lettered_mark(buffer_t *self, char letter, mark_t **ret_mark) {
259 MLBUF_ENSURE_AZ(letter);
260 *ret_mark = MLBUF_LETT_MARK(self, letter);
261 return MLBUF_OK;
262 }
263
264 // Remove mark from buffer and free it, removing any range srules that use it
buffer_destroy_mark(buffer_t * self,mark_t * mark)265 int buffer_destroy_mark(buffer_t *self, mark_t *mark) {
266 srule_node_t *node;
267 srule_node_t *node_tmp;
268 DL_DELETE(mark->bline->marks, mark);
269 if (mark->letter) MLBUF_LETT_MARK(self, mark->letter) = NULL;
270 DL_FOREACH_SAFE(self->multi_srules, node, node_tmp) {
271 if (node->srule->type == MLBUF_SRULE_TYPE_RANGE
272 && (node->srule->range_a == mark
273 || node->srule->range_b == mark)
274 ) {
275 buffer_remove_srule(self, node->srule);
276 }
277 }
278 free(mark);
279 return MLBUF_OK;
280 }
281
282 // Get buffer contents and length
buffer_get(buffer_t * self,char ** ret_data,bint_t * ret_data_len)283 int buffer_get(buffer_t *self, char **ret_data, bint_t *ret_data_len) {
284 bline_t *bline;
285 char *data_cursor;
286 bint_t alloc_size;
287 if (self->is_data_dirty) {
288 // Refresh self->data
289 alloc_size = self->byte_count + 2;
290 self->data = self->data != NULL
291 ? realloc(self->data, alloc_size)
292 : malloc(alloc_size);
293 data_cursor = self->data;
294 for (bline = self->first_line; bline != NULL; bline = bline->next) {
295 if (bline->data_len > 0) {
296 memcpy(data_cursor, bline->data, bline->data_len);
297 data_cursor += bline->data_len;
298 self->data_len += bline->data_len;
299 }
300 if (bline->next) {
301 *data_cursor = '\n';
302 data_cursor += 1;
303 }
304 }
305 *data_cursor = '\0';
306 self->data_len = (bint_t)(data_cursor - self->data);
307 self->is_data_dirty = 0;
308 }
309 *ret_data = self->data;
310 *ret_data_len = self->data_len;
311 return MLBUF_OK;
312 }
313
buffer_clear(buffer_t * self)314 int buffer_clear(buffer_t *self) {
315 return buffer_delete(self, 0, self->byte_count);
316 }
317
318 // Set buffer contents
buffer_set(buffer_t * self,char * data,bint_t data_len)319 int buffer_set(buffer_t *self, char *data, bint_t data_len) {
320 int rc;
321 MLBUF_MAKE_GT_EQ0(data_len);
322 if ((rc = buffer_clear(self)) != MLBUF_OK) {
323 return rc;
324 }
325 rc = buffer_insert(self, 0, data, data_len, NULL);
326 if (self->actions) _buffer_truncate_undo_stack(self, self->actions);
327 return rc;
328 }
329
330 // Set buffer contents more efficiently
buffer_set_mmapped(buffer_t * self,char * data,bint_t data_len)331 int buffer_set_mmapped(buffer_t *self, char *data, bint_t data_len) {
332 bint_t nlines;
333 bint_t line_num;
334 bline_t *blines;
335 bint_t data_remaining_len;
336 char *data_cursor;
337 char *data_newline;
338 bint_t line_len;
339
340 if (buffer_clear(self) != MLBUF_OK) {
341 return MLBUF_ERR;
342 }
343
344 // Count number of lines
345 nlines = 1;
346 data_cursor = data;
347 data_remaining_len = data_len;
348 while (data_remaining_len > 0 && (data_newline = memchr(data_cursor, '\n', data_remaining_len)) != NULL) {
349 data_remaining_len -= (bint_t)(data_newline - data_cursor) + 1;
350 data_cursor = data_newline + 1;
351 nlines += 1;
352 }
353
354 // Allocate blines and chars. These are freed in buffer_destroy.
355 self->slabbed_chars = calloc(data_len, sizeof(bline_char_t));
356 self->slabbed_blines = malloc(nlines * sizeof(bline_t));
357 blines = self->slabbed_blines;
358
359 // Populate blines
360 line_num = 0;
361 data_cursor = data;
362 data_remaining_len = data_len;
363 while (1) {
364 data_newline = data_remaining_len > 0
365 ? memchr(data_cursor, '\n', data_remaining_len)
366 : NULL;
367 line_len = data_newline ?
368 (bint_t)(data_newline - data_cursor)
369 : data_remaining_len;
370 blines[line_num] = (bline_t){
371 .buffer = self,
372 .data = data_cursor,
373 .data_len = line_len,
374 .data_cap = line_len,
375 .line_index = line_num,
376 .char_count = line_len,
377 .char_vwidth = line_len,
378 .chars = (self->slabbed_chars + (data_len - data_remaining_len)),
379 .chars_cap = line_len,
380 .marks = NULL,
381 .bol_rule = NULL,
382 .eol_rule = NULL,
383 .is_chars_dirty = 1,
384 .is_slabbed = 1,
385 .is_data_slabbed = 1,
386 .next = NULL,
387 .prev = NULL
388 };
389 if (line_num > 0) {
390 blines[line_num-1].next = blines + line_num;
391 blines[line_num].prev = blines + (line_num-1);
392 }
393 if (data_newline) {
394 data_remaining_len -= line_len + 1;
395 data_cursor = data_newline + 1;
396 line_num += 1;
397 } else {
398 break;
399 }
400 }
401
402 if (self->first_line) _buffer_bline_free(self->first_line, NULL, 0);
403 self->first_line = blines;
404 self->last_line = blines + line_num;
405 self->byte_count = data_len;
406 self->line_count = line_num + 1;
407 self->is_data_dirty = 1;
408 return MLBUF_OK;
409 }
410
411 // Insert data into buffer given a buffer offset
buffer_insert(buffer_t * self,bint_t offset,char * data,bint_t data_len,bint_t * optret_num_chars)412 int buffer_insert(buffer_t *self, bint_t offset, char *data, bint_t data_len, bint_t *optret_num_chars) {
413 int rc;
414 bline_t *start_line;
415 bint_t start_col;
416 MLBUF_MAKE_GT_EQ0(offset);
417
418 // Find start line and col
419 if ((rc = buffer_get_bline_col(self, offset, &start_line, &start_col)) != MLBUF_OK) {
420 return rc;
421 }
422
423 return buffer_insert_w_bline(self, start_line, start_col, data, data_len, optret_num_chars);
424 }
425
426 // Insert data into buffer given a bline/col
buffer_insert_w_bline(buffer_t * self,bline_t * start_line,bint_t start_col,char * data,bint_t data_len,bint_t * optret_num_chars)427 int buffer_insert_w_bline(buffer_t *self, bline_t *start_line, bint_t start_col, char *data, bint_t data_len, bint_t *optret_num_chars) {
428 bline_t *cur_line;
429 bint_t cur_col;
430 bline_t *new_line;
431 char *data_cursor;
432 char *data_newline;
433 bint_t data_remaining_len;
434 bint_t insert_len;
435 bint_t num_lines_added;
436 char *ins_data;
437 bint_t ins_data_len;
438 bint_t ins_data_nchars;
439 baction_t *action;
440 MLBUF_MAKE_GT_EQ0(data_len);
441
442 // Exit early if no data
443 if (data_len < 1) {
444 return MLBUF_OK;
445 }
446
447 // Insert lines
448 data_cursor = data;
449 data_remaining_len = data_len;
450 cur_line = start_line;
451 cur_col = start_col;
452 num_lines_added = 0;
453 while (data_remaining_len > 0 && (data_newline = memchr(data_cursor, '\n', data_remaining_len)) != NULL) {
454 insert_len = (bint_t)(data_newline - data_cursor);
455 new_line = _buffer_bline_break(cur_line, cur_col);
456 num_lines_added += 1;
457 if (insert_len > 0) {
458 _buffer_bline_insert(cur_line, cur_col, data_cursor, insert_len, 1);
459 }
460 data_remaining_len -= (data_newline - data_cursor) + 1;
461 data_cursor = data_newline + 1;
462 cur_line = new_line;
463 cur_col = 0;
464 }
465 if (data_remaining_len > 0) {
466 cur_col += _buffer_bline_insert(cur_line, cur_col, data_cursor, data_remaining_len, 1);
467 }
468
469 // Get inserted data
470 buffer_substr(self, start_line, start_col, cur_line, cur_col, &ins_data, &ins_data_len, &ins_data_nchars);
471
472 // Add baction
473 action = calloc(1, sizeof(baction_t));
474 action->type = MLBUF_BACTION_TYPE_INSERT;
475 action->buffer = self;
476 action->start_line = start_line;
477 action->start_line_index = start_line->line_index;
478 action->start_col = start_col;
479 action->maybe_end_line = cur_line;
480 action->maybe_end_line_index = action->start_line_index + num_lines_added;
481 action->maybe_end_col = cur_col;
482 action->byte_delta = (bint_t)ins_data_len;
483 action->char_delta = (bint_t)ins_data_nchars;
484 action->line_delta = (bint_t)num_lines_added;
485 action->data = ins_data;
486 action->data_len = ins_data_len;
487 _buffer_update(self, action);
488 if (optret_num_chars) *optret_num_chars = ins_data_nchars;
489
490 return MLBUF_OK;
491 }
492
493 // Delete data from buffer given an offset
buffer_delete(buffer_t * self,bint_t offset,bint_t num_chars)494 int buffer_delete(buffer_t *self, bint_t offset, bint_t num_chars) {
495 bline_t *start_line;
496 bint_t start_col;
497 MLBUF_MAKE_GT_EQ0(offset);
498 buffer_get_bline_col(self, offset, &start_line, &start_col);
499 return buffer_delete_w_bline(self, start_line, start_col, num_chars);
500 }
501
502 // Delete data from buffer given a bline/col
buffer_delete_w_bline(buffer_t * self,bline_t * start_line,bint_t start_col,bint_t num_chars)503 int buffer_delete_w_bline(buffer_t *self, bline_t *start_line, bint_t start_col, bint_t num_chars) {
504 bline_t *end_line;
505 bint_t end_col;
506 bline_t *tmp_line;
507 bline_t *swap_line;
508 bline_t *next_line;
509 bint_t tmp_len;
510 char *del_data;
511 bint_t del_data_len;
512 bint_t del_data_nchars;
513 bint_t num_lines_removed;
514 bint_t safe_num_chars;
515 bint_t orig_char_count;
516 baction_t *action;
517 MLBUF_MAKE_GT_EQ0(num_chars);
518
519 // Find end line and col
520 _buffer_find_end_pos(start_line, start_col, num_chars, &end_line, &end_col, &num_chars);
521
522 // Exit early if there is nothing to delete
523 MLBUF_BLINE_ENSURE_CHARS(self->last_line);
524 if (start_line == end_line && start_col >= end_col) {
525 return MLBUF_OK;
526 } else if (start_line == self->last_line && start_col == self->last_line->char_count) {
527 return MLBUF_OK;
528 }
529
530 // Get deleted data
531 buffer_substr(self, start_line, start_col, end_line, end_col, &del_data, &del_data_len, &del_data_nchars);
532
533 // Delete suffix starting at start_line:start_col
534 MLBUF_BLINE_ENSURE_CHARS(start_line);
535 safe_num_chars = MLBUF_MIN(num_chars, start_line->char_count - start_col);
536 if (safe_num_chars > 0) {
537 _buffer_bline_delete(start_line, start_col, safe_num_chars);
538 }
539
540 // Copy remaining portion of end_line to start_line:start_col
541 MLBUF_BLINE_ENSURE_CHARS(start_line);
542 orig_char_count = start_line->char_count;
543 if (start_line != end_line && (tmp_len = end_line->data_len - _buffer_bline_col_to_index(end_line, end_col)) > 0) {
544 _buffer_bline_insert(
545 start_line,
546 start_col,
547 end_line->data + (end_line->data_len - tmp_len),
548 tmp_len,
549 0
550 );
551 }
552
553 // Remove lines after start_line thru end_line
554 // Relocate marks to end of start_line
555 num_lines_removed = 0;
556 swap_line = end_line->next;
557 next_line = NULL;
558 tmp_line = start_line->next;
559 while (tmp_line != NULL && tmp_line != swap_line) {
560 next_line = tmp_line->next;
561 _buffer_bline_free(tmp_line, start_line, orig_char_count - end_col);
562 num_lines_removed += 1;
563 tmp_line = next_line;
564 }
565 start_line->next = swap_line;
566 if (swap_line) swap_line->prev = start_line;
567
568 // Add baction
569 action = calloc(1, sizeof(baction_t));
570 action->type = MLBUF_BACTION_TYPE_DELETE;
571 action->buffer = self;
572 action->start_line = start_line;
573 action->start_line_index = start_line->line_index;
574 action->start_col = start_col;
575 action->byte_delta = -1 * (bint_t)del_data_len;
576 action->char_delta = -1 * (bint_t)del_data_nchars;
577 action->line_delta = -1 * (bint_t)num_lines_removed;
578 action->data = del_data;
579 action->data_len = del_data_len;
580 _buffer_update(self, action);
581
582 return MLBUF_OK;
583 }
584
585 // Replace num_chars in buffer at offset with data
buffer_replace(buffer_t * self,bint_t offset,bint_t num_chars,char * data,bint_t data_len)586 int buffer_replace(buffer_t *self, bint_t offset, bint_t num_chars, char *data, bint_t data_len) {
587 int rc;
588 bline_t *start_line;
589 bint_t start_col;
590 MLBUF_MAKE_GT_EQ0(offset);
591
592 // Find start line and col
593 if ((rc = buffer_get_bline_col(self, offset, &start_line, &start_col)) != MLBUF_OK) {
594 return rc;
595 }
596
597 return buffer_replace_w_bline(self, start_line, start_col, num_chars, data, data_len);
598 }
599
600 // Replace num_chars from start_line:start_col with data
buffer_replace_w_bline(buffer_t * self,bline_t * start_line,bint_t start_col,bint_t del_chars,char * data,bint_t data_len)601 int buffer_replace_w_bline(buffer_t *self, bline_t *start_line, bint_t start_col, bint_t del_chars, char *data, bint_t data_len) {
602 bline_t *cur_line;
603 bint_t cur_col;
604 bint_t insert_rem;
605 bint_t delete_rem;
606 bint_t data_linelen;
607 bint_t nchars_ins;
608 bint_t nlines;
609 char *data_cursor;
610 char *data_newline;
611 baction_t *action;
612 str_t del_data = {0};
613
614 // Replace data on common lines
615 insert_rem = data_len;
616 delete_rem = del_chars;
617 cur_line = start_line;
618 cur_col = start_col;
619 data_cursor = data;
620 nchars_ins = 0;
621 nlines = 0;
622 MLBUF_BLINE_ENSURE_CHARS(cur_line);
623 while (insert_rem > 0 && delete_rem > (cur_line->char_count - cur_col)) {
624 data_newline = memchr(data_cursor, '\n', insert_rem);
625 data_linelen = data_newline ? (bint_t)(data_newline - data_cursor) : insert_rem;
626 delete_rem -= cur_line->char_count - cur_col;
627 _buffer_bline_replace(cur_line, cur_col, data_cursor, data_linelen, &del_data);
628 nchars_ins += cur_line->char_count - cur_col;
629 insert_rem -= data_linelen;
630 data_cursor += data_linelen;
631 cur_col = cur_line->char_count;
632 if (data_newline && insert_rem >= 2 && delete_rem >= 2 && cur_line->next) {
633 data_cursor += 1;
634 insert_rem -= 1;
635 delete_rem -= 1;
636 cur_line = cur_line->next;
637 cur_col = 0;
638 nchars_ins += 1;
639 str_append_len(&del_data, "\n", 1);
640 } else {
641 break;
642 }
643 nlines += 1;
644 MLBUF_BLINE_ENSURE_CHARS(cur_line);
645 }
646
647 // Add delete baction
648 if (del_data.len > 0) {
649 action = calloc(1, sizeof(baction_t));
650 action->type = MLBUF_BACTION_TYPE_DELETE;
651 action->buffer = self;
652 action->start_line = start_line;
653 action->start_line_index = start_line->line_index;
654 action->start_col = start_col;
655 action->byte_delta = -1 * (bint_t)del_data.len;
656 action->char_delta = -1 * (del_chars - delete_rem);
657 action->line_delta = -1 * nlines;
658 action->data = del_data.data;
659 action->data_len = (bint_t)del_data.len;
660 _buffer_update(self, action);
661 }
662
663 // Add insert baction
664 if (data_len - insert_rem > 0) {
665 action = calloc(1, sizeof(baction_t));
666 action->type = MLBUF_BACTION_TYPE_INSERT;
667 action->buffer = self;
668 action->start_line = start_line;
669 action->start_line_index = start_line->line_index;
670 action->start_col = start_col;
671 action->maybe_end_line = cur_line;
672 action->maybe_end_line_index = action->start_line_index + nlines;
673 action->maybe_end_col = cur_col;
674 action->byte_delta = data_len - insert_rem;
675 action->char_delta = nchars_ins;
676 action->line_delta = nlines;
677 action->data = strndup(data, action->byte_delta);
678 action->data_len = action->byte_delta;
679 _buffer_update(self, action);
680 }
681
682 // Delete left over data
683 if (delete_rem > 0) {
684 buffer_delete_w_bline(self, cur_line, cur_col, delete_rem);
685 }
686
687 // Insert left over data
688 if (insert_rem > 0) {
689 buffer_insert_w_bline(self, cur_line, cur_col, data_cursor, insert_rem, NULL);
690 }
691
692 return MLBUF_OK;
693 }
694
695 // Return a line given a line_index
buffer_get_bline(buffer_t * self,bint_t line_index,bline_t ** ret_bline)696 int buffer_get_bline(buffer_t *self, bint_t line_index, bline_t **ret_bline) {
697 bline_t *tmp_line;
698 MLBUF_MAKE_GT_EQ0(line_index);
699 for (tmp_line = self->first_line; tmp_line; tmp_line = tmp_line->next) {
700 if (tmp_line->line_index == line_index) {
701 *ret_bline = tmp_line;
702 return MLBUF_OK;
703 }
704 }
705 *ret_bline = self->last_line;
706 return MLBUF_ERR;
707 }
708
709 // Return a line and col for the given offset
buffer_get_bline_col(buffer_t * self,bint_t offset,bline_t ** ret_bline,bint_t * ret_col)710 int buffer_get_bline_col(buffer_t *self, bint_t offset, bline_t **ret_bline, bint_t *ret_col) {
711 bline_t *tmp_line;
712 bline_t *good_line = NULL;
713 bint_t remaining_chars;
714 MLBUF_MAKE_GT_EQ0(offset);
715
716 remaining_chars = offset;
717 for (tmp_line = self->first_line; tmp_line != NULL; tmp_line = tmp_line->next) {
718 MLBUF_BLINE_ENSURE_CHARS(tmp_line);
719 if (tmp_line->char_count >= remaining_chars) {
720 *ret_bline = tmp_line;
721 *ret_col = remaining_chars;
722 return MLBUF_OK;
723 } else {
724 remaining_chars -= (tmp_line->char_count + 1); // Plus 1 for newline
725 }
726 good_line = tmp_line;
727 }
728
729 if (!good_line) good_line = self->first_line;
730 *ret_bline = good_line;
731 *ret_col = good_line->char_count;
732 return MLBUF_OK;
733 }
734
735 // Return an offset given a line and col
buffer_get_offset(buffer_t * self,bline_t * bline,bint_t col,bint_t * ret_offset)736 int buffer_get_offset(buffer_t *self, bline_t *bline, bint_t col, bint_t *ret_offset) {
737 bline_t *tmp_line;
738 bint_t offset;
739 MLBUF_MAKE_GT_EQ0(col);
740
741 offset = 0;
742 for (tmp_line = self->first_line; tmp_line != bline->next; tmp_line = tmp_line->next) {
743 MLBUF_BLINE_ENSURE_CHARS(tmp_line);
744 if (tmp_line == bline) {
745 offset += MLBUF_MIN(tmp_line->char_count, col);
746 break;
747 } else {
748 offset += tmp_line->char_count + 1; // Plus 1 for newline
749 }
750 }
751
752 *ret_offset = offset;
753 return MLBUF_OK;
754 }
755
756 // Add a style rule to the buffer
buffer_add_srule(buffer_t * self,srule_t * srule)757 int buffer_add_srule(buffer_t *self, srule_t *srule) {
758 srule_node_t *node;
759 node = calloc(1, sizeof(srule_node_t));
760 node->srule = srule;
761 if (srule->type == MLBUF_SRULE_TYPE_SINGLE) {
762 DL_APPEND(self->single_srules, node);
763 } else {
764 DL_APPEND(self->multi_srules, node);
765 }
766 if (srule->type == MLBUF_SRULE_TYPE_RANGE) {
767 srule->range_a->range_srule = srule;
768 srule->range_b->range_srule = srule;
769 }
770 return buffer_apply_styles(self, self->first_line, self->line_count - 1);
771 }
772
773 // Remove a style rule from the buffer
buffer_remove_srule(buffer_t * self,srule_t * srule)774 int buffer_remove_srule(buffer_t *self, srule_t *srule) {
775 int found;
776 srule_node_t **head;
777 srule_node_t *node;
778 srule_node_t *node_tmp;
779 if (srule->type == MLBUF_SRULE_TYPE_SINGLE) {
780 head = &self->single_srules;
781 } else {
782 head = &self->multi_srules;
783 }
784 found = 0;
785 DL_FOREACH_SAFE(*head, node, node_tmp) {
786 if (node->srule != srule) continue;
787 if (srule->type == MLBUF_SRULE_TYPE_RANGE) {
788 srule->range_a->range_srule = NULL;
789 srule->range_b->range_srule = NULL;
790 }
791 DL_DELETE(*head, node);
792 free(node);
793 found = 1;
794 break;
795 }
796 if (!found) return MLBUF_ERR;
797 return buffer_apply_styles(self, self->first_line, self->line_count - 1);
798 }
799
800 // Set callback to cb. Pass in NULL to unset callback.
buffer_set_callback(buffer_t * self,buffer_callback_t cb,void * udata)801 int buffer_set_callback(buffer_t *self, buffer_callback_t cb, void *udata) {
802 if (cb) {
803 self->callback = cb;
804 self->callback_udata = udata;
805 } else {
806 self->callback = NULL;
807 self->callback_udata = NULL;
808 }
809 return MLBUF_OK;
810 }
811
812 // Set tab_width and recalculate all line char vwidths
buffer_set_tab_width(buffer_t * self,int tab_width)813 int buffer_set_tab_width(buffer_t *self, int tab_width) {
814 bline_t *tmp_line;
815 if (tab_width < 1) {
816 return MLBUF_ERR;
817 }
818 self->tab_width = tab_width;
819 for (tmp_line = self->first_line; tmp_line; tmp_line = tmp_line->next) {
820 bline_count_chars(tmp_line);
821 }
822 return MLBUF_OK;
823 }
824
825 // Return data from start_line:start_col thru end_line:end_col
buffer_substr(buffer_t * self,bline_t * start_line,bint_t start_col,bline_t * end_line,bint_t end_col,char ** ret_data,bint_t * ret_data_len,bint_t * ret_data_nchars)826 int buffer_substr(buffer_t *self, bline_t *start_line, bint_t start_col, bline_t *end_line, bint_t end_col, char **ret_data, bint_t *ret_data_len, bint_t *ret_data_nchars) {
827 char *data;
828 bint_t data_len;
829 bint_t data_size;
830 bline_t *tmp_line;
831 bint_t copy_len;
832 bint_t copy_index;
833 bint_t add_len;
834 bint_t nchars;
835 MLBUF_MAKE_GT_EQ0(start_col);
836 MLBUF_MAKE_GT_EQ0(end_col);
837
838 data = calloc(2, sizeof(char));
839 data_len = 0;
840 data_size = 2;
841 nchars = 0;
842
843 for (tmp_line = start_line; tmp_line != end_line->next; tmp_line = tmp_line->next) {
844 // Get copy_index + copy_len
845 // Also increment nchars
846 if (start_line == end_line) {
847 copy_index = _buffer_bline_col_to_index(start_line, start_col);
848 copy_len = _buffer_bline_col_to_index(start_line, end_col) - copy_index;
849 nchars += end_col - start_col;
850 } else if (tmp_line == start_line) {
851 copy_index = _buffer_bline_col_to_index(start_line, start_col);
852 copy_len = tmp_line->data_len - copy_index;
853 MLBUF_BLINE_ENSURE_CHARS(start_line);
854 nchars += start_line->char_count - start_col;
855 } else if (tmp_line == end_line) {
856 copy_index = 0;
857 copy_len = _buffer_bline_col_to_index(end_line, end_col);
858 nchars += end_col;
859 } else {
860 copy_index = 0;
861 copy_len = tmp_line->data_len;
862 MLBUF_BLINE_ENSURE_CHARS(tmp_line);
863 nchars += tmp_line->char_count;
864 }
865
866 // Add 1 for newline if not on end_line
867 add_len = copy_len + (tmp_line != end_line ? 1 : 0); // Plus 1 for newline
868 nchars += (tmp_line != end_line ? 1 : 0);
869
870 // Copy add_len bytes from copy_index into data
871 if (add_len > 0) {
872 if (data_len + add_len + 1 > data_size) {
873 data_size = data_len + add_len + 1; // Plus 1 for nullchar
874 data = realloc(data, data_size);
875 }
876 if (copy_len > 0) {
877 memcpy(data + data_len, tmp_line->data + copy_index, copy_len);
878 data_len += copy_len;
879 }
880 if (tmp_line != end_line) {
881 *(data + data_len) = '\n';
882 data_len += 1;
883 }
884 }
885 }
886
887 *(data + data_len) = '\0';
888 *ret_data = data;
889 *ret_data_len = data_len;
890 *ret_data_nchars = nchars;
891
892 return MLBUF_OK;
893 }
894
895 // Undo an action
buffer_undo(buffer_t * self)896 int buffer_undo(buffer_t *self) {
897 baction_t *action_to_undo;
898 bline_t *bline;
899 int rc;
900
901 // Find action to undo
902 if (self->action_undone) {
903 if (self->action_undone == self->actions) {
904 return MLBUF_ERR;
905 } else if (!self->action_undone->prev) {
906 return MLBUF_ERR;
907 }
908 action_to_undo = self->action_undone->prev;
909 } else if (self->action_tail) {
910 action_to_undo = self->action_tail;
911 } else {
912 return MLBUF_ERR;
913 }
914
915 // Get line to perform undo on
916 bline = NULL;
917 buffer_get_bline(self, action_to_undo->start_line_index, &bline);
918 MLBUF_BLINE_ENSURE_CHARS(bline);
919 if (!bline) {
920 return MLBUF_ERR;
921 } else if (action_to_undo->start_col > bline->char_count) {
922 return MLBUF_ERR;
923 }
924
925 // Perform action
926 rc = _buffer_baction_do(self, bline, action_to_undo, 0, NULL);
927
928 // Update action_undone
929 if (rc == MLBUF_OK) {
930 self->action_undone = action_to_undo;
931 }
932 return rc;
933 }
934
935 // Redo an undone action
buffer_redo(buffer_t * self)936 int buffer_redo(buffer_t *self) {
937 baction_t *action_to_redo;
938 bline_t *bline;
939 int rc;
940
941 // Find action to undo
942 if (!self->action_undone) {
943 return MLBUF_ERR;
944 }
945 action_to_redo = self->action_undone;
946
947 // Get line to perform undo on
948 bline = NULL;
949 buffer_get_bline(self, action_to_redo->start_line_index, &bline);
950 MLBUF_BLINE_ENSURE_CHARS(bline);
951 if (!bline) {
952 return MLBUF_ERR;
953 } else if (action_to_redo->start_col > bline->char_count) {
954 return MLBUF_ERR;
955 }
956
957 // Perform action
958 rc = _buffer_baction_do(self, bline, action_to_redo, 1, NULL);
959
960 // Update action_undone
961 if (rc == MLBUF_OK) {
962 self->action_undone = self->action_undone->next;
963 }
964 return rc;
965 }
966
967 // Toggle is_style_disabled
buffer_set_styles_enabled(buffer_t * self,int is_enabled)968 int buffer_set_styles_enabled(buffer_t *self, int is_enabled) {
969 if (!self->is_style_disabled && !is_enabled) {
970 self->is_style_disabled = 1;
971 } else if (self->is_style_disabled && is_enabled) {
972 self->is_style_disabled = 0;
973 buffer_apply_styles(self, self->first_line, self->line_count);
974 }
975 return MLBUF_OK;
976 }
977
978 // Apply styles from start_line
buffer_apply_styles(buffer_t * self,bline_t * start_line,bint_t line_delta)979 int buffer_apply_styles(buffer_t *self, bline_t *start_line, bint_t line_delta) {
980 bint_t min_nlines;
981 srule_node_t *srule_node;
982 int count_tmp;
983 int srule_count;
984
985 if (self->is_style_disabled) {
986 return MLBUF_OK;
987 }
988
989 // min_nlines, minimum number of lines to style
990 // line_delta < 0: 2 (start_line + 1)
991 // line_delta == 0: 1 (start_line)
992 // line_delta > 0: 1 + line_delta (start_line + added lines)
993 min_nlines = 1 + (line_delta < 0 ? 1 : line_delta);
994
995 // Count current srules
996 srule_count = 0;
997 DL_COUNT(self->single_srules, srule_node, count_tmp);
998 srule_count += count_tmp;
999 DL_COUNT(self->multi_srules, srule_node, count_tmp);
1000 srule_count += count_tmp;
1001
1002 // Apply rules if there are any, or if the number of rules changed
1003 if (srule_count > 0 || self->num_applied_srules != srule_count) {
1004 _buffer_apply_styles_singles(start_line, min_nlines);
1005 _buffer_apply_styles_multis(start_line, min_nlines, MLBUF_SRULE_TYPE_MULTI);
1006 _buffer_apply_styles_multis(start_line, min_nlines, MLBUF_SRULE_TYPE_RANGE);
1007 self->num_applied_srules = srule_count;
1008 }
1009
1010 return MLBUF_OK;
1011 }
1012
1013 // Set register
buffer_register_set(buffer_t * self,char reg,char * data,size_t data_len)1014 int buffer_register_set(buffer_t *self, char reg, char *data, size_t data_len) {
1015 MLBUF_ENSURE_AZ(reg);
1016 str_set_len(MLBUF_REG_PTR(self, reg), data, data_len);
1017 return MLBUF_OK;
1018 }
1019
1020 // Append to register
buffer_register_append(buffer_t * self,char reg,char * data,size_t data_len)1021 int buffer_register_append(buffer_t *self, char reg, char *data, size_t data_len) {
1022 MLBUF_ENSURE_AZ(reg);
1023 str_append_len(MLBUF_REG_PTR(self, reg), data, data_len);
1024 return MLBUF_OK;
1025 }
1026
1027 // Prepend to register
buffer_register_prepend(buffer_t * self,char reg,char * data,size_t data_len)1028 int buffer_register_prepend(buffer_t *self, char reg, char *data, size_t data_len) {
1029 MLBUF_ENSURE_AZ(reg);
1030 str_prepend_len(MLBUF_REG_PTR(self, reg), data, data_len);
1031 return MLBUF_OK;
1032 }
1033
1034 // Clear register
buffer_register_clear(buffer_t * self,char reg)1035 int buffer_register_clear(buffer_t *self, char reg) {
1036 MLBUF_ENSURE_AZ(reg);
1037 str_free(MLBUF_REG_PTR(self, reg));
1038 return MLBUF_OK;
1039 }
1040
1041 // Get register, optionally allocating duplicate
buffer_register_get(buffer_t * self,char reg,int dup,char ** ret_data,size_t * ret_data_len)1042 int buffer_register_get(buffer_t *self, char reg, int dup, char **ret_data, size_t *ret_data_len) {
1043 str_t *sreg;
1044 MLBUF_ENSURE_AZ(reg);
1045 sreg = MLBUF_REG_PTR(self, reg);
1046 if (dup) {
1047 *ret_data = strndup(sreg->data, sreg->len);
1048 *ret_data_len = strlen(*ret_data);
1049 } else {
1050 *ret_data = sreg->len > 0 ? sreg->data : "";
1051 *ret_data_len = sreg->len;
1052 }
1053 return MLBUF_OK;
1054 }
1055
_buffer_open_mmap(buffer_t * self,int fd,size_t size)1056 static int _buffer_open_mmap(buffer_t *self, int fd, size_t size) {
1057 char tmppath[16];
1058 int tmpfd;
1059 char readbuf[1024];
1060 ssize_t nread;
1061 char *mmap_buf;
1062
1063 // Copy fd to tmp file
1064 sprintf(tmppath, "%s", "/tmp/mle-XXXXXX");
1065 tmpfd = mkstemp(tmppath);
1066 if (tmpfd < 0) {
1067 return MLBUF_ERR;
1068 }
1069 unlink(tmppath);
1070 while (1) {
1071 nread = read(fd, &readbuf, 1024);
1072 if (nread == 0) {
1073 break;
1074 } else if (nread < 0) {
1075 close(tmpfd);
1076 return MLBUF_ERR;
1077 }
1078 if (write(tmpfd, readbuf, nread) != nread) {
1079 close(tmpfd);
1080 return MLBUF_ERR;
1081 }
1082 }
1083
1084 // Now mmap tmp file
1085 mmap_buf = mmap(NULL, size, PROT_READ, MAP_PRIVATE, tmpfd, 0);
1086 if (mmap_buf == MAP_FAILED) {
1087 return MLBUF_ERR;
1088 } else if (buffer_set_mmapped(self, mmap_buf, (bint_t)size) != MLBUF_OK) {
1089 return MLBUF_ERR;
1090 }
1091
1092 _buffer_munmap(self);
1093 self->mmap = mmap_buf;
1094 self->mmap_len = size;
1095 self->mmap_fd = tmpfd;
1096 return MLBUF_OK;
1097 }
1098
_buffer_open_read(buffer_t * self,int fd,size_t size)1099 static int _buffer_open_read(buffer_t *self, int fd, size_t size) {
1100 int rc;
1101 char *buf;
1102 if (size <= PTRDIFF_MAX) {
1103 buf = malloc(size);
1104 } else {
1105 return MLBUF_ERR;
1106 }
1107 rc = MLBUF_OK;
1108 if (size != (size_t)read(fd, buf, size)) {
1109 rc = MLBUF_ERR;
1110 } else if (buffer_set(self, buf, (bint_t)size) != MLBUF_OK) {
1111 rc = MLBUF_ERR;
1112 }
1113 free(buf);
1114 return rc;
1115 }
1116
_buffer_bline_unslab(bline_t * self)1117 static int _buffer_bline_unslab(bline_t *self) {
1118 char *data;
1119 bline_char_t *chars;
1120 if (!self->is_data_slabbed) {
1121 return MLBUF_ERR;
1122 }
1123 data = malloc(self->data_len);
1124 chars = malloc(self->data_len * sizeof(bline_char_t));
1125 memcpy(data, self->data, self->data_len);
1126 memcpy(chars, self->chars, self->data_len * sizeof(bline_char_t));
1127 self->data = data;
1128 self->data_cap = self->data_len;
1129 self->chars = chars;
1130 self->chars_cap = self->data_len;
1131 self->is_data_slabbed = 0;
1132 return bline_count_chars(self);
1133 }
1134
_buffer_stat(buffer_t * self)1135 static void _buffer_stat(buffer_t *self) {
1136 if (!self->path) {
1137 return;
1138 }
1139 stat(self->path, &self->st); // TODO err?
1140 }
1141
_buffer_baction_do(buffer_t * self,bline_t * bline,baction_t * action,int is_redo,bint_t * opt_repeat_offset)1142 static int _buffer_baction_do(buffer_t *self, bline_t *bline, baction_t *action, int is_redo, bint_t *opt_repeat_offset) {
1143 int rc;
1144 bint_t col;
1145 bint_t offset;
1146 self->_is_in_undo = 1;
1147 col = opt_repeat_offset ? *opt_repeat_offset : action->start_col;
1148 buffer_get_offset(self, bline, col, &offset);
1149 if ((action->type == MLBUF_BACTION_TYPE_DELETE && is_redo)
1150 || (action->type == MLBUF_BACTION_TYPE_INSERT && !is_redo)
1151 ) {
1152 rc = buffer_delete(self, offset, (bint_t)((is_redo ? -1 : 1) * action->char_delta));
1153 } else {
1154 rc = buffer_insert(self, offset, action->data, action->data_len, NULL);
1155 }
1156 self->_is_in_undo = 0;
1157 return rc;
1158 }
1159
_buffer_update(buffer_t * self,baction_t * action)1160 static int _buffer_update(buffer_t *self, baction_t *action) {
1161 bline_t *tmp_line;
1162 bline_t *last_line;
1163 bint_t new_line_index;
1164
1165 // Adjust counts
1166 self->byte_count += action->byte_delta;
1167 self->line_count += action->line_delta;
1168 self->is_data_dirty = 1;
1169
1170 // Set unsaved
1171 self->is_unsaved = 1;
1172
1173 // Renumber lines
1174 if (action->line_delta != 0) {
1175 last_line = NULL;
1176 new_line_index = action->start_line->line_index;
1177 for (tmp_line = action->start_line->next; tmp_line != NULL; tmp_line = tmp_line->next) {
1178 tmp_line->line_index = ++new_line_index;
1179 last_line = tmp_line;
1180 }
1181 self->last_line = last_line ? last_line : action->start_line;
1182 }
1183
1184 // Restyle from start_line
1185 buffer_apply_styles(self, action->start_line, action->line_delta);
1186
1187 // Raise event on listener
1188 if (self->callback && !self->is_in_callback) {
1189 self->is_in_callback = 1;
1190 self->callback(self, action, self->callback_udata);
1191 self->is_in_callback = 0;
1192 }
1193
1194 // Handle undo stack
1195 if (self->_is_in_undo) {
1196 _baction_destroy(action);
1197 } else {
1198 _buffer_add_to_undo_stack(self, action);
1199 }
1200
1201 return MLBUF_OK;
1202 }
1203
_buffer_truncate_undo_stack(buffer_t * self,baction_t * action_from)1204 static int _buffer_truncate_undo_stack(buffer_t *self, baction_t *action_from) {
1205 baction_t *action_target;
1206 baction_t *action_tmp;
1207 int do_delete;
1208 self->action_tail = action_from->prev != action_from ? action_from->prev : NULL;
1209 do_delete = 0;
1210 DL_FOREACH_SAFE(self->actions, action_target, action_tmp) {
1211 if (!do_delete && action_target == action_from) {
1212 do_delete = 1;
1213 }
1214 if (do_delete) {
1215 DL_DELETE(self->actions, action_target);
1216 _baction_destroy(action_target);
1217 }
1218 }
1219 return MLBUF_OK;
1220 }
1221
_buffer_add_to_undo_stack(buffer_t * self,baction_t * action)1222 static int _buffer_add_to_undo_stack(buffer_t *self, baction_t *action) {
1223 if (self->action_undone) {
1224 // We are recording an action after an undo has been performed, so we
1225 // need to chop off the tail of the baction list before recording the
1226 // new one.
1227 // TODO could implement multilevel undo here instead
1228 _buffer_truncate_undo_stack(self, self->action_undone);
1229 self->action_undone = NULL;
1230 }
1231
1232 // Append action to list
1233 DL_APPEND(self->actions, action);
1234 self->action_tail = action;
1235 return MLBUF_OK;
1236 }
1237
_buffer_apply_styles_singles(bline_t * start_line,bint_t min_nlines)1238 static int _buffer_apply_styles_singles(bline_t *start_line, bint_t min_nlines) {
1239 bline_t *cur_line;
1240 srule_node_t *srule_node;
1241 bint_t styled_nlines;
1242 bint_t i;
1243
1244 // Apply styles starting at start_line
1245 cur_line = start_line;
1246 styled_nlines = 0;
1247 while (cur_line && styled_nlines < min_nlines) {
1248 // Reset styles of cur_line
1249 for (i = 0; i < cur_line->data_cap; i++) {
1250 cur_line->chars[i].style = (sblock_t){0, 0};
1251 }
1252
1253 // Apply single-line styles to cur_line
1254 if (cur_line->data_len > 0) {
1255 DL_FOREACH(start_line->buffer->single_srules, srule_node) {
1256 _buffer_bline_apply_style_single(srule_node->srule, cur_line);
1257 }
1258 }
1259
1260 // Done styling cur_line; increment styled_nlines
1261 styled_nlines += 1;
1262
1263 // Continue to next line
1264 cur_line = cur_line->next;
1265 } // end while (cur_line)
1266
1267 return MLBUF_OK;
1268 }
1269
_buffer_apply_styles_multis(bline_t * start_line,bint_t min_nlines,int srule_type)1270 static int _buffer_apply_styles_multis(bline_t *start_line, bint_t min_nlines, int srule_type) {
1271 bline_t *cur_line;
1272 srule_node_t *srule_node;
1273 srule_t *open_rule;
1274 bint_t styled_nlines;
1275 bint_t multi_look_offset;
1276 int open_rule_ended;
1277 int already_open;
1278
1279 // Apply styles starting at start_line
1280 cur_line = start_line;
1281 open_rule = NULL;
1282 styled_nlines = 0;
1283 open_rule_ended = 0;
1284 multi_look_offset = 0;
1285 while (cur_line) {
1286 if (cur_line->prev && cur_line->prev->eol_rule && !open_rule && !open_rule_ended) {
1287 // Resume open_rule from previous line
1288 open_rule = cur_line->prev->eol_rule;
1289 }
1290 if (!open_rule_ended) {
1291 multi_look_offset = 0;
1292 }
1293 if (open_rule) {
1294 // Apply open_rule to cur_line
1295 already_open = cur_line->eol_rule == open_rule ? 1 : 0;
1296 _buffer_bline_apply_style_multi(open_rule, cur_line, &open_rule, &multi_look_offset);
1297 if (open_rule) {
1298 // open_rule is still open
1299 if (styled_nlines > min_nlines && already_open) {
1300 // We are past min_nlines and styles have not changed; done
1301 break;
1302 }
1303 } else {
1304 // open_rule ended on this line; resume normal styling on same line
1305 open_rule_ended = 1;
1306 continue;
1307 }
1308 } else {
1309 if (srule_type == MLBUF_SRULE_TYPE_MULTI) {
1310 // Re-apply single line rules if a multi-line rule was resolved
1311 if (cur_line->prev && cur_line->bol_rule != cur_line->prev->eol_rule) {
1312 _buffer_apply_styles_singles(cur_line, 1);
1313 }
1314 // Reset bol_rule and eol_rule
1315 if (!open_rule_ended) cur_line->bol_rule = NULL;
1316 cur_line->eol_rule = NULL;
1317 }
1318 // Apply multi-line styles to cur_line
1319 DL_FOREACH(start_line->buffer->multi_srules, srule_node) {
1320 if (srule_node->srule->type != srule_type) continue;
1321 _buffer_bline_apply_style_multi(srule_node->srule, cur_line, &open_rule, &multi_look_offset);
1322 multi_look_offset = 0;
1323 if (open_rule) break; // We have an open_rule; break
1324 }
1325 }
1326
1327 // Done styling cur_line; increment styled_nlines
1328 styled_nlines += 1;
1329
1330 // If there is no open_rule and we are past min_nlines, we are done.
1331 if (!open_rule && (!cur_line->next || !cur_line->next->bol_rule) && styled_nlines > min_nlines) {
1332 break;
1333 }
1334
1335 // Reset open_rule_ended flag
1336 if (open_rule_ended) open_rule_ended = 0;
1337
1338 // Continue to next line
1339 cur_line = cur_line->next;
1340 } // end while (cur_line)
1341
1342 return MLBUF_OK;
1343 }
1344
_buffer_bline_apply_style_single(srule_t * srule,bline_t * bline)1345 static int _buffer_bline_apply_style_single(srule_t *srule, bline_t *bline) {
1346 int rc;
1347 int substrs[3];
1348 bint_t start;
1349 bint_t stop;
1350 bint_t look_offset;
1351 look_offset = 0;
1352
1353 MLBUF_BLINE_ENSURE_CHARS(bline);
1354 while (look_offset < bline->data_len) {
1355 if ((rc = pcre_exec(srule->cre, srule->crex, bline->data, bline->data_len, look_offset, 0, substrs, 3)) >= 0) {
1356 if (substrs[1] < 0) {
1357 // substrs[0..1] can be -1 sometimes, See http://pcre.org/pcre.txt
1358 break;
1359 }
1360 start = _buffer_bline_index_to_col(bline, substrs[0]);
1361 stop = _buffer_bline_index_to_col(bline, substrs[1]);
1362 for (; start < stop; start++) {
1363 bline->chars[start].style = srule->style;
1364 }
1365 look_offset = MLBUF_MAX(substrs[1], look_offset + 1);
1366 } else {
1367 break;
1368 }
1369 }
1370 return MLBUF_OK;
1371 }
1372
_buffer_bline_apply_style_multi(srule_t * srule,bline_t * bline,srule_t ** open_rule,bint_t * look_offset)1373 static int _buffer_bline_apply_style_multi(srule_t *srule, bline_t *bline, srule_t **open_rule, bint_t *look_offset) {
1374 bint_t start;
1375 bint_t start_stop;
1376 bint_t end;
1377 int found_start;
1378 int found_end;
1379
1380 if (srule->type == MLBUF_SRULE_TYPE_RANGE
1381 && mark_is_eq(srule->range_a, srule->range_b)
1382 ) {
1383 // Empty range rule
1384 return MLBUF_OK;
1385 }
1386
1387 MLBUF_BLINE_ENSURE_CHARS(bline);
1388
1389 do {
1390 found_start = 0;
1391 found_end = 0;
1392 if (*open_rule == NULL) {
1393 // Look for start and end of rule
1394 if ((found_start = _srule_multi_find_start(srule, bline, *look_offset, &start, &start_stop))) {
1395 *look_offset = start_stop;
1396 found_end = *look_offset < bline->char_count
1397 ? _srule_multi_find_end(srule, bline, *look_offset, &end)
1398 : 0;
1399 if (found_end) *look_offset = end;
1400 } else {
1401 return MLBUF_OK; // No match; bail
1402 }
1403 } else {
1404 // Look for end of rule
1405 start = 0;
1406 bline->bol_rule = srule;
1407 found_end = _srule_multi_find_end(srule, bline, *look_offset, &end);
1408 }
1409
1410 // Set start, end, bol_rule, and eol_rule
1411 if (!found_end) {
1412 end = bline->char_count; // Style until eol
1413 bline->eol_rule = srule; // Set eol_rule
1414 *open_rule = srule;
1415 } else if (*open_rule != NULL) {
1416 *open_rule = NULL;
1417 }
1418
1419 // Write styles
1420 for (; start < end; start++) {
1421 bline->chars[start].style = srule->style;
1422 }
1423
1424 // Range rules can only match once
1425 if (srule->type == MLBUF_SRULE_TYPE_RANGE) {
1426 break;
1427 }
1428 } while (found_start && found_end && *look_offset < bline->char_count);
1429
1430 return MLBUF_OK;
1431 }
1432
_buffer_bline_new(buffer_t * self)1433 static bline_t *_buffer_bline_new(buffer_t *self) {
1434 bline_t *bline;
1435 bline = calloc(1, sizeof(bline_t));
1436 bline->buffer = self;
1437 return bline;
1438 }
1439
_buffer_bline_free(bline_t * bline,bline_t * maybe_mark_line,bint_t col_delta)1440 static int _buffer_bline_free(bline_t *bline, bline_t *maybe_mark_line, bint_t col_delta) {
1441 mark_t *mark;
1442 mark_t *mark_tmp;
1443 if (!bline->is_data_slabbed) {
1444 if (bline->data) free(bline->data);
1445 if (bline->chars) free(bline->chars);
1446 }
1447 if (bline->marks) {
1448 DL_FOREACH_SAFE(bline->marks, mark, mark_tmp) {
1449 if (maybe_mark_line) {
1450 _mark_mark_move_inner(mark, maybe_mark_line, mark->col + col_delta, 1, 0);
1451 } else {
1452 buffer_destroy_mark(bline->buffer, mark);
1453 }
1454 }
1455 }
1456 if (!bline->is_slabbed) {
1457 free(bline);
1458 }
1459 return MLBUF_OK;
1460 }
1461
_buffer_bline_break(bline_t * bline,bint_t col)1462 static bline_t *_buffer_bline_break(bline_t *bline, bint_t col) {
1463 bint_t index;
1464 bint_t len;
1465 bline_t *new_line;
1466 bline_t *tmp_line;
1467 mark_t *mark;
1468 mark_t *mark_tmp;
1469
1470 // Unslab if needed
1471 if (bline->is_data_slabbed) _buffer_bline_unslab(bline);
1472
1473 // Make new_line
1474 new_line = _buffer_bline_new(bline->buffer);
1475
1476 // Find byte index to break on
1477 index = _buffer_bline_col_to_index(bline, col);
1478 len = bline->data_len - index;
1479
1480 if (len > 0) {
1481 // Move data to new line
1482 new_line->data = malloc(len);
1483 memcpy(new_line->data, bline->data + index, len);
1484 new_line->data_len = len;
1485 new_line->data_cap = len;
1486 bline_count_chars(new_line); // Update char widths
1487
1488 // Truncate orig line
1489 bline->data_len -= len;
1490 bline_count_chars(bline); // Update char widths
1491 }
1492
1493 // Insert new_line in linked list
1494 tmp_line = bline->next;
1495 bline->next = new_line;
1496 new_line->next = tmp_line;
1497 new_line->prev = bline;
1498 if (tmp_line) tmp_line->prev = new_line;
1499
1500 // Move marks at or past col to new_line
1501 DL_FOREACH_SAFE(bline->marks, mark, mark_tmp) {
1502 if (mark->col >= col) {
1503 _mark_mark_move_inner(mark, new_line, mark->col - col, 1, 0);
1504 }
1505 }
1506
1507 return new_line;
1508 }
1509
1510 // Given start_line:start_col + num_chars, find end_line:end_col
_buffer_find_end_pos(bline_t * start_line,bint_t start_col,bint_t num_chars,bline_t ** ret_end_line,bint_t * ret_end_col,bint_t * ret_safe_num_chars)1511 static void _buffer_find_end_pos(bline_t *start_line, bint_t start_col, bint_t num_chars, bline_t **ret_end_line, bint_t *ret_end_col, bint_t *ret_safe_num_chars) {
1512 bline_t *end_line;
1513 bint_t end_col;
1514 bint_t num_chars_rem;
1515 end_line = start_line;
1516 end_col = start_col;
1517 num_chars_rem = num_chars;
1518 while (num_chars_rem > 0) {
1519 MLBUF_BLINE_ENSURE_CHARS(end_line);
1520 if (end_line->char_count - end_col >= num_chars_rem) {
1521 end_col += num_chars_rem;
1522 num_chars_rem = 0;
1523 } else {
1524 num_chars_rem -= (end_line->char_count - end_col) + 1;
1525 if (end_line->next) {
1526 end_line = end_line->next;
1527 end_col = 0;
1528 } else {
1529 end_col = end_line->char_count;
1530 break;
1531 }
1532 }
1533 }
1534 num_chars -= num_chars_rem;
1535 *ret_end_line = end_line;
1536 *ret_end_col = end_col;
1537 *ret_safe_num_chars = num_chars;
1538 }
1539
_buffer_bline_replace(bline_t * bline,bint_t start_col,char * data,bint_t data_len,str_t * del_data)1540 static void _buffer_bline_replace(bline_t *bline, bint_t start_col, char *data, bint_t data_len, str_t *del_data) {
1541 bint_t start_index;
1542 mark_t *mark;
1543
1544 // Unslab if needed
1545 if (bline->is_data_slabbed) _buffer_bline_unslab(bline);
1546
1547 // Realloc if needed
1548 MLBUF_BLINE_ENSURE_CHARS(bline);
1549 if (start_col <= 0) {
1550 start_index = 0;
1551 } else if (start_col >= bline->char_count) {
1552 start_index = bline->data_len;
1553 } else {
1554 start_index = bline->chars[start_col].index;
1555 }
1556 if (start_index + data_len >= bline->data_cap) {
1557 bline->data = realloc(bline->data, start_index + data_len);
1558 bline->data_cap = start_index + data_len;
1559 }
1560
1561 // Store del_data
1562 str_append_len(del_data, bline->data + start_index, bline->data_len - start_index);
1563
1564 // Copy data into slot and update chars
1565 memmove(bline->data + start_index, data, (size_t)data_len);
1566 bline->data_len = start_index + data_len;
1567 bline_count_chars(bline);
1568
1569 // Fix marks
1570 DL_FOREACH(bline->marks, mark) {
1571 if (mark->col > bline->char_count) {
1572 mark->col = bline->char_count;
1573 }
1574 }
1575 }
1576
_buffer_bline_insert(bline_t * bline,bint_t col,char * data,bint_t data_len,int move_marks)1577 static bint_t _buffer_bline_insert(bline_t *bline, bint_t col, char *data, bint_t data_len, int move_marks) {
1578 bint_t index;
1579 mark_t *mark;
1580 mark_t *mark_tmp;
1581 bint_t orig_char_count;
1582 bint_t num_chars_added;
1583
1584 // Unslab if needed
1585 if (bline->is_data_slabbed) _buffer_bline_unslab(bline);
1586
1587 // Get orig char_count
1588 MLBUF_BLINE_ENSURE_CHARS(bline);
1589 orig_char_count = bline->char_count;
1590
1591 // Ensure space for data
1592 if (!bline->data) {
1593 bline->data = malloc(data_len);
1594 bline->data_cap = data_len;
1595 } else if (bline->data_len + data_len > bline->data_cap) {
1596 bline->data = realloc(bline->data, bline->data_len + data_len);
1597 bline->data_cap = bline->data_len + data_len;
1598 }
1599
1600 // Find insert point
1601 index = _buffer_bline_col_to_index(bline, col);
1602
1603 // Make room for insert data
1604 if (index < bline->data_len) {
1605 memmove(bline->data + index + data_len, bline->data + index, bline->data_len - index);
1606 }
1607 bline->data_len += data_len;
1608
1609 // Insert data
1610 memcpy(bline->data + index, data, data_len);
1611
1612 // Update chars
1613 bline_count_chars(bline);
1614 num_chars_added = bline->char_count - orig_char_count;
1615
1616 // Move marks after col right by num_chars_added
1617 if (move_marks) {
1618 DL_FOREACH_SAFE(bline->marks, mark, mark_tmp) {
1619 if (mark_is_after_col_minus_lefties(mark, col)) {
1620 mark->col += num_chars_added;
1621 }
1622 }
1623 }
1624
1625 return num_chars_added;
1626 }
1627
_buffer_bline_delete(bline_t * bline,bint_t col,bint_t num_chars)1628 static bint_t _buffer_bline_delete(bline_t *bline, bint_t col, bint_t num_chars) {
1629 bint_t safe_num_chars;
1630 bint_t index;
1631 bint_t index_end;
1632 bint_t move_len;
1633 mark_t *mark;
1634 mark_t *mark_tmp;
1635 bint_t orig_char_count;
1636 bint_t num_chars_deleted;
1637
1638 // Unslab if needed
1639 if (bline->is_data_slabbed) _buffer_bline_unslab(bline);
1640
1641 // Get orig char_count
1642 MLBUF_BLINE_ENSURE_CHARS(bline);
1643 orig_char_count = bline->char_count;
1644
1645 // Clamp num_chars
1646 safe_num_chars = MLBUF_MIN(bline->char_count - col, num_chars);
1647 if (safe_num_chars != num_chars) {
1648 MLBUF_DEBUG_PRINTF("num_chars=%" PRIdMAX " does not match safe_num_chars=%" PRIdMAX "\n", num_chars, safe_num_chars);
1649 }
1650
1651 // Nothing to do if safe_num_chars is 0
1652 if (safe_num_chars < 1) {
1653 MLBUF_DEBUG_PRINTF("safe_num_chars=%" PRIdMAX " lt 1\n", safe_num_chars);
1654 return MLBUF_OK;
1655 }
1656
1657 // Find delete bounds
1658 index = _buffer_bline_col_to_index(bline, col);
1659 index_end = _buffer_bline_col_to_index(bline, col + safe_num_chars);
1660 move_len = (bint_t)(bline->data_len - index_end);
1661
1662 // Shift data
1663 if (move_len > 0) {
1664 memmove(bline->data + index, bline->data + index_end, move_len);
1665 }
1666 bline->data_len -= index_end - index;
1667
1668 // Update chars
1669 bline_count_chars(bline);
1670 num_chars_deleted = orig_char_count - bline->char_count;
1671
1672 // Move marks after col left by num_chars_deleted
1673 DL_FOREACH_SAFE(bline->marks, mark, mark_tmp) {
1674 if (mark->col > col) {
1675 mark->col = MLBUF_MAX(0, mark->col - num_chars_deleted);
1676 }
1677 }
1678
1679 return num_chars_deleted;
1680 }
1681
_buffer_bline_col_to_index(bline_t * bline,bint_t col)1682 static bint_t _buffer_bline_col_to_index(bline_t *bline, bint_t col) {
1683 bint_t index;
1684 MLBUF_BLINE_ENSURE_CHARS(bline);
1685 if (!bline->chars) {
1686 return 0;
1687 }
1688 if (col >= bline->char_count) {
1689 index = bline->data_len;
1690 } else {
1691 index = bline->chars[col].index;
1692 }
1693 return index;
1694 }
1695
_buffer_bline_index_to_col(bline_t * bline,bint_t index)1696 static bint_t _buffer_bline_index_to_col(bline_t *bline, bint_t index) {
1697 MLBUF_BLINE_ENSURE_CHARS(bline);
1698 if (index < 1) {
1699 return 0;
1700 } else if (index >= bline->data_len) {
1701 return bline->char_count;
1702 }
1703 return bline->chars[index].index_to_vcol;
1704 }
1705
1706 // Close self->fd and self->mmap if needed
_buffer_munmap(buffer_t * self)1707 static int _buffer_munmap(buffer_t *self) {
1708 if (self->mmap) {
1709 munmap(self->mmap, self->mmap_len);
1710 close(self->mmap_fd);
1711 self->mmap = NULL;
1712 self->mmap_len = 0;
1713 self->mmap_fd = -1;
1714 }
1715 return MLBUF_OK;
1716 }
1717
1718 // Make a new single-line style rule
srule_new_single(char * re,bint_t re_len,int caseless,uint16_t fg,uint16_t bg)1719 srule_t *srule_new_single(char *re, bint_t re_len, int caseless, uint16_t fg, uint16_t bg) {
1720 srule_t *rule;
1721 const char *re_error;
1722 int re_erroffset;
1723 rule = calloc(1, sizeof(srule_t));
1724 rule->type = MLBUF_SRULE_TYPE_SINGLE;
1725 rule->style.fg = fg;
1726 rule->style.bg = bg;
1727 rule->re = malloc((re_len + 1) * sizeof(char));
1728 snprintf(rule->re, re_len + 1, "%.*s", (int)re_len, re);
1729 rule->cre = pcre_compile((const char*)rule->re, PCRE_NO_AUTO_CAPTURE | (caseless ? PCRE_CASELESS : 0), &re_error, &re_erroffset, NULL);
1730 if (!rule->cre) {
1731 // TODO log error
1732 srule_destroy(rule);
1733 return NULL;
1734 }
1735 rule->crex = pcre_study(rule->cre, PCRE_STUDY_JIT_COMPILE, &re_error);
1736 return rule;
1737 }
1738
1739 // Make a new multi-line style rule
srule_new_multi(char * re,bint_t re_len,char * re_end,bint_t re_end_len,uint16_t fg,uint16_t bg)1740 srule_t *srule_new_multi(char *re, bint_t re_len, char *re_end, bint_t re_end_len, uint16_t fg, uint16_t bg) {
1741 srule_t *rule;
1742 const char *re_error;
1743 int re_erroffset;
1744 rule = calloc(1, sizeof(srule_t));
1745 rule->type = MLBUF_SRULE_TYPE_MULTI;
1746 rule->style.fg = fg;
1747 rule->style.bg = bg;
1748 rule->re = malloc((re_len + 1) * sizeof(char));
1749 rule->re_end = malloc((re_end_len + 1) * sizeof(char));
1750 snprintf(rule->re, re_len + 1, "%.*s", (int)re_len, re);
1751 snprintf(rule->re_end, re_end_len + 1, "%.*s", (int)re_end_len, re_end);
1752 rule->cre = pcre_compile((const char*)rule->re, PCRE_NO_AUTO_CAPTURE, &re_error, &re_erroffset, NULL);
1753 rule->cre_end = pcre_compile((const char*)rule->re_end, PCRE_NO_AUTO_CAPTURE, &re_error, &re_erroffset, NULL);
1754 if (!rule->cre || !rule->cre_end) {
1755 // TODO log error
1756 srule_destroy(rule);
1757 return NULL;
1758 }
1759 rule->crex = pcre_study(rule->cre, PCRE_STUDY_JIT_COMPILE, &re_error);
1760 rule->crex_end = pcre_study(rule->cre_end, PCRE_STUDY_JIT_COMPILE, &re_error);
1761 return rule;
1762 }
1763
1764 // Make a new range style rule
srule_new_range(mark_t * range_a,mark_t * range_b,uint16_t fg,uint16_t bg)1765 srule_t *srule_new_range(mark_t *range_a, mark_t *range_b, uint16_t fg, uint16_t bg) {
1766 srule_t *rule;
1767 rule = calloc(1, sizeof(srule_t));
1768 rule->type = MLBUF_SRULE_TYPE_RANGE;
1769 rule->style.fg = fg;
1770 rule->style.bg = bg;
1771 rule->range_a = range_a;
1772 rule->range_b = range_b;
1773 return rule;
1774 }
1775
1776 // Free an srule
srule_destroy(srule_t * srule)1777 int srule_destroy(srule_t *srule) {
1778 if (srule->re) free(srule->re);
1779 if (srule->re_end) free(srule->re_end);
1780 if (srule->cre) pcre_free(srule->cre);
1781 if (srule->cre_end) pcre_free(srule->cre_end);
1782 if (srule->crex) pcre_free_study_ex(srule->crex);
1783 if (srule->crex_end) pcre_free_study_ex(srule->crex_end);
1784 free(srule);
1785 return MLBUF_OK;
1786 }
1787
_srule_multi_find(srule_t * rule,int find_end,bline_t * bline,bint_t start_offset,bint_t * ret_start,bint_t * ret_stop)1788 static int _srule_multi_find(srule_t *rule, int find_end, bline_t *bline, bint_t start_offset, bint_t *ret_start, bint_t *ret_stop) {
1789 int rc;
1790 pcre *cre;
1791 pcre_extra *crex;
1792 int substrs[3];
1793 bint_t start_index;
1794 mark_t *mark;
1795
1796 if (rule->type == MLBUF_SRULE_TYPE_RANGE) {
1797 mark = mark_is_gt(rule->range_a, rule->range_b)
1798 ? (find_end ? rule->range_a : rule->range_b)
1799 : (find_end ? rule->range_b : rule->range_a);
1800 if (mark->bline == bline && mark->col >= start_offset) {
1801 *ret_start = mark->col;
1802 *ret_stop = mark->col;
1803 return 1;
1804 }
1805 return 0;
1806 }
1807
1808 // MLBUF_SRULE_TYPE_MULTI
1809 cre = find_end ? rule->cre_end : rule->cre;
1810 crex = find_end ? rule->crex_end : rule->crex;
1811 start_index = _buffer_bline_col_to_index(bline, start_offset);
1812 if ((rc = pcre_exec(cre, crex, bline->data, bline->data_len, start_index, 0, substrs, 3)) >= 0) {
1813 *ret_start = _buffer_bline_index_to_col(bline, substrs[0]);
1814 *ret_stop = _buffer_bline_index_to_col(bline, substrs[1]);
1815 return 1;
1816 }
1817 return 0;
1818 }
1819
_srule_multi_find_start(srule_t * rule,bline_t * bline,bint_t start_offset,bint_t * ret_start,bint_t * ret_stop)1820 static int _srule_multi_find_start(srule_t *rule, bline_t *bline, bint_t start_offset, bint_t *ret_start, bint_t *ret_stop) {
1821 return _srule_multi_find(rule, 0, bline, start_offset, ret_start, ret_stop);
1822 }
1823
_srule_multi_find_end(srule_t * rule,bline_t * bline,bint_t start_offset,bint_t * ret_stop)1824 static int _srule_multi_find_end(srule_t *rule, bline_t *bline, bint_t start_offset, bint_t *ret_stop) {
1825 bint_t ignore;
1826 return _srule_multi_find(rule, 1, bline, start_offset, &ignore, ret_stop);
1827 }
1828
_baction_destroy(baction_t * action)1829 static int _baction_destroy(baction_t *action) {
1830 if (action->data) free(action->data);
1831 free(action);
1832 return MLBUF_OK;
1833 }
1834