Manual pages: mcmcdiffmceditmcview

root/src/editor/editbuffer.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. edit_buffer_get_byte_ptr
  2. edit_buffer_init
  3. edit_buffer_clean
  4. edit_buffer_get_byte
  5. edit_buffer_get_utf
  6. edit_buffer_get_prev_utf
  7. edit_buffer_count_lines
  8. edit_buffer_get_bol
  9. edit_buffer_get_eol
  10. edit_buffer_get_word_from_pos
  11. edit_buffer_find_word_start
  12. edit_buffer_insert
  13. edit_buffer_insert_ahead
  14. edit_buffer_delete
  15. edit_buffer_backspace
  16. edit_buffer_get_forward_offset
  17. edit_buffer_get_backward_offset
  18. edit_buffer_read_file
  19. edit_buffer_write_file
  20. edit_buffer_calc_percent

   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][next][first][last][top][bottom][index][help]  */
 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][next][first][last][top][bottom][index][help]  */
 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][next][first][last][top][bottom][index][help]  */
 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][next][first][last][top][bottom][index][help]  */
 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][next][first][last][top][bottom][index][help]  */
 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[UTF8_CHAR_LEN + 1];
 237 
 238         for (i = 0; i < UTF8_CHAR_LEN; 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][next][first][last][top][bottom][index][help]  */
 275 {
 276     size_t i;
 277     gchar utf8_buf[3 * UTF8_CHAR_LEN + 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 * UTF8_CHAR_LEN); i++)
 289         utf8_buf[i] = edit_buffer_get_byte (buf, byte_index + i - (2 * UTF8_CHAR_LEN));
 290     utf8_buf[i] = '\0';
 291 
 292     cursor_buf_ptr = utf8_buf + (2 * UTF8_CHAR_LEN);
 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][next][first][last][top][bottom][index][help]  */
 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][next][first][last][top][bottom][index][help]  */
 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][next][first][last][top][bottom][index][help]  */
 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][next][first][last][top][bottom][index][help]  */
 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][next][first][last][top][bottom][index][help]  */
 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][next][first][last][top][bottom][index][help]  */
 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][next][first][last][top][bottom][index][help]  */
 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][next][first][last][top][bottom][index][help]  */
 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][next][first][last][top][bottom][index][help]  */
 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][next][first][last][top][bottom][index][help]  */
 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][next][first][last][top][bottom][index][help]  */
 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][next][first][last][top][bottom][index][help]  */
 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][next][first][last][top][bottom][index][help]  */
 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][next][first][last][top][bottom][index][help]  */
 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 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */