root/src/editor/edit.c

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

DEFINITIONS

This source file includes following definitions.
  1. edit_load_status_update_cb
  2. edit_load_file_fast
  3. edit_find_filter
  4. edit_get_filter
  5. edit_insert_stream
  6. check_file_access
  7. edit_load_file
  8. edit_load_position
  9. edit_save_position
  10. edit_purge_widget
  11. edit_pop_undo_action
  12. edit_pop_redo_action
  13. get_prev_undo_action
  14. edit_modification
  15. is_in_indent
  16. is_blank
  17. edit_find_line
  18. edit_move_up_paragraph
  19. edit_move_down_paragraph
  20. edit_begin_page
  21. edit_end_page
  22. edit_move_to_top
  23. edit_move_to_bottom
  24. edit_cursor_to_bol
  25. edit_cursor_to_eol
  26. my_type_of
  27. edit_left_word_move
  28. edit_left_word_move_cmd
  29. edit_right_word_move
  30. edit_right_word_move_cmd
  31. edit_right_char_move_cmd
  32. edit_left_char_move_cmd
  33. edit_move_updown
  34. edit_right_delete_word
  35. edit_left_delete_word
  36. edit_do_undo
  37. edit_do_redo
  38. edit_group_undo
  39. edit_delete_to_line_end
  40. edit_delete_to_line_begin
  41. is_aligned_on_a_tab
  42. right_of_four_spaces
  43. left_of_four_spaces
  44. edit_auto_indent
  45. edit_double_newline
  46. insert_spaces_tab
  47. edit_tab_cmd
  48. check_and_wrap_line
  49. edit_get_bracket
  50. edit_goto_matching_bracket
  51. edit_move_block_to_right
  52. edit_move_block_to_left
  53. edit_print_string
  54. edit_insert_column_from_file
  55. edit_user_menu
  56. edit_get_write_filter
  57. edit_write_stream
  58. is_break_char
  59. edit_insert_file
  60. edit_init
  61. edit_clean
  62. edit_reload_line
  63. edit_set_codeset
  64. edit_push_undo_action
  65. edit_push_redo_action
  66. edit_insert
  67. edit_insert_ahead
  68. edit_insert_over
  69. edit_delete
  70. edit_backspace
  71. edit_cursor_move
  72. edit_move_forward3
  73. edit_get_cursor_offset
  74. edit_get_col
  75. edit_update_curs_row
  76. edit_update_curs_col
  77. edit_get_curs_col
  78. edit_scroll_upward
  79. edit_scroll_downward
  80. edit_scroll_right
  81. edit_scroll_left
  82. edit_move_to_prev_col
  83. edit_line_is_blank
  84. edit_move_to_line
  85. edit_move_display
  86. edit_push_markers
  87. edit_set_markers
  88. eval_marks
  89. edit_mark_cmd
  90. edit_mark_current_word_cmd
  91. edit_mark_current_line_cmd
  92. edit_delete_line
  93. edit_push_key_press
  94. edit_find_bracket
  95. edit_execute_key_command
  96. edit_execute_cmd
  97. edit_stack_init
  98. edit_stack_free
  99. edit_move_up
  100. edit_move_down

   1 /*
   2    Editor low level data handling and cursor fundamentals.
   3 
   4    Copyright (C) 1996-2024
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Paul Sheer 1996, 1997
   9    Ilia Maslakov <il.smind@gmail.com> 2009, 2010, 2011
  10    Andrew Borodin <aborodin@vmail.ru> 2012-2022
  11 
  12    This file is part of the Midnight Commander.
  13 
  14    The Midnight Commander is free software: you can redistribute it
  15    and/or modify it under the terms of the GNU General Public License as
  16    published by the Free Software Foundation, either version 3 of the License,
  17    or (at your option) any later version.
  18 
  19    The Midnight Commander is distributed in the hope that it will be useful,
  20    but WITHOUT ANY WARRANTY; without even the implied warranty of
  21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22    GNU General Public License for more details.
  23 
  24    You should have received a copy of the GNU General Public License
  25    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  26  */
  27 
  28 /** \file
  29  *  \brief Source: editor low level data handling and cursor fundamentals
  30  *  \author Paul Sheer
  31  *  \date 1996, 1997
  32  */
  33 
  34 #include <config.h>
  35 #include <stdio.h>
  36 #include <stdarg.h>
  37 #include <sys/types.h>
  38 #include <unistd.h>
  39 #include <string.h>
  40 #include <ctype.h>
  41 #include <sys/stat.h>
  42 #include <stdint.h>             /* UINTMAX_MAX */
  43 #include <stdlib.h>
  44 
  45 #include "lib/global.h"
  46 
  47 #include "lib/tty/color.h"
  48 #include "lib/tty/tty.h"        /* attrset() */
  49 #include "lib/tty/key.h"        /* is_idle() */
  50 #include "lib/skin.h"           /* EDITOR_NORMAL_COLOR */
  51 #include "lib/fileloc.h"        /* EDIT_HOME_BLOCK_FILE */
  52 #include "lib/vfs/vfs.h"
  53 #include "lib/strutil.h"        /* utf string functions */
  54 #include "lib/util.h"           /* load_file_position(), save_file_position() */
  55 #include "lib/timefmt.h"        /* time formatting */
  56 #include "lib/lock.h"
  57 #include "lib/widget.h"
  58 
  59 #ifdef HAVE_CHARSET
  60 #include "lib/charsets.h"       /* get_codepage_id */
  61 #endif
  62 
  63 #include "src/usermenu.h"       /* user_menu_cmd() */
  64 
  65 #include "src/keymap.h"
  66 
  67 #include "edit-impl.h"
  68 #include "editwidget.h"
  69 #include "editsearch.h"
  70 #include "editcomplete.h"       /* edit_complete_word_cmd() */
  71 #include "editmacros.h"
  72 #include "etags.h"              /* edit_get_match_keyword_cmd() */
  73 #ifdef HAVE_ASPELL
  74 #include "spell.h"
  75 #endif
  76 
  77 /*** global variables ****************************************************************************/
  78 
  79 edit_options_t edit_options = {
  80     .word_wrap_line_length = DEFAULT_WRAP_LINE_LENGTH,
  81     .typewriter_wrap = FALSE,
  82     .auto_para_formatting = FALSE,
  83     .fill_tabs_with_spaces = FALSE,
  84     .return_does_auto_indent = TRUE,
  85     .backspace_through_tabs = FALSE,
  86     .fake_half_tabs = TRUE,
  87     .persistent_selections = TRUE,
  88     .drop_selection_on_copy = TRUE,
  89     .cursor_beyond_eol = FALSE,
  90     .cursor_after_inserted_block = FALSE,
  91     .state_full_filename = FALSE,
  92     .line_state = FALSE,
  93     .line_state_width = 0,
  94     .save_mode = EDIT_QUICK_SAVE,
  95     .confirm_save = TRUE,
  96     .save_position = TRUE,
  97     .syntax_highlighting = TRUE,
  98     .group_undo = FALSE,
  99     .backup_ext = NULL,
 100     .filesize_threshold = NULL,
 101     .stop_format_chars = NULL,
 102     .visible_tabs = TRUE,
 103     .visible_tws = TRUE,
 104     .show_right_margin = FALSE,
 105     .simple_statusbar = FALSE,
 106     .check_nl_at_eof = FALSE
 107 };
 108 
 109 int max_undo = 32768;
 110 
 111 gboolean enable_show_tabs_tws = TRUE;
 112 
 113 unsigned int edit_stack_iterator = 0;
 114 edit_stack_type edit_history_moveto[MAX_HISTORY_MOVETO];
 115 /* magic sequence for say than block is vertical */
 116 const char VERTICAL_MAGIC[] = { '\1', '\1', '\1', '\1', '\n' };
 117 
 118 /*** file scope macro definitions ****************************************************************/
 119 
 120 #define TEMP_BUF_LEN 1024
 121 
 122 #define space_width 1
 123 
 124 /*** file scope type declarations ****************************************************************/
 125 
 126 /*** forward declarations (file scope functions) *************************************************/
 127 
 128 /*** file scope variables ************************************************************************/
 129 
 130 /* detecting an error on save is easy: just check if every byte has been written. */
 131 /* detecting an error on read, is not so easy 'cos there is not way to tell
 132    whether you read everything or not. */
 133 /* FIXME: add proper 'triple_pipe_open' to read, write and check errors. */
 134 static const struct edit_filters
 135 {
 136     const char *read, *write, *extension;
 137 } all_filters[] =
 138 {
 139     /* *INDENT-OFF* */
 140     { "xz -cd %s 2>&1", "xz > %s", ".xz"},
 141     { "zstd -cd %s 2>&1", "zstd > %s", ".zst"},
 142     { "lz4 -cd %s 2>&1", "lz4 > %s", ".lz4" },
 143     { "lzip -cd %s 2>&1", "lzip > %s", ".lz"},
 144     { "lzma -cd %s 2>&1", "lzma > %s", ".lzma" },
 145     { "lzop -cd %s 2>&1", "lzop > %s", ".lzo"},
 146     { "bzip2 -cd %s 2>&1", "bzip2 > %s", ".bz2" },
 147     { "gzip -cd %s 2>&1", "gzip > %s", ".gz" },
 148     { "gzip -cd %s 2>&1", "gzip > %s", ".Z" }
 149     /* *INDENT-ON* */
 150 };
 151 
 152 static const off_t filesize_default_threshold = 64 * 1024 * 1024;       /* 64 MB */
 153 
 154 /* --------------------------------------------------------------------------------------------- */
 155 /*** file scope functions ************************************************************************/
 156 /* --------------------------------------------------------------------------------------------- */
 157 
 158 static int
 159 edit_load_status_update_cb (status_msg_t * sm)
     /* [previous][next][first][last][top][bottom][index][help]  */
 160 {
 161     simple_status_msg_t *ssm = SIMPLE_STATUS_MSG (sm);
 162     edit_buffer_read_file_status_msg_t *rsm = (edit_buffer_read_file_status_msg_t *) sm;
 163     Widget *wd = WIDGET (sm->dlg);
 164 
 165     if (verbose)
 166         label_set_textv (ssm->label, _("Loading: %3d%%"),
 167                          edit_buffer_calc_percent (rsm->buf, rsm->loaded));
 168     else
 169         label_set_text (ssm->label, _("Loading..."));
 170 
 171     if (rsm->first)
 172     {
 173         Widget *lw = WIDGET (ssm->label);
 174         WRect r;
 175 
 176         r = wd->rect;
 177         r.cols = MAX (r.cols, lw->rect.cols + 6);
 178         widget_set_size_rect (wd, &r);
 179         r = lw->rect;
 180         r.x = wd->rect.x + (wd->rect.cols - r.cols) / 2;
 181         widget_set_size_rect (lw, &r);
 182         rsm->first = FALSE;
 183     }
 184 
 185     return status_msg_common_update (sm);
 186 }
 187 
 188 /* --------------------------------------------------------------------------------------------- */
 189 /**
 190  * Load file OR text into buffers.  Set cursor to the beginning of file.
 191  *
 192  * @return FALSE on error.
 193  */
 194 
 195 static gboolean
 196 edit_load_file_fast (edit_buffer_t * buf, const vfs_path_t * filename_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 197 {
 198     int file;
 199     gboolean ret;
 200     edit_buffer_read_file_status_msg_t rsm;
 201     gboolean aborted;
 202 
 203     file = mc_open (filename_vpath, O_RDONLY | O_BINARY);
 204     if (file < 0)
 205     {
 206         gchar *errmsg;
 207 
 208         errmsg =
 209             g_strdup_printf (_("Cannot open %s for reading"), vfs_path_as_str (filename_vpath));
 210         edit_error_dialog (_("Error"), errmsg);
 211         g_free (errmsg);
 212         return FALSE;
 213     }
 214 
 215     rsm.first = TRUE;
 216     rsm.buf = buf;
 217     rsm.loaded = 0;
 218 
 219     status_msg_init (STATUS_MSG (&rsm), _("Load file"), 1.0, simple_status_msg_init_cb,
 220                      edit_load_status_update_cb, NULL);
 221 
 222     ret = (edit_buffer_read_file (buf, file, buf->size, &rsm, &aborted) == buf->size);
 223 
 224     status_msg_deinit (STATUS_MSG (&rsm));
 225 
 226     if (!ret && !aborted)
 227     {
 228         gchar *errmsg;
 229 
 230         errmsg = g_strdup_printf (_("Error reading %s"), vfs_path_as_str (filename_vpath));
 231         edit_error_dialog (_("Error"), errmsg);
 232         g_free (errmsg);
 233     }
 234 
 235     mc_close (file);
 236     return ret;
 237 }
 238 
 239 /* --------------------------------------------------------------------------------------------- */
 240 /** Return index of the filter or -1 is there is no appropriate filter */
 241 
 242 static int
 243 edit_find_filter (const vfs_path_t * filename_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 244 {
 245     if (filename_vpath != NULL)
 246     {
 247         const char *s;
 248         size_t i;
 249 
 250         s = vfs_path_as_str (filename_vpath);
 251 
 252         for (i = 0; i < G_N_ELEMENTS (all_filters); i++)
 253             if (g_str_has_suffix (s, all_filters[i].extension))
 254                 return i;
 255     }
 256 
 257     return -1;
 258 }
 259 
 260 /* --------------------------------------------------------------------------------------------- */
 261 
 262 static char *
 263 edit_get_filter (const vfs_path_t * filename_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 264 {
 265     int i;
 266     char *quoted_name;
 267     char *p = NULL;
 268 
 269     i = edit_find_filter (filename_vpath);
 270     if (i < 0)
 271         return NULL;
 272 
 273     quoted_name = name_quote (vfs_path_as_str (filename_vpath), FALSE);
 274     if (quoted_name != NULL)
 275     {
 276         p = g_strdup_printf (all_filters[i].read, quoted_name);
 277         g_free (quoted_name);
 278     }
 279 
 280     return p;
 281 }
 282 
 283 /* --------------------------------------------------------------------------------------------- */
 284 
 285 static off_t
 286 edit_insert_stream (WEdit * edit, FILE * f)
     /* [previous][next][first][last][top][bottom][index][help]  */
 287 {
 288     int c;
 289     off_t i;
 290 
 291     for (i = 0; (c = fgetc (f)) >= 0; i++)
 292         edit_insert (edit, c);
 293 
 294     return i;
 295 }
 296 
 297 /* --------------------------------------------------------------------------------------------- */
 298 /**
 299   * Open file and create it if necessary.
 300   *
 301   * @param edit           editor object
 302   * @param filename_vpath file name
 303   * @param st             buffer for store stat info
 304   * @return TRUE for success, FALSE for error.
 305   */
 306 
 307 static gboolean
 308 check_file_access (WEdit * edit, const vfs_path_t * filename_vpath, struct stat *st)
     /* [previous][next][first][last][top][bottom][index][help]  */
 309 {
 310     static uintmax_t threshold = UINTMAX_MAX;
 311     int file;
 312     gchar *errmsg = NULL;
 313     gboolean ret = TRUE;
 314 
 315     /* Try opening an existing file */
 316     file = mc_open (filename_vpath, O_NONBLOCK | O_RDONLY | O_BINARY, 0666);
 317     if (file < 0)
 318     {
 319         /*
 320          * Try creating the file. O_EXCL prevents following broken links
 321          * and opening existing files.
 322          */
 323         file = mc_open (filename_vpath, O_NONBLOCK | O_RDONLY | O_BINARY | O_CREAT | O_EXCL, 0666);
 324         if (file < 0)
 325         {
 326             errmsg =
 327                 g_strdup_printf (_("Cannot open %s for reading"), vfs_path_as_str (filename_vpath));
 328             goto cleanup;
 329         }
 330 
 331         /* New file, delete it if it's not modified or saved */
 332         edit->delete_file = 1;
 333     }
 334 
 335     /* Check what we have opened */
 336     if (mc_fstat (file, st) < 0)
 337     {
 338         errmsg =
 339             g_strdup_printf (_("Cannot get size/permissions for %s"),
 340                              vfs_path_as_str (filename_vpath));
 341         goto cleanup;
 342     }
 343 
 344     /* We want to open regular files only */
 345     if (!S_ISREG (st->st_mode))
 346     {
 347         errmsg =
 348             g_strdup_printf (_("\"%s\" is not a regular file"), vfs_path_as_str (filename_vpath));
 349         goto cleanup;
 350     }
 351 
 352     /* get file size threshold for alarm */
 353     if (threshold == UINTMAX_MAX)
 354     {
 355         gboolean err = FALSE;
 356 
 357         threshold = parse_integer (edit_options.filesize_threshold, &err);
 358         if (err)
 359             threshold = filesize_default_threshold;
 360     }
 361 
 362     /*
 363      * Don't delete non-empty files.
 364      * O_EXCL should prevent it, but let's be on the safe side.
 365      */
 366     if (st->st_size > 0)
 367         edit->delete_file = 0;
 368 
 369     if ((uintmax_t) st->st_size > threshold)
 370     {
 371         int act;
 372 
 373         errmsg = g_strdup_printf (_("File \"%s\" is too large.\nOpen it anyway?"),
 374                                   vfs_path_as_str (filename_vpath));
 375         act = edit_query_dialog2 (_("Warning"), errmsg, _("&Yes"), _("&No"));
 376         MC_PTR_FREE (errmsg);
 377 
 378         if (act != 0)
 379             ret = FALSE;
 380     }
 381 
 382   cleanup:
 383     (void) mc_close (file);
 384 
 385     if (errmsg != NULL)
 386     {
 387         edit_error_dialog (_("Error"), errmsg);
 388         g_free (errmsg);
 389         ret = FALSE;
 390     }
 391 
 392     return ret;
 393 }
 394 
 395 /* --------------------------------------------------------------------------------------------- */
 396 
 397 /**
 398  * Open the file and load it into the buffers, either directly or using
 399  * a filter.  Return TRUE on success, FALSE on error.
 400  *
 401  * Fast loading (edit_load_file_fast) is used when the file size is
 402  * known.  In this case the data is read into the buffers by blocks.
 403  * If the file size is not known, the data is loaded byte by byte in
 404  * edit_insert_file.
 405  *
 406  * @param edit editor object
 407  * @return TRUE if file was successfully opened and loaded to buffers, FALSE otherwise
 408  */
 409 static gboolean
 410 edit_load_file (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 411 {
 412     gboolean fast_load = TRUE;
 413 
 414     /* Cannot do fast load if a filter is used */
 415     if (edit_find_filter (edit->filename_vpath) >= 0)
 416         fast_load = FALSE;
 417 
 418     /*
 419      * FIXME: line end translation should disable fast loading as well
 420      * Consider doing fseek() to the end and ftell() for the real size.
 421      */
 422     if (edit->filename_vpath != NULL)
 423     {
 424         /*
 425          * VFS may report file size incorrectly, and slow load is not a big
 426          * deal considering overhead in VFS.
 427          */
 428         if (!vfs_file_is_local (edit->filename_vpath))
 429             fast_load = FALSE;
 430 
 431         /* If we are dealing with a real file, check that it exists */
 432         if (!check_file_access (edit, edit->filename_vpath, &edit->stat1))
 433         {
 434             edit_clean (edit);
 435             return FALSE;
 436         }
 437     }
 438     else
 439     {
 440         /* nothing to load */
 441         fast_load = FALSE;
 442     }
 443 
 444     if (fast_load)
 445     {
 446         edit_buffer_init (&edit->buffer, edit->stat1.st_size);
 447 
 448         if (!edit_load_file_fast (&edit->buffer, edit->filename_vpath))
 449         {
 450             edit_clean (edit);
 451             return FALSE;
 452         }
 453     }
 454     else
 455     {
 456         edit_buffer_init (&edit->buffer, 0);
 457 
 458         if (edit->filename_vpath != NULL
 459             && *(vfs_path_get_by_index (edit->filename_vpath, 0)->path) != '\0')
 460         {
 461             edit->undo_stack_disable = 1;
 462             if (edit_insert_file (edit, edit->filename_vpath) < 0)
 463             {
 464                 edit_clean (edit);
 465                 return FALSE;
 466             }
 467             edit->undo_stack_disable = 0;
 468         }
 469     }
 470     edit->lb = LB_ASIS;
 471     return TRUE;
 472 }
 473 
 474 /* --------------------------------------------------------------------------------------------- */
 475 /**
 476  * Restore saved cursor position and/or bookmarks in the file
 477  *
 478  * @param edit editor object
 479  * @param load_position If TRUE, load bookmarks and cursor position and apply them.
 480  *                      If FALSE, load bookmarks only.
 481  */
 482 
 483 static void
 484 edit_load_position (WEdit * edit, gboolean load_position)
     /* [previous][next][first][last][top][bottom][index][help]  */
 485 {
 486     long line, column;
 487     off_t offset;
 488     off_t b;
 489 
 490     if (edit->filename_vpath == NULL
 491         || *(vfs_path_get_by_index (edit->filename_vpath, 0)->path) == '\0')
 492         return;
 493 
 494     load_file_position (edit->filename_vpath, &line, &column, &offset, &edit->serialized_bookmarks);
 495     /* apply bookmarks in any case */
 496     book_mark_restore (edit, BOOK_MARK_COLOR);
 497 
 498     if (!load_position)
 499         return;
 500 
 501     if (line > 0)
 502     {
 503         edit_move_to_line (edit, line - 1);
 504         edit->prev_col = column;
 505     }
 506     else if (offset > 0)
 507     {
 508         edit_cursor_move (edit, offset);
 509         line = edit->buffer.curs_line;
 510         edit->search_start = edit->buffer.curs1;
 511     }
 512 
 513     b = edit_buffer_get_current_bol (&edit->buffer);
 514     edit_move_to_prev_col (edit, b);
 515     edit_move_display (edit, line - (WIDGET (edit)->rect.lines / 2));
 516 }
 517 
 518 /* --------------------------------------------------------------------------------------------- */
 519 /** Save cursor position in the file */
 520 
 521 static void
 522 edit_save_position (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 523 {
 524     if (edit->filename_vpath == NULL
 525         || *(vfs_path_get_by_index (edit->filename_vpath, 0)->path) == '\0')
 526         return;
 527 
 528     book_mark_serialize (edit, BOOK_MARK_COLOR);
 529     save_file_position (edit->filename_vpath, edit->buffer.curs_line + 1, edit->curs_col,
 530                         edit->buffer.curs1, edit->serialized_bookmarks);
 531     edit->serialized_bookmarks = NULL;
 532 }
 533 
 534 /* --------------------------------------------------------------------------------------------- */
 535 /** Clean the WEdit stricture except the widget part */
 536 
 537 static void
 538 edit_purge_widget (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 539 {
 540     size_t len = sizeof (WEdit) - sizeof (Widget);
 541     char *start = (char *) edit + sizeof (Widget);
 542     memset (start, 0, len);
 543 }
 544 
 545 /* --------------------------------------------------------------------------------------------- */
 546 
 547 /*
 548    TODO: if the user undos until the stack bottom, and the stack has not wrapped,
 549    then the file should be as it was when he loaded up. Then set edit->modified to 0.
 550  */
 551 
 552 static long
 553 edit_pop_undo_action (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 554 {
 555     long c;
 556     unsigned long sp = edit->undo_stack_pointer;
 557 
 558     if (sp == edit->undo_stack_bottom)
 559         return STACK_BOTTOM;
 560 
 561     sp = (sp - 1) & edit->undo_stack_size_mask;
 562     c = edit->undo_stack[sp];
 563     if (c >= 0)
 564     {
 565         /*      edit->undo_stack[sp] = '@'; */
 566         edit->undo_stack_pointer = (edit->undo_stack_pointer - 1) & edit->undo_stack_size_mask;
 567         return c;
 568     }
 569 
 570     if (sp == edit->undo_stack_bottom)
 571         return STACK_BOTTOM;
 572 
 573     c = edit->undo_stack[(sp - 1) & edit->undo_stack_size_mask];
 574     if (edit->undo_stack[sp] == -2)
 575     {
 576         /*      edit->undo_stack[sp] = '@'; */
 577         edit->undo_stack_pointer = sp;
 578     }
 579     else
 580         edit->undo_stack[sp]++;
 581 
 582     return c;
 583 }
 584 
 585 /* --------------------------------------------------------------------------------------------- */
 586 
 587 static long
 588 edit_pop_redo_action (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 589 {
 590     long c;
 591     unsigned long sp = edit->redo_stack_pointer;
 592 
 593     if (sp == edit->redo_stack_bottom)
 594         return STACK_BOTTOM;
 595 
 596     sp = (sp - 1) & edit->redo_stack_size_mask;
 597     c = edit->redo_stack[sp];
 598     if (c >= 0)
 599     {
 600         edit->redo_stack_pointer = (edit->redo_stack_pointer - 1) & edit->redo_stack_size_mask;
 601         return c;
 602     }
 603 
 604     if (sp == edit->redo_stack_bottom)
 605         return STACK_BOTTOM;
 606 
 607     c = edit->redo_stack[(sp - 1) & edit->redo_stack_size_mask];
 608     if (edit->redo_stack[sp] == -2)
 609         edit->redo_stack_pointer = sp;
 610     else
 611         edit->redo_stack[sp]++;
 612 
 613     return c;
 614 }
 615 
 616 /* --------------------------------------------------------------------------------------------- */
 617 
 618 static long
 619 get_prev_undo_action (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 620 {
 621     long c;
 622     unsigned long sp = edit->undo_stack_pointer;
 623 
 624     if (sp == edit->undo_stack_bottom)
 625         return STACK_BOTTOM;
 626 
 627     sp = (sp - 1) & edit->undo_stack_size_mask;
 628     c = edit->undo_stack[sp];
 629     if (c >= 0)
 630         return c;
 631 
 632     if (sp == edit->undo_stack_bottom)
 633         return STACK_BOTTOM;
 634 
 635     c = edit->undo_stack[(sp - 1) & edit->undo_stack_size_mask];
 636     return c;
 637 }
 638 
 639 /* --------------------------------------------------------------------------------------------- */
 640 /** is called whenever a modification is made by one of the four routines below */
 641 
 642 static void
 643 edit_modification (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 644 {
 645     edit->caches_valid = FALSE;
 646 
 647     /* raise lock when file modified */
 648     if (edit->modified == 0 && edit->delete_file == 0)
 649         edit->locked = lock_file (edit->filename_vpath);
 650     edit->modified = 1;
 651 }
 652 
 653 /* --------------------------------------------------------------------------------------------- */
 654 /* high level cursor movement commands */
 655 /* --------------------------------------------------------------------------------------------- */
 656 /** check whether cursor is in indent part of line
 657  *
 658  * @param edit editor object
 659  *
 660  * @return TRUE if cursor is in indent, FALSE otherwise
 661  */
 662 
 663 static gboolean
 664 is_in_indent (const edit_buffer_t * buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 665 {
 666     off_t p;
 667 
 668     for (p = edit_buffer_get_current_bol (buf); p < buf->curs1; p++)
 669         if (strchr (" \t", edit_buffer_get_byte (buf, p)) == NULL)
 670             return FALSE;
 671 
 672     return TRUE;
 673 }
 674 
 675 /* --------------------------------------------------------------------------------------------- */
 676 /** check whether line in editor is blank or not
 677  *
 678  * @param edit editor object
 679  * @param offset position in file
 680  *
 681  * @return TRUE if line in blank, FALSE otherwise
 682  */
 683 
 684 static gboolean
 685 is_blank (const edit_buffer_t * buf, off_t offset)
     /* [previous][next][first][last][top][bottom][index][help]  */
 686 {
 687     off_t s, f;
 688 
 689     s = edit_buffer_get_bol (buf, offset);
 690     f = edit_buffer_get_eol (buf, offset);
 691     for (; s < f; s++)
 692     {
 693         int c;
 694 
 695         c = edit_buffer_get_byte (buf, s);
 696         if (!isspace (c))
 697             return FALSE;
 698     }
 699     return TRUE;
 700 }
 701 
 702 /* --------------------------------------------------------------------------------------------- */
 703 /** returns the offset of line i */
 704 
 705 static off_t
 706 edit_find_line (WEdit * edit, long line)
     /* [previous][next][first][last][top][bottom][index][help]  */
 707 {
 708     long i;
 709     long j = 0;
 710     long m = 2000000000;        /* what is the magic number? */
 711 
 712     if (!edit->caches_valid)
 713     {
 714         memset (edit->line_numbers, 0, sizeof (edit->line_numbers));
 715         memset (edit->line_offsets, 0, sizeof (edit->line_offsets));
 716         /* three offsets that we *know* are line 0 at 0 and these two: */
 717         edit->line_numbers[1] = edit->buffer.curs_line;
 718         edit->line_offsets[1] = edit_buffer_get_current_bol (&edit->buffer);
 719         edit->line_numbers[2] = edit->buffer.lines;
 720         edit->line_offsets[2] = edit_buffer_get_bol (&edit->buffer, edit->buffer.size);
 721         edit->caches_valid = TRUE;
 722     }
 723     if (line >= edit->buffer.lines)
 724         return edit->line_offsets[2];
 725     if (line <= 0)
 726         return 0;
 727     /* find the closest known point */
 728     for (i = 0; i < N_LINE_CACHES; i++)
 729     {
 730         long n;
 731 
 732         n = labs (edit->line_numbers[i] - line);
 733         if (n < m)
 734         {
 735             m = n;
 736             j = i;
 737         }
 738     }
 739     if (m == 0)
 740         return edit->line_offsets[j];   /* know the offset exactly */
 741     if (m == 1 && j >= 3)
 742         i = j;                  /* one line different - caller might be looping, so stay in this cache */
 743     else
 744         i = 3 + (rand () % (N_LINE_CACHES - 3));
 745     if (line > edit->line_numbers[j])
 746         edit->line_offsets[i] =
 747             edit_buffer_get_forward_offset (&edit->buffer, edit->line_offsets[j],
 748                                             line - edit->line_numbers[j], 0);
 749     else
 750         edit->line_offsets[i] =
 751             edit_buffer_get_backward_offset (&edit->buffer, edit->line_offsets[j],
 752                                              edit->line_numbers[j] - line);
 753     edit->line_numbers[i] = line;
 754     return edit->line_offsets[i];
 755 }
 756 
 757 /* --------------------------------------------------------------------------------------------- */
 758 /** moves up until a blank line is reached, or until just
 759    before a non-blank line is reached */
 760 
 761 static void
 762 edit_move_up_paragraph (WEdit * edit, gboolean do_scroll)
     /* [previous][next][first][last][top][bottom][index][help]  */
 763 {
 764     long i = 0;
 765 
 766     if (edit->buffer.curs_line > 1)
 767     {
 768         if (!edit_line_is_blank (edit, edit->buffer.curs_line))
 769         {
 770             for (i = edit->buffer.curs_line - 1; i != 0; i--)
 771                 if (edit_line_is_blank (edit, i))
 772                     break;
 773         }
 774         else if (edit_line_is_blank (edit, edit->buffer.curs_line - 1))
 775         {
 776             for (i = edit->buffer.curs_line - 1; i != 0; i--)
 777                 if (!edit_line_is_blank (edit, i))
 778                 {
 779                     i++;
 780                     break;
 781                 }
 782         }
 783         else
 784         {
 785             for (i = edit->buffer.curs_line - 1; i != 0; i--)
 786                 if (edit_line_is_blank (edit, i))
 787                     break;
 788         }
 789     }
 790 
 791     edit_move_up (edit, edit->buffer.curs_line - i, do_scroll);
 792 }
 793 
 794 /* --------------------------------------------------------------------------------------------- */
 795 /** moves down until a blank line is reached, or until just
 796    before a non-blank line is reached */
 797 
 798 static void
 799 edit_move_down_paragraph (WEdit * edit, gboolean do_scroll)
     /* [previous][next][first][last][top][bottom][index][help]  */
 800 {
 801     long i;
 802 
 803     if (edit->buffer.curs_line >= edit->buffer.lines - 1)
 804         i = edit->buffer.lines;
 805     else if (!edit_line_is_blank (edit, edit->buffer.curs_line))
 806     {
 807         for (i = edit->buffer.curs_line + 1; i != 0; i++)
 808             if (edit_line_is_blank (edit, i) || i >= edit->buffer.lines)
 809                 break;
 810     }
 811     else if (edit_line_is_blank (edit, edit->buffer.curs_line + 1))
 812     {
 813         for (i = edit->buffer.curs_line + 1; i != 0; i++)
 814             if (!edit_line_is_blank (edit, i) || i > edit->buffer.lines)
 815             {
 816                 i--;
 817                 break;
 818             }
 819     }
 820     else
 821     {
 822         for (i = edit->buffer.curs_line + 1; i != 0; i++)
 823             if (edit_line_is_blank (edit, i) || i >= edit->buffer.lines)
 824                 break;
 825     }
 826     edit_move_down (edit, i - edit->buffer.curs_line, do_scroll);
 827 }
 828 
 829 /* --------------------------------------------------------------------------------------------- */
 830 
 831 static void
 832 edit_begin_page (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 833 {
 834     edit_update_curs_row (edit);
 835     edit_move_up (edit, edit->curs_row, FALSE);
 836 }
 837 
 838 /* --------------------------------------------------------------------------------------------- */
 839 
 840 static void
 841 edit_end_page (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 842 {
 843     edit_update_curs_row (edit);
 844     edit_move_down (edit, WIDGET (edit)->rect.lines - edit->curs_row - 1, FALSE);
 845 }
 846 
 847 
 848 /* --------------------------------------------------------------------------------------------- */
 849 /** goto beginning of text */
 850 
 851 static void
 852 edit_move_to_top (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 853 {
 854     if (edit->buffer.curs_line != 0)
 855     {
 856         edit_cursor_move (edit, -edit->buffer.curs1);
 857         edit_move_to_prev_col (edit, 0);
 858         edit->force |= REDRAW_PAGE;
 859         edit->search_start = 0;
 860         edit_update_curs_row (edit);
 861     }
 862 }
 863 
 864 /* --------------------------------------------------------------------------------------------- */
 865 /** goto end of text */
 866 
 867 static void
 868 edit_move_to_bottom (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 869 {
 870     if (edit->buffer.curs_line < edit->buffer.lines)
 871     {
 872         edit_move_down (edit, edit->buffer.lines - edit->curs_row, FALSE);
 873         edit->start_display = edit->buffer.size;
 874         edit->start_line = edit->buffer.lines;
 875         edit_scroll_upward (edit, WIDGET (edit)->rect.lines - 1);
 876         edit->force |= REDRAW_PAGE;
 877     }
 878 }
 879 
 880 /* --------------------------------------------------------------------------------------------- */
 881 /** goto beginning of line */
 882 
 883 static void
 884 edit_cursor_to_bol (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 885 {
 886     off_t b;
 887 
 888     b = edit_buffer_get_current_bol (&edit->buffer);
 889     edit_cursor_move (edit, b - edit->buffer.curs1);
 890     edit->search_start = edit->buffer.curs1;
 891     edit->prev_col = edit_get_col (edit);
 892     edit->over_col = 0;
 893 }
 894 
 895 /* --------------------------------------------------------------------------------------------- */
 896 /** goto end of line */
 897 
 898 static void
 899 edit_cursor_to_eol (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 900 {
 901     off_t b;
 902 
 903     b = edit_buffer_get_current_eol (&edit->buffer);
 904     edit_cursor_move (edit, b - edit->buffer.curs1);
 905     edit->search_start = edit->buffer.curs1;
 906     edit->prev_col = edit_get_col (edit);
 907     edit->over_col = 0;
 908 }
 909 
 910 /* --------------------------------------------------------------------------------------------- */
 911 
 912 static unsigned long
 913 my_type_of (int c)
     /* [previous][next][first][last][top][bottom][index][help]  */
 914 {
 915     unsigned long r = 0;
 916     const char *q;
 917     const char chars_move_whole_word[] =
 918         "!=&|<>^~ !:;, !'!`!.?!\"!( !) !{ !} !Aa0 !+-*/= |<> ![ !] !\\#! ";
 919 
 920     if (c == 0)
 921         return 0;
 922     if (c == '!')
 923         return 2;
 924 
 925     if (g_ascii_isupper ((gchar) c))
 926         c = 'A';
 927     else if (g_ascii_islower ((gchar) c))
 928         c = 'a';
 929     else if (g_ascii_isalpha (c))
 930         c = 'a';
 931     else if (isdigit (c))
 932         c = '0';
 933     else if (isspace (c))
 934         c = ' ';
 935     q = strchr (chars_move_whole_word, c);
 936     if (q == NULL)
 937         return 0xFFFFFFFFUL;
 938 
 939     do
 940     {
 941         unsigned long x;
 942         const char *p;
 943 
 944         for (x = 1, p = chars_move_whole_word; p < q; p++)
 945             if (*p == '!')
 946                 x <<= 1;
 947         r |= x;
 948     }
 949     while ((q = strchr (q + 1, c)) != NULL);
 950 
 951     return r;
 952 }
 953 
 954 /* --------------------------------------------------------------------------------------------- */
 955 
 956 static void
 957 edit_left_word_move (WEdit * edit, int s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 958 {
 959     while (TRUE)
 960     {
 961         int c1, c2;
 962 
 963         if (edit->column_highlight
 964             && edit->mark1 != edit->mark2
 965             && edit->over_col == 0
 966             && edit->buffer.curs1 == edit_buffer_get_current_bol (&edit->buffer))
 967             break;
 968         edit_cursor_move (edit, -1);
 969         if (edit->buffer.curs1 == 0)
 970             break;
 971         c1 = edit_buffer_get_previous_byte (&edit->buffer);
 972         if (c1 == '\n')
 973             break;
 974         c2 = edit_buffer_get_current_byte (&edit->buffer);
 975         if (c2 == '\n')
 976             break;
 977         if ((my_type_of (c1) & my_type_of (c2)) == 0)
 978             break;
 979         if (isspace (c1) && !isspace (c2))
 980             break;
 981         if (s != 0 && !isspace (c1) && isspace (c2))
 982             break;
 983     }
 984 }
 985 
 986 /* --------------------------------------------------------------------------------------------- */
 987 
 988 static void
 989 edit_left_word_move_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 990 {
 991     edit_left_word_move (edit, 0);
 992     edit->force |= REDRAW_PAGE;
 993 }
 994 
 995 /* --------------------------------------------------------------------------------------------- */
 996 
 997 static void
 998 edit_right_word_move (WEdit * edit, int s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 999 {
1000     while (TRUE)
1001     {
1002         int c1, c2;
1003 
1004         if (edit->column_highlight
1005             && edit->mark1 != edit->mark2
1006             && edit->over_col == 0
1007             && edit->buffer.curs1 == edit_buffer_get_current_eol (&edit->buffer))
1008             break;
1009         edit_cursor_move (edit, 1);
1010         if (edit->buffer.curs1 >= edit->buffer.size)
1011             break;
1012         c1 = edit_buffer_get_previous_byte (&edit->buffer);
1013         if (c1 == '\n')
1014             break;
1015         c2 = edit_buffer_get_current_byte (&edit->buffer);
1016         if (c2 == '\n')
1017             break;
1018         if ((my_type_of (c1) & my_type_of (c2)) == 0)
1019             break;
1020         if (isspace (c1) && !isspace (c2))
1021             break;
1022         if (s != 0 && !isspace (c1) && isspace (c2))
1023             break;
1024     }
1025 }
1026 
1027 /* --------------------------------------------------------------------------------------------- */
1028 
1029 static void
1030 edit_right_word_move_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1031 {
1032     edit_right_word_move (edit, 0);
1033     edit->force |= REDRAW_PAGE;
1034 }
1035 
1036 /* --------------------------------------------------------------------------------------------- */
1037 
1038 static void
1039 edit_right_char_move_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1040 {
1041     int char_length = 1;
1042     int c;
1043 
1044 #ifdef HAVE_CHARSET
1045     if (edit->utf8)
1046     {
1047         c = edit_buffer_get_utf (&edit->buffer, edit->buffer.curs1, &char_length);
1048         if (char_length < 1)
1049             char_length = 1;
1050     }
1051     else
1052 #endif
1053         c = edit_buffer_get_current_byte (&edit->buffer);
1054 
1055     if (edit_options.cursor_beyond_eol && c == '\n')
1056         edit->over_col++;
1057     else
1058         edit_cursor_move (edit, char_length);
1059 }
1060 
1061 /* --------------------------------------------------------------------------------------------- */
1062 
1063 static void
1064 edit_left_char_move_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1065 {
1066     int char_length = 1;
1067 
1068     if (edit->column_highlight
1069         && edit_options.cursor_beyond_eol
1070         && edit->mark1 != edit->mark2
1071         && edit->over_col == 0 && edit->buffer.curs1 == edit_buffer_get_current_bol (&edit->buffer))
1072         return;
1073 #ifdef HAVE_CHARSET
1074     if (edit->utf8)
1075     {
1076         edit_buffer_get_prev_utf (&edit->buffer, edit->buffer.curs1, &char_length);
1077         if (char_length < 1)
1078             char_length = 1;
1079     }
1080 #endif
1081 
1082     if (edit_options.cursor_beyond_eol && edit->over_col > 0)
1083         edit->over_col--;
1084     else
1085         edit_cursor_move (edit, -char_length);
1086 }
1087 
1088 /* --------------------------------------------------------------------------------------------- */
1089 /** Up or down cursor moving.
1090  direction = TRUE - move up
1091            = FALSE - move down
1092 */
1093 
1094 static void
1095 edit_move_updown (WEdit * edit, long lines, gboolean do_scroll, gboolean direction)
     /* [previous][next][first][last][top][bottom][index][help]  */
1096 {
1097     long p;
1098     long l = direction ? edit->buffer.curs_line : edit->buffer.lines - edit->buffer.curs_line;
1099 
1100     if (lines > l)
1101         lines = l;
1102 
1103     if (lines == 0)
1104         return;
1105 
1106     if (lines > 1)
1107         edit->force |= REDRAW_PAGE;
1108     if (do_scroll)
1109     {
1110         if (direction)
1111             edit_scroll_upward (edit, lines);
1112         else
1113             edit_scroll_downward (edit, lines);
1114     }
1115     p = edit_buffer_get_current_bol (&edit->buffer);
1116     p = direction ? edit_buffer_get_backward_offset (&edit->buffer, p, lines) :
1117         edit_buffer_get_forward_offset (&edit->buffer, p, lines, 0);
1118     edit_cursor_move (edit, p - edit->buffer.curs1);
1119     edit_move_to_prev_col (edit, p);
1120 
1121 #ifdef HAVE_CHARSET
1122     /* search start of current multibyte char (like CJK) */
1123     if (edit->buffer.curs1 > 0 && edit->buffer.curs1 + 1 < edit->buffer.size
1124         && edit_buffer_get_current_byte (&edit->buffer) >= 256)
1125     {
1126         edit_right_char_move_cmd (edit);
1127         edit_left_char_move_cmd (edit);
1128     }
1129 #endif
1130 
1131     edit->search_start = edit->buffer.curs1;
1132     edit->found_len = 0;
1133 }
1134 
1135 /* --------------------------------------------------------------------------------------------- */
1136 
1137 static void
1138 edit_right_delete_word (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1139 {
1140     while (edit->buffer.curs1 < edit->buffer.size)
1141     {
1142         int c1, c2;
1143 
1144         c1 = edit_delete (edit, TRUE);
1145         if (c1 == '\n')
1146             break;
1147         c2 = edit_buffer_get_current_byte (&edit->buffer);
1148         if (c2 == '\n')
1149             break;
1150         if ((isspace (c1) == 0) != (isspace (c2) == 0))
1151             break;
1152         if ((my_type_of (c1) & my_type_of (c2)) == 0)
1153             break;
1154     }
1155 }
1156 
1157 /* --------------------------------------------------------------------------------------------- */
1158 
1159 static void
1160 edit_left_delete_word (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1161 {
1162     while (edit->buffer.curs1 > 0)
1163     {
1164         int c1, c2;
1165 
1166         c1 = edit_backspace (edit, TRUE);
1167         if (c1 == '\n')
1168             break;
1169         c2 = edit_buffer_get_previous_byte (&edit->buffer);
1170         if (c2 == '\n')
1171             break;
1172         if ((isspace (c1) == 0) != (isspace (c2) == 0))
1173             break;
1174         if ((my_type_of (c1) & my_type_of (c2)) == 0)
1175             break;
1176     }
1177 }
1178 
1179 /* --------------------------------------------------------------------------------------------- */
1180 /**
1181    the start column position is not recorded, and hence does not
1182    undo as it happed. But who would notice.
1183  */
1184 
1185 static void
1186 edit_do_undo (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1187 {
1188     long ac;
1189     long count = 0;
1190 
1191     edit->undo_stack_disable = 1;       /* don't record undo's onto undo stack! */
1192     edit->over_col = 0;
1193 
1194     while ((ac = edit_pop_undo_action (edit)) < KEY_PRESS)
1195     {
1196         off_t b;
1197 
1198         switch ((int) ac)
1199         {
1200         case STACK_BOTTOM:
1201             goto done_undo;
1202         case CURS_RIGHT:
1203             edit_cursor_move (edit, 1);
1204             break;
1205         case CURS_LEFT:
1206             edit_cursor_move (edit, -1);
1207             break;
1208         case BACKSPACE:
1209         case BACKSPACE_BR:
1210             edit_backspace (edit, TRUE);
1211             break;
1212         case DELCHAR:
1213         case DELCHAR_BR:
1214             edit_delete (edit, TRUE);
1215             break;
1216         case COLUMN_ON:
1217             edit->column_highlight = 1;
1218             break;
1219         case COLUMN_OFF:
1220             edit->column_highlight = 0;
1221             break;
1222         default:
1223             break;
1224         }
1225         if (ac >= 256 && ac < 512)
1226             edit_insert_ahead (edit, ac - 256);
1227         if (ac >= 0 && ac < 256)
1228             edit_insert (edit, ac);
1229 
1230         if (ac >= MARK_1 - 2 && ac < MARK_2 - 2)
1231         {
1232             edit->mark1 = ac - MARK_1;
1233             b = edit_buffer_get_bol (&edit->buffer, edit->mark1);
1234             edit->column1 = (long) edit_move_forward3 (edit, b, 0, edit->mark1);
1235         }
1236         if (ac >= MARK_2 - 2 && ac < MARK_CURS - 2)
1237         {
1238             edit->mark2 = ac - MARK_2;
1239             b = edit_buffer_get_bol (&edit->buffer, edit->mark2);
1240             edit->column2 = (long) edit_move_forward3 (edit, b, 0, edit->mark2);
1241         }
1242         else if (ac >= MARK_CURS - 2 && ac < KEY_PRESS)
1243         {
1244             edit->end_mark_curs = ac - MARK_CURS;
1245         }
1246         if (count++)
1247             edit->force |= REDRAW_PAGE; /* more than one pop usually means something big */
1248     }
1249 
1250     if (edit->start_display > ac - KEY_PRESS)
1251     {
1252         edit->start_line -=
1253             edit_buffer_count_lines (&edit->buffer, ac - KEY_PRESS, edit->start_display);
1254         edit->force |= REDRAW_PAGE;
1255     }
1256     else if (edit->start_display < ac - KEY_PRESS)
1257     {
1258         edit->start_line +=
1259             edit_buffer_count_lines (&edit->buffer, edit->start_display, ac - KEY_PRESS);
1260         edit->force |= REDRAW_PAGE;
1261     }
1262     edit->start_display = ac - KEY_PRESS;       /* see push and pop above */
1263     edit_update_curs_row (edit);
1264 
1265   done_undo:
1266     edit->undo_stack_disable = 0;
1267 }
1268 
1269 /* --------------------------------------------------------------------------------------------- */
1270 
1271 static void
1272 edit_do_redo (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1273 {
1274     long ac;
1275     long count = 0;
1276 
1277     if (edit->redo_stack_reset)
1278         return;
1279 
1280     edit->over_col = 0;
1281 
1282     while ((ac = edit_pop_redo_action (edit)) < KEY_PRESS)
1283     {
1284         off_t b;
1285 
1286         switch ((int) ac)
1287         {
1288         case STACK_BOTTOM:
1289             goto done_redo;
1290         case CURS_RIGHT:
1291             edit_cursor_move (edit, 1);
1292             break;
1293         case CURS_LEFT:
1294             edit_cursor_move (edit, -1);
1295             break;
1296         case BACKSPACE:
1297             edit_backspace (edit, TRUE);
1298             break;
1299         case DELCHAR:
1300             edit_delete (edit, TRUE);
1301             break;
1302         case COLUMN_ON:
1303             edit->column_highlight = 1;
1304             break;
1305         case COLUMN_OFF:
1306             edit->column_highlight = 0;
1307             break;
1308         default:
1309             break;
1310         }
1311         if (ac >= 256 && ac < 512)
1312             edit_insert_ahead (edit, ac - 256);
1313         if (ac >= 0 && ac < 256)
1314             edit_insert (edit, ac);
1315 
1316         if (ac >= MARK_1 - 2 && ac < MARK_2 - 2)
1317         {
1318             edit->mark1 = ac - MARK_1;
1319             b = edit_buffer_get_bol (&edit->buffer, edit->mark1);
1320             edit->column1 = (long) edit_move_forward3 (edit, b, 0, edit->mark1);
1321         }
1322         else if (ac >= MARK_2 - 2 && ac < KEY_PRESS)
1323         {
1324             edit->mark2 = ac - MARK_2;
1325             b = edit_buffer_get_bol (&edit->buffer, edit->mark2);
1326             edit->column2 = (long) edit_move_forward3 (edit, b, 0, edit->mark2);
1327         }
1328         /* more than one pop usually means something big */
1329         if (count++ != 0)
1330             edit->force |= REDRAW_PAGE;
1331     }
1332 
1333     if (edit->start_display > ac - KEY_PRESS)
1334     {
1335         edit->start_line -=
1336             edit_buffer_count_lines (&edit->buffer, ac - KEY_PRESS, edit->start_display);
1337         edit->force |= REDRAW_PAGE;
1338     }
1339     else if (edit->start_display < ac - KEY_PRESS)
1340     {
1341         edit->start_line +=
1342             edit_buffer_count_lines (&edit->buffer, edit->start_display, ac - KEY_PRESS);
1343         edit->force |= REDRAW_PAGE;
1344     }
1345     edit->start_display = ac - KEY_PRESS;       /* see push and pop above */
1346     edit_update_curs_row (edit);
1347 
1348   done_redo:
1349     ;
1350 }
1351 
1352 /* --------------------------------------------------------------------------------------------- */
1353 
1354 static void
1355 edit_group_undo (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1356 {
1357     long ac = KEY_PRESS;
1358     long cur_ac = KEY_PRESS;
1359 
1360     while (ac != STACK_BOTTOM && ac == cur_ac)
1361     {
1362         cur_ac = get_prev_undo_action (edit);
1363         edit_do_undo (edit);
1364         ac = get_prev_undo_action (edit);
1365         /* exit from cycle if edit_options.group_undo is not set,
1366          * and make single UNDO operation
1367          */
1368         if (!edit_options.group_undo)
1369             ac = STACK_BOTTOM;
1370     }
1371 }
1372 
1373 /* --------------------------------------------------------------------------------------------- */
1374 
1375 static void
1376 edit_delete_to_line_end (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1377 {
1378     while (edit_buffer_get_current_byte (&edit->buffer) != '\n' && edit->buffer.curs2 != 0)
1379         edit_delete (edit, TRUE);
1380 }
1381 
1382 /* --------------------------------------------------------------------------------------------- */
1383 
1384 static void
1385 edit_delete_to_line_begin (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1386 {
1387     while (edit_buffer_get_previous_byte (&edit->buffer) != '\n' && edit->buffer.curs1 != 0)
1388         edit_backspace (edit, TRUE);
1389 }
1390 
1391 /* --------------------------------------------------------------------------------------------- */
1392 
1393 static gboolean
1394 is_aligned_on_a_tab (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1395 {
1396     long curs_col;
1397 
1398     edit_update_curs_col (edit);
1399     curs_col = edit->curs_col % (TAB_SIZE * space_width);
1400     return (curs_col == 0 || curs_col == (HALF_TAB_SIZE * space_width));
1401 }
1402 
1403 /* --------------------------------------------------------------------------------------------- */
1404 
1405 static gboolean
1406 right_of_four_spaces (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1407 {
1408     int i;
1409     int ch = 0;
1410 
1411     for (i = 1; i <= HALF_TAB_SIZE; i++)
1412         ch |= edit_buffer_get_byte (&edit->buffer, edit->buffer.curs1 - i);
1413 
1414     return (ch == ' ' && is_aligned_on_a_tab (edit));
1415 }
1416 
1417 /* --------------------------------------------------------------------------------------------- */
1418 
1419 static gboolean
1420 left_of_four_spaces (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1421 {
1422     int i, ch = 0;
1423 
1424     for (i = 0; i < HALF_TAB_SIZE; i++)
1425         ch |= edit_buffer_get_byte (&edit->buffer, edit->buffer.curs1 + i);
1426 
1427     return (ch == ' ' && is_aligned_on_a_tab (edit));
1428 }
1429 
1430 /* --------------------------------------------------------------------------------------------- */
1431 
1432 static void
1433 edit_auto_indent (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1434 {
1435     off_t p;
1436 
1437     p = edit->buffer.curs1;
1438     /* use the previous line as a template */
1439     p = edit_buffer_get_backward_offset (&edit->buffer, p, 1);
1440     /* copy the leading whitespace of the line */
1441     while (TRUE)
1442     {                           /* no range check - the line _is_ \n-terminated */
1443         char c;
1444 
1445         c = edit_buffer_get_byte (&edit->buffer, p++);
1446         if (!whitespace (c))
1447             break;
1448         edit_insert (edit, c);
1449     }
1450 }
1451 
1452 /* --------------------------------------------------------------------------------------------- */
1453 
1454 static inline void
1455 edit_double_newline (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1456 {
1457     edit_insert (edit, '\n');
1458     if (edit_buffer_get_current_byte (&edit->buffer) == '\n'
1459         || edit_buffer_get_byte (&edit->buffer, edit->buffer.curs1 - 2) == '\n')
1460         return;
1461     edit->force |= REDRAW_PAGE;
1462     edit_insert (edit, '\n');
1463 }
1464 
1465 /* --------------------------------------------------------------------------------------------- */
1466 
1467 static void
1468 insert_spaces_tab (WEdit * edit, gboolean half)
     /* [previous][next][first][last][top][bottom][index][help]  */
1469 {
1470     long i;
1471 
1472     edit_update_curs_col (edit);
1473     i = TAB_SIZE * space_width;
1474     if (half)
1475         i /= 2;
1476     if (i != 0)
1477         for (i = ((edit->curs_col / i) + 1) * i - edit->curs_col; i > 0; i -= space_width)
1478             edit_insert (edit, ' ');
1479 }
1480 
1481 /* --------------------------------------------------------------------------------------------- */
1482 
1483 static inline void
1484 edit_tab_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1485 {
1486     if (edit_options.fake_half_tabs && is_in_indent (&edit->buffer))
1487     {
1488         /* insert a half tab (usually four spaces) unless there is a
1489            half tab already behind, then delete it and insert a
1490            full tab. */
1491         if (edit_options.fill_tabs_with_spaces || !right_of_four_spaces (edit))
1492             insert_spaces_tab (edit, TRUE);
1493         else
1494         {
1495             int i;
1496 
1497             for (i = 1; i <= HALF_TAB_SIZE; i++)
1498                 edit_backspace (edit, TRUE);
1499             edit_insert (edit, '\t');
1500         }
1501     }
1502     else if (edit_options.fill_tabs_with_spaces)
1503         insert_spaces_tab (edit, FALSE);
1504     else
1505         edit_insert (edit, '\t');
1506 }
1507 
1508 /* --------------------------------------------------------------------------------------------- */
1509 
1510 static void
1511 check_and_wrap_line (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1512 {
1513     off_t curs;
1514 
1515     if (!edit_options.typewriter_wrap)
1516         return;
1517     edit_update_curs_col (edit);
1518     if (edit->curs_col < edit_options.word_wrap_line_length)
1519         return;
1520     curs = edit->buffer.curs1;
1521     while (TRUE)
1522     {
1523         int c;
1524 
1525         curs--;
1526         c = edit_buffer_get_byte (&edit->buffer, curs);
1527         if (c == '\n' || curs <= 0)
1528         {
1529             edit_insert (edit, '\n');
1530             return;
1531         }
1532         if (whitespace (c))
1533         {
1534             off_t current = edit->buffer.curs1;
1535             edit_cursor_move (edit, curs - edit->buffer.curs1 + 1);
1536             edit_insert (edit, '\n');
1537             edit_cursor_move (edit, current - edit->buffer.curs1 + 1);
1538             return;
1539         }
1540     }
1541 }
1542 
1543 /* --------------------------------------------------------------------------------------------- */
1544 /** this find the matching bracket in either direction, and sets edit->bracket
1545  *
1546  * @param edit editor object
1547  * @param in_screen search only on the current screen
1548  * @param furthest_bracket_search count of the bytes for search
1549  *
1550  * @return position of the found bracket (-1 if no match)
1551  */
1552 
1553 static off_t
1554 edit_get_bracket (WEdit * edit, gboolean in_screen, unsigned long furthest_bracket_search)
     /* [previous][next][first][last][top][bottom][index][help]  */
1555 {
1556     const char *const b = "{}{[][()(", *p;
1557     int i = 1, inc = -1, c, d, n = 0;
1558     unsigned long j = 0;
1559     off_t q;
1560 
1561     edit_update_curs_row (edit);
1562     c = edit_buffer_get_current_byte (&edit->buffer);
1563     p = strchr (b, c);
1564     /* not on a bracket at all */
1565     if (p == NULL || *p == '\0')
1566         return -1;
1567     /* the matching bracket */
1568     d = p[1];
1569     /* going left or right? */
1570     if (strchr ("{[(", c) != NULL)
1571         inc = 1;
1572     /* no limit */
1573     if (furthest_bracket_search == 0)
1574         furthest_bracket_search--;      /* ULONG_MAX */
1575     for (q = edit->buffer.curs1 + inc;; q += inc)
1576     {
1577         int a;
1578 
1579         /* out of buffer? */
1580         if (q >= edit->buffer.size || q < 0)
1581             break;
1582         a = edit_buffer_get_byte (&edit->buffer, q);
1583         /* don't want to eat CPU */
1584         if (j++ > furthest_bracket_search)
1585             break;
1586         /* out of screen? */
1587         if (in_screen)
1588         {
1589             if (q < edit->start_display)
1590                 break;
1591             /* count lines if searching downward */
1592             if (inc > 0 && a == '\n')
1593                 if (n++ >= WIDGET (edit)->rect.lines - edit->curs_row)  /* out of screen */
1594                     break;
1595         }
1596         /* count bracket depth */
1597         i += (a == c) - (a == d);
1598         /* return if bracket depth is zero */
1599         if (i == 0)
1600             return q;
1601     }
1602     /* no match */
1603     return -1;
1604 }
1605 
1606 /* --------------------------------------------------------------------------------------------- */
1607 
1608 static inline void
1609 edit_goto_matching_bracket (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1610 {
1611     off_t q;
1612 
1613     q = edit_get_bracket (edit, 0, 0);
1614     if (q >= 0)
1615     {
1616         edit->bracket = edit->buffer.curs1;
1617         edit->force |= REDRAW_PAGE;
1618         edit_cursor_move (edit, q - edit->buffer.curs1);
1619     }
1620 }
1621 
1622 /* --------------------------------------------------------------------------------------------- */
1623 
1624 static void
1625 edit_move_block_to_right (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1626 {
1627     off_t start_mark, end_mark;
1628     long cur_bol, start_bol;
1629 
1630     if (!eval_marks (edit, &start_mark, &end_mark))
1631         return;
1632 
1633     start_bol = edit_buffer_get_bol (&edit->buffer, start_mark);
1634     cur_bol = edit_buffer_get_bol (&edit->buffer, end_mark - 1);
1635 
1636     do
1637     {
1638         off_t b;
1639 
1640         edit_cursor_move (edit, cur_bol - edit->buffer.curs1);
1641         if (!edit_line_is_blank (edit, edit->buffer.curs_line))
1642         {
1643             if (edit_options.fill_tabs_with_spaces)
1644                 insert_spaces_tab (edit, edit_options.fake_half_tabs);
1645             else
1646                 edit_insert (edit, '\t');
1647 
1648             b = edit_buffer_get_bol (&edit->buffer, cur_bol);
1649             edit_cursor_move (edit, b - edit->buffer.curs1);
1650         }
1651 
1652         if (cur_bol == 0)
1653             break;
1654 
1655         cur_bol = edit_buffer_get_bol (&edit->buffer, cur_bol - 1);
1656     }
1657     while (cur_bol >= start_bol);
1658 
1659     edit->force |= REDRAW_PAGE;
1660 }
1661 
1662 /* --------------------------------------------------------------------------------------------- */
1663 
1664 static void
1665 edit_move_block_to_left (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1666 {
1667     off_t start_mark, end_mark;
1668     off_t cur_bol, start_bol;
1669 
1670     if (!eval_marks (edit, &start_mark, &end_mark))
1671         return;
1672 
1673     start_bol = edit_buffer_get_bol (&edit->buffer, start_mark);
1674     cur_bol = edit_buffer_get_bol (&edit->buffer, end_mark - 1);
1675 
1676     do
1677     {
1678         int del_tab_width;
1679         int next_char;
1680 
1681         edit_cursor_move (edit, cur_bol - edit->buffer.curs1);
1682 
1683         del_tab_width = edit_options.fake_half_tabs ? HALF_TAB_SIZE : TAB_SIZE;
1684 
1685         next_char = edit_buffer_get_current_byte (&edit->buffer);
1686         if (next_char == '\t')
1687             edit_delete (edit, TRUE);
1688         else if (next_char == ' ')
1689         {
1690             int i;
1691 
1692             for (i = 0; i < del_tab_width; i++)
1693             {
1694                 if (next_char == ' ')
1695                     edit_delete (edit, TRUE);
1696                 next_char = edit_buffer_get_current_byte (&edit->buffer);
1697             }
1698         }
1699 
1700         if (cur_bol == 0)
1701             break;
1702 
1703         cur_bol = edit_buffer_get_bol (&edit->buffer, cur_bol - 1);
1704     }
1705     while (cur_bol >= start_bol);
1706 
1707     edit->force |= REDRAW_PAGE;
1708 }
1709 
1710 /* --------------------------------------------------------------------------------------------- */
1711 /**
1712  * prints at the cursor
1713  * @return number of chars printed
1714  */
1715 
1716 static size_t
1717 edit_print_string (WEdit * e, const char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
1718 {
1719     size_t i;
1720 
1721     for (i = 0; s[i] != '\0'; i++)
1722         edit_execute_cmd (e, CK_InsertChar, (unsigned char) s[i]);
1723     e->force |= REDRAW_COMPLETELY;
1724     edit_update_screen (e);
1725     return i;
1726 }
1727 
1728 /* --------------------------------------------------------------------------------------------- */
1729 
1730 static off_t
1731 edit_insert_column_from_file (WEdit * edit, int file, off_t * start_pos, off_t * end_pos,
     /* [previous][next][first][last][top][bottom][index][help]  */
1732                               long *col1, long *col2)
1733 {
1734     off_t cursor;
1735     long col;
1736     off_t blocklen = -1, width = 0;
1737     unsigned char *data;
1738 
1739     cursor = edit->buffer.curs1;
1740     col = edit_get_col (edit);
1741     data = g_malloc0 (TEMP_BUF_LEN);
1742 
1743     while ((blocklen = mc_read (file, (char *) data, TEMP_BUF_LEN)) > 0)
1744     {
1745         off_t i;
1746         char *pn;
1747 
1748         pn = strchr ((char *) data, '\n');
1749         width = pn == NULL ? blocklen : pn - (char *) data;
1750 
1751         for (i = 0; i < blocklen; i++)
1752         {
1753             if (data[i] != '\n')
1754                 edit_insert (edit, data[i]);
1755             else
1756             {                   /* fill in and move to next line */
1757                 long l;
1758                 off_t p;
1759 
1760                 if (edit_buffer_get_current_byte (&edit->buffer) != '\n')
1761                     for (l = width - (edit_get_col (edit) - col); l > 0; l -= space_width)
1762                         edit_insert (edit, ' ');
1763 
1764                 for (p = edit->buffer.curs1;; p++)
1765                 {
1766                     if (p == edit->buffer.size)
1767                     {
1768                         edit_cursor_move (edit, edit->buffer.size - edit->buffer.curs1);
1769                         edit_insert_ahead (edit, '\n');
1770                         p++;
1771                         break;
1772                     }
1773                     if (edit_buffer_get_byte (&edit->buffer, p) == '\n')
1774                     {
1775                         p++;
1776                         break;
1777                     }
1778                 }
1779 
1780                 edit_cursor_move (edit, edit_move_forward3 (edit, p, col, 0) - edit->buffer.curs1);
1781 
1782                 for (l = col - edit_get_col (edit); l >= space_width; l -= space_width)
1783                     edit_insert (edit, ' ');
1784             }
1785         }
1786     }
1787     *col1 = col;
1788     *col2 = col + width;
1789     *start_pos = cursor;
1790     *end_pos = edit->buffer.curs1;
1791     edit_cursor_move (edit, cursor - edit->buffer.curs1);
1792     g_free (data);
1793 
1794     return blocklen;
1795 }
1796 
1797 /* --------------------------------------------------------------------------------------------- */
1798 /*** public functions ****************************************************************************/
1799 /* --------------------------------------------------------------------------------------------- */
1800 
1801 /** User edit menu, like user menu (F2) but only in editor. */
1802 
1803 void
1804 edit_user_menu (WEdit * edit, const char *menu_file, int selected_entry)
     /* [previous][next][first][last][top][bottom][index][help]  */
1805 {
1806     char *block_file;
1807     gboolean mark;
1808     off_t curs;
1809     off_t start_mark, end_mark;
1810     struct stat status;
1811     vfs_path_t *block_file_vpath;
1812 
1813     block_file = mc_config_get_full_path (EDIT_HOME_BLOCK_FILE);
1814     block_file_vpath = vfs_path_from_str (block_file);
1815     curs = edit->buffer.curs1;
1816     mark = eval_marks (edit, &start_mark, &end_mark);
1817     if (mark)
1818         edit_save_block (edit, block_file, start_mark, end_mark);
1819 
1820     /* run shell scripts from menu */
1821     if (user_menu_cmd (CONST_WIDGET (edit), menu_file, selected_entry)
1822         && (mc_stat (block_file_vpath, &status) == 0) && (status.st_size != 0))
1823     {
1824         gboolean rc = FALSE;
1825         FILE *fd;
1826 
1827         /* i.e. we have marked block */
1828         if (mark)
1829             rc = edit_block_delete_cmd (edit);
1830 
1831         if (!rc)
1832         {
1833             off_t ins_len;
1834 
1835             ins_len = edit_insert_file (edit, block_file_vpath);
1836             if (mark && ins_len > 0)
1837                 edit_set_markers (edit, start_mark, start_mark + ins_len, 0, 0);
1838         }
1839         /* truncate block file */
1840         fd = fopen (block_file, "w");
1841         if (fd != NULL)
1842             fclose (fd);
1843     }
1844     g_free (block_file);
1845     vfs_path_free (block_file_vpath, TRUE);
1846 
1847     edit_cursor_move (edit, curs - edit->buffer.curs1);
1848     edit->force |= REDRAW_PAGE;
1849     widget_draw (WIDGET (edit));
1850 }
1851 
1852 /* --------------------------------------------------------------------------------------------- */
1853 
1854 char *
1855 edit_get_write_filter (const vfs_path_t * write_name_vpath, const vfs_path_t * filename_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1856 {
1857     int i;
1858     const char *write_name;
1859     char *write_name_quoted;
1860     char *p = NULL;
1861 
1862     i = edit_find_filter (filename_vpath);
1863     if (i < 0)
1864         return NULL;
1865 
1866     write_name = vfs_path_get_last_path_str (write_name_vpath);
1867     write_name_quoted = name_quote (write_name, FALSE);
1868     if (write_name_quoted != NULL)
1869     {
1870         p = g_strdup_printf (all_filters[i].write, write_name_quoted);
1871         g_free (write_name_quoted);
1872     }
1873     return p;
1874 }
1875 
1876 /* --------------------------------------------------------------------------------------------- */
1877 /**
1878  * @param edit   editor object
1879  * @param f      value of stream file
1880  * @return       the length of the file
1881  */
1882 
1883 off_t
1884 edit_write_stream (WEdit * edit, FILE * f)
     /* [previous][next][first][last][top][bottom][index][help]  */
1885 {
1886     long i;
1887 
1888     if (edit->lb == LB_ASIS)
1889     {
1890         for (i = 0; i < edit->buffer.size; i++)
1891             if (fputc (edit_buffer_get_byte (&edit->buffer, i), f) < 0)
1892                 break;
1893         return i;
1894     }
1895 
1896     /* change line breaks */
1897     for (i = 0; i < edit->buffer.size; i++)
1898     {
1899         unsigned char c;
1900 
1901         c = edit_buffer_get_byte (&edit->buffer, i);
1902         if (!(c == '\n' || c == '\r'))
1903         {
1904             /* not line break */
1905             if (fputc (c, f) < 0)
1906                 return i;
1907         }
1908         else
1909         {                       /* (c == '\n' || c == '\r') */
1910             unsigned char c1;
1911 
1912             c1 = edit_buffer_get_byte (&edit->buffer, i + 1);   /* next char */
1913 
1914             switch (edit->lb)
1915             {
1916             case LB_UNIX:      /* replace "\r\n" or '\r' to '\n' */
1917                 /* put one line break unconditionally */
1918                 if (fputc ('\n', f) < 0)
1919                     return i;
1920 
1921                 i++;            /* 2 chars are processed */
1922 
1923                 if (c == '\r' && c1 == '\n')
1924                     /* Windows line break; go to the next char */
1925                     break;
1926 
1927                 if (c == '\r' && c1 == '\r')
1928                 {
1929                     /* two Macintosh line breaks; put second line break */
1930                     if (fputc ('\n', f) < 0)
1931                         return i;
1932                     break;
1933                 }
1934 
1935                 if (fputc (c1, f) < 0)
1936                     return i;
1937                 break;
1938 
1939             case LB_WIN:       /* replace '\n' or '\r' to "\r\n" */
1940                 /* put one line break unconditionally */
1941                 if (fputc ('\r', f) < 0 || fputc ('\n', f) < 0)
1942                     return i;
1943 
1944                 if (c == '\r' && c1 == '\n')
1945                     /* Windows line break; go to the next char */
1946                     i++;
1947                 break;
1948 
1949             case LB_MAC:       /* replace "\r\n" or '\n' to '\r' */
1950                 /* put one line break unconditionally */
1951                 if (fputc ('\r', f) < 0)
1952                     return i;
1953 
1954                 i++;            /* 2 chars are processed */
1955 
1956                 if (c == '\r' && c1 == '\n')
1957                     /* Windows line break; go to the next char */
1958                     break;
1959 
1960                 if (c == '\n' && c1 == '\n')
1961                 {
1962                     /* two Windows line breaks; put second line break */
1963                     if (fputc ('\r', f) < 0)
1964                         return i;
1965                     break;
1966                 }
1967 
1968                 if (fputc (c1, f) < 0)
1969                     return i;
1970                 break;
1971             case LB_ASIS:      /* default without changes */
1972             default:
1973                 break;
1974             }
1975         }
1976     }
1977 
1978     return edit->buffer.size;
1979 }
1980 
1981 /* --------------------------------------------------------------------------------------------- */
1982 
1983 gboolean
1984 is_break_char (char c)
     /* [previous][next][first][last][top][bottom][index][help]  */
1985 {
1986     return (isspace (c) || strchr ("{}[]()<>=|/\\!?~-+`'\",.;:#$%^&*", c) != NULL);
1987 }
1988 
1989 /* --------------------------------------------------------------------------------------------- */
1990 /** inserts a file at the cursor, returns count of inserted bytes on success */
1991 
1992 off_t
1993 edit_insert_file (WEdit * edit, const vfs_path_t * filename_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1994 {
1995     char *p;
1996     off_t current;
1997     off_t ins_len = 0;
1998 
1999     p = edit_get_filter (filename_vpath);
2000     current = edit->buffer.curs1;
2001 
2002     if (p != NULL)
2003     {
2004         FILE *f;
2005 
2006         f = (FILE *) popen (p, "r");
2007         if (f != NULL)
2008         {
2009             edit_insert_stream (edit, f);
2010 
2011             /* Place cursor at the end of text selection */
2012             if (!edit_options.cursor_after_inserted_block)
2013             {
2014                 ins_len = edit->buffer.curs1 - current;
2015                 edit_cursor_move (edit, -ins_len);
2016             }
2017             if (pclose (f) > 0)
2018             {
2019                 char *errmsg;
2020 
2021                 errmsg = g_strdup_printf (_("Error reading from pipe: %s"), p);
2022                 edit_error_dialog (_("Error"), errmsg);
2023                 g_free (errmsg);
2024                 ins_len = -1;
2025             }
2026         }
2027         else
2028         {
2029             char *errmsg;
2030 
2031             errmsg = g_strdup_printf (_("Cannot open pipe for reading: %s"), p);
2032             edit_error_dialog (_("Error"), errmsg);
2033             g_free (errmsg);
2034             ins_len = -1;
2035         }
2036         g_free (p);
2037     }
2038     else
2039     {
2040         int file;
2041         off_t blocklen;
2042         gboolean vertical_insertion = FALSE;
2043         char *buf;
2044 
2045         file = mc_open (filename_vpath, O_RDONLY | O_BINARY);
2046         if (file == -1)
2047             return -1;
2048 
2049         buf = g_malloc0 (TEMP_BUF_LEN);
2050         blocklen = mc_read (file, buf, sizeof (VERTICAL_MAGIC));
2051         if (blocklen > 0)
2052         {
2053             /* if contain signature VERTICAL_MAGIC then it vertical block */
2054             if (memcmp (buf, VERTICAL_MAGIC, sizeof (VERTICAL_MAGIC)) == 0)
2055                 vertical_insertion = TRUE;
2056             else
2057                 mc_lseek (file, 0, SEEK_SET);
2058         }
2059 
2060         if (vertical_insertion)
2061         {
2062             off_t mark1, mark2;
2063             long c1, c2;
2064 
2065             blocklen = edit_insert_column_from_file (edit, file, &mark1, &mark2, &c1, &c2);
2066             edit_set_markers (edit, edit->buffer.curs1, mark2, c1, c2);
2067 
2068             /* highlight inserted text then not persistent blocks */
2069             if (!edit_options.persistent_selections && edit->modified)
2070             {
2071                 if (!edit->column_highlight)
2072                     edit_push_undo_action (edit, COLUMN_OFF);
2073                 edit->column_highlight = 1;
2074             }
2075         }
2076         else
2077         {
2078             off_t i;
2079 
2080             while ((blocklen = mc_read (file, (char *) buf, TEMP_BUF_LEN)) > 0)
2081             {
2082                 for (i = 0; i < blocklen; i++)
2083                     edit_insert (edit, buf[i]);
2084             }
2085             /* highlight inserted text then not persistent blocks */
2086             if (!edit_options.persistent_selections && edit->modified)
2087             {
2088                 edit_set_markers (edit, edit->buffer.curs1, current, 0, 0);
2089                 if (edit->column_highlight)
2090                     edit_push_undo_action (edit, COLUMN_ON);
2091                 edit->column_highlight = 0;
2092             }
2093 
2094             /* Place cursor at the end of text selection */
2095             if (!edit_options.cursor_after_inserted_block)
2096             {
2097                 ins_len = edit->buffer.curs1 - current;
2098                 edit_cursor_move (edit, -ins_len);
2099             }
2100         }
2101 
2102         edit->force |= REDRAW_PAGE;
2103         g_free (buf);
2104         mc_close (file);
2105         if (blocklen != 0)
2106             ins_len = 0;
2107     }
2108 
2109     return ins_len;
2110 }
2111 
2112 /* --------------------------------------------------------------------------------------------- */
2113 /**
2114  * Fill in the edit structure.  Return NULL on failure.  Pass edit as
2115  * NULL to allocate a new structure.
2116  *
2117  * If line is 0, try to restore saved position.  Otherwise put the
2118  * cursor on that line and show it in the middle of the screen.
2119  */
2120 
2121 WEdit *
2122 edit_init (WEdit * edit, const WRect * r, const vfs_path_t * filename_vpath, long line)
     /* [previous][next][first][last][top][bottom][index][help]  */
2123 {
2124     gboolean to_free = FALSE;
2125 
2126     auto_syntax = TRUE;         /* Resetting to auto on every invocation */
2127     edit_options.line_state_width = edit_options.line_state ? LINE_STATE_WIDTH : 0;
2128 
2129     if (edit != NULL)
2130     {
2131         gboolean fullscreen;
2132         WRect loc_prev;
2133 
2134         /* save some widget parameters */
2135         fullscreen = edit->fullscreen;
2136         loc_prev = edit->loc_prev;
2137 
2138         edit_purge_widget (edit);
2139 
2140         /* restore saved parameters */
2141         edit->fullscreen = fullscreen;
2142         edit->loc_prev = loc_prev;
2143     }
2144     else
2145     {
2146         Widget *w;
2147 
2148         edit = g_malloc0 (sizeof (WEdit));
2149         to_free = TRUE;
2150 
2151         w = WIDGET (edit);
2152         widget_init (w, r, NULL, NULL);
2153         w->options |= WOP_SELECTABLE | WOP_TOP_SELECT | WOP_WANT_CURSOR;
2154         w->keymap = editor_map;
2155         w->ext_keymap = editor_x_map;
2156         edit->fullscreen = TRUE;
2157         edit_save_size (edit);
2158     }
2159 
2160     edit->drag_state = MCEDIT_DRAG_NONE;
2161 
2162     edit->stat1.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
2163     edit->stat1.st_uid = getuid ();
2164     edit->stat1.st_gid = getgid ();
2165     edit->stat1.st_mtime = 0;
2166 
2167     edit->attrs_ok = (mc_fgetflags (filename_vpath, &edit->attrs) == 0);
2168 
2169     edit->over_col = 0;
2170     edit->bracket = -1;
2171     edit->last_bracket = -1;
2172     edit->force |= REDRAW_PAGE;
2173 
2174     /* set file name before load file */
2175     edit_set_filename (edit, filename_vpath);
2176 
2177     edit->undo_stack_size = START_STACK_SIZE;
2178     edit->undo_stack_size_mask = START_STACK_SIZE - 1;
2179     edit->undo_stack = g_malloc0 ((edit->undo_stack_size + 10) * sizeof (long));
2180 
2181     edit->redo_stack_size = START_STACK_SIZE;
2182     edit->redo_stack_size_mask = START_STACK_SIZE - 1;
2183     edit->redo_stack = g_malloc0 ((edit->redo_stack_size + 10) * sizeof (long));
2184 
2185 #ifdef HAVE_CHARSET
2186     edit->utf8 = FALSE;
2187     edit->converter = str_cnv_from_term;
2188     edit_set_codeset (edit);
2189 #endif
2190 
2191     if (!edit_load_file (edit))
2192     {
2193         /* edit_load_file already gives an error message */
2194         if (to_free)
2195             g_free (edit);
2196         return NULL;
2197     }
2198 
2199     edit->loading_done = 1;
2200     edit->modified = 0;
2201     edit->locked = 0;
2202     edit_load_syntax (edit, NULL, NULL);
2203     edit_get_syntax_color (edit, -1);
2204 
2205     /* load saved cursor position and/or boolmarks */
2206     if ((line == 0) && edit_options.save_position)
2207         edit_load_position (edit, TRUE);
2208     else
2209     {
2210         edit_load_position (edit, FALSE);
2211         if (line <= 0)
2212             line = 1;
2213         edit_move_display (edit, line - 1);
2214         edit_move_to_line (edit, line - 1);
2215     }
2216 
2217     edit_load_macro_cmd (edit);
2218 
2219     return edit;
2220 }
2221 
2222 /* --------------------------------------------------------------------------------------------- */
2223 
2224 /** Clear the edit struct, freeing everything in it.  Return TRUE on success */
2225 gboolean
2226 edit_clean (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2227 {
2228     if (edit == NULL)
2229         return FALSE;
2230 
2231     /* a stale lock, remove it */
2232     if (edit->locked)
2233         (void) unlock_file (edit->filename_vpath);
2234 
2235     /* save cursor position */
2236     if (edit_options.save_position)
2237         edit_save_position (edit);
2238     else if (edit->serialized_bookmarks != NULL)
2239         g_array_free (edit->serialized_bookmarks, TRUE);
2240 
2241     /* File specified on the mcedit command line and never saved */
2242     if (edit->delete_file)
2243         unlink (vfs_path_get_last_path_str (edit->filename_vpath));
2244 
2245     edit_free_syntax_rules (edit);
2246     book_mark_flush (edit, -1);
2247 
2248     edit_buffer_clean (&edit->buffer);
2249 
2250     g_free (edit->undo_stack);
2251     g_free (edit->redo_stack);
2252     vfs_path_free (edit->filename_vpath, TRUE);
2253     vfs_path_free (edit->dir_vpath, TRUE);
2254     edit_search_deinit (edit);
2255 
2256 #ifdef HAVE_CHARSET
2257     if (edit->converter != str_cnv_from_term)
2258         str_close_conv (edit->converter);
2259 #endif
2260 
2261     edit_purge_widget (edit);
2262 
2263     return TRUE;
2264 }
2265 
2266 /* --------------------------------------------------------------------------------------------- */
2267 
2268 /**
2269  * Load a new file into the editor and set line.  If it fails, preserve the old file.
2270  * To do it, allocate a new widget, initialize it and, if the new file
2271  * was loaded, copy the data to the old widget.
2272  *
2273  * @return TRUE on success, FALSE on failure.
2274  */
2275 gboolean
2276 edit_reload_line (WEdit * edit, const vfs_path_t * filename_vpath, long line)
     /* [previous][next][first][last][top][bottom][index][help]  */
2277 {
2278     Widget *w = WIDGET (edit);
2279     WEdit *e;
2280 
2281     e = g_malloc0 (sizeof (WEdit));
2282     *WIDGET (e) = *w;
2283     /* save some widget parameters */
2284     e->fullscreen = edit->fullscreen;
2285     e->loc_prev = edit->loc_prev;
2286 
2287     if (edit_init (e, &w->rect, filename_vpath, line) == NULL)
2288     {
2289         g_free (e);
2290         return FALSE;
2291     }
2292 
2293     edit_clean (edit);
2294     memcpy (edit, e, sizeof (*edit));
2295     g_free (e);
2296 
2297     return TRUE;
2298 }
2299 
2300 /* --------------------------------------------------------------------------------------------- */
2301 
2302 #ifdef HAVE_CHARSET
2303 void
2304 edit_set_codeset (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2305 {
2306     const char *cp_id;
2307 
2308     cp_id =
2309         get_codepage_id (mc_global.source_codepage >=
2310                          0 ? mc_global.source_codepage : mc_global.display_codepage);
2311 
2312     if (cp_id != NULL)
2313     {
2314         GIConv conv;
2315         conv = str_crt_conv_from (cp_id);
2316         if (conv != INVALID_CONV)
2317         {
2318             if (edit->converter != str_cnv_from_term)
2319                 str_close_conv (edit->converter);
2320             edit->converter = conv;
2321         }
2322     }
2323 
2324     if (cp_id != NULL)
2325         edit->utf8 = str_isutf8 (cp_id);
2326 }
2327 #endif
2328 
2329 /* --------------------------------------------------------------------------------------------- */
2330 
2331 /**
2332  * Recording stack for undo:
2333  * The following is an implementation of a compressed stack. Identical
2334  * pushes are recorded by a negative prefix indicating the number of times the
2335  * same char was pushed. This saves space for repeated curs-left or curs-right
2336  * delete etc.
2337  *
2338  * eg:
2339  *
2340  * pushed:       stored:
2341  *
2342  * a
2343  * b             a
2344  * b            -3
2345  * b             b
2346  * c  -->       -4
2347  * c             c
2348  * c             d
2349  * c
2350  * d
2351  *
2352  * If the stack long int is 0-255 it represents a normal insert (from a backspace),
2353  * 256-512 is an insert ahead (from a delete), If it is between 600 and 700 it is one
2354  * of the cursor functions define'd in edit-impl.h. 1000 through 700'000'000 is to
2355  * set edit->mark1 position. 700'000'000 through 1400'000'000 is to set edit->mark2
2356  * position.
2357  *
2358  * The only way the cursor moves or the buffer is changed is through the routines:
2359  * insert, backspace, insert_ahead, delete, and cursor_move.
2360  * These record the reverse undo movements onto the stack each time they are
2361  * called.
2362  *
2363  * Each key press results in a set of actions (insert; delete ...). So each time
2364  * a key is pressed the current position of start_display is pushed as
2365  * KEY_PRESS + start_display. Then for undoing, we pop until we get to a number
2366  * over KEY_PRESS. We then assign this number less KEY_PRESS to start_display. So undo
2367  * tracks scrolling and key actions exactly. (KEY_PRESS is about (2^31) * (2/3) = 1400'000'000)
2368  *
2369  *
2370  *
2371  * @param edit editor object
2372  * @param c code of the action
2373  */
2374 
2375 void
2376 edit_push_undo_action (WEdit * edit, long c)
     /* [previous][next][first][last][top][bottom][index][help]  */
2377 {
2378     unsigned long sp = edit->undo_stack_pointer;
2379     unsigned long spm1;
2380 
2381     /* first enlarge the stack if necessary */
2382     if (sp > edit->undo_stack_size - 10)
2383     {                           /* say */
2384         if (max_undo < 256)
2385             max_undo = 256;
2386         if (edit->undo_stack_size < (unsigned long) max_undo)
2387         {
2388             long *t;
2389 
2390             t = g_realloc (edit->undo_stack, (edit->undo_stack_size * 2 + 10) * sizeof (long));
2391             if (t != NULL)
2392             {
2393                 edit->undo_stack = t;
2394                 edit->undo_stack_size <<= 1;
2395                 edit->undo_stack_size_mask = edit->undo_stack_size - 1;
2396             }
2397         }
2398     }
2399     spm1 = (edit->undo_stack_pointer - 1) & edit->undo_stack_size_mask;
2400     if (edit->undo_stack_disable)
2401     {
2402         edit_push_redo_action (edit, KEY_PRESS);
2403         edit_push_redo_action (edit, c);
2404         return;
2405     }
2406 
2407     if (edit->redo_stack_reset)
2408         edit->redo_stack_bottom = edit->redo_stack_pointer = 0;
2409 
2410     if (edit->undo_stack_bottom != sp
2411         && spm1 != edit->undo_stack_bottom
2412         && ((sp - 2) & edit->undo_stack_size_mask) != edit->undo_stack_bottom)
2413     {
2414         long d;
2415 
2416         if (edit->undo_stack[spm1] < 0)
2417         {
2418             d = edit->undo_stack[(sp - 2) & edit->undo_stack_size_mask];
2419             if (d == c && edit->undo_stack[spm1] > -1000000000)
2420             {
2421                 if (c < KEY_PRESS)      /* --> no need to push multiple do-nothings */
2422                     edit->undo_stack[spm1]--;
2423                 return;
2424             }
2425         }
2426         else
2427         {
2428             d = edit->undo_stack[spm1];
2429             if (d == c)
2430             {
2431                 if (c >= KEY_PRESS)
2432                     return;     /* --> no need to push multiple do-nothings */
2433                 edit->undo_stack[sp] = -2;
2434                 goto check_bottom;
2435             }
2436         }
2437     }
2438     edit->undo_stack[sp] = c;
2439 
2440   check_bottom:
2441     edit->undo_stack_pointer = (edit->undo_stack_pointer + 1) & edit->undo_stack_size_mask;
2442 
2443     /* if the sp wraps round and catches the undo_stack_bottom then erase
2444      * the first set of actions on the stack to make space - by moving
2445      * undo_stack_bottom forward one "key press" */
2446     c = (edit->undo_stack_pointer + 2) & edit->undo_stack_size_mask;
2447     if ((unsigned long) c == edit->undo_stack_bottom ||
2448         (((unsigned long) c + 1) & edit->undo_stack_size_mask) == edit->undo_stack_bottom)
2449         do
2450         {
2451             edit->undo_stack_bottom = (edit->undo_stack_bottom + 1) & edit->undo_stack_size_mask;
2452         }
2453         while (edit->undo_stack[edit->undo_stack_bottom] < KEY_PRESS
2454                && edit->undo_stack_bottom != edit->undo_stack_pointer);
2455 
2456     /*If a single key produced enough pushes to wrap all the way round then we would notice that the [undo_stack_bottom] does not contain KEY_PRESS. The stack is then initialised: */
2457     if (edit->undo_stack_pointer != edit->undo_stack_bottom
2458         && edit->undo_stack[edit->undo_stack_bottom] < KEY_PRESS)
2459     {
2460         edit->undo_stack_bottom = edit->undo_stack_pointer = 0;
2461     }
2462 }
2463 
2464 /* --------------------------------------------------------------------------------------------- */
2465 
2466 void
2467 edit_push_redo_action (WEdit * edit, long c)
     /* [previous][next][first][last][top][bottom][index][help]  */
2468 {
2469     unsigned long sp = edit->redo_stack_pointer;
2470     unsigned long spm1;
2471     /* first enlarge the stack if necessary */
2472     if (sp > edit->redo_stack_size - 10)
2473     {                           /* say */
2474         if (max_undo < 256)
2475             max_undo = 256;
2476         if (edit->redo_stack_size < (unsigned long) max_undo)
2477         {
2478             long *t;
2479 
2480             t = g_realloc (edit->redo_stack, (edit->redo_stack_size * 2 + 10) * sizeof (long));
2481             if (t != NULL)
2482             {
2483                 edit->redo_stack = t;
2484                 edit->redo_stack_size <<= 1;
2485                 edit->redo_stack_size_mask = edit->redo_stack_size - 1;
2486             }
2487         }
2488     }
2489     spm1 = (edit->redo_stack_pointer - 1) & edit->redo_stack_size_mask;
2490 
2491     if (edit->redo_stack_bottom != sp
2492         && spm1 != edit->redo_stack_bottom
2493         && ((sp - 2) & edit->redo_stack_size_mask) != edit->redo_stack_bottom)
2494     {
2495         long d;
2496 
2497         if (edit->redo_stack[spm1] < 0)
2498         {
2499             d = edit->redo_stack[(sp - 2) & edit->redo_stack_size_mask];
2500             if (d == c && edit->redo_stack[spm1] > -1000000000)
2501             {
2502                 if (c < KEY_PRESS)      /* --> no need to push multiple do-nothings */
2503                     edit->redo_stack[spm1]--;
2504                 return;
2505             }
2506         }
2507         else
2508         {
2509             d = edit->redo_stack[spm1];
2510             if (d == c)
2511             {
2512                 if (c >= KEY_PRESS)
2513                     return;     /* --> no need to push multiple do-nothings */
2514                 edit->redo_stack[sp] = -2;
2515                 goto redo_check_bottom;
2516             }
2517         }
2518     }
2519     edit->redo_stack[sp] = c;
2520 
2521   redo_check_bottom:
2522     edit->redo_stack_pointer = (edit->redo_stack_pointer + 1) & edit->redo_stack_size_mask;
2523 
2524     /* if the sp wraps round and catches the redo_stack_bottom then erase
2525      * the first set of actions on the stack to make space - by moving
2526      * redo_stack_bottom forward one "key press" */
2527     c = (edit->redo_stack_pointer + 2) & edit->redo_stack_size_mask;
2528     if ((unsigned long) c == edit->redo_stack_bottom ||
2529         (((unsigned long) c + 1) & edit->redo_stack_size_mask) == edit->redo_stack_bottom)
2530         do
2531         {
2532             edit->redo_stack_bottom = (edit->redo_stack_bottom + 1) & edit->redo_stack_size_mask;
2533         }
2534         while (edit->redo_stack[edit->redo_stack_bottom] < KEY_PRESS
2535                && edit->redo_stack_bottom != edit->redo_stack_pointer);
2536 
2537     /*
2538      * If a single key produced enough pushes to wrap all the way round then
2539      * we would notice that the [redo_stack_bottom] does not contain KEY_PRESS.
2540      * The stack is then initialised:
2541      */
2542 
2543     if (edit->redo_stack_pointer != edit->redo_stack_bottom
2544         && edit->redo_stack[edit->redo_stack_bottom] < KEY_PRESS)
2545         edit->redo_stack_bottom = edit->redo_stack_pointer = 0;
2546 }
2547 
2548 /* --------------------------------------------------------------------------------------------- */
2549 /**
2550    Basic low level single character buffer alterations and movements at the cursor.
2551  */
2552 
2553 void
2554 edit_insert (WEdit * edit, int c)
     /* [previous][next][first][last][top][bottom][index][help]  */
2555 {
2556     /* first we must update the position of the display window */
2557     if (edit->buffer.curs1 < edit->start_display)
2558     {
2559         edit->start_display++;
2560         if (c == '\n')
2561             edit->start_line++;
2562     }
2563 
2564     /* Mark file as modified, unless the file hasn't been fully loaded */
2565     if (edit->loading_done)
2566         edit_modification (edit);
2567 
2568     /* now we must update some info on the file and check if a redraw is required */
2569     if (c == '\n')
2570     {
2571         book_mark_inc (edit, edit->buffer.curs_line);
2572         edit->buffer.curs_line++;
2573         edit->buffer.lines++;
2574         edit->force |= REDRAW_LINE_ABOVE | REDRAW_AFTER_CURSOR;
2575     }
2576 
2577     /* save the reverse command onto the undo stack */
2578     /* ordinary char and not space */
2579     if (c > 32)
2580         edit_push_undo_action (edit, BACKSPACE);
2581     else
2582         edit_push_undo_action (edit, BACKSPACE_BR);
2583     /* update markers */
2584     edit->mark1 += (edit->mark1 > edit->buffer.curs1) ? 1 : 0;
2585     edit->mark2 += (edit->mark2 > edit->buffer.curs1) ? 1 : 0;
2586     edit->last_get_rule += (edit->last_get_rule > edit->buffer.curs1) ? 1 : 0;
2587 
2588     edit_buffer_insert (&edit->buffer, c);
2589 }
2590 
2591 /* --------------------------------------------------------------------------------------------- */
2592 /** same as edit_insert and move left */
2593 
2594 void
2595 edit_insert_ahead (WEdit * edit, int c)
     /* [previous][next][first][last][top][bottom][index][help]  */
2596 {
2597     if (edit->buffer.curs1 < edit->start_display)
2598     {
2599         edit->start_display++;
2600         if (c == '\n')
2601             edit->start_line++;
2602     }
2603     edit_modification (edit);
2604     if (c == '\n')
2605     {
2606         book_mark_inc (edit, edit->buffer.curs_line);
2607         edit->buffer.lines++;
2608         edit->force |= REDRAW_AFTER_CURSOR;
2609     }
2610     /* ordinary char and not space */
2611     if (c > 32)
2612         edit_push_undo_action (edit, DELCHAR);
2613     else
2614         edit_push_undo_action (edit, DELCHAR_BR);
2615 
2616     edit->mark1 += (edit->mark1 >= edit->buffer.curs1) ? 1 : 0;
2617     edit->mark2 += (edit->mark2 >= edit->buffer.curs1) ? 1 : 0;
2618     edit->last_get_rule += (edit->last_get_rule >= edit->buffer.curs1) ? 1 : 0;
2619 
2620     edit_buffer_insert_ahead (&edit->buffer, c);
2621 }
2622 
2623 /* --------------------------------------------------------------------------------------------- */
2624 
2625 void
2626 edit_insert_over (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2627 {
2628     long i;
2629 
2630     for (i = 0; i < edit->over_col; i++)
2631         edit_insert (edit, ' ');
2632 
2633     edit->over_col = 0;
2634 }
2635 
2636 /* --------------------------------------------------------------------------------------------- */
2637 
2638 int
2639 edit_delete (WEdit * edit, gboolean byte_delete)
     /* [previous][next][first][last][top][bottom][index][help]  */
2640 {
2641     int p = 0;
2642     int char_length = 1;
2643     int i;
2644 
2645     if (edit->buffer.curs2 == 0)
2646         return 0;
2647 
2648 #ifdef HAVE_CHARSET
2649     /* if byte_delete == TRUE then delete only one byte not multibyte char */
2650     if (edit->utf8 && !byte_delete)
2651     {
2652         edit_buffer_get_utf (&edit->buffer, edit->buffer.curs1, &char_length);
2653         if (char_length < 1)
2654             char_length = 1;
2655     }
2656 #else
2657     (void) byte_delete;
2658 #endif
2659 
2660     if (edit->mark2 != edit->mark1)
2661         edit_push_markers (edit);
2662 
2663     for (i = 1; i <= char_length; i++)
2664     {
2665         if (edit->mark1 > edit->buffer.curs1)
2666         {
2667             edit->mark1--;
2668             edit->end_mark_curs--;
2669         }
2670         if (edit->mark2 > edit->buffer.curs1)
2671             edit->mark2--;
2672         if (edit->last_get_rule > edit->buffer.curs1)
2673             edit->last_get_rule--;
2674 
2675         p = edit_buffer_delete (&edit->buffer);
2676 
2677         edit_push_undo_action (edit, p + 256);
2678     }
2679 
2680     edit_modification (edit);
2681     if (p == '\n')
2682     {
2683         book_mark_dec (edit, edit->buffer.curs_line);
2684         edit->buffer.lines--;
2685         edit->force |= REDRAW_AFTER_CURSOR;
2686     }
2687     if (edit->buffer.curs1 < edit->start_display)
2688     {
2689         edit->start_display--;
2690         if (p == '\n')
2691             edit->start_line--;
2692     }
2693 
2694     return p;
2695 }
2696 
2697 /* --------------------------------------------------------------------------------------------- */
2698 
2699 int
2700 edit_backspace (WEdit * edit, gboolean byte_delete)
     /* [previous][next][first][last][top][bottom][index][help]  */
2701 {
2702     int p = 0;
2703     int char_length = 1;
2704     int i;
2705 
2706     if (edit->buffer.curs1 == 0)
2707         return 0;
2708 
2709     if (edit->mark2 != edit->mark1)
2710         edit_push_markers (edit);
2711 
2712 #ifdef HAVE_CHARSET
2713     if (edit->utf8 && !byte_delete)
2714     {
2715         edit_buffer_get_prev_utf (&edit->buffer, edit->buffer.curs1, &char_length);
2716         if (char_length < 1)
2717             char_length = 1;
2718     }
2719 #else
2720     (void) byte_delete;
2721 #endif
2722 
2723     for (i = 1; i <= char_length; i++)
2724     {
2725         if (edit->mark1 >= edit->buffer.curs1)
2726         {
2727             edit->mark1--;
2728             edit->end_mark_curs--;
2729         }
2730         if (edit->mark2 >= edit->buffer.curs1)
2731             edit->mark2--;
2732         if (edit->last_get_rule >= edit->buffer.curs1)
2733             edit->last_get_rule--;
2734 
2735         p = edit_buffer_backspace (&edit->buffer);
2736 
2737         edit_push_undo_action (edit, p);
2738     }
2739     edit_modification (edit);
2740     if (p == '\n')
2741     {
2742         book_mark_dec (edit, edit->buffer.curs_line);
2743         edit->buffer.curs_line--;
2744         edit->buffer.lines--;
2745         edit->force |= REDRAW_AFTER_CURSOR;
2746     }
2747 
2748     if (edit->buffer.curs1 < edit->start_display)
2749     {
2750         edit->start_display--;
2751         if (p == '\n')
2752             edit->start_line--;
2753     }
2754 
2755     return p;
2756 }
2757 
2758 /* --------------------------------------------------------------------------------------------- */
2759 /** moves the cursor right or left: increment positive or negative respectively */
2760 
2761 void
2762 edit_cursor_move (WEdit * edit, off_t increment)
     /* [previous][next][first][last][top][bottom][index][help]  */
2763 {
2764     if (increment < 0)
2765     {
2766         for (; increment < 0 && edit->buffer.curs1 != 0; increment++)
2767         {
2768             int c;
2769 
2770             edit_push_undo_action (edit, CURS_RIGHT);
2771 
2772             c = edit_buffer_get_previous_byte (&edit->buffer);
2773             edit_buffer_insert_ahead (&edit->buffer, c);
2774             c = edit_buffer_backspace (&edit->buffer);
2775             if (c == '\n')
2776             {
2777                 edit->buffer.curs_line--;
2778                 edit->force |= REDRAW_LINE_BELOW;
2779             }
2780         }
2781     }
2782     else
2783     {
2784         for (; increment > 0 && edit->buffer.curs2 != 0; increment--)
2785         {
2786             int c;
2787 
2788             edit_push_undo_action (edit, CURS_LEFT);
2789 
2790             c = edit_buffer_get_current_byte (&edit->buffer);
2791             edit_buffer_insert (&edit->buffer, c);
2792             c = edit_buffer_delete (&edit->buffer);
2793             if (c == '\n')
2794             {
2795                 edit->buffer.curs_line++;
2796                 edit->force |= REDRAW_LINE_ABOVE;
2797             }
2798         }
2799     }
2800 }
2801 
2802 /* --------------------------------------------------------------------------------------------- */
2803 /* If cols is zero this returns the count of columns from current to upto. */
2804 /* If upto is zero returns index of cols across from current. */
2805 
2806 off_t
2807 edit_move_forward3 (const WEdit * edit, off_t current, long cols, off_t upto)
     /* [previous][next][first][last][top][bottom][index][help]  */
2808 {
2809     off_t p, q;
2810     long col;
2811 
2812     if (upto != 0)
2813     {
2814         q = upto;
2815         cols = -10;
2816     }
2817     else
2818         q = edit->buffer.size + 2;
2819 
2820     for (col = 0, p = current; p < q; p++)
2821     {
2822         int c, orig_c;
2823 
2824         if (cols != -10)
2825         {
2826             if (col == cols)
2827                 return p;
2828             if (col > cols)
2829                 return p - 1;
2830         }
2831 
2832         orig_c = c = edit_buffer_get_byte (&edit->buffer, p);
2833 
2834 #ifdef HAVE_CHARSET
2835         if (edit->utf8)
2836         {
2837             int utf_ch;
2838             int char_length = 1;
2839 
2840             utf_ch = edit_buffer_get_utf (&edit->buffer, p, &char_length);
2841             if (mc_global.utf8_display)
2842             {
2843                 if (char_length > 1)
2844                     col -= char_length - 1;
2845                 if (g_unichar_iswide (utf_ch))
2846                     col++;
2847             }
2848             else if (char_length > 1 && g_unichar_isprint (utf_ch))
2849                 col -= char_length - 1;
2850         }
2851 
2852         c = convert_to_display_c (c);
2853 #endif
2854 
2855         if (c == '\n')
2856             return (upto != 0 ? (off_t) col : p);
2857         if (c == '\t')
2858             col += TAB_SIZE - col % TAB_SIZE;
2859         else if ((c < 32 || c == 127) && (orig_c == c
2860 #ifdef HAVE_CHARSET
2861                                           || (!mc_global.utf8_display && !edit->utf8)
2862 #endif
2863                  ))
2864             /* '\r' is shown as ^M, so we must advance 2 characters */
2865             /* Caret notation for control characters */
2866             col += 2;
2867         else
2868             col++;
2869     }
2870     return (off_t) col;
2871 }
2872 
2873 /* --------------------------------------------------------------------------------------------- */
2874 /** returns the current offset of the cursor from the beginning of a file */
2875 
2876 off_t
2877 edit_get_cursor_offset (const WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2878 {
2879     return edit->buffer.curs1;
2880 }
2881 
2882 /* --------------------------------------------------------------------------------------------- */
2883 /** returns the current column position of the cursor */
2884 
2885 long
2886 edit_get_col (const WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2887 {
2888     off_t b;
2889 
2890     b = edit_buffer_get_current_bol (&edit->buffer);
2891     return (long) edit_move_forward3 (edit, b, 0, edit->buffer.curs1);
2892 }
2893 
2894 /* --------------------------------------------------------------------------------------------- */
2895 /* Scrolling functions */
2896 /* --------------------------------------------------------------------------------------------- */
2897 
2898 void
2899 edit_update_curs_row (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2900 {
2901     edit->curs_row = edit->buffer.curs_line - edit->start_line;
2902 }
2903 
2904 /* --------------------------------------------------------------------------------------------- */
2905 
2906 void
2907 edit_update_curs_col (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2908 {
2909     off_t b;
2910 
2911     b = edit_buffer_get_current_bol (&edit->buffer);
2912     edit->curs_col = (long) edit_move_forward3 (edit, b, 0, edit->buffer.curs1);
2913 }
2914 
2915 /* --------------------------------------------------------------------------------------------- */
2916 
2917 long
2918 edit_get_curs_col (const WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2919 {
2920     return edit->curs_col;
2921 }
2922 
2923 /* --------------------------------------------------------------------------------------------- */
2924 /** moves the display start position up by i lines */
2925 
2926 void
2927 edit_scroll_upward (WEdit * edit, long i)
     /* [previous][next][first][last][top][bottom][index][help]  */
2928 {
2929     long lines_above = edit->start_line;
2930 
2931     if (i > lines_above)
2932         i = lines_above;
2933     if (i != 0)
2934     {
2935         edit->start_line -= i;
2936         edit->start_display =
2937             edit_buffer_get_backward_offset (&edit->buffer, edit->start_display, i);
2938         edit->force |= REDRAW_PAGE;
2939         edit->force &= (0xfff - REDRAW_CHAR_ONLY);
2940     }
2941     edit_update_curs_row (edit);
2942 }
2943 
2944 /* --------------------------------------------------------------------------------------------- */
2945 
2946 void
2947 edit_scroll_downward (WEdit * edit, long i)
     /* [previous][next][first][last][top][bottom][index][help]  */
2948 {
2949     long lines_below;
2950 
2951     lines_below = edit->buffer.lines - edit->start_line - (WIDGET (edit)->rect.lines - 1);
2952     if (lines_below > 0)
2953     {
2954         if (i > lines_below)
2955             i = lines_below;
2956         edit->start_line += i;
2957         edit->start_display =
2958             edit_buffer_get_forward_offset (&edit->buffer, edit->start_display, i, 0);
2959         edit->force |= REDRAW_PAGE;
2960         edit->force &= (0xfff - REDRAW_CHAR_ONLY);
2961     }
2962     edit_update_curs_row (edit);
2963 }
2964 
2965 /* --------------------------------------------------------------------------------------------- */
2966 
2967 void
2968 edit_scroll_right (WEdit * edit, long i)
     /* [previous][next][first][last][top][bottom][index][help]  */
2969 {
2970     edit->force |= REDRAW_PAGE;
2971     edit->force &= (0xfff - REDRAW_CHAR_ONLY);
2972     edit->start_col -= i;
2973 }
2974 
2975 /* --------------------------------------------------------------------------------------------- */
2976 
2977 void
2978 edit_scroll_left (WEdit * edit, long i)
     /* [previous][next][first][last][top][bottom][index][help]  */
2979 {
2980     if (edit->start_col)
2981     {
2982         edit->start_col += i;
2983         if (edit->start_col > 0)
2984             edit->start_col = 0;
2985         edit->force |= REDRAW_PAGE;
2986         edit->force &= (0xfff - REDRAW_CHAR_ONLY);
2987     }
2988 }
2989 
2990 /* --------------------------------------------------------------------------------------------- */
2991 /* high level cursor movement commands */
2992 /* --------------------------------------------------------------------------------------------- */
2993 
2994 void
2995 edit_move_to_prev_col (WEdit * edit, off_t p)
     /* [previous][next][first][last][top][bottom][index][help]  */
2996 {
2997     long prev = edit->prev_col;
2998     long over = edit->over_col;
2999     off_t b;
3000 
3001     edit_cursor_move (edit,
3002                       edit_move_forward3 (edit, p, prev + edit->over_col, 0) - edit->buffer.curs1);
3003 
3004     if (edit_options.cursor_beyond_eol)
3005     {
3006         off_t e;
3007         long line_len;
3008 
3009         b = edit_buffer_get_current_bol (&edit->buffer);
3010         e = edit_buffer_get_current_eol (&edit->buffer);
3011         line_len = (long) edit_move_forward3 (edit, b, 0, e);
3012         if (line_len < prev + edit->over_col)
3013         {
3014             edit->over_col = prev + over - line_len;
3015             edit->prev_col = line_len;
3016             edit->curs_col = line_len;
3017         }
3018         else
3019         {
3020             edit->over_col = 0;
3021             edit->prev_col = edit->curs_col;
3022             edit->curs_col = prev + over;
3023         }
3024     }
3025     else
3026     {
3027         edit->over_col = 0;
3028         if (edit_options.fake_half_tabs && is_in_indent (&edit->buffer))
3029         {
3030             long fake_half_tabs;
3031 
3032             edit_update_curs_col (edit);
3033 
3034             fake_half_tabs = HALF_TAB_SIZE * space_width;
3035             if (fake_half_tabs != 0 && edit->curs_col % fake_half_tabs != 0)
3036             {
3037                 long q;
3038 
3039                 q = edit->curs_col;
3040                 edit->curs_col -= (edit->curs_col % fake_half_tabs);
3041                 p = edit_buffer_get_current_bol (&edit->buffer);
3042                 b = edit_move_forward3 (edit, p, edit->curs_col, 0);
3043                 edit_cursor_move (edit, b - edit->buffer.curs1);
3044                 if (!left_of_four_spaces (edit))
3045                 {
3046                     b = edit_move_forward3 (edit, p, q, 0);
3047                     edit_cursor_move (edit, b - edit->buffer.curs1);
3048                 }
3049             }
3050         }
3051     }
3052 }
3053 
3054 /* --------------------------------------------------------------------------------------------- */
3055 /** check whether line in editor is blank or not
3056  *
3057  * @param edit editor object
3058  * @param line number of line
3059  *
3060  * @return TRUE if line in blank, FALSE otherwise
3061  */
3062 
3063 gboolean
3064 edit_line_is_blank (WEdit * edit, long line)
     /* [previous][next][first][last][top][bottom][index][help]  */
3065 {
3066     return is_blank (&edit->buffer, edit_find_line (edit, line));
3067 }
3068 
3069 /* --------------------------------------------------------------------------------------------- */
3070 /** move cursor to line 'line' */
3071 
3072 void
3073 edit_move_to_line (WEdit * e, long line)
     /* [previous][next][first][last][top][bottom][index][help]  */
3074 {
3075     if (line < e->buffer.curs_line)
3076         edit_move_up (e, e->buffer.curs_line - line, FALSE);
3077     else
3078         edit_move_down (e, line - e->buffer.curs_line, FALSE);
3079     edit_scroll_screen_over_cursor (e);
3080 }
3081 
3082 /* --------------------------------------------------------------------------------------------- */
3083 /** scroll window so that first visible line is 'line' */
3084 
3085 void
3086 edit_move_display (WEdit * e, long line)
     /* [previous][next][first][last][top][bottom][index][help]  */
3087 {
3088     if (line < e->start_line)
3089         edit_scroll_upward (e, e->start_line - line);
3090     else
3091         edit_scroll_downward (e, line - e->start_line);
3092 }
3093 
3094 /* --------------------------------------------------------------------------------------------- */
3095 /** save markers onto undo stack */
3096 
3097 void
3098 edit_push_markers (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3099 {
3100     edit_push_undo_action (edit, MARK_1 + edit->mark1);
3101     edit_push_undo_action (edit, MARK_2 + edit->mark2);
3102     edit_push_undo_action (edit, MARK_CURS + edit->end_mark_curs);
3103 }
3104 
3105 /* --------------------------------------------------------------------------------------------- */
3106 
3107 void
3108 edit_set_markers (WEdit * edit, off_t m1, off_t m2, long c1, long c2)
     /* [previous][next][first][last][top][bottom][index][help]  */
3109 {
3110     edit->mark1 = m1;
3111     edit->mark2 = m2;
3112     edit->column1 = c1;
3113     edit->column2 = c2;
3114 }
3115 
3116 /* --------------------------------------------------------------------------------------------- */
3117 /**
3118    if mark2 is -1 then marking is from mark1 to the cursor.
3119    Otherwise its between the markers. This handles this.
3120    Returns FALSE if no text is marked.
3121  */
3122 
3123 gboolean
3124 eval_marks (WEdit * edit, off_t * start_mark, off_t * end_mark)
     /* [previous][next][first][last][top][bottom][index][help]  */
3125 {
3126     long end_mark_curs;
3127 
3128     if (edit->mark1 == edit->mark2)
3129     {
3130         *start_mark = *end_mark = 0;
3131         edit->column2 = edit->column1 = 0;
3132         return FALSE;
3133     }
3134 
3135     if (edit->end_mark_curs < 0)
3136         end_mark_curs = edit->buffer.curs1;
3137     else
3138         end_mark_curs = edit->end_mark_curs;
3139 
3140     if (edit->mark2 >= 0)
3141     {
3142         *start_mark = MIN (edit->mark1, edit->mark2);
3143         *end_mark = MAX (edit->mark1, edit->mark2);
3144     }
3145     else
3146     {
3147         *start_mark = MIN (edit->mark1, end_mark_curs);
3148         *end_mark = MAX (edit->mark1, end_mark_curs);
3149         edit->column2 = edit->curs_col + edit->over_col;
3150     }
3151 
3152     if (edit->column_highlight
3153         && ((edit->mark1 > end_mark_curs && edit->column1 < edit->column2)
3154             || (edit->mark1 < end_mark_curs && edit->column1 > edit->column2)))
3155     {
3156         off_t start_bol, start_eol;
3157         off_t end_bol, end_eol;
3158         long col1, col2;
3159         off_t diff1, diff2;
3160 
3161         start_bol = edit_buffer_get_bol (&edit->buffer, *start_mark);
3162         start_eol = edit_buffer_get_eol (&edit->buffer, start_bol - 1) + 1;
3163         end_bol = edit_buffer_get_bol (&edit->buffer, *end_mark);
3164         end_eol = edit_buffer_get_eol (&edit->buffer, *end_mark);
3165         col1 = MIN (edit->column1, edit->column2);
3166         col2 = MAX (edit->column1, edit->column2);
3167 
3168         diff1 = edit_move_forward3 (edit, start_bol, col2, 0) -
3169             edit_move_forward3 (edit, start_bol, col1, 0);
3170         diff2 = edit_move_forward3 (edit, end_bol, col2, 0) -
3171             edit_move_forward3 (edit, end_bol, col1, 0);
3172 
3173         *start_mark -= diff1;
3174         *end_mark += diff2;
3175         *start_mark = MAX (*start_mark, start_eol);
3176         *end_mark = MIN (*end_mark, end_eol);
3177     }
3178 
3179     return TRUE;
3180 }
3181 
3182 /* --------------------------------------------------------------------------------------------- */
3183 /** highlight marker toggle */
3184 
3185 void
3186 edit_mark_cmd (WEdit * edit, gboolean unmark)
     /* [previous][next][first][last][top][bottom][index][help]  */
3187 {
3188     edit_push_markers (edit);
3189     if (unmark)
3190     {
3191         edit_set_markers (edit, 0, 0, 0, 0);
3192         edit->force |= REDRAW_PAGE;
3193     }
3194     else if (edit->mark2 >= 0)
3195     {
3196         edit->end_mark_curs = -1;
3197         edit_set_markers (edit, edit->buffer.curs1, -1, edit->curs_col + edit->over_col,
3198                           edit->curs_col + edit->over_col);
3199         edit->force |= REDRAW_PAGE;
3200     }
3201     else
3202     {
3203         edit->end_mark_curs = edit->buffer.curs1;
3204         edit_set_markers (edit, edit->mark1, edit->buffer.curs1, edit->column1,
3205                           edit->curs_col + edit->over_col);
3206     }
3207 }
3208 
3209 /* --------------------------------------------------------------------------------------------- */
3210 /** highlight the word under cursor */
3211 
3212 void
3213 edit_mark_current_word_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3214 {
3215     long pos;
3216 
3217     for (pos = edit->buffer.curs1; pos != 0; pos--)
3218     {
3219         int c1, c2;
3220 
3221         c1 = edit_buffer_get_byte (&edit->buffer, pos);
3222         c2 = edit_buffer_get_byte (&edit->buffer, pos - 1);
3223         if (!isspace (c1) && isspace (c2))
3224             break;
3225         if ((my_type_of (c1) & my_type_of (c2)) == 0)
3226             break;
3227     }
3228     edit->mark1 = pos;
3229 
3230     for (; pos < edit->buffer.size; pos++)
3231     {
3232         int c1, c2;
3233 
3234         c1 = edit_buffer_get_byte (&edit->buffer, pos);
3235         c2 = edit_buffer_get_byte (&edit->buffer, pos + 1);
3236         if (!isspace (c1) && isspace (c2))
3237             break;
3238         if ((my_type_of (c1) & my_type_of (c2)) == 0)
3239             break;
3240     }
3241     edit->mark2 = MIN (pos + 1, edit->buffer.size);
3242 
3243     edit->force |= REDRAW_LINE_ABOVE | REDRAW_AFTER_CURSOR;
3244 }
3245 
3246 /* --------------------------------------------------------------------------------------------- */
3247 
3248 void
3249 edit_mark_current_line_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3250 {
3251     edit->mark1 = edit_buffer_get_current_bol (&edit->buffer);
3252     edit->mark2 = edit_buffer_get_current_eol (&edit->buffer);
3253 
3254     edit->force |= REDRAW_LINE_ABOVE | REDRAW_AFTER_CURSOR;
3255 }
3256 
3257 /* --------------------------------------------------------------------------------------------- */
3258 
3259 void
3260 edit_delete_line (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3261 {
3262     /*
3263      * Delete right part of the line.
3264      * Note that edit_buffer_get_byte() returns '\n' when byte position is
3265      *   beyond EOF.
3266      */
3267     while (edit_buffer_get_current_byte (&edit->buffer) != '\n')
3268         (void) edit_delete (edit, TRUE);
3269 
3270     /*
3271      * Delete '\n' char.
3272      * Note that edit_delete() will not corrupt anything if called while
3273      *   cursor position is EOF.
3274      */
3275     (void) edit_delete (edit, TRUE);
3276 
3277     /*
3278      * Delete left part of the line.
3279      * Note, that edit_buffer_get_byte() returns '\n' when byte position is < 0.
3280      */
3281     while (edit_buffer_get_previous_byte (&edit->buffer) != '\n')
3282         (void) edit_backspace (edit, TRUE);
3283 }
3284 
3285 /* --------------------------------------------------------------------------------------------- */
3286 
3287 void
3288 edit_push_key_press (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3289 {
3290     edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
3291     if (edit->mark2 == -1)
3292     {
3293         edit_push_undo_action (edit, MARK_1 + edit->mark1);
3294         edit_push_undo_action (edit, MARK_CURS + edit->end_mark_curs);
3295     }
3296 }
3297 
3298 /* --------------------------------------------------------------------------------------------- */
3299 
3300 void
3301 edit_find_bracket (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3302 {
3303     edit->bracket = edit_get_bracket (edit, 1, 10000);
3304     if (edit->last_bracket != edit->bracket)
3305         edit->force |= REDRAW_PAGE;
3306     edit->last_bracket = edit->bracket;
3307 }
3308 
3309 /* --------------------------------------------------------------------------------------------- */
3310 /**
3311  * This executes a command as though the user initiated it through a key
3312  * press.  Callback with MSG_KEY as a message calls this after
3313  * translating the key press.  This function can be used to pass any
3314  * command to the editor.  Note that the screen wouldn't update
3315  * automatically.  Either of command or char_for_insertion must be
3316  * passed as -1.  Commands are executed, and char_for_insertion is
3317  * inserted at the cursor.
3318  */
3319 
3320 void
3321 edit_execute_key_command (WEdit * edit, long command, int char_for_insertion)
     /* [previous][next][first][last][top][bottom][index][help]  */
3322 {
3323     if (command == CK_MacroStartRecord || command == CK_RepeatStartRecord
3324         || (macro_index < 0
3325             && (command == CK_MacroStartStopRecord || command == CK_RepeatStartStopRecord)))
3326     {
3327         macro_index = 0;
3328         edit->force |= REDRAW_CHAR_ONLY | REDRAW_LINE;
3329         return;
3330     }
3331     if (macro_index != -1)
3332     {
3333         edit->force |= REDRAW_COMPLETELY;
3334         if (command == CK_MacroStopRecord || command == CK_MacroStartStopRecord)
3335         {
3336             edit_store_macro_cmd (edit);
3337             macro_index = -1;
3338             return;
3339         }
3340         if (command == CK_RepeatStopRecord || command == CK_RepeatStartStopRecord)
3341         {
3342             edit_repeat_macro_cmd (edit);
3343             macro_index = -1;
3344             return;
3345         }
3346     }
3347 
3348     if (macro_index >= 0 && macro_index < MAX_MACRO_LENGTH - 1)
3349     {
3350         record_macro_buf[macro_index].action = command;
3351         record_macro_buf[macro_index++].ch = char_for_insertion;
3352     }
3353     /* record the beginning of a set of editing actions initiated by a key press */
3354     if (command != CK_Undo && command != CK_ExtendedKeyMap)
3355         edit_push_key_press (edit);
3356 
3357     edit_execute_cmd (edit, command, char_for_insertion);
3358     if (edit->column_highlight)
3359         edit->force |= REDRAW_PAGE;
3360 }
3361 
3362 /* --------------------------------------------------------------------------------------------- */
3363 /**
3364    This executes a command at a lower level than macro recording.
3365    It also does not push a key_press onto the undo stack. This means
3366    that if it is called many times, a single undo command will undo
3367    all of them. It also does not check for the Undo command.
3368  */
3369 void
3370 edit_execute_cmd (WEdit * edit, long command, int char_for_insertion)
     /* [previous][next][first][last][top][bottom][index][help]  */
3371 {
3372     WRect *w = &WIDGET (edit)->rect;
3373 
3374     if (command == CK_WindowFullscreen)
3375     {
3376         edit_toggle_fullscreen (edit);
3377         return;
3378     }
3379 
3380     /* handle window state */
3381     if (edit_handle_move_resize (edit, command))
3382         return;
3383 
3384     edit->force |= REDRAW_LINE;
3385 
3386     /* The next key press will unhighlight the found string, so update
3387      * the whole page */
3388     if (edit->found_len || edit->column_highlight)
3389         edit->force |= REDRAW_PAGE;
3390 
3391     switch (command)
3392     {
3393         /* a mark command with shift-arrow */
3394     case CK_MarkLeft:
3395     case CK_MarkRight:
3396     case CK_MarkToWordBegin:
3397     case CK_MarkToWordEnd:
3398     case CK_MarkToHome:
3399     case CK_MarkToEnd:
3400     case CK_MarkUp:
3401     case CK_MarkDown:
3402     case CK_MarkPageUp:
3403     case CK_MarkPageDown:
3404     case CK_MarkToFileBegin:
3405     case CK_MarkToFileEnd:
3406     case CK_MarkToPageBegin:
3407     case CK_MarkToPageEnd:
3408     case CK_MarkScrollUp:
3409     case CK_MarkScrollDown:
3410     case CK_MarkParagraphUp:
3411     case CK_MarkParagraphDown:
3412         /* a mark command with alt-arrow */
3413     case CK_MarkColumnPageUp:
3414     case CK_MarkColumnPageDown:
3415     case CK_MarkColumnLeft:
3416     case CK_MarkColumnRight:
3417     case CK_MarkColumnUp:
3418     case CK_MarkColumnDown:
3419     case CK_MarkColumnScrollUp:
3420     case CK_MarkColumnScrollDown:
3421     case CK_MarkColumnParagraphUp:
3422     case CK_MarkColumnParagraphDown:
3423         edit->column_highlight = 0;
3424         if (edit->highlight == 0 || (edit->mark2 != -1 && edit->mark1 != edit->mark2))
3425         {
3426             edit_mark_cmd (edit, TRUE); /* clear */
3427             edit_mark_cmd (edit, FALSE);        /* marking on */
3428         }
3429         edit->highlight = 1;
3430         break;
3431 
3432         /* any other command */
3433     default:
3434         if (edit->highlight)
3435             edit_mark_cmd (edit, FALSE);        /* clear */
3436         edit->highlight = 0;
3437     }
3438 
3439     /* first check for undo */
3440     if (command == CK_Undo)
3441     {
3442         edit->redo_stack_reset = 0;
3443         edit_group_undo (edit);
3444         edit->found_len = 0;
3445         edit->prev_col = edit_get_col (edit);
3446         edit->search_start = edit->buffer.curs1;
3447         return;
3448     }
3449     /*  check for redo */
3450     if (command == CK_Redo)
3451     {
3452         edit->redo_stack_reset = 0;
3453         edit_do_redo (edit);
3454         edit->found_len = 0;
3455         edit->prev_col = edit_get_col (edit);
3456         edit->search_start = edit->buffer.curs1;
3457         return;
3458     }
3459 
3460     edit->redo_stack_reset = 1;
3461 
3462     /* An ordinary key press */
3463     if (char_for_insertion >= 0)
3464     {
3465         /* if non persistent selection and text selected */
3466         if (!edit_options.persistent_selections && edit->mark1 != edit->mark2)
3467             edit_block_delete_cmd (edit);
3468 
3469         if (edit->overwrite)
3470         {
3471             /* remove char only one time, after input first byte, multibyte chars */
3472 #ifdef HAVE_CHARSET
3473             if (!mc_global.utf8_display || edit->charpoint == 0)
3474 #endif
3475                 if (edit_buffer_get_current_byte (&edit->buffer) != '\n')
3476                     edit_delete (edit, FALSE);
3477         }
3478         if (edit_options.cursor_beyond_eol && edit->over_col > 0)
3479             edit_insert_over (edit);
3480 #ifdef HAVE_CHARSET
3481         /**
3482            Encode 8-bit input as UTF-8, if display (locale) is *not* UTF-8,
3483            *but* source encoding *is* set to UTF-8; see ticket #3843 for the details.
3484         */
3485         if (char_for_insertion > 127 && str_isutf8 (get_codepage_id (mc_global.source_codepage))
3486             && !mc_global.utf8_display)
3487         {
3488             unsigned char str[UTF8_CHAR_LEN + 1];
3489             size_t i;
3490             int res;
3491 
3492             res = g_unichar_to_utf8 (char_for_insertion, (char *) str);
3493             if (res == 0)
3494             {
3495                 str[0] = '.';
3496                 str[1] = '\0';
3497             }
3498             else
3499                 str[res] = '\0';
3500 
3501             for (i = 0; i <= UTF8_CHAR_LEN && str[i] != '\0'; i++)
3502             {
3503                 char_for_insertion = str[i];
3504                 edit_insert (edit, char_for_insertion);
3505             }
3506         }
3507         else
3508 #endif
3509             edit_insert (edit, char_for_insertion);
3510 
3511         if (edit_options.auto_para_formatting)
3512         {
3513             format_paragraph (edit, FALSE);
3514             edit->force |= REDRAW_PAGE;
3515         }
3516         else
3517             check_and_wrap_line (edit);
3518         edit->found_len = 0;
3519         edit->prev_col = edit_get_col (edit);
3520         edit->search_start = edit->buffer.curs1;
3521         edit_find_bracket (edit);
3522         return;
3523     }
3524 
3525     switch (command)
3526     {
3527     case CK_TopOnScreen:
3528     case CK_BottomOnScreen:
3529     case CK_Top:
3530     case CK_Bottom:
3531     case CK_PageUp:
3532     case CK_PageDown:
3533     case CK_Home:
3534     case CK_End:
3535     case CK_Up:
3536     case CK_Down:
3537     case CK_Left:
3538     case CK_Right:
3539     case CK_WordLeft:
3540     case CK_WordRight:
3541         if (!edit_options.persistent_selections && edit->mark2 >= 0)
3542         {
3543             if (edit->column_highlight)
3544                 edit_push_undo_action (edit, COLUMN_ON);
3545             edit->column_highlight = 0;
3546             edit_mark_cmd (edit, TRUE);
3547         }
3548         break;
3549     default:
3550         break;
3551     }
3552 
3553     switch (command)
3554     {
3555     case CK_TopOnScreen:
3556     case CK_BottomOnScreen:
3557     case CK_MarkToPageBegin:
3558     case CK_MarkToPageEnd:
3559     case CK_Up:
3560     case CK_Down:
3561     case CK_WordLeft:
3562     case CK_WordRight:
3563     case CK_MarkToWordBegin:
3564     case CK_MarkToWordEnd:
3565     case CK_MarkUp:
3566     case CK_MarkDown:
3567     case CK_MarkColumnUp:
3568     case CK_MarkColumnDown:
3569         if (edit->mark2 == -1)
3570             break;              /*marking is following the cursor: may need to highlight a whole line */
3571         MC_FALLTHROUGH;
3572     case CK_Left:
3573     case CK_Right:
3574     case CK_MarkLeft:
3575     case CK_MarkRight:
3576         edit->force |= REDRAW_CHAR_ONLY;
3577         break;
3578     default:
3579         break;
3580     }
3581 
3582     /* basic cursor key commands */
3583     switch (command)
3584     {
3585     case CK_BackSpace:
3586         /* if non persistent selection and text selected */
3587         if (!edit_options.persistent_selections && edit->mark1 != edit->mark2)
3588             edit_block_delete_cmd (edit);
3589         else if (edit_options.cursor_beyond_eol && edit->over_col > 0)
3590             edit->over_col--;
3591         else if (edit_options.backspace_through_tabs && is_in_indent (&edit->buffer))
3592         {
3593             while (edit_buffer_get_previous_byte (&edit->buffer) != '\n' && edit->buffer.curs1 > 0)
3594                 edit_backspace (edit, TRUE);
3595         }
3596         else if (edit_options.fake_half_tabs && is_in_indent (&edit->buffer)
3597                  && right_of_four_spaces (edit))
3598         {
3599             int i;
3600 
3601             for (i = 0; i < HALF_TAB_SIZE; i++)
3602                 edit_backspace (edit, TRUE);
3603         }
3604         else
3605             edit_backspace (edit, FALSE);
3606         break;
3607     case CK_Delete:
3608         /* if non persistent selection and text selected */
3609         if (!edit_options.persistent_selections && edit->mark1 != edit->mark2)
3610             edit_block_delete_cmd (edit);
3611         else
3612         {
3613             if (edit_options.cursor_beyond_eol && edit->over_col > 0)
3614                 edit_insert_over (edit);
3615 
3616             if (edit_options.fake_half_tabs && is_in_indent (&edit->buffer)
3617                 && left_of_four_spaces (edit))
3618             {
3619                 int i;
3620 
3621                 for (i = 1; i <= HALF_TAB_SIZE; i++)
3622                     edit_delete (edit, TRUE);
3623             }
3624             else
3625                 edit_delete (edit, FALSE);
3626         }
3627         break;
3628     case CK_DeleteToWordBegin:
3629         edit->over_col = 0;
3630         edit_left_delete_word (edit);
3631         break;
3632     case CK_DeleteToWordEnd:
3633         if (edit_options.cursor_beyond_eol && edit->over_col > 0)
3634             edit_insert_over (edit);
3635 
3636         edit_right_delete_word (edit);
3637         break;
3638     case CK_DeleteLine:
3639         edit_delete_line (edit);
3640         break;
3641     case CK_DeleteToHome:
3642         edit_delete_to_line_begin (edit);
3643         break;
3644     case CK_DeleteToEnd:
3645         edit_delete_to_line_end (edit);
3646         break;
3647     case CK_Enter:
3648         edit->over_col = 0;
3649         if (edit_options.auto_para_formatting)
3650         {
3651             edit_double_newline (edit);
3652             if (edit_options.return_does_auto_indent && !bracketed_pasting_in_progress)
3653                 edit_auto_indent (edit);
3654             format_paragraph (edit, FALSE);
3655         }
3656         else
3657         {
3658             edit_insert (edit, '\n');
3659             if (edit_options.return_does_auto_indent && !bracketed_pasting_in_progress)
3660                 edit_auto_indent (edit);
3661         }
3662         break;
3663     case CK_Return:
3664         edit_insert (edit, '\n');
3665         break;
3666 
3667     case CK_MarkColumnPageUp:
3668         edit->column_highlight = 1;
3669         MC_FALLTHROUGH;
3670     case CK_PageUp:
3671     case CK_MarkPageUp:
3672         edit_move_up (edit, w->lines - 1, TRUE);
3673         break;
3674     case CK_MarkColumnPageDown:
3675         edit->column_highlight = 1;
3676         MC_FALLTHROUGH;
3677     case CK_PageDown:
3678     case CK_MarkPageDown:
3679         edit_move_down (edit, w->lines - 1, TRUE);
3680         break;
3681     case CK_MarkColumnLeft:
3682         edit->column_highlight = 1;
3683         MC_FALLTHROUGH;
3684     case CK_Left:
3685     case CK_MarkLeft:
3686         if (edit_options.fake_half_tabs && is_in_indent (&edit->buffer)
3687             && right_of_four_spaces (edit))
3688         {
3689             if (edit_options.cursor_beyond_eol && edit->over_col > 0)
3690                 edit->over_col--;
3691             else
3692                 edit_cursor_move (edit, -HALF_TAB_SIZE);
3693             edit->force &= (0xFFF - REDRAW_CHAR_ONLY);
3694         }
3695         else
3696             edit_left_char_move_cmd (edit);
3697         break;
3698     case CK_MarkColumnRight:
3699         edit->column_highlight = 1;
3700         MC_FALLTHROUGH;
3701     case CK_Right:
3702     case CK_MarkRight:
3703         if (edit_options.fake_half_tabs && is_in_indent (&edit->buffer)
3704             && left_of_four_spaces (edit))
3705         {
3706             edit_cursor_move (edit, HALF_TAB_SIZE);
3707             edit->force &= (0xFFF - REDRAW_CHAR_ONLY);
3708         }
3709         else
3710             edit_right_char_move_cmd (edit);
3711         break;
3712     case CK_TopOnScreen:
3713     case CK_MarkToPageBegin:
3714         edit_begin_page (edit);
3715         break;
3716     case CK_BottomOnScreen:
3717     case CK_MarkToPageEnd:
3718         edit_end_page (edit);
3719         break;
3720     case CK_WordLeft:
3721     case CK_MarkToWordBegin:
3722         edit->over_col = 0;
3723         edit_left_word_move_cmd (edit);
3724         break;
3725     case CK_WordRight:
3726     case CK_MarkToWordEnd:
3727         edit->over_col = 0;
3728         edit_right_word_move_cmd (edit);
3729         break;
3730     case CK_MarkColumnUp:
3731         edit->column_highlight = 1;
3732         MC_FALLTHROUGH;
3733     case CK_Up:
3734     case CK_MarkUp:
3735         edit_move_up (edit, 1, FALSE);
3736         break;
3737     case CK_MarkColumnDown:
3738         edit->column_highlight = 1;
3739         MC_FALLTHROUGH;
3740     case CK_Down:
3741     case CK_MarkDown:
3742         edit_move_down (edit, 1, FALSE);
3743         break;
3744     case CK_MarkColumnParagraphUp:
3745         edit->column_highlight = 1;
3746         MC_FALLTHROUGH;
3747     case CK_ParagraphUp:
3748     case CK_MarkParagraphUp:
3749         edit_move_up_paragraph (edit, FALSE);
3750         break;
3751     case CK_MarkColumnParagraphDown:
3752         edit->column_highlight = 1;
3753         MC_FALLTHROUGH;
3754     case CK_ParagraphDown:
3755     case CK_MarkParagraphDown:
3756         edit_move_down_paragraph (edit, FALSE);
3757         break;
3758     case CK_MarkColumnScrollUp:
3759         edit->column_highlight = 1;
3760         MC_FALLTHROUGH;
3761     case CK_ScrollUp:
3762     case CK_MarkScrollUp:
3763         edit_move_up (edit, 1, TRUE);
3764         break;
3765     case CK_MarkColumnScrollDown:
3766         edit->column_highlight = 1;
3767         MC_FALLTHROUGH;
3768     case CK_ScrollDown:
3769     case CK_MarkScrollDown:
3770         edit_move_down (edit, 1, TRUE);
3771         break;
3772     case CK_Home:
3773     case CK_MarkToHome:
3774         edit_cursor_to_bol (edit);
3775         break;
3776     case CK_End:
3777     case CK_MarkToEnd:
3778         edit_cursor_to_eol (edit);
3779         break;
3780     case CK_Tab:
3781         /* if text marked shift block */
3782         if (edit->mark1 != edit->mark2 && !edit_options.persistent_selections)
3783         {
3784             if (edit->mark2 < 0)
3785                 edit_mark_cmd (edit, FALSE);
3786             edit_move_block_to_right (edit);
3787         }
3788         else
3789         {
3790             if (edit_options.cursor_beyond_eol)
3791                 edit_insert_over (edit);
3792             edit_tab_cmd (edit);
3793             if (edit_options.auto_para_formatting)
3794             {
3795                 format_paragraph (edit, FALSE);
3796                 edit->force |= REDRAW_PAGE;
3797             }
3798             else
3799                 check_and_wrap_line (edit);
3800         }
3801         break;
3802 
3803     case CK_InsertOverwrite:
3804         edit->overwrite = !edit->overwrite;
3805         break;
3806 
3807     case CK_Mark:
3808         if (edit->mark2 >= 0)
3809         {
3810             if (edit->column_highlight)
3811                 edit_push_undo_action (edit, COLUMN_ON);
3812             edit->column_highlight = 0;
3813         }
3814         edit_mark_cmd (edit, FALSE);
3815         break;
3816     case CK_MarkColumn:
3817         if (!edit->column_highlight)
3818             edit_push_undo_action (edit, COLUMN_OFF);
3819         edit->column_highlight = 1;
3820         edit_mark_cmd (edit, FALSE);
3821         break;
3822     case CK_MarkAll:
3823         edit_set_markers (edit, 0, edit->buffer.size, 0, 0);
3824         edit->force |= REDRAW_PAGE;
3825         break;
3826     case CK_Unmark:
3827         if (edit->column_highlight)
3828             edit_push_undo_action (edit, COLUMN_ON);
3829         edit->column_highlight = 0;
3830         edit_mark_cmd (edit, TRUE);
3831         break;
3832     case CK_MarkWord:
3833         if (edit->column_highlight)
3834             edit_push_undo_action (edit, COLUMN_ON);
3835         edit->column_highlight = 0;
3836         edit_mark_current_word_cmd (edit);
3837         break;
3838     case CK_MarkLine:
3839         if (edit->column_highlight)
3840             edit_push_undo_action (edit, COLUMN_ON);
3841         edit->column_highlight = 0;
3842         edit_mark_current_line_cmd (edit);
3843         break;
3844 
3845     case CK_Bookmark:
3846         book_mark_clear (edit, edit->buffer.curs_line, BOOK_MARK_FOUND_COLOR);
3847         if (book_mark_query_color (edit, edit->buffer.curs_line, BOOK_MARK_COLOR))
3848             book_mark_clear (edit, edit->buffer.curs_line, BOOK_MARK_COLOR);
3849         else
3850             book_mark_insert (edit, edit->buffer.curs_line, BOOK_MARK_COLOR);
3851         break;
3852     case CK_BookmarkFlush:
3853         book_mark_flush (edit, BOOK_MARK_COLOR);
3854         book_mark_flush (edit, BOOK_MARK_FOUND_COLOR);
3855         edit->force |= REDRAW_PAGE;
3856         break;
3857     case CK_BookmarkNext:
3858         if (edit->book_mark != NULL)
3859         {
3860             edit_book_mark_t *p;
3861 
3862             p = book_mark_find (edit, edit->buffer.curs_line);
3863             if (p->next != NULL)
3864             {
3865                 p = p->next;
3866                 if (p->line >= edit->start_line + w->lines || p->line < edit->start_line)
3867                     edit_move_display (edit, p->line - w->lines / 2);
3868                 edit_move_to_line (edit, p->line);
3869             }
3870         }
3871         break;
3872     case CK_BookmarkPrev:
3873         if (edit->book_mark != NULL)
3874         {
3875             edit_book_mark_t *p;
3876 
3877             p = book_mark_find (edit, edit->buffer.curs_line);
3878             while (p->line == edit->buffer.curs_line)
3879                 if (p->prev != NULL)
3880                     p = p->prev;
3881             if (p->line >= 0)
3882             {
3883                 if (p->line >= edit->start_line + w->lines || p->line < edit->start_line)
3884                     edit_move_display (edit, p->line - w->lines / 2);
3885                 edit_move_to_line (edit, p->line);
3886             }
3887         }
3888         break;
3889 
3890     case CK_Top:
3891     case CK_MarkToFileBegin:
3892         edit_move_to_top (edit);
3893         break;
3894     case CK_Bottom:
3895     case CK_MarkToFileEnd:
3896         edit_move_to_bottom (edit);
3897         break;
3898 
3899     case CK_Copy:
3900         if (edit_options.cursor_beyond_eol && edit->over_col > 0)
3901             edit_insert_over (edit);
3902         edit_block_copy_cmd (edit);
3903         break;
3904     case CK_Remove:
3905         edit_block_delete_cmd (edit);
3906         break;
3907     case CK_Move:
3908         edit_block_move_cmd (edit);
3909         break;
3910 
3911     case CK_BlockShiftLeft:
3912         if (edit->mark1 != edit->mark2)
3913             edit_move_block_to_left (edit);
3914         break;
3915     case CK_BlockShiftRight:
3916         if (edit->mark1 != edit->mark2)
3917             edit_move_block_to_right (edit);
3918         break;
3919     case CK_Store:
3920         edit_copy_to_X_buf_cmd (edit);
3921         break;
3922     case CK_Cut:
3923         edit_cut_to_X_buf_cmd (edit);
3924         break;
3925     case CK_Paste:
3926         /* if non persistent selection and text selected */
3927         if (!edit_options.persistent_selections && edit->mark1 != edit->mark2)
3928             edit_block_delete_cmd (edit);
3929         if (edit_options.cursor_beyond_eol && edit->over_col > 0)
3930             edit_insert_over (edit);
3931         edit_paste_from_X_buf_cmd (edit);
3932         if (!edit_options.persistent_selections && edit->mark2 >= 0)
3933         {
3934             if (edit->column_highlight)
3935                 edit_push_undo_action (edit, COLUMN_ON);
3936             edit->column_highlight = 0;
3937             edit_mark_cmd (edit, TRUE);
3938         }
3939         break;
3940     case CK_History:
3941         edit_paste_from_history (edit);
3942         break;
3943 
3944     case CK_SaveAs:
3945         edit_save_as_cmd (edit);
3946         break;
3947     case CK_Save:
3948         edit_save_confirm_cmd (edit);
3949         break;
3950     case CK_BlockSave:
3951         edit_save_block_cmd (edit);
3952         break;
3953     case CK_InsertFile:
3954         edit_insert_file_cmd (edit);
3955         break;
3956 
3957     case CK_FilePrev:
3958         edit_load_back_cmd (edit);
3959         break;
3960     case CK_FileNext:
3961         edit_load_forward_cmd (edit);
3962         break;
3963 
3964     case CK_SyntaxChoose:
3965         edit_syntax_dialog (edit);
3966         break;
3967 
3968     case CK_Search:
3969         edit_search_cmd (edit, FALSE);
3970         break;
3971     case CK_SearchContinue:
3972         edit_search_cmd (edit, TRUE);
3973         break;
3974     case CK_Replace:
3975         edit_replace_cmd (edit, FALSE);
3976         break;
3977     case CK_ReplaceContinue:
3978         edit_replace_cmd (edit, TRUE);
3979         break;
3980     case CK_Complete:
3981         /* if text marked shift block */
3982         if (edit->mark1 != edit->mark2 && !edit_options.persistent_selections)
3983             edit_move_block_to_left (edit);
3984         else
3985             edit_complete_word_cmd (edit);
3986         break;
3987     case CK_Find:
3988         edit_get_match_keyword_cmd (edit);
3989         break;
3990 
3991 #ifdef HAVE_ASPELL
3992     case CK_SpellCheckCurrentWord:
3993         edit_suggest_current_word (edit);
3994         break;
3995     case CK_SpellCheck:
3996         edit_spellcheck_file (edit);
3997         break;
3998     case CK_SpellCheckSelectLang:
3999         edit_set_spell_lang ();
4000         break;
4001 #endif
4002 
4003     case CK_Date:
4004         {
4005             char s[BUF_MEDIUM];
4006             /* fool gcc to prevent a Y2K warning */
4007             char time_format[] = "_c";
4008             time_format[0] = '%';
4009 
4010             FMT_LOCALTIME_CURRENT (s, sizeof (s), time_format);
4011             edit_print_string (edit, s);
4012             edit->force |= REDRAW_PAGE;
4013         }
4014         break;
4015     case CK_Goto:
4016         edit_goto_cmd (edit);
4017         break;
4018     case CK_ParagraphFormat:
4019         format_paragraph (edit, TRUE);
4020         edit->force |= REDRAW_PAGE;
4021         break;
4022     case CK_MacroDelete:
4023         edit_delete_macro_cmd (edit);
4024         break;
4025     case CK_MatchBracket:
4026         edit_goto_matching_bracket (edit);
4027         break;
4028     case CK_UserMenu:
4029         edit_user_menu (edit, NULL, -1);
4030         break;
4031     case CK_Sort:
4032         edit_sort_cmd (edit);
4033         break;
4034     case CK_ExternalCommand:
4035         edit_ext_cmd (edit);
4036         break;
4037     case CK_EditMail:
4038         edit_mail_dialog (edit);
4039         break;
4040 #ifdef HAVE_CHARSET
4041     case CK_SelectCodepage:
4042         edit_select_codepage_cmd (edit);
4043         break;
4044 #endif
4045     case CK_InsertLiteral:
4046         edit_insert_literal_cmd (edit);
4047         break;
4048     case CK_MacroStartStopRecord:
4049         edit_begin_end_macro_cmd (edit);
4050         break;
4051     case CK_RepeatStartStopRecord:
4052         edit_begin_end_repeat_cmd (edit);
4053         break;
4054     case CK_ExtendedKeyMap:
4055         WIDGET (edit)->ext_mode = TRUE;
4056         break;
4057     default:
4058         break;
4059     }
4060 
4061     /* CK_PipeBlock */
4062     if ((command / CK_PipeBlock (0)) == 1)
4063         edit_block_process_cmd (edit, command - CK_PipeBlock (0));
4064 
4065     /* keys which must set the col position, and the search vars */
4066     switch (command)
4067     {
4068     case CK_Search:
4069     case CK_SearchContinue:
4070     case CK_Replace:
4071     case CK_ReplaceContinue:
4072     case CK_Complete:
4073         edit->prev_col = edit_get_col (edit);
4074         break;
4075     case CK_Up:
4076     case CK_MarkUp:
4077     case CK_MarkColumnUp:
4078     case CK_Down:
4079     case CK_MarkDown:
4080     case CK_MarkColumnDown:
4081     case CK_PageUp:
4082     case CK_MarkPageUp:
4083     case CK_MarkColumnPageUp:
4084     case CK_PageDown:
4085     case CK_MarkPageDown:
4086     case CK_MarkColumnPageDown:
4087     case CK_Top:
4088     case CK_MarkToFileBegin:
4089     case CK_Bottom:
4090     case CK_MarkToFileEnd:
4091     case CK_ParagraphUp:
4092     case CK_MarkParagraphUp:
4093     case CK_MarkColumnParagraphUp:
4094     case CK_ParagraphDown:
4095     case CK_MarkParagraphDown:
4096     case CK_MarkColumnParagraphDown:
4097     case CK_ScrollUp:
4098     case CK_MarkScrollUp:
4099     case CK_MarkColumnScrollUp:
4100     case CK_ScrollDown:
4101     case CK_MarkScrollDown:
4102     case CK_MarkColumnScrollDown:
4103         edit->search_start = edit->buffer.curs1;
4104         edit->found_len = 0;
4105         break;
4106     default:
4107         edit->found_len = 0;
4108         edit->prev_col = edit_get_col (edit);
4109         edit->search_start = edit->buffer.curs1;
4110     }
4111     edit_find_bracket (edit);
4112 
4113     if (edit_options.auto_para_formatting)
4114     {
4115         switch (command)
4116         {
4117         case CK_BackSpace:
4118         case CK_Delete:
4119         case CK_DeleteToWordBegin:
4120         case CK_DeleteToWordEnd:
4121         case CK_DeleteToHome:
4122         case CK_DeleteToEnd:
4123             format_paragraph (edit, FALSE);
4124             edit->force |= REDRAW_PAGE;
4125             break;
4126         default:
4127             break;
4128         }
4129     }
4130 }
4131 
4132 /* --------------------------------------------------------------------------------------------- */
4133 
4134 void
4135 edit_stack_init (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
4136 {
4137     for (edit_stack_iterator = 0; edit_stack_iterator < MAX_HISTORY_MOVETO; edit_stack_iterator++)
4138     {
4139         edit_history_moveto[edit_stack_iterator].filename_vpath = NULL;
4140         edit_history_moveto[edit_stack_iterator].line = -1;
4141     }
4142 
4143     edit_stack_iterator = 0;
4144 }
4145 
4146 /* --------------------------------------------------------------------------------------------- */
4147 
4148 void
4149 edit_stack_free (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
4150 {
4151     for (edit_stack_iterator = 0; edit_stack_iterator < MAX_HISTORY_MOVETO; edit_stack_iterator++)
4152         vfs_path_free (edit_history_moveto[edit_stack_iterator].filename_vpath, TRUE);
4153 }
4154 
4155 /* --------------------------------------------------------------------------------------------- */
4156 /** move i lines */
4157 
4158 void
4159 edit_move_up (WEdit * edit, long i, gboolean do_scroll)
     /* [previous][next][first][last][top][bottom][index][help]  */
4160 {
4161     edit_move_updown (edit, i, do_scroll, TRUE);
4162 }
4163 
4164 /* --------------------------------------------------------------------------------------------- */
4165 /** move i lines */
4166 
4167 void
4168 edit_move_down (WEdit * edit, long i, gboolean do_scroll)
     /* [previous][next][first][last][top][bottom][index][help]  */
4169 {
4170     edit_move_updown (edit, i, do_scroll, FALSE);
4171 }
4172 
4173 /* --------------------------------------------------------------------------------------------- */

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