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

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