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

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