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, gsize *cut)
     /* [previous][next][first][last][top][bottom][index][help]  */
 398 {
 399     off_t word_start;
 400     gsize cut_len = 0;
 401     GString *match_expr;
 402     int c1, c2;
 403 
 404     for (word_start = start_pos; word_start != 0; word_start--, cut_len++)
 405     {
 406         c1 = edit_buffer_get_byte (buf, word_start);
 407         c2 = edit_buffer_get_byte (buf, word_start - 1);
 408 
 409         if (is_break_char (c1) != is_break_char (c2) || c1 == '\n' || c2 == '\n')
 410             break;
 411     }
 412 
 413     match_expr = g_string_sized_new (16);
 414 
 415     do
 416     {
 417         c1 = edit_buffer_get_byte (buf, word_start + match_expr->len);
 418         c2 = edit_buffer_get_byte (buf, word_start + match_expr->len + 1);
 419         g_string_append_c (match_expr, c1);
 420     }
 421     while (!(is_break_char (c1) != is_break_char (c2) || c1 == '\n' || c2 == '\n'));
 422 
 423     *start = word_start;
 424     *cut = cut_len;
 425 
 426     return match_expr;
 427 }
 428 
 429 /* --------------------------------------------------------------------------------------------- */
 430 /**
 431  * Find first character of current word
 432  *
 433  * @param buf editor buffer
 434  * @param word_start position of first character of current word
 435  * @param word_len length of current word
 436  *
 437  * @return TRUE if first character of word is found and this character is not 1) a digit and
 438  *         2) a begin of file, FALSE otherwise
 439  */
 440 
 441 gboolean
 442 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]  */
 443 {
 444     int c;
 445     off_t i;
 446 
 447     /* return if at begin of file */
 448     if (buf->curs1 <= 0)
 449         return FALSE;
 450 
 451     c = edit_buffer_get_previous_byte (buf);
 452     /* return if not at end or in word */
 453     if (is_break_char (c))
 454         return FALSE;
 455 
 456     /* search start of word */
 457     for (i = 1;; i++)
 458     {
 459         int last;
 460 
 461         last = c;
 462         c = edit_buffer_get_byte (buf, buf->curs1 - i - 1);
 463 
 464         if (is_break_char (c))
 465         {
 466             /* return if word starts with digit */
 467             if (isdigit (last))
 468                 return FALSE;
 469 
 470             break;
 471         }
 472     }
 473 
 474     /* success */
 475     *word_start = buf->curs1 - i;       /* start found */
 476     *word_len = (gsize) i;
 477 
 478     return TRUE;
 479 }
 480 
 481 /* --------------------------------------------------------------------------------------------- */
 482 /**
 483  * Basic low level single character buffer alterations and movements at the cursor: insert character
 484  * at the cursor position and move right.
 485  *
 486  * @param buf pointer to editor buffer
 487  * @param c character to insert
 488  */
 489 
 490 void
 491 edit_buffer_insert (edit_buffer_t *buf, int c)
     /* [previous][next][first][last][top][bottom][index][help]  */
 492 {
 493     void *b;
 494     off_t i;
 495 
 496     i = buf->curs1 & M_EDIT_BUF_SIZE;
 497 
 498     /* add a new buffer if we've reached the end of the last one */
 499     if (i == 0)
 500         g_ptr_array_add (buf->b1, g_malloc0 (EDIT_BUF_SIZE));
 501 
 502     /* perform the insertion */
 503     b = g_ptr_array_index (buf->b1, buf->curs1 >> S_EDIT_BUF_SIZE);
 504     *((unsigned char *) b + i) = (unsigned char) c;
 505 
 506     /* update cursor position */
 507     buf->curs1++;
 508 
 509     /* update file length */
 510     buf->size++;
 511 }
 512 
 513 /* --------------------------------------------------------------------------------------------- */
 514 /**
 515  * Basic low level single character buffer alterations and movements at the cursor: insert character
 516  * at the cursor position and move left.
 517  *
 518  * @param buf pointer to editor buffer
 519  * @param c character to insert
 520  */
 521 
 522 void
 523 edit_buffer_insert_ahead (edit_buffer_t *buf, int c)
     /* [previous][next][first][last][top][bottom][index][help]  */
 524 {
 525     void *b;
 526     off_t i;
 527 
 528     i = buf->curs2 & M_EDIT_BUF_SIZE;
 529 
 530     /* add a new buffer if we've reached the end of the last one */
 531     if (i == 0)
 532         g_ptr_array_add (buf->b2, g_malloc0 (EDIT_BUF_SIZE));
 533 
 534     /* perform the insertion */
 535     b = g_ptr_array_index (buf->b2, buf->curs2 >> S_EDIT_BUF_SIZE);
 536     *((unsigned char *) b + EDIT_BUF_SIZE - 1 - i) = (unsigned char) c;
 537 
 538     /* update cursor position */
 539     buf->curs2++;
 540 
 541     /* update file length */
 542     buf->size++;
 543 }
 544 
 545 /* --------------------------------------------------------------------------------------------- */
 546 /**
 547  * Basic low level single character buffer alterations and movements at the cursor: delete character
 548  * at the cursor position.
 549  *
 550  * @param buf pointer to editor buffer
 551  * @param c character to insert
 552  */
 553 
 554 int
 555 edit_buffer_delete (edit_buffer_t *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 556 {
 557     void *b;
 558     unsigned char c;
 559     off_t prev;
 560     off_t i;
 561 
 562     prev = buf->curs2 - 1;
 563 
 564     b = g_ptr_array_index (buf->b2, prev >> S_EDIT_BUF_SIZE);
 565     i = prev & M_EDIT_BUF_SIZE;
 566     c = *((unsigned char *) b + EDIT_BUF_SIZE - 1 - i);
 567 
 568     if (i == 0)
 569     {
 570         guint j;
 571 
 572         j = buf->b2->len - 1;
 573         b = g_ptr_array_index (buf->b2, j);
 574         g_ptr_array_remove_index (buf->b2, j);
 575     }
 576 
 577     buf->curs2 = prev;
 578 
 579     /* update file length */
 580     buf->size--;
 581 
 582     return c;
 583 }
 584 
 585 /* --------------------------------------------------------------------------------------------- */
 586 /**
 587  * Basic low level single character buffer alterations and movements at the cursor: delete character
 588  * before the cursor position and move left.
 589  *
 590  * @param buf pointer to editor buffer
 591  * @param c character to insert
 592  */
 593 
 594 int
 595 edit_buffer_backspace (edit_buffer_t *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 596 {
 597     void *b;
 598     unsigned char c;
 599     off_t prev;
 600     off_t i;
 601 
 602     prev = buf->curs1 - 1;
 603 
 604     b = g_ptr_array_index (buf->b1, prev >> S_EDIT_BUF_SIZE);
 605     i = prev & M_EDIT_BUF_SIZE;
 606     c = *((unsigned char *) b + i);
 607 
 608     if (i == 0)
 609     {
 610         guint j;
 611 
 612         j = buf->b1->len - 1;
 613         b = g_ptr_array_index (buf->b1, j);
 614         g_ptr_array_remove_index (buf->b1, j);
 615     }
 616 
 617     buf->curs1 = prev;
 618 
 619     /* update file length */
 620     buf->size--;
 621 
 622     return c;
 623 }
 624 
 625 /* --------------------------------------------------------------------------------------------- */
 626 /**
 627  * Calculate forward offset with specified number of lines.
 628  *
 629  * @param buf editor buffer
 630  * @param current current offset
 631  * @param lines number of lines to move forward
 632  * @param upto offset to count lines between current and upto.
 633  *
 634  * @return If lines is zero returns the count of lines from current to upto.
 635  *         If upto is zero returns offset of lines forward current.
 636  *         Else returns forward offset with specified number of lines
 637  */
 638 
 639 off_t
 640 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]  */
 641 {
 642     if (upto != 0)
 643         return (off_t) edit_buffer_count_lines (buf, current, upto);
 644 
 645     lines = MAX (lines, 0);
 646 
 647     while (lines-- != 0)
 648     {
 649         long next;
 650 
 651         next = edit_buffer_get_eol (buf, current) + 1;
 652         if (next > buf->size)
 653             break;
 654         current = next;
 655     }
 656 
 657     return current;
 658 }
 659 
 660 /* --------------------------------------------------------------------------------------------- */
 661 /**
 662  * Calculate backward offset with specified number of lines.
 663  *
 664  * @param buf editor buffer
 665  * @param current current offset
 666  * @param lines number of lines to move backward
 667  *
 668  * @return backward offset with specified number of lines.
 669  */
 670 
 671 off_t
 672 edit_buffer_get_backward_offset (const edit_buffer_t *buf, off_t current, long lines)
     /* [previous][next][first][last][top][bottom][index][help]  */
 673 {
 674     lines = MAX (lines, 0);
 675     current = edit_buffer_get_bol (buf, current);
 676 
 677     while (lines-- != 0 && current != 0)
 678         current = edit_buffer_get_bol (buf, current - 1);
 679 
 680     return current;
 681 }
 682 
 683 /* --------------------------------------------------------------------------------------------- */
 684 /**
 685  * Load file into editor buffer
 686  *
 687  * @param buf pointer to editor buffer
 688  * @param fd file descriptor
 689  * @param size file size
 690  *
 691  * @return number of read bytes
 692  */
 693 
 694 off_t
 695 edit_buffer_read_file (edit_buffer_t *buf, int fd, off_t size,
     /* [previous][next][first][last][top][bottom][index][help]  */
 696                        edit_buffer_read_file_status_msg_t *sm, gboolean *aborted)
 697 {
 698     off_t ret = 0;
 699     off_t i, j;
 700     off_t data_size;
 701     void *b;
 702     status_msg_t *s = STATUS_MSG (sm);
 703     unsigned short update_cnt = 0;
 704 
 705     *aborted = FALSE;
 706 
 707     buf->lines = 0;
 708     buf->curs2 = size;
 709     i = buf->curs2 >> S_EDIT_BUF_SIZE;
 710 
 711     /* fill last part of b2 */
 712     data_size = buf->curs2 & M_EDIT_BUF_SIZE;
 713     if (data_size != 0)
 714     {
 715         b = g_malloc0 (EDIT_BUF_SIZE);
 716         g_ptr_array_add (buf->b2, b);
 717         b = (char *) b + EDIT_BUF_SIZE - data_size;
 718         ret = mc_read (fd, b, data_size);
 719 
 720         /* count lines */
 721         for (j = 0; j < ret; j++)
 722             if (*((char *) b + j) == '\n')
 723                 buf->lines++;
 724 
 725         if (ret < 0 || ret != data_size)
 726             return ret;
 727     }
 728 
 729     /* fulfill other parts of b2 from end to begin */
 730     data_size = EDIT_BUF_SIZE;
 731     for (--i; i >= 0; i--)
 732     {
 733         off_t sz;
 734 
 735         b = g_malloc0 (data_size);
 736         g_ptr_array_add (buf->b2, b);
 737         sz = mc_read (fd, b, data_size);
 738         if (sz >= 0)
 739             ret += sz;
 740 
 741         /* count lines */
 742         for (j = 0; j < sz; j++)
 743             if (*((char *) b + j) == '\n')
 744                 buf->lines++;
 745 
 746         if (s != NULL && s->update != NULL)
 747         {
 748             update_cnt = (update_cnt + 1) & 0xf;
 749             if (update_cnt == 0)
 750             {
 751                 /* FIXME: overcare */
 752                 if (sm->buf == NULL)
 753                     sm->buf = buf;
 754 
 755                 sm->loaded = ret;
 756                 if (s->update (s) == B_CANCEL)
 757                 {
 758                     *aborted = TRUE;
 759                     return (-1);
 760                 }
 761             }
 762         }
 763 
 764         if (sz != data_size)
 765             break;
 766     }
 767 
 768     /* reverse buffer */
 769     for (i = 0; i < (off_t) buf->b2->len / 2; i++)
 770     {
 771         void **b1, **b2;
 772 
 773         b1 = &g_ptr_array_index (buf->b2, i);
 774         b2 = &g_ptr_array_index (buf->b2, buf->b2->len - 1 - i);
 775 
 776         b = *b1;
 777         *b1 = *b2;
 778         *b2 = b;
 779 
 780         if (s != NULL && s->update != NULL)
 781         {
 782             update_cnt = (update_cnt + 1) & 0xf;
 783             if (update_cnt == 0)
 784             {
 785                 sm->loaded = ret;
 786                 if (s->update (s) == B_CANCEL)
 787                 {
 788                     *aborted = TRUE;
 789                     return (-1);
 790                 }
 791             }
 792         }
 793     }
 794 
 795     return ret;
 796 }
 797 
 798 /* --------------------------------------------------------------------------------------------- */
 799 /**
 800  * Write editor buffer content to file
 801  *
 802  * @param buf pointer to editor buffer
 803  * @param fd file descriptor
 804  *
 805  * @return number of written bytes
 806  */
 807 
 808 off_t
 809 edit_buffer_write_file (edit_buffer_t *buf, int fd)
     /* [previous][next][first][last][top][bottom][index][help]  */
 810 {
 811     off_t ret = 0;
 812     off_t i;
 813     off_t data_size, sz;
 814     void *b;
 815 
 816     /* write all fulfilled parts of b1 from begin to end */
 817     if (buf->b1->len != 0)
 818     {
 819         data_size = EDIT_BUF_SIZE;
 820         for (i = 0; i < (off_t) buf->b1->len - 1; i++)
 821         {
 822             b = g_ptr_array_index (buf->b1, i);
 823             sz = mc_write (fd, b, data_size);
 824             if (sz >= 0)
 825                 ret += sz;
 826             else if (i == 0)
 827                 ret = sz;
 828             if (sz != data_size)
 829                 return ret;
 830         }
 831 
 832         /* write last partially filled part of b1 */
 833         data_size = ((buf->curs1 - 1) & M_EDIT_BUF_SIZE) + 1;
 834         b = g_ptr_array_index (buf->b1, i);
 835         sz = mc_write (fd, b, data_size);
 836         if (sz >= 0)
 837             ret += sz;
 838         if (sz != data_size)
 839             return ret;
 840     }
 841 
 842     /* write b2 from end to begin, if b2 contains some data */
 843     if (buf->b2->len != 0)
 844     {
 845         /* write last partially filled part of b2 */
 846         i = buf->b2->len - 1;
 847         b = g_ptr_array_index (buf->b2, i);
 848         data_size = ((buf->curs2 - 1) & M_EDIT_BUF_SIZE) + 1;
 849         sz = mc_write (fd, (char *) b + EDIT_BUF_SIZE - data_size, data_size);
 850         if (sz >= 0)
 851             ret += sz;
 852 
 853         if (sz == data_size)
 854         {
 855             /* write other fulfilled parts of b2 from end to begin */
 856             data_size = EDIT_BUF_SIZE;
 857             while (--i >= 0)
 858             {
 859                 b = g_ptr_array_index (buf->b2, i);
 860                 sz = mc_write (fd, b, data_size);
 861                 if (sz >= 0)
 862                     ret += sz;
 863                 if (sz != data_size)
 864                     break;
 865             }
 866         }
 867     }
 868 
 869     return ret;
 870 }
 871 
 872 /* --------------------------------------------------------------------------------------------- */
 873 /**
 874  * Calculate percentage of specified character offset
 875  *
 876  * @param buf pointer to editor buffer
 877  * @param p character offset
 878  *
 879  * @return percentage of specified character offset
 880  */
 881 
 882 int
 883 edit_buffer_calc_percent (const edit_buffer_t *buf, off_t offset)
     /* [previous][next][first][last][top][bottom][index][help]  */
 884 {
 885     int percent;
 886 
 887     if (buf->size == 0)
 888         percent = 0;
 889     else if (offset >= buf->size)
 890         percent = 100;
 891     else if (offset > (INT_MAX / 100))
 892         percent = offset / (buf->size / 100);
 893     else
 894         percent = offset * 100 / buf->size;
 895 
 896     return percent;
 897 }
 898 
 899 /* --------------------------------------------------------------------------------------------- */

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