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_insert
  12. edit_buffer_insert_ahead
  13. edit_buffer_delete
  14. edit_buffer_backspace
  15. edit_buffer_get_forward_offset
  16. edit_buffer_get_backward_offset
  17. edit_buffer_read_file
  18. edit_buffer_write_file
  19. edit_buffer_calc_percent

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

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