Manual pages: mcmcdiffmceditmcview

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
  101. edit_arg_vpath_new
  102. edit_arg_new
  103. edit_arg_init
  104. edit_arg_assign
  105. edit_arg_free
  106. edit_get_file_name

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

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