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

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