1 /*
2 Editor text keep buffer.
3
4 Copyright (C) 2013-2025
5 Free Software Foundation, Inc.
6
7 Written by:
8 Andrew Borodin <aborodin@vmail.ru> 2013
9
10 This file is part of the Midnight Commander.
11
12 The Midnight Commander is free software: you can redistribute it
13 and/or modify it under the terms of the GNU General Public License as
14 published by the Free Software Foundation, either version 3 of the License,
15 or (at your option) any later version.
16
17 The Midnight Commander is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <https://www.gnu.org/licenses/>.
24 */
25
26 /** \file
27 * \brief Source: editor text keep buffer.
28 * \author Andrew Borodin
29 * \date 2013
30 */
31
32 #include <config.h>
33
34 #include <ctype.h> // isdigit()
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/types.h>
38
39 #include "lib/global.h"
40
41 #include "lib/vfs/vfs.h"
42
43 #include "edit-impl.h"
44 #include "editbuffer.h"
45
46 /* --------------------------------------------------------------------------------------------- */
47 /*-
48 *
49 * here's a quick sketch of the layout: (don't run this through indent.)
50 *
51 * |
52 * \0\0\0\0\0m e _ f i l e . \nf i n . \n|T h i s _ i s _ s o\0\0\0\0\0\0\0\0\0
53 * ______________________________________|______________________________________
54 * |
55 * ... | b2[2] | b2[1] | b2[0] | b1[0] | b1[1] | b1[2] | ...
56 * |-> |-> |-> |-> |-> |-> |
57 * |
58 * _<------------------------->|<----------------->_
59 * curs2 | curs1
60 * ^ | ^
61 * | ^|^ |
62 * cursor ||| cursor
63 * |||
64 * file end|||file beginning
65 * |
66 * |
67 *
68 * _
69 * This_is_some_file
70 * fin.
71 *
72 *
73 * This is called a "gap buffer".
74 * See also:
75 * https://en.wikipedia.org/wiki/Gap_buffer
76 * https://stackoverflow.com/questions/4199694/data-structure-for-text-editor
77 */
78
79 /*** global variables ****************************************************************************/
80
81 /*** file scope macro definitions ****************************************************************/
82
83 /*
84 * The editor keeps data in two arrays of buffers.
85 * All buffers have the same size, which must be a power of 2.
86 */
87
88 /* Configurable: log2 of the buffer size in bytes */
89 #ifndef S_EDIT_BUF_SIZE
90 #define S_EDIT_BUF_SIZE 16
91 #endif
92
93 /* Size of the buffer */
94 #define EDIT_BUF_SIZE (((off_t) 1) << S_EDIT_BUF_SIZE)
95
96 /* Buffer mask (used to find cursor position relative to the buffer) */
97 #define M_EDIT_BUF_SIZE (EDIT_BUF_SIZE - 1)
98
99 /*** file scope type declarations ****************************************************************/
100
101 /*** forward declarations (file scope functions) *************************************************/
102
103 /*** file scope variables ************************************************************************/
104
105 /* --------------------------------------------------------------------------------------------- */
106 /*** file scope functions ************************************************************************/
107 /* --------------------------------------------------------------------------------------------- */
108 /**
109 * Get pointer to byte at specified index
110 *
111 * @param buf pointer to editor buffer
112 * @param byte_index byte index
113 *
114 * @return NULL if byte_index is negative or larger than file size; pointer to byte otherwise.
115 */
116 static char *
117 edit_buffer_get_byte_ptr (const edit_buffer_t *buf, off_t byte_index)
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
118 {
119 void *b;
120
121 if (byte_index >= (buf->curs1 + buf->curs2) || byte_index < 0)
122 return NULL;
123
124 if (byte_index >= buf->curs1)
125 {
126 off_t p;
127
128 p = buf->curs1 + buf->curs2 - byte_index - 1;
129 b = g_ptr_array_index (buf->b2, p >> S_EDIT_BUF_SIZE);
130 return (char *) b + EDIT_BUF_SIZE - 1 - (p & M_EDIT_BUF_SIZE);
131 }
132
133 b = g_ptr_array_index (buf->b1, byte_index >> S_EDIT_BUF_SIZE);
134 return (char *) b + (byte_index & M_EDIT_BUF_SIZE);
135 }
136
137 /* --------------------------------------------------------------------------------------------- */
138 /*** public functions ****************************************************************************/
139 /* --------------------------------------------------------------------------------------------- */
140 /**
141 * Initialize editor buffers.
142 *
143 * @param buf pointer to editor buffer
144 */
145
146 void
147 edit_buffer_init (edit_buffer_t *buf, off_t size)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
148 {
149 buf->b1 = g_ptr_array_new_full (32, g_free);
150 buf->b2 = g_ptr_array_new_full (32, g_free);
151
152 buf->curs1 = 0;
153 buf->curs2 = 0;
154
155 buf->size = size;
156 buf->lines = 0;
157 }
158
159 /* --------------------------------------------------------------------------------------------- */
160 /**
161 * Clean editor buffers.
162 *
163 * @param buf pointer to editor buffer
164 */
165
166 void
167 edit_buffer_clean (edit_buffer_t *buf)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
168 {
169 if (buf->b1 != NULL)
170 g_ptr_array_free (buf->b1, TRUE);
171
172 if (buf->b2 != NULL)
173 g_ptr_array_free (buf->b2, TRUE);
174 }
175
176 /* --------------------------------------------------------------------------------------------- */
177 /**
178 * Get byte at specified index
179 *
180 * @param buf pointer to editor buffer
181 * @param byte_index byte index
182 *
183 * @return '\n' if byte_index is negative or larger than file size; byte at byte_index otherwise.
184 */
185
186 int
187 edit_buffer_get_byte (const edit_buffer_t *buf, off_t byte_index)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
188 {
189 char *p;
190
191 p = edit_buffer_get_byte_ptr (buf, byte_index);
192
193 return (p != NULL) ? *(unsigned char *) p : '\n';
194 }
195
196 /* --------------------------------------------------------------------------------------------- */
197
198 /**
199 * Get utf-8 symbol at specified index
200 *
201 * @param buf pointer to editor buffer
202 * @param byte_index byte index
203 * @param char_length length of returned symbol
204 *
205 * @return '\n' if byte_index is negative or larger than file size;
206 * 0 if utf-8 symbol at specified index is invalid;
207 * utf-8 symbol otherwise
208 */
209
210 int
211 edit_buffer_get_utf (const edit_buffer_t *buf, off_t byte_index, int *char_length)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
212 {
213 gchar *str = NULL;
214 gunichar res;
215 gunichar ch;
216 gchar *next_ch = NULL;
217
218 if (byte_index >= (buf->curs1 + buf->curs2) || byte_index < 0)
219 {
220 *char_length = 0;
221 return '\n';
222 }
223
224 str = edit_buffer_get_byte_ptr (buf, byte_index);
225 if (str == NULL)
226 {
227 *char_length = 0;
228 return 0;
229 }
230
231 res = g_utf8_get_char_validated (str, -1);
232 if (res == (gunichar) (-2) || res == (gunichar) (-1))
233 {
234 // Retry with explicit bytes to make sure it's not a buffer boundary
235 size_t i;
236 gchar utf8_buf[MB_LEN_MAX + 1];
237
238 for (i = 0; i < MB_LEN_MAX; i++)
239 utf8_buf[i] = edit_buffer_get_byte (buf, byte_index + i);
240 utf8_buf[i] = '\0';
241 res = g_utf8_get_char_validated (utf8_buf, -1);
242 }
243
244 if (res == (gunichar) (-2) || res == (gunichar) (-1))
245 {
246 ch = *str;
247 *char_length = 0;
248 }
249 else
250 {
251 ch = res;
252 // Calculate UTF-8 char length
253 next_ch = g_utf8_next_char (str);
254 *char_length = next_ch - str;
255 }
256
257 return (int) ch;
258 }
259
260 /* --------------------------------------------------------------------------------------------- */
261 /**
262 * Get utf-8 symbol before specified index
263 *
264 * @param buf pointer to editor buffer
265 * @param byte_index byte index
266 * @param char_length length of returned symbol
267 *
268 * @return 0 if byte_index is negative or larger than file size;
269 * 1-byte value before specified index if utf-8 symbol before specified index is invalid;
270 * utf-8 symbol otherwise
271 */
272
273 int
274 edit_buffer_get_prev_utf (const edit_buffer_t *buf, off_t byte_index, int *char_length)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
275 {
276 size_t i;
277 gchar utf8_buf[3 * MB_LEN_MAX + 1];
278 gchar *str;
279 gchar *cursor_buf_ptr;
280 gunichar res;
281
282 if (byte_index > (buf->curs1 + buf->curs2) || byte_index <= 0)
283 {
284 *char_length = 0;
285 return 0;
286 }
287
288 for (i = 0; i < (3 * MB_LEN_MAX); i++)
289 utf8_buf[i] = edit_buffer_get_byte (buf, byte_index + i - (2 * MB_LEN_MAX));
290 utf8_buf[i] = '\0';
291
292 cursor_buf_ptr = utf8_buf + (2 * MB_LEN_MAX);
293 str = g_utf8_find_prev_char (utf8_buf, cursor_buf_ptr);
294
295 if (str == NULL || g_utf8_next_char (str) != cursor_buf_ptr)
296 {
297 *char_length = 1;
298 return *(cursor_buf_ptr - 1);
299 }
300
301 res = g_utf8_get_char_validated (str, -1);
302 if (res == (gunichar) (-2) || res == (gunichar) (-1))
303 {
304 *char_length = 1;
305 return *(cursor_buf_ptr - 1);
306 }
307
308 *char_length = cursor_buf_ptr - str;
309 return (int) res;
310 }
311
312 /* --------------------------------------------------------------------------------------------- */
313 /**
314 * Count lines in editor buffer.
315 *
316 * @param buf editor buffer
317 * @param first start byte offset
318 * @param last finish byte offset
319 *
320 * @return line numbers between "first" and "last" bytes
321 */
322
323 long
324 edit_buffer_count_lines (const edit_buffer_t *buf, off_t first, off_t last)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
325 {
326 long lines = 0;
327
328 first = MAX (first, 0);
329 last = MIN (last, buf->size);
330
331 while (first < last)
332 if (edit_buffer_get_byte (buf, first++) == '\n')
333 lines++;
334
335 return lines;
336 }
337
338 /* --------------------------------------------------------------------------------------------- */
339 /**
340 * Get "begin-of-line" offset of line contained specified byte offset
341 *
342 * @param buf editor buffer
343 * @param current byte offset
344 *
345 * @return index of first char of line
346 */
347
348 off_t
349 edit_buffer_get_bol (const edit_buffer_t *buf, off_t current)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
350 {
351 if (current <= 0)
352 return 0;
353
354 for (; edit_buffer_get_byte (buf, current - 1) != '\n'; current--)
355 ;
356
357 return current;
358 }
359
360 /* --------------------------------------------------------------------------------------------- */
361 /**
362 * Get "end-of-line" offset of line contained specified byte offset
363 *
364 * @param buf editor buffer
365 * @param current byte offset
366 *
367 * @return index of last char of line + 1
368 */
369
370 off_t
371 edit_buffer_get_eol (const edit_buffer_t *buf, off_t current)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
372 {
373 if (current >= buf->size)
374 return buf->size;
375
376 for (; edit_buffer_get_byte (buf, current) != '\n'; current++)
377 ;
378
379 return current;
380 }
381
382 /* --------------------------------------------------------------------------------------------- */
383 /**
384 * Get word from specified offset.
385 *
386 * @param buf editor buffer
387 * @param current start_pos offset
388 * @param start actual start word ofset
389 * @param cut
390 *
391 * @return word as newly allocated object
392 */
393
394 GString *
395 edit_buffer_get_word_from_pos (const edit_buffer_t *buf, off_t start_pos, off_t *start, gsize *cut)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
396 {
397 off_t word_start;
398 gsize cut_len = 0;
399 GString *match_expr;
400 int c1, c2;
401
402 for (word_start = start_pos; word_start != 0; word_start--, cut_len++)
403 {
404 c1 = edit_buffer_get_byte (buf, word_start);
405 c2 = edit_buffer_get_byte (buf, word_start - 1);
406
407 if (is_break_char (c1) != is_break_char (c2) || c1 == '\n' || c2 == '\n')
408 break;
409 }
410
411 match_expr = g_string_sized_new (16);
412
413 do
414 {
415 c1 = edit_buffer_get_byte (buf, word_start + match_expr->len);
416 c2 = edit_buffer_get_byte (buf, word_start + match_expr->len + 1);
417 g_string_append_c (match_expr, c1);
418 }
419 while (!(is_break_char (c1) != is_break_char (c2) || c1 == '\n' || c2 == '\n'));
420
421 *start = word_start;
422 *cut = cut_len;
423
424 return match_expr;
425 }
426
427 /* --------------------------------------------------------------------------------------------- */
428 /**
429 * Find first character of current word
430 *
431 * @param buf editor buffer
432 * @param word_start position of first character of current word
433 * @param word_len length of current word
434 *
435 * @return TRUE if first character of word is found and this character is not 1) a digit and
436 * 2) a begin of file, FALSE otherwise
437 */
438
439 gboolean
440 edit_buffer_find_word_start (const edit_buffer_t *buf, off_t *word_start, gsize *word_len)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
441 {
442 int c;
443 off_t i;
444
445 // return if at begin of file
446 if (buf->curs1 <= 0)
447 return FALSE;
448
449 c = edit_buffer_get_previous_byte (buf);
450 // return if not at end or in word
451 if (is_break_char (c))
452 return FALSE;
453
454 // search start of word
455 for (i = 1;; i++)
456 {
457 int last;
458
459 last = c;
460 c = edit_buffer_get_byte (buf, buf->curs1 - i - 1);
461
462 if (is_break_char (c))
463 {
464 // return if word starts with digit
465 if (isdigit (last))
466 return FALSE;
467
468 break;
469 }
470 }
471
472 // success
473 *word_start = buf->curs1 - i; // start found
474 *word_len = (gsize) i;
475
476 return TRUE;
477 }
478
479 /* --------------------------------------------------------------------------------------------- */
480 /**
481 * Basic low level single character buffer alterations and movements at the cursor: insert character
482 * at the cursor position and move right.
483 *
484 * @param buf pointer to editor buffer
485 * @param c character to insert
486 */
487
488 void
489 edit_buffer_insert (edit_buffer_t *buf, int c)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
490 {
491 void *b;
492 off_t i;
493
494 i = buf->curs1 & M_EDIT_BUF_SIZE;
495
496 // add a new buffer if we've reached the end of the last one
497 if (i == 0)
498 g_ptr_array_add (buf->b1, g_malloc0 (EDIT_BUF_SIZE));
499
500 // perform the insertion
501 b = g_ptr_array_index (buf->b1, buf->curs1 >> S_EDIT_BUF_SIZE);
502 *((unsigned char *) b + i) = (unsigned char) c;
503
504 // update cursor position
505 buf->curs1++;
506
507 // update file length
508 buf->size++;
509 }
510
511 /* --------------------------------------------------------------------------------------------- */
512 /**
513 * Basic low level single character buffer alterations and movements at the cursor: insert character
514 * at the cursor position and move left.
515 *
516 * @param buf pointer to editor buffer
517 * @param c character to insert
518 */
519
520 void
521 edit_buffer_insert_ahead (edit_buffer_t *buf, int c)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
522 {
523 void *b;
524 off_t i;
525
526 i = buf->curs2 & M_EDIT_BUF_SIZE;
527
528 // add a new buffer if we've reached the end of the last one
529 if (i == 0)
530 g_ptr_array_add (buf->b2, g_malloc0 (EDIT_BUF_SIZE));
531
532 // perform the insertion
533 b = g_ptr_array_index (buf->b2, buf->curs2 >> S_EDIT_BUF_SIZE);
534 *((unsigned char *) b + EDIT_BUF_SIZE - 1 - i) = (unsigned char) c;
535
536 // update cursor position
537 buf->curs2++;
538
539 // update file length
540 buf->size++;
541 }
542
543 /* --------------------------------------------------------------------------------------------- */
544 /**
545 * Basic low level single character buffer alterations and movements at the cursor: delete character
546 * at the cursor position.
547 *
548 * @param buf pointer to editor buffer
549 * @param c character to insert
550 */
551
552 int
553 edit_buffer_delete (edit_buffer_t *buf)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
554 {
555 void *b;
556 unsigned char c;
557 off_t prev;
558 off_t i;
559
560 prev = buf->curs2 - 1;
561
562 b = g_ptr_array_index (buf->b2, prev >> S_EDIT_BUF_SIZE);
563 i = prev & M_EDIT_BUF_SIZE;
564 c = *((unsigned char *) b + EDIT_BUF_SIZE - 1 - i);
565
566 if (i == 0)
567 {
568 guint j;
569
570 j = buf->b2->len - 1;
571 b = g_ptr_array_index (buf->b2, j);
572 g_ptr_array_remove_index (buf->b2, j);
573 }
574
575 buf->curs2 = prev;
576
577 // update file length
578 buf->size--;
579
580 return c;
581 }
582
583 /* --------------------------------------------------------------------------------------------- */
584 /**
585 * Basic low level single character buffer alterations and movements at the cursor: delete character
586 * before the cursor position and move left.
587 *
588 * @param buf pointer to editor buffer
589 * @param c character to insert
590 */
591
592 int
593 edit_buffer_backspace (edit_buffer_t *buf)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
594 {
595 void *b;
596 unsigned char c;
597 off_t prev;
598 off_t i;
599
600 prev = buf->curs1 - 1;
601
602 b = g_ptr_array_index (buf->b1, prev >> S_EDIT_BUF_SIZE);
603 i = prev & M_EDIT_BUF_SIZE;
604 c = *((unsigned char *) b + i);
605
606 if (i == 0)
607 {
608 guint j;
609
610 j = buf->b1->len - 1;
611 b = g_ptr_array_index (buf->b1, j);
612 g_ptr_array_remove_index (buf->b1, j);
613 }
614
615 buf->curs1 = prev;
616
617 // update file length
618 buf->size--;
619
620 return c;
621 }
622
623 /* --------------------------------------------------------------------------------------------- */
624 /**
625 * Calculate forward offset with specified number of lines.
626 *
627 * @param buf editor buffer
628 * @param current current offset
629 * @param lines number of lines to move forward
630 * @param upto offset to count lines between current and upto.
631 *
632 * @return If lines is zero returns the count of lines from current to upto.
633 * If upto is zero returns offset of lines forward current.
634 * Else returns forward offset with specified number of lines
635 */
636
637 off_t
638 edit_buffer_get_forward_offset (const edit_buffer_t *buf, off_t current, long lines, off_t upto)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
639 {
640 if (upto != 0)
641 return (off_t) edit_buffer_count_lines (buf, current, upto);
642
643 lines = MAX (lines, 0);
644
645 while (lines-- != 0)
646 {
647 long next;
648
649 next = edit_buffer_get_eol (buf, current) + 1;
650 if (next > buf->size)
651 break;
652 current = next;
653 }
654
655 return current;
656 }
657
658 /* --------------------------------------------------------------------------------------------- */
659 /**
660 * Calculate backward offset with specified number of lines.
661 *
662 * @param buf editor buffer
663 * @param current current offset
664 * @param lines number of lines to move backward
665 *
666 * @return backward offset with specified number of lines.
667 */
668
669 off_t
670 edit_buffer_get_backward_offset (const edit_buffer_t *buf, off_t current, long lines)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
671 {
672 lines = MAX (lines, 0);
673 current = edit_buffer_get_bol (buf, current);
674
675 while (lines-- != 0 && current != 0)
676 current = edit_buffer_get_bol (buf, current - 1);
677
678 return current;
679 }
680
681 /* --------------------------------------------------------------------------------------------- */
682 /**
683 * Load file into editor buffer
684 *
685 * @param buf pointer to editor buffer
686 * @param fd file descriptor
687 * @param size file size
688 *
689 * @return number of read bytes
690 */
691
692 off_t
693 edit_buffer_read_file (edit_buffer_t *buf, int fd, off_t size,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
694 edit_buffer_read_file_status_msg_t *sm, gboolean *aborted)
695 {
696 off_t ret = 0;
697 off_t i, j;
698 off_t data_size;
699 void *b;
700 status_msg_t *s = STATUS_MSG (sm);
701 unsigned short update_cnt = 0;
702
703 *aborted = FALSE;
704
705 buf->lines = 0;
706 buf->curs2 = size;
707 i = buf->curs2 >> S_EDIT_BUF_SIZE;
708
709 // fill last part of b2
710 data_size = buf->curs2 & M_EDIT_BUF_SIZE;
711 if (data_size != 0)
712 {
713 b = g_malloc0 (EDIT_BUF_SIZE);
714 g_ptr_array_add (buf->b2, b);
715 b = (char *) b + EDIT_BUF_SIZE - data_size;
716 ret = mc_read (fd, b, data_size);
717
718 // count lines
719 for (j = 0; j < ret; j++)
720 if (*((char *) b + j) == '\n')
721 buf->lines++;
722
723 if (ret < 0 || ret != data_size)
724 return ret;
725 }
726
727 // fulfill other parts of b2 from end to begin
728 data_size = EDIT_BUF_SIZE;
729 for (--i; i >= 0; i--)
730 {
731 off_t sz;
732
733 b = g_malloc0 (data_size);
734 g_ptr_array_add (buf->b2, b);
735 sz = mc_read (fd, b, data_size);
736 if (sz >= 0)
737 ret += sz;
738
739 // count lines
740 for (j = 0; j < sz; j++)
741 if (*((char *) b + j) == '\n')
742 buf->lines++;
743
744 if (s != NULL && s->update != NULL)
745 {
746 update_cnt = (update_cnt + 1) & 0xf;
747 if (update_cnt == 0)
748 {
749 // FIXME: overcare
750 if (sm->buf == NULL)
751 sm->buf = buf;
752
753 sm->loaded = ret;
754 if (s->update (s) == B_CANCEL)
755 {
756 *aborted = TRUE;
757 return (-1);
758 }
759 }
760 }
761
762 if (sz != data_size)
763 break;
764 }
765
766 // reverse buffer
767 for (i = 0; i < (off_t) buf->b2->len / 2; i++)
768 {
769 void **b1, **b2;
770
771 b1 = &g_ptr_array_index (buf->b2, i);
772 b2 = &g_ptr_array_index (buf->b2, buf->b2->len - 1 - i);
773
774 b = *b1;
775 *b1 = *b2;
776 *b2 = b;
777
778 if (s != NULL && s->update != NULL)
779 {
780 update_cnt = (update_cnt + 1) & 0xf;
781 if (update_cnt == 0)
782 {
783 sm->loaded = ret;
784 if (s->update (s) == B_CANCEL)
785 {
786 *aborted = TRUE;
787 return (-1);
788 }
789 }
790 }
791 }
792
793 return ret;
794 }
795
796 /* --------------------------------------------------------------------------------------------- */
797 /**
798 * Write editor buffer content to file
799 *
800 * @param buf pointer to editor buffer
801 * @param fd file descriptor
802 *
803 * @return number of written bytes
804 */
805
806 off_t
807 edit_buffer_write_file (edit_buffer_t *buf, int fd)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
808 {
809 off_t ret = 0;
810 off_t i;
811 off_t data_size, sz;
812 void *b;
813
814 // write all fulfilled parts of b1 from begin to end
815 if (buf->b1->len != 0)
816 {
817 data_size = EDIT_BUF_SIZE;
818 for (i = 0; i < (off_t) buf->b1->len - 1; i++)
819 {
820 b = g_ptr_array_index (buf->b1, i);
821 sz = mc_write (fd, b, data_size);
822 if (sz >= 0)
823 ret += sz;
824 else if (i == 0)
825 ret = sz;
826 if (sz != data_size)
827 return ret;
828 }
829
830 // write last partially filled part of b1
831 data_size = ((buf->curs1 - 1) & M_EDIT_BUF_SIZE) + 1;
832 b = g_ptr_array_index (buf->b1, i);
833 sz = mc_write (fd, b, data_size);
834 if (sz >= 0)
835 ret += sz;
836 if (sz != data_size)
837 return ret;
838 }
839
840 // write b2 from end to begin, if b2 contains some data
841 if (buf->b2->len != 0)
842 {
843 // write last partially filled part of b2
844 i = buf->b2->len - 1;
845 b = g_ptr_array_index (buf->b2, i);
846 data_size = ((buf->curs2 - 1) & M_EDIT_BUF_SIZE) + 1;
847 sz = mc_write (fd, (char *) b + EDIT_BUF_SIZE - data_size, data_size);
848 if (sz >= 0)
849 ret += sz;
850
851 if (sz == data_size)
852 {
853 // write other fulfilled parts of b2 from end to begin
854 data_size = EDIT_BUF_SIZE;
855 while (--i >= 0)
856 {
857 b = g_ptr_array_index (buf->b2, i);
858 sz = mc_write (fd, b, data_size);
859 if (sz >= 0)
860 ret += sz;
861 if (sz != data_size)
862 break;
863 }
864 }
865 }
866
867 return ret;
868 }
869
870 /* --------------------------------------------------------------------------------------------- */
871 /**
872 * Calculate percentage of specified character offset
873 *
874 * @param buf pointer to editor buffer
875 * @param p character offset
876 *
877 * @return percentage of specified character offset
878 */
879
880 int
881 edit_buffer_calc_percent (const edit_buffer_t *buf, off_t offset)
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
882 {
883 int percent;
884
885 if (buf->size == 0)
886 percent = 0;
887 else if (offset >= buf->size)
888 percent = 100;
889 else if (offset > (INT_MAX / 100))
890 percent = offset / (buf->size / 100);
891 else
892 percent = offset * 100 / buf->size;
893
894 return percent;
895 }
896
897 /* --------------------------------------------------------------------------------------------- */