Manual pages: mcmcdiffmceditmcview

root/src/editor/edit.c

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

DEFINITIONS

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

   1 /*
   2    Editor low level data handling and cursor fundamentals.
   3 
   4    Copyright (C) 1996-2025
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Paul Sheer 1996, 1997
   9    Ilia Maslakov <il.smind@gmail.com> 2009, 2010, 2011
  10    Andrew Borodin <aborodin@vmail.ru> 2012-2022
  11 
  12    This file is part of the Midnight Commander.
  13 
  14    The Midnight Commander is free software: you can redistribute it
  15    and/or modify it under the terms of the GNU General Public License as
  16    published by the Free Software Foundation, either version 3 of the License,
  17    or (at your option) any later version.
  18 
  19    The Midnight Commander is distributed in the hope that it will be useful,
  20    but WITHOUT ANY WARRANTY; without even the implied warranty of
  21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22    GNU General Public License for more details.
  23 
  24    You should have received a copy of the GNU General Public License
  25    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  26  */
  27 
  28 /** \file
  29  *  \brief Source: editor low level data handling and cursor fundamentals
  30  *  \author Paul Sheer
  31  *  \date 1996, 1997
  32  */
  33 
  34 #include <config.h>
  35 #include <stdio.h>
  36 #include <stdarg.h>
  37 #include <sys/types.h>
  38 #include <unistd.h>
  39 #include <string.h>
  40 #include <ctype.h>
  41 #include <sys/stat.h>
  42 #include <stdint.h>  // UINTMAX_MAX
  43 #include <stdlib.h>
  44 
  45 #include "lib/global.h"
  46 
  47 #include "lib/tty/color.h"
  48 #include "lib/tty/tty.h"  // attrset()
  49 #include "lib/tty/key.h"  // is_idle()
  50 #include "lib/skin.h"     // EDITOR_NORMAL_COLOR
  51 #include "lib/fileloc.h"  // EDIT_HOME_BLOCK_FILE
  52 #include "lib/vfs/vfs.h"
  53 #include "lib/strutil.h"  // utf string functions
  54 #include "lib/util.h"     // load_file_position(), save_file_position()
  55 #include "lib/timefmt.h"  // time formatting
  56 #include "lib/lock.h"
  57 #include "lib/widget.h"
  58 #include "lib/charsets.h"  // get_codepage_id
  59 
  60 #include "src/usermenu.h"  // user_menu_cmd()
  61 
  62 #include "src/keymap.h"
  63 
  64 #include "edit-impl.h"
  65 #include "editwidget.h"
  66 #include "editsearch.h"
  67 #include "editcomplete.h"  // edit_complete_word_cmd()
  68 #include "editmacros.h"
  69 #include "etags.h"  // edit_get_match_keyword_cmd()
  70 #ifdef HAVE_ASPELL
  71 #include "spell.h"
  72 #endif
  73 
  74 /*** global variables ****************************************************************************/
  75 
  76 edit_options_t edit_options = {
  77     .word_wrap_line_length = DEFAULT_WRAP_LINE_LENGTH,
  78     .typewriter_wrap = FALSE,
  79     .auto_para_formatting = FALSE,
  80     .fill_tabs_with_spaces = FALSE,
  81     .return_does_auto_indent = TRUE,
  82     .backspace_through_tabs = FALSE,
  83     .fake_half_tabs = TRUE,
  84     .persistent_selections = TRUE,
  85     .drop_selection_on_copy = TRUE,
  86     .cursor_beyond_eol = FALSE,
  87     .cursor_after_inserted_block = FALSE,
  88     .state_full_filename = FALSE,
  89     .line_state = FALSE,
  90     .line_state_width = 0,
  91     .save_mode = EDIT_QUICK_SAVE,
  92     .confirm_save = TRUE,
  93     .save_position = TRUE,
  94     .syntax_highlighting = TRUE,
  95     .group_undo = FALSE,
  96     .backup_ext = NULL,
  97     .filesize_threshold = NULL,
  98     .stop_format_chars = NULL,
  99     .visible_tabs = TRUE,
 100     .visible_tws = TRUE,
 101     .show_right_margin = FALSE,
 102     .simple_statusbar = FALSE,
 103     .check_nl_at_eof = FALSE,
 104 };
 105 
 106 int max_undo = 32768;
 107 
 108 gboolean enable_show_tabs_tws = TRUE;
 109 
 110 unsigned int edit_stack_iterator = 0;
 111 edit_arg_t edit_history_moveto[MAX_HISTORY_MOVETO];
 112 /* magic sequence for say than block is vertical */
 113 const char VERTICAL_MAGIC[] = { '\1', '\1', '\1', '\1', '\n' };
 114 
 115 /*** file scope macro definitions ****************************************************************/
 116 
 117 #define TEMP_BUF_LEN 1024
 118 
 119 #define space_width  1
 120 
 121 /*** file scope type declarations ****************************************************************/
 122 
 123 /*** forward declarations (file scope functions) *************************************************/
 124 
 125 /*** file scope variables ************************************************************************/
 126 
 127 /* detecting an error on save is easy: just check if every byte has been written. */
 128 /* detecting an error on read, is not so easy 'cos there is not way to tell
 129    whether you read everything or not. */
 130 /* FIXME: add proper 'triple_pipe_open' to read, write and check errors. */
 131 static const struct edit_filters
 132 {
 133     const char *read, *write, *extension;
 134 } all_filters[] = {
 135     { "xz -cd %s 2>&1", "xz > %s", ".xz" },         //
 136     { "zstd -cd %s 2>&1", "zstd > %s", ".zst" },    //
 137     { "lz4 -cd %s 2>&1", "lz4 > %s", ".lz4" },      //
 138     { "lzip -cd %s 2>&1", "lzip > %s", ".lz" },     //
 139     { "lzma -cd %s 2>&1", "lzma > %s", ".lzma" },   //
 140     { "lzop -cd %s 2>&1", "lzop > %s", ".lzo" },    //
 141     { "bzip2 -cd %s 2>&1", "bzip2 > %s", ".bz2" },  //
 142     { "gzip -cd %s 2>&1", "gzip > %s", ".gz" },     //
 143     { "gzip -cd %s 2>&1", "gzip > %s", ".Z" },
 144 };
 145 
 146 static const off_t filesize_default_threshold = 64 * 1024 * 1024;  // 64 MB
 147 
 148 /* --------------------------------------------------------------------------------------------- */
 149 /*** file scope functions ************************************************************************/
 150 /* --------------------------------------------------------------------------------------------- */
 151 
 152 static int
 153 edit_load_status_update_cb (status_msg_t *sm)
     /* [previous][next][first][last][top][bottom][index][help]  */
 154 {
 155     simple_status_msg_t *ssm = SIMPLE_STATUS_MSG (sm);
 156     edit_buffer_read_file_status_msg_t *rsm = (edit_buffer_read_file_status_msg_t *) sm;
 157     Widget *wd = WIDGET (sm->dlg);
 158 
 159     if (verbose)
 160         label_set_textv (ssm->label, _ ("Loading: %3d%%"),
 161                          edit_buffer_calc_percent (rsm->buf, rsm->loaded));
 162     else
 163         label_set_text (ssm->label, _ ("Loading..."));
 164 
 165     if (rsm->first)
 166     {
 167         Widget *lw = WIDGET (ssm->label);
 168         WRect r;
 169 
 170         r = wd->rect;
 171         r.cols = MAX (r.cols, lw->rect.cols + 6);
 172         widget_set_size_rect (wd, &r);
 173         r = lw->rect;
 174         r.x = wd->rect.x + (wd->rect.cols - r.cols) / 2;
 175         widget_set_size_rect (lw, &r);
 176         rsm->first = FALSE;
 177     }
 178 
 179     return status_msg_common_update (sm);
 180 }
 181 
 182 /* --------------------------------------------------------------------------------------------- */
 183 /**
 184  * Load file OR text into buffers.  Set cursor to the beginning of file.
 185  *
 186  * @return FALSE on error.
 187  */
 188 
 189 static gboolean
 190 edit_load_file_fast (edit_buffer_t *buf, const vfs_path_t *filename_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 191 {
 192     int file;
 193     gboolean ret;
 194     edit_buffer_read_file_status_msg_t rsm;
 195     gboolean aborted;
 196 
 197     file = mc_open (filename_vpath, O_RDONLY | O_BINARY);
 198     if (file < 0)
 199     {
 200         message (D_ERROR, MSG_ERROR, _ ("Cannot open %s for reading"),
 201                  vfs_path_as_str (filename_vpath));
 202         return FALSE;
 203     }
 204 
 205     rsm.first = TRUE;
 206     rsm.buf = buf;
 207     rsm.loaded = 0;
 208 
 209     status_msg_init (STATUS_MSG (&rsm), _ ("Load file"), 1.0, simple_status_msg_init_cb,
 210                      edit_load_status_update_cb, NULL);
 211 
 212     ret = (edit_buffer_read_file (buf, file, buf->size, &rsm, &aborted) == buf->size);
 213 
 214     status_msg_deinit (STATUS_MSG (&rsm));
 215 
 216     if (!ret && !aborted)
 217         message (D_ERROR, MSG_ERROR, _ ("Error reading %s"), vfs_path_as_str (filename_vpath));
 218 
 219     mc_close (file);
 220     return ret;
 221 }
 222 
 223 /* --------------------------------------------------------------------------------------------- */
 224 /** Return index of the filter or -1 is there is no appropriate filter */
 225 
 226 static int
 227 edit_find_filter (const vfs_path_t *filename_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 228 {
 229     if (filename_vpath != NULL)
 230     {
 231         const char *s;
 232         size_t i;
 233 
 234         s = vfs_path_as_str (filename_vpath);
 235 
 236         for (i = 0; i < G_N_ELEMENTS (all_filters); i++)
 237             if (g_str_has_suffix (s, all_filters[i].extension))
 238                 return i;
 239     }
 240 
 241     return -1;
 242 }
 243 
 244 /* --------------------------------------------------------------------------------------------- */
 245 
 246 static char *
 247 edit_get_filter (const vfs_path_t *filename_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 248 {
 249     int i;
 250     char *quoted_name;
 251     char *p = NULL;
 252 
 253     i = edit_find_filter (filename_vpath);
 254     if (i < 0)
 255         return NULL;
 256 
 257     quoted_name = name_quote (vfs_path_as_str (filename_vpath), FALSE);
 258     if (quoted_name != NULL)
 259     {
 260         p = g_strdup_printf (all_filters[i].read, quoted_name);
 261         g_free (quoted_name);
 262     }
 263 
 264     return p;
 265 }
 266 
 267 /* --------------------------------------------------------------------------------------------- */
 268 
 269 static off_t
 270 edit_insert_stream (WEdit *edit, FILE *f)
     /* [previous][next][first][last][top][bottom][index][help]  */
 271 {
 272     int c;
 273     off_t i;
 274 
 275     for (i = 0; (c = fgetc (f)) >= 0; i++)
 276         edit_insert (edit, c);
 277 
 278     return i;
 279 }
 280 
 281 /* --------------------------------------------------------------------------------------------- */
 282 /**
 283  * Open file and create it if necessary.
 284  *
 285  * @param edit           editor object
 286  * @param filename_vpath file name
 287  * @param st             buffer for store stat info
 288  * @return TRUE for success, FALSE for error.
 289  */
 290 
 291 static gboolean
 292 check_file_access (WEdit *edit, const vfs_path_t *filename_vpath, struct stat *st)
     /* [previous][next][first][last][top][bottom][index][help]  */
 293 {
 294     static uintmax_t threshold = UINTMAX_MAX;
 295     int file;
 296     gchar *errmsg = NULL;
 297     gboolean ret = TRUE;
 298 
 299     // Try opening an existing file
 300     file = mc_open (filename_vpath, O_NONBLOCK | O_RDONLY | O_BINARY, 0666);
 301     if (file < 0)
 302     {
 303         /*
 304          * Try creating the file. O_EXCL prevents following broken links
 305          * and opening existing files.
 306          */
 307         file = mc_open (filename_vpath, O_NONBLOCK | O_RDONLY | O_BINARY | O_CREAT | O_EXCL, 0666);
 308         if (file < 0)
 309         {
 310             errmsg = g_strdup_printf (_ ("Cannot open %s for reading"),
 311                                       vfs_path_as_str (filename_vpath));
 312             goto cleanup;
 313         }
 314 
 315         // New file, delete it if it's not modified or saved
 316         edit->delete_file = 1;
 317     }
 318 
 319     // Check what we have opened
 320     if (mc_fstat (file, st) < 0)
 321     {
 322         errmsg = g_strdup_printf (_ ("Cannot get size/permissions for %s"),
 323                                   vfs_path_as_str (filename_vpath));
 324         goto cleanup;
 325     }
 326 
 327     // We want to open regular files only
 328     if (!S_ISREG (st->st_mode))
 329     {
 330         errmsg =
 331             g_strdup_printf (_ ("\"%s\" is not a regular file"), vfs_path_as_str (filename_vpath));
 332         goto cleanup;
 333     }
 334 
 335     // get file size threshold for alarm
 336     if (threshold == UINTMAX_MAX)
 337     {
 338         gboolean err = FALSE;
 339 
 340         threshold = parse_integer (edit_options.filesize_threshold, &err);
 341         if (err)
 342             threshold = filesize_default_threshold;
 343     }
 344 
 345     /*
 346      * Don't delete non-empty files.
 347      * O_EXCL should prevent it, but let's be on the safe side.
 348      */
 349     if (st->st_size > 0)
 350         edit->delete_file = 0;
 351 
 352     if ((uintmax_t) st->st_size > threshold)
 353     {
 354         int act;
 355 
 356         errmsg = g_strdup_printf (_ ("File \"%s\" is too large.\nOpen it anyway?"),
 357                                   vfs_path_as_str (filename_vpath));
 358         act = edit_query_dialog2 (_ ("Warning"), errmsg, _ ("&Yes"), _ ("&No"));
 359         MC_PTR_FREE (errmsg);
 360 
 361         if (act != 0)
 362             ret = FALSE;
 363     }
 364 
 365 cleanup:
 366     (void) mc_close (file);
 367 
 368     if (errmsg != NULL)
 369     {
 370         message (D_ERROR, MSG_ERROR, "%s", errmsg);
 371         g_free (errmsg);
 372         ret = FALSE;
 373     }
 374 
 375     return ret;
 376 }
 377 
 378 /* --------------------------------------------------------------------------------------------- */
 379 
 380 /**
 381  * Open the file and load it into the buffers, either directly or using
 382  * a filter.  Return TRUE on success, FALSE on error.
 383  *
 384  * Fast loading (edit_load_file_fast) is used when the file size is
 385  * known.  In this case the data is read into the buffers by blocks.
 386  * If the file size is not known, the data is loaded byte by byte in
 387  * edit_insert_file.
 388  *
 389  * @param edit editor object
 390  * @return TRUE if file was successfully opened and loaded to buffers, FALSE otherwise
 391  */
 392 static gboolean
 393 edit_load_file (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 394 {
 395     gboolean fast_load = TRUE;
 396 
 397     // Cannot do fast load if a filter is used
 398     if (edit_find_filter (edit->filename_vpath) >= 0)
 399         fast_load = FALSE;
 400 
 401     /*
 402      * FIXME: line end translation should disable fast loading as well
 403      * Consider doing fseek() to the end and ftell() for the real size.
 404      */
 405     if (edit->filename_vpath != NULL)
 406     {
 407         /*
 408          * VFS may report file size incorrectly, and slow load is not a big
 409          * deal considering overhead in VFS.
 410          */
 411         if (!vfs_file_is_local (edit->filename_vpath))
 412             fast_load = FALSE;
 413 
 414         // If we are dealing with a real file, check that it exists
 415         if (!check_file_access (edit, edit->filename_vpath, &edit->stat1))
 416         {
 417             edit_clean (edit);
 418             return FALSE;
 419         }
 420     }
 421     else
 422     {
 423         // nothing to load
 424         fast_load = FALSE;
 425     }
 426 
 427     if (fast_load)
 428     {
 429         edit_buffer_init (&edit->buffer, edit->stat1.st_size);
 430 
 431         if (!edit_load_file_fast (&edit->buffer, edit->filename_vpath))
 432         {
 433             edit_clean (edit);
 434             return FALSE;
 435         }
 436     }
 437     else
 438     {
 439         edit_buffer_init (&edit->buffer, 0);
 440 
 441         if (edit->filename_vpath != NULL
 442             && *(vfs_path_get_by_index (edit->filename_vpath, 0)->path) != '\0')
 443         {
 444             edit->undo_stack_disable = 1;
 445             if (edit_insert_file (edit, edit->filename_vpath) < 0)
 446             {
 447                 edit_clean (edit);
 448                 return FALSE;
 449             }
 450             edit->undo_stack_disable = 0;
 451         }
 452     }
 453     edit->lb = LB_ASIS;
 454     return TRUE;
 455 }
 456 
 457 /* --------------------------------------------------------------------------------------------- */
 458 /**
 459  * Restore saved cursor position and/or bookmarks in the file
 460  *
 461  * @param edit editor object
 462  * @param load_position If TRUE, load bookmarks and cursor position and apply them.
 463  *                      If FALSE, load bookmarks only.
 464  */
 465 
 466 static void
 467 edit_load_position (WEdit *edit, gboolean load_position)
     /* [previous][next][first][last][top][bottom][index][help]  */
 468 {
 469     long line, column;
 470     off_t offset;
 471     off_t b;
 472 
 473     if (edit->filename_vpath == NULL
 474         || *(vfs_path_get_by_index (edit->filename_vpath, 0)->path) == '\0')
 475         return;
 476 
 477     load_file_position (edit->filename_vpath, &line, &column, &offset, &edit->serialized_bookmarks);
 478     // apply bookmarks in any case
 479     book_mark_restore (edit, BOOK_MARK_COLOR);
 480 
 481     if (!load_position)
 482         return;
 483 
 484     if (line > 0)
 485     {
 486         edit_move_to_line (edit, line - 1);
 487         edit->prev_col = column;
 488     }
 489     else if (offset > 0)
 490     {
 491         edit_cursor_move (edit, offset);
 492         line = edit->buffer.curs_line;
 493         edit->search_start = edit->buffer.curs1;
 494     }
 495 
 496     b = edit_buffer_get_current_bol (&edit->buffer);
 497     edit_move_to_prev_col (edit, b);
 498     edit_move_display (edit, line - (WIDGET (edit)->rect.lines / 2));
 499 }
 500 
 501 /* --------------------------------------------------------------------------------------------- */
 502 /** Save cursor position in the file */
 503 
 504 static void
 505 edit_save_position (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 506 {
 507     if (edit->filename_vpath == NULL
 508         || *(vfs_path_get_by_index (edit->filename_vpath, 0)->path) == '\0')
 509         return;
 510 
 511     book_mark_serialize (edit, BOOK_MARK_COLOR);
 512     save_file_position (edit->filename_vpath, edit->buffer.curs_line + 1, edit->curs_col,
 513                         edit->buffer.curs1, edit->serialized_bookmarks);
 514     edit->serialized_bookmarks = NULL;
 515 }
 516 
 517 /* --------------------------------------------------------------------------------------------- */
 518 /** Clean the WEdit stricture except the widget part */
 519 
 520 static void
 521 edit_purge_widget (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 522 {
 523     size_t len = sizeof (WEdit) - sizeof (Widget);
 524     char *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     gboolean mark;
1777     off_t curs;
1778     off_t start_mark, end_mark;
1779     struct stat status;
1780     vfs_path_t *block_file_vpath;
1781 
1782     block_file = mc_config_get_full_path (EDIT_HOME_BLOCK_FILE);
1783     block_file_vpath = vfs_path_from_str (block_file);
1784     curs = edit->buffer.curs1;
1785     mark = eval_marks (edit, &start_mark, &end_mark);
1786     if (mark)
1787         edit_save_block (edit, block_file, start_mark, end_mark);
1788 
1789     // run shell scripts from menu
1790     if (user_menu_cmd (CONST_WIDGET (edit), menu_file, selected_entry)
1791         && (mc_stat (block_file_vpath, &status) == 0) && (status.st_size != 0))
1792     {
1793         gboolean rc = TRUE;
1794         FILE *fd;
1795 
1796         // i.e. we have marked block
1797         if (mark)
1798             rc = edit_block_delete_cmd (edit);
1799 
1800         if (rc)
1801         {
1802             off_t ins_len;
1803 
1804             ins_len = edit_insert_file (edit, block_file_vpath);
1805             if (mark && ins_len > 0)
1806                 edit_set_markers (edit, start_mark, start_mark + ins_len, 0, 0);
1807         }
1808         // truncate block file
1809         fd = fopen (block_file, "w");
1810         if (fd != NULL)
1811             fclose (fd);
1812     }
1813     g_free (block_file);
1814     vfs_path_free (block_file_vpath, TRUE);
1815 
1816     edit_cursor_move (edit, curs - edit->buffer.curs1);
1817     edit->force |= REDRAW_PAGE;
1818     widget_draw (WIDGET (edit));
1819 }
1820 
1821 /* --------------------------------------------------------------------------------------------- */
1822 
1823 char *
1824 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]  */
1825 {
1826     int i;
1827     const char *write_name;
1828     char *write_name_quoted;
1829     char *p = NULL;
1830 
1831     i = edit_find_filter (filename_vpath);
1832     if (i < 0)
1833         return NULL;
1834 
1835     write_name = vfs_path_get_last_path_str (write_name_vpath);
1836     write_name_quoted = name_quote (write_name, FALSE);
1837     if (write_name_quoted != NULL)
1838     {
1839         p = g_strdup_printf (all_filters[i].write, write_name_quoted);
1840         g_free (write_name_quoted);
1841     }
1842     return p;
1843 }
1844 
1845 /* --------------------------------------------------------------------------------------------- */
1846 /**
1847  * @param edit   editor object
1848  * @param f      value of stream file
1849  * @return       the length of the file
1850  */
1851 
1852 off_t
1853 edit_write_stream (WEdit *edit, FILE *f)
     /* [previous][next][first][last][top][bottom][index][help]  */
1854 {
1855     long i;
1856 
1857     if (edit->lb == LB_ASIS)
1858     {
1859         for (i = 0; i < edit->buffer.size; i++)
1860             if (fputc (edit_buffer_get_byte (&edit->buffer, i), f) < 0)
1861                 break;
1862         return i;
1863     }
1864 
1865     // change line breaks
1866     for (i = 0; i < edit->buffer.size; i++)
1867     {
1868         unsigned char c;
1869 
1870         c = edit_buffer_get_byte (&edit->buffer, i);
1871         if (!(c == '\n' || c == '\r'))
1872         {
1873             // not line break
1874             if (fputc (c, f) < 0)
1875                 return i;
1876         }
1877         else
1878         {  // (c == '\n' || c == '\r')
1879             unsigned char c1;
1880 
1881             c1 = edit_buffer_get_byte (&edit->buffer, i + 1);  // next char
1882 
1883             switch (edit->lb)
1884             {
1885             case LB_UNIX:  // replace "\r\n" or '\r' to '\n'
1886                 // put one line break unconditionally
1887                 if (fputc ('\n', f) < 0)
1888                     return i;
1889 
1890                 i++;  // 2 chars are processed
1891 
1892                 if (c == '\r' && c1 == '\n')
1893                     // Windows line break; go to the next char
1894                     break;
1895 
1896                 if (c == '\r' && c1 == '\r')
1897                 {
1898                     // two Macintosh line breaks; put second line break
1899                     if (fputc ('\n', f) < 0)
1900                         return i;
1901                     break;
1902                 }
1903 
1904                 if (fputc (c1, f) < 0)
1905                     return i;
1906                 break;
1907 
1908             case LB_WIN:  // replace '\n' or '\r' to "\r\n"
1909                 // put one line break unconditionally
1910                 if (fputc ('\r', f) < 0 || fputc ('\n', f) < 0)
1911                     return i;
1912 
1913                 if (c == '\r' && c1 == '\n')
1914                     // Windows line break; go to the next char
1915                     i++;
1916                 break;
1917 
1918             case LB_MAC:  // replace "\r\n" or '\n' to '\r'
1919                 // put one line break unconditionally
1920                 if (fputc ('\r', f) < 0)
1921                     return i;
1922 
1923                 i++;  // 2 chars are processed
1924 
1925                 if (c == '\r' && c1 == '\n')
1926                     // Windows line break; go to the next char
1927                     break;
1928 
1929                 if (c == '\n' && c1 == '\n')
1930                 {
1931                     // two Windows line breaks; put second line break
1932                     if (fputc ('\r', f) < 0)
1933                         return i;
1934                     break;
1935                 }
1936 
1937                 if (fputc (c1, f) < 0)
1938                     return i;
1939                 break;
1940             case LB_ASIS:  // default without changes
1941             default:
1942                 break;
1943             }
1944         }
1945     }
1946 
1947     return edit->buffer.size;
1948 }
1949 
1950 /* --------------------------------------------------------------------------------------------- */
1951 
1952 gboolean
1953 is_break_char (char c)
     /* [previous][next][first][last][top][bottom][index][help]  */
1954 {
1955     return (isspace (c) || strchr ("{}[]()<>=|/\\!?~-+`'\",.;:#$%^&*", c) != NULL);
1956 }
1957 
1958 /* --------------------------------------------------------------------------------------------- */
1959 /** inserts a file at the cursor, returns count of inserted bytes on success */
1960 
1961 off_t
1962 edit_insert_file (WEdit *edit, const vfs_path_t *filename_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1963 {
1964     char *p;
1965     off_t current;
1966     off_t ins_len = 0;
1967 
1968     p = edit_get_filter (filename_vpath);
1969     current = edit->buffer.curs1;
1970 
1971     if (p != NULL)
1972     {
1973         FILE *f;
1974 
1975         f = (FILE *) popen (p, "r");
1976         if (f != NULL)
1977         {
1978             edit_insert_stream (edit, f);
1979 
1980             // Place cursor at the end of text selection
1981             if (!edit_options.cursor_after_inserted_block)
1982             {
1983                 ins_len = edit->buffer.curs1 - current;
1984                 edit_cursor_move (edit, -ins_len);
1985             }
1986             if (pclose (f) > 0)
1987             {
1988                 message (D_ERROR, MSG_ERROR, _ ("Error reading from pipe: %s"), p);
1989                 ins_len = -1;
1990             }
1991         }
1992         else
1993         {
1994             message (D_ERROR, MSG_ERROR, _ ("Cannot open pipe for reading: %s"), p);
1995             ins_len = -1;
1996         }
1997         g_free (p);
1998     }
1999     else
2000     {
2001         int file;
2002         off_t blocklen;
2003         gboolean vertical_insertion = FALSE;
2004         char *buf;
2005 
2006         file = mc_open (filename_vpath, O_RDONLY | O_BINARY);
2007         if (file == -1)
2008             return -1;
2009 
2010         buf = g_malloc0 (TEMP_BUF_LEN);
2011         blocklen = mc_read (file, buf, sizeof (VERTICAL_MAGIC));
2012         if (blocklen > 0)
2013         {
2014             // if contain signature VERTICAL_MAGIC then it vertical block
2015             if (memcmp (buf, VERTICAL_MAGIC, sizeof (VERTICAL_MAGIC)) == 0)
2016                 vertical_insertion = TRUE;
2017             else
2018                 mc_lseek (file, 0, SEEK_SET);
2019         }
2020 
2021         if (vertical_insertion)
2022         {
2023             off_t mark1, mark2;
2024             long c1, c2;
2025 
2026             blocklen = edit_insert_column_from_file (edit, file, &mark1, &mark2, &c1, &c2);
2027             edit_set_markers (edit, edit->buffer.curs1, mark2, c1, c2);
2028 
2029             // highlight inserted text then not persistent blocks
2030             if (!edit_options.persistent_selections && edit->modified != 0)
2031             {
2032                 if (edit->column_highlight == 0)
2033                     edit_push_undo_action (edit, COLUMN_OFF);
2034                 edit->column_highlight = 1;
2035             }
2036         }
2037         else
2038         {
2039             off_t i;
2040 
2041             while ((blocklen = mc_read (file, (char *) buf, TEMP_BUF_LEN)) > 0)
2042             {
2043                 for (i = 0; i < blocklen; i++)
2044                     edit_insert (edit, buf[i]);
2045             }
2046             // highlight inserted text then not persistent blocks
2047             if (!edit_options.persistent_selections && edit->modified != 0)
2048             {
2049                 edit_set_markers (edit, edit->buffer.curs1, current, 0, 0);
2050                 if (edit->column_highlight != 0)
2051                     edit_push_undo_action (edit, COLUMN_ON);
2052                 edit->column_highlight = 0;
2053             }
2054 
2055             // Place cursor at the end of text selection
2056             if (!edit_options.cursor_after_inserted_block)
2057             {
2058                 ins_len = edit->buffer.curs1 - current;
2059                 edit_cursor_move (edit, -ins_len);
2060             }
2061         }
2062 
2063         edit->force |= REDRAW_PAGE;
2064         g_free (buf);
2065         mc_close (file);
2066         if (blocklen != 0)
2067             ins_len = 0;
2068     }
2069 
2070     return ins_len;
2071 }
2072 
2073 /* --------------------------------------------------------------------------------------------- */
2074 /**
2075  * Fill in the edit structure.  Return NULL on failure.  Pass edit as
2076  * NULL to allocate a new structure.
2077  *
2078  * If arg is NULL or arg->line_number is 0, try to restore saved position.  Otherwise put the
2079  * cursor on that line and show it in the middle of the screen.
2080  */
2081 
2082 WEdit *
2083 edit_init (WEdit *edit, const WRect *r, const edit_arg_t *arg)
     /* [previous][next][first][last][top][bottom][index][help]  */
2084 {
2085     gboolean to_free = FALSE;
2086     long line;
2087 
2088     auto_syntax = TRUE;  // Resetting to auto on every invocation
2089     edit_options.line_state_width = edit_options.line_state ? LINE_STATE_WIDTH : 0;
2090 
2091     if (edit != NULL)
2092     {
2093         int fullscreen;
2094         WRect loc_prev;
2095 
2096         // save some widget parameters
2097         fullscreen = edit->fullscreen;
2098         loc_prev = edit->loc_prev;
2099 
2100         edit_purge_widget (edit);
2101 
2102         // restore saved parameters
2103         edit->fullscreen = fullscreen;
2104         edit->loc_prev = loc_prev;
2105     }
2106     else
2107     {
2108         Widget *w;
2109 
2110         edit = g_malloc0 (sizeof (WEdit));
2111         to_free = TRUE;
2112 
2113         w = WIDGET (edit);
2114         widget_init (w, r, NULL, NULL);
2115         w->options |= WOP_SELECTABLE | WOP_TOP_SELECT | WOP_WANT_CURSOR;
2116         w->keymap = editor_map;
2117         w->ext_keymap = editor_x_map;
2118         edit->fullscreen = 1;
2119         edit_save_size (edit);
2120     }
2121 
2122     edit->drag_state = MCEDIT_DRAG_NONE;
2123 
2124     edit->stat1.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
2125     edit->stat1.st_uid = getuid ();
2126     edit->stat1.st_gid = getgid ();
2127     edit->stat1.st_mtime = 0;
2128 
2129     if (arg != NULL)
2130         edit->attrs_ok = (mc_fgetflags (arg->file_vpath, &edit->attrs) == 0);
2131     else
2132         edit->attrs_ok = FALSE;
2133 
2134     edit->over_col = 0;
2135     edit->bracket = -1;
2136     edit->last_bracket = -1;
2137     edit->force |= REDRAW_PAGE;
2138 
2139     // set file name before load file
2140     if (arg != NULL)
2141     {
2142         edit_set_filename (edit, arg->file_vpath);
2143         line = arg->line_number;
2144     }
2145     else
2146     {
2147         edit_set_filename (edit, NULL);
2148         line = 0;
2149     }
2150 
2151     edit->undo_stack_size = START_STACK_SIZE;
2152     edit->undo_stack_size_mask = START_STACK_SIZE - 1;
2153     edit->undo_stack = g_malloc0 ((edit->undo_stack_size + 10) * sizeof (long));
2154 
2155     edit->redo_stack_size = START_STACK_SIZE;
2156     edit->redo_stack_size_mask = START_STACK_SIZE - 1;
2157     edit->redo_stack = g_malloc0 ((edit->redo_stack_size + 10) * sizeof (long));
2158 
2159     edit->utf8 = FALSE;
2160     edit->converter = str_cnv_from_term;
2161     edit_set_codeset (edit);
2162 
2163     if (!edit_load_file (edit))
2164     {
2165         // edit_load_file already gives an error message
2166         if (to_free)
2167             g_free (edit);
2168         return NULL;
2169     }
2170 
2171     edit->loading_done = 1;
2172     edit->modified = 0;
2173     edit->locked = 0;
2174     edit_load_syntax (edit, NULL, NULL);
2175     edit_get_syntax_color (edit, -1);
2176 
2177     // load saved cursor position and/or boolmarks
2178     if ((line == 0) && edit_options.save_position)
2179         edit_load_position (edit, TRUE);
2180     else
2181     {
2182         edit_load_position (edit, FALSE);
2183         if (line <= 0)
2184             line = 1;
2185         edit_move_display (edit, line - 1);
2186         edit_move_to_line (edit, line - 1);
2187     }
2188 
2189     edit_load_macro_cmd (edit);
2190 
2191     return edit;
2192 }
2193 
2194 /* --------------------------------------------------------------------------------------------- */
2195 
2196 /** Clear the edit struct, freeing everything in it.  Return TRUE on success */
2197 gboolean
2198 edit_clean (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2199 {
2200     if (edit == NULL)
2201         return FALSE;
2202 
2203     // a stale lock, remove it
2204     if (edit->locked)
2205         edit->locked = unlock_file (edit->filename_vpath);
2206 
2207     // save cursor position
2208     if (edit_options.save_position)
2209         edit_save_position (edit);
2210     else if (edit->serialized_bookmarks != NULL)
2211         g_array_free (edit->serialized_bookmarks, TRUE);
2212 
2213     // File specified on the mcedit command line and never saved
2214     if (edit->delete_file != 0)
2215         unlink (vfs_path_get_last_path_str (edit->filename_vpath));
2216 
2217     edit_free_syntax_rules (edit);
2218     book_mark_flush (edit, -1);
2219 
2220     edit_buffer_clean (&edit->buffer);
2221 
2222     g_free (edit->undo_stack);
2223     g_free (edit->redo_stack);
2224     vfs_path_free (edit->filename_vpath, TRUE);
2225     vfs_path_free (edit->dir_vpath, TRUE);
2226     edit_search_deinit (edit);
2227 
2228     if (edit->converter != str_cnv_from_term)
2229         str_close_conv (edit->converter);
2230 
2231     edit_purge_widget (edit);
2232 
2233     return TRUE;
2234 }
2235 
2236 /* --------------------------------------------------------------------------------------------- */
2237 
2238 /**
2239  * Load a new file into the editor and set line.  If it fails, preserve the old file.
2240  * To do it, allocate a new widget, initialize it and, if the new file
2241  * was loaded, copy the data to the old widget.
2242  *
2243  * @return TRUE on success, FALSE on failure.
2244  */
2245 gboolean
2246 edit_reload_line (WEdit *edit, const edit_arg_t *arg)
     /* [previous][next][first][last][top][bottom][index][help]  */
2247 {
2248     Widget *w = WIDGET (edit);
2249     WEdit *e;
2250 
2251     e = g_malloc0 (sizeof (WEdit));
2252     *WIDGET (e) = *w;
2253     // save some widget parameters
2254     e->fullscreen = edit->fullscreen;
2255     e->loc_prev = edit->loc_prev;
2256 
2257     if (edit_init (e, &w->rect, arg) == NULL)
2258     {
2259         g_free (e);
2260         return FALSE;
2261     }
2262 
2263     edit_clean (edit);
2264     memcpy (edit, e, sizeof (*edit));
2265     g_free (e);
2266 
2267     return TRUE;
2268 }
2269 
2270 /* --------------------------------------------------------------------------------------------- */
2271 
2272 void
2273 edit_set_codeset (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2274 {
2275     const char *cp_id;
2276 
2277     cp_id = get_codepage_id (mc_global.source_codepage >= 0 ? mc_global.source_codepage
2278                                                             : mc_global.display_codepage);
2279 
2280     if (cp_id != NULL)
2281     {
2282         GIConv conv;
2283         conv = str_crt_conv_from (cp_id);
2284         if (conv != INVALID_CONV)
2285         {
2286             if (edit->converter != str_cnv_from_term)
2287                 str_close_conv (edit->converter);
2288             edit->converter = conv;
2289         }
2290     }
2291 
2292     if (cp_id != NULL)
2293         edit->utf8 = str_isutf8 (cp_id);
2294 }
2295 
2296 /* --------------------------------------------------------------------------------------------- */
2297 
2298 /**
2299  * Recording stack for undo:
2300  * The following is an implementation of a compressed stack. Identical
2301  * pushes are recorded by a negative prefix indicating the number of times the
2302  * same char was pushed. This saves space for repeated curs-left or curs-right
2303  * delete etc.
2304  *
2305  * eg:
2306  *
2307  * pushed:       stored:
2308  *
2309  * a
2310  * b             a
2311  * b            -3
2312  * b             b
2313  * c  -->       -4
2314  * c             c
2315  * c             d
2316  * c
2317  * d
2318  *
2319  * If the stack long int is 0-255 it represents a normal insert (from a backspace),
2320  * 256-512 is an insert ahead (from a delete), If it is between 600 and 700 it is one
2321  * of the cursor functions define'd in edit-impl.h. 1000 through 700'000'000 is to
2322  * set edit->mark1 position. 700'000'000 through 1400'000'000 is to set edit->mark2
2323  * position.
2324  *
2325  * The only way the cursor moves or the buffer is changed is through the routines:
2326  * insert, backspace, insert_ahead, delete, and cursor_move.
2327  * These record the reverse undo movements onto the stack each time they are
2328  * called.
2329  *
2330  * Each key press results in a set of actions (insert; delete ...). So each time
2331  * a key is pressed the current position of start_display is pushed as
2332  * KEY_PRESS + start_display. Then for undoing, we pop until we get to a number
2333  * over KEY_PRESS. We then assign this number less KEY_PRESS to start_display. So undo
2334  * tracks scrolling and key actions exactly. (KEY_PRESS is about (2^31) * (2/3) = 1400'000'000)
2335  *
2336  *
2337  *
2338  * @param edit editor object
2339  * @param c code of the action
2340  */
2341 
2342 void
2343 edit_push_undo_action (WEdit *edit, long c)
     /* [previous][next][first][last][top][bottom][index][help]  */
2344 {
2345     unsigned long sp = edit->undo_stack_pointer;
2346     unsigned long spm1;
2347 
2348     // first enlarge the stack if necessary
2349     if (sp > edit->undo_stack_size - 10)
2350     {  // say
2351         if (max_undo < 256)
2352             max_undo = 256;
2353         if (edit->undo_stack_size < (unsigned long) max_undo)
2354         {
2355             long *t;
2356 
2357             t = g_realloc (edit->undo_stack, (edit->undo_stack_size * 2 + 10) * sizeof (long));
2358             if (t != NULL)
2359             {
2360                 edit->undo_stack = t;
2361                 edit->undo_stack_size <<= 1;
2362                 edit->undo_stack_size_mask = edit->undo_stack_size - 1;
2363             }
2364         }
2365     }
2366     spm1 = (edit->undo_stack_pointer - 1) & edit->undo_stack_size_mask;
2367     if (edit->undo_stack_disable)
2368     {
2369         edit_push_redo_action (edit, KEY_PRESS);
2370         edit_push_redo_action (edit, c);
2371         return;
2372     }
2373 
2374     if (edit->redo_stack_reset)
2375         edit->redo_stack_bottom = edit->redo_stack_pointer = 0;
2376 
2377     if (edit->undo_stack_bottom != sp && spm1 != edit->undo_stack_bottom
2378         && ((sp - 2) & edit->undo_stack_size_mask) != edit->undo_stack_bottom)
2379     {
2380         long d;
2381 
2382         if (edit->undo_stack[spm1] < 0)
2383         {
2384             d = edit->undo_stack[(sp - 2) & edit->undo_stack_size_mask];
2385             if (d == c && edit->undo_stack[spm1] > -1000000000)
2386             {
2387                 if (c < KEY_PRESS)  // --> no need to push multiple do-nothings
2388                     edit->undo_stack[spm1]--;
2389                 return;
2390             }
2391         }
2392         else
2393         {
2394             d = edit->undo_stack[spm1];
2395             if (d == c)
2396             {
2397                 if (c >= KEY_PRESS)
2398                     return;  // --> no need to push multiple do-nothings
2399                 edit->undo_stack[sp] = -2;
2400                 goto check_bottom;
2401             }
2402         }
2403     }
2404     edit->undo_stack[sp] = c;
2405 
2406 check_bottom:
2407     edit->undo_stack_pointer = (edit->undo_stack_pointer + 1) & edit->undo_stack_size_mask;
2408 
2409     /* if the sp wraps round and catches the undo_stack_bottom then erase
2410      * the first set of actions on the stack to make space - by moving
2411      * undo_stack_bottom forward one "key press" */
2412     c = (edit->undo_stack_pointer + 2) & edit->undo_stack_size_mask;
2413     if ((unsigned long) c == edit->undo_stack_bottom
2414         || (((unsigned long) c + 1) & edit->undo_stack_size_mask) == edit->undo_stack_bottom)
2415         do
2416         {
2417             edit->undo_stack_bottom = (edit->undo_stack_bottom + 1) & edit->undo_stack_size_mask;
2418         }
2419         while (edit->undo_stack[edit->undo_stack_bottom] < KEY_PRESS
2420                && edit->undo_stack_bottom != edit->undo_stack_pointer);
2421 
2422     // If a single key produced enough pushes to wrap all the way round then we would notice that
2423     // the [undo_stack_bottom] does not contain KEY_PRESS. The stack is then initialised:
2424     if (edit->undo_stack_pointer != edit->undo_stack_bottom
2425         && edit->undo_stack[edit->undo_stack_bottom] < KEY_PRESS)
2426     {
2427         edit->undo_stack_bottom = edit->undo_stack_pointer = 0;
2428     }
2429 }
2430 
2431 /* --------------------------------------------------------------------------------------------- */
2432 
2433 void
2434 edit_push_redo_action (WEdit *edit, long c)
     /* [previous][next][first][last][top][bottom][index][help]  */
2435 {
2436     unsigned long sp = edit->redo_stack_pointer;
2437     unsigned long spm1;
2438     // first enlarge the stack if necessary
2439     if (sp > edit->redo_stack_size - 10)
2440     {  // say
2441         if (max_undo < 256)
2442             max_undo = 256;
2443         if (edit->redo_stack_size < (unsigned long) max_undo)
2444         {
2445             long *t;
2446 
2447             t = g_realloc (edit->redo_stack, (edit->redo_stack_size * 2 + 10) * sizeof (long));
2448             if (t != NULL)
2449             {
2450                 edit->redo_stack = t;
2451                 edit->redo_stack_size <<= 1;
2452                 edit->redo_stack_size_mask = edit->redo_stack_size - 1;
2453             }
2454         }
2455     }
2456     spm1 = (edit->redo_stack_pointer - 1) & edit->redo_stack_size_mask;
2457 
2458     if (edit->redo_stack_bottom != sp && spm1 != edit->redo_stack_bottom
2459         && ((sp - 2) & edit->redo_stack_size_mask) != edit->redo_stack_bottom)
2460     {
2461         long d;
2462 
2463         if (edit->redo_stack[spm1] < 0)
2464         {
2465             d = edit->redo_stack[(sp - 2) & edit->redo_stack_size_mask];
2466             if (d == c && edit->redo_stack[spm1] > -1000000000)
2467             {
2468                 if (c < KEY_PRESS)  // --> no need to push multiple do-nothings
2469                     edit->redo_stack[spm1]--;
2470                 return;
2471             }
2472         }
2473         else
2474         {
2475             d = edit->redo_stack[spm1];
2476             if (d == c)
2477             {
2478                 if (c >= KEY_PRESS)
2479                     return;  // --> no need to push multiple do-nothings
2480                 edit->redo_stack[sp] = -2;
2481                 goto redo_check_bottom;
2482             }
2483         }
2484     }
2485     edit->redo_stack[sp] = c;
2486 
2487 redo_check_bottom:
2488     edit->redo_stack_pointer = (edit->redo_stack_pointer + 1) & edit->redo_stack_size_mask;
2489 
2490     /* if the sp wraps round and catches the redo_stack_bottom then erase
2491      * the first set of actions on the stack to make space - by moving
2492      * redo_stack_bottom forward one "key press" */
2493     c = (edit->redo_stack_pointer + 2) & edit->redo_stack_size_mask;
2494     if ((unsigned long) c == edit->redo_stack_bottom
2495         || (((unsigned long) c + 1) & edit->redo_stack_size_mask) == edit->redo_stack_bottom)
2496         do
2497         {
2498             edit->redo_stack_bottom = (edit->redo_stack_bottom + 1) & edit->redo_stack_size_mask;
2499         }
2500         while (edit->redo_stack[edit->redo_stack_bottom] < KEY_PRESS
2501                && edit->redo_stack_bottom != edit->redo_stack_pointer);
2502 
2503     /*
2504      * If a single key produced enough pushes to wrap all the way round then
2505      * we would notice that the [redo_stack_bottom] does not contain KEY_PRESS.
2506      * The stack is then initialised:
2507      */
2508 
2509     if (edit->redo_stack_pointer != edit->redo_stack_bottom
2510         && edit->redo_stack[edit->redo_stack_bottom] < KEY_PRESS)
2511         edit->redo_stack_bottom = edit->redo_stack_pointer = 0;
2512 }
2513 
2514 /* --------------------------------------------------------------------------------------------- */
2515 /**
2516    Basic low level single character buffer alterations and movements at the cursor.
2517  */
2518 
2519 void
2520 edit_insert (WEdit *edit, int c)
     /* [previous][next][first][last][top][bottom][index][help]  */
2521 {
2522     // first we must update the position of the display window
2523     if (edit->buffer.curs1 < edit->start_display)
2524     {
2525         edit->start_display++;
2526         if (c == '\n')
2527             edit->start_line++;
2528     }
2529 
2530     // Mark file as modified, unless the file hasn't been fully loaded
2531     if (edit->loading_done != 0)
2532         edit_modification (edit);
2533 
2534     // now we must update some info on the file and check if a redraw is required
2535     if (c == '\n')
2536     {
2537         book_mark_inc (edit, edit->buffer.curs_line);
2538         edit->buffer.curs_line++;
2539         edit->buffer.lines++;
2540         edit->force |= REDRAW_LINE_ABOVE | REDRAW_AFTER_CURSOR;
2541     }
2542 
2543     // save the reverse command onto the undo stack
2544     // ordinary char and not space
2545     if (c > 32)
2546         edit_push_undo_action (edit, BACKSPACE);
2547     else
2548         edit_push_undo_action (edit, BACKSPACE_BR);
2549     // update markers
2550     edit->mark1 += (edit->mark1 > edit->buffer.curs1) ? 1 : 0;
2551     edit->mark2 += (edit->mark2 > edit->buffer.curs1) ? 1 : 0;
2552     edit->last_get_rule += (edit->last_get_rule > edit->buffer.curs1) ? 1 : 0;
2553 
2554     edit_buffer_insert (&edit->buffer, c);
2555 }
2556 
2557 /* --------------------------------------------------------------------------------------------- */
2558 /** same as edit_insert and move left */
2559 
2560 void
2561 edit_insert_ahead (WEdit *edit, int c)
     /* [previous][next][first][last][top][bottom][index][help]  */
2562 {
2563     if (edit->buffer.curs1 < edit->start_display)
2564     {
2565         edit->start_display++;
2566         if (c == '\n')
2567             edit->start_line++;
2568     }
2569     edit_modification (edit);
2570     if (c == '\n')
2571     {
2572         book_mark_inc (edit, edit->buffer.curs_line);
2573         edit->buffer.lines++;
2574         edit->force |= REDRAW_AFTER_CURSOR;
2575     }
2576     // ordinary char and not space
2577     if (c > 32)
2578         edit_push_undo_action (edit, DELCHAR);
2579     else
2580         edit_push_undo_action (edit, DELCHAR_BR);
2581 
2582     edit->mark1 += (edit->mark1 >= edit->buffer.curs1) ? 1 : 0;
2583     edit->mark2 += (edit->mark2 >= edit->buffer.curs1) ? 1 : 0;
2584     edit->last_get_rule += (edit->last_get_rule >= edit->buffer.curs1) ? 1 : 0;
2585 
2586     edit_buffer_insert_ahead (&edit->buffer, c);
2587 }
2588 
2589 /* --------------------------------------------------------------------------------------------- */
2590 
2591 void
2592 edit_insert_over (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2593 {
2594     long i;
2595 
2596     for (i = 0; i < edit->over_col; i++)
2597         edit_insert (edit, ' ');
2598 
2599     edit->over_col = 0;
2600 }
2601 
2602 /* --------------------------------------------------------------------------------------------- */
2603 
2604 int
2605 edit_delete (WEdit *edit, gboolean byte_delete)
     /* [previous][next][first][last][top][bottom][index][help]  */
2606 {
2607     int p = 0;
2608     int char_length = 1;
2609     int i;
2610 
2611     if (edit->buffer.curs2 == 0)
2612         return 0;
2613 
2614     // if byte_delete == TRUE then delete only one byte not multibyte char
2615     if (edit->utf8 && !byte_delete)
2616     {
2617         edit_buffer_get_utf (&edit->buffer, edit->buffer.curs1, &char_length);
2618         if (char_length < 1)
2619             char_length = 1;
2620     }
2621 
2622     if (edit->mark2 != edit->mark1)
2623         edit_push_markers (edit);
2624 
2625     for (i = 1; i <= char_length; i++)
2626     {
2627         if (edit->mark1 > edit->buffer.curs1)
2628         {
2629             edit->mark1--;
2630             edit->end_mark_curs--;
2631         }
2632         if (edit->mark2 > edit->buffer.curs1)
2633             edit->mark2--;
2634         if (edit->last_get_rule > edit->buffer.curs1)
2635             edit->last_get_rule--;
2636 
2637         p = edit_buffer_delete (&edit->buffer);
2638 
2639         edit_push_undo_action (edit, p + 256);
2640     }
2641 
2642     edit_modification (edit);
2643     if (p == '\n')
2644     {
2645         book_mark_dec (edit, edit->buffer.curs_line);
2646         edit->buffer.lines--;
2647         edit->force |= REDRAW_AFTER_CURSOR;
2648     }
2649     if (edit->buffer.curs1 < edit->start_display)
2650     {
2651         edit->start_display--;
2652         if (p == '\n')
2653             edit->start_line--;
2654     }
2655 
2656     return p;
2657 }
2658 
2659 /* --------------------------------------------------------------------------------------------- */
2660 
2661 int
2662 edit_backspace (WEdit *edit, gboolean byte_delete)
     /* [previous][next][first][last][top][bottom][index][help]  */
2663 {
2664     int p = 0;
2665     int char_length = 1;
2666     int i;
2667 
2668     if (edit->buffer.curs1 == 0)
2669         return 0;
2670 
2671     if (edit->mark2 != edit->mark1)
2672         edit_push_markers (edit);
2673 
2674     if (edit->utf8 && !byte_delete)
2675     {
2676         edit_buffer_get_prev_utf (&edit->buffer, edit->buffer.curs1, &char_length);
2677         if (char_length < 1)
2678             char_length = 1;
2679     }
2680 
2681     for (i = 1; i <= char_length; i++)
2682     {
2683         if (edit->mark1 >= edit->buffer.curs1)
2684         {
2685             edit->mark1--;
2686             edit->end_mark_curs--;
2687         }
2688         if (edit->mark2 >= edit->buffer.curs1)
2689             edit->mark2--;
2690         if (edit->last_get_rule >= edit->buffer.curs1)
2691             edit->last_get_rule--;
2692 
2693         p = edit_buffer_backspace (&edit->buffer);
2694 
2695         edit_push_undo_action (edit, p);
2696     }
2697     edit_modification (edit);
2698     if (p == '\n')
2699     {
2700         book_mark_dec (edit, edit->buffer.curs_line);
2701         edit->buffer.curs_line--;
2702         edit->buffer.lines--;
2703         edit->force |= REDRAW_AFTER_CURSOR;
2704     }
2705 
2706     if (edit->buffer.curs1 < edit->start_display)
2707     {
2708         edit->start_display--;
2709         if (p == '\n')
2710             edit->start_line--;
2711     }
2712 
2713     return p;
2714 }
2715 
2716 /* --------------------------------------------------------------------------------------------- */
2717 /** moves the cursor right or left: increment positive or negative respectively */
2718 
2719 void
2720 edit_cursor_move (WEdit *edit, off_t increment)
     /* [previous][next][first][last][top][bottom][index][help]  */
2721 {
2722     if (increment < 0)
2723     {
2724         for (; increment < 0 && edit->buffer.curs1 != 0; increment++)
2725         {
2726             int c;
2727 
2728             edit_push_undo_action (edit, CURS_RIGHT);
2729 
2730             c = edit_buffer_get_previous_byte (&edit->buffer);
2731             edit_buffer_insert_ahead (&edit->buffer, c);
2732             c = edit_buffer_backspace (&edit->buffer);
2733             if (c == '\n')
2734             {
2735                 edit->buffer.curs_line--;
2736                 edit->force |= REDRAW_LINE_BELOW;
2737             }
2738         }
2739     }
2740     else
2741     {
2742         for (; increment > 0 && edit->buffer.curs2 != 0; increment--)
2743         {
2744             int c;
2745 
2746             edit_push_undo_action (edit, CURS_LEFT);
2747 
2748             c = edit_buffer_get_current_byte (&edit->buffer);
2749             edit_buffer_insert (&edit->buffer, c);
2750             c = edit_buffer_delete (&edit->buffer);
2751             if (c == '\n')
2752             {
2753                 edit->buffer.curs_line++;
2754                 edit->force |= REDRAW_LINE_ABOVE;
2755             }
2756         }
2757     }
2758 }
2759 
2760 /* --------------------------------------------------------------------------------------------- */
2761 /* If cols is zero this returns the count of columns from current to upto. */
2762 /* If upto is zero returns index of cols across from current. */
2763 
2764 off_t
2765 edit_move_forward3 (const WEdit *edit, off_t current, long cols, off_t upto)
     /* [previous][next][first][last][top][bottom][index][help]  */
2766 {
2767     off_t p, q;
2768     long col;
2769 
2770     if (upto != 0)
2771     {
2772         q = upto;
2773         cols = -10;
2774     }
2775     else
2776         q = edit->buffer.size + 2;
2777 
2778     for (col = 0, p = current; p < q; p++)
2779     {
2780         int c, orig_c;
2781 
2782         if (cols != -10)
2783         {
2784             if (col == cols)
2785                 return p;
2786             if (col > cols)
2787                 return p - 1;
2788         }
2789 
2790         orig_c = c = edit_buffer_get_byte (&edit->buffer, p);
2791 
2792         if (edit->utf8)
2793         {
2794             int utf_ch;
2795             int char_length = 1;
2796 
2797             utf_ch = edit_buffer_get_utf (&edit->buffer, p, &char_length);
2798             if (mc_global.utf8_display)
2799             {
2800                 if (char_length > 1)
2801                     col -= char_length - 1;
2802                 if (g_unichar_iswide (utf_ch))
2803                     col++;
2804             }
2805             else if (char_length > 1 && g_unichar_isprint (utf_ch))
2806                 col -= char_length - 1;
2807         }
2808 
2809         c = convert_to_display_c (c);
2810 
2811         if (c == '\n')
2812             return (upto != 0 ? (off_t) col : p);
2813         if (c == '\t')
2814             col += TAB_SIZE - col % TAB_SIZE;
2815         else if ((c < 32 || c == 127) && (orig_c == c || (!mc_global.utf8_display && !edit->utf8)))
2816             // '\r' is shown as ^M, so we must advance 2 characters
2817             // Caret notation for control characters
2818             col += 2;
2819         else
2820             col++;
2821     }
2822     return (off_t) col;
2823 }
2824 
2825 /* --------------------------------------------------------------------------------------------- */
2826 /** returns the current offset of the cursor from the beginning of a file */
2827 
2828 off_t
2829 edit_get_cursor_offset (const WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2830 {
2831     return edit->buffer.curs1;
2832 }
2833 
2834 /* --------------------------------------------------------------------------------------------- */
2835 /** returns the current column position of the cursor */
2836 
2837 long
2838 edit_get_col (const WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2839 {
2840     off_t b;
2841 
2842     b = edit_buffer_get_current_bol (&edit->buffer);
2843     return (long) edit_move_forward3 (edit, b, 0, edit->buffer.curs1);
2844 }
2845 
2846 /* --------------------------------------------------------------------------------------------- */
2847 /* Scrolling functions */
2848 /* --------------------------------------------------------------------------------------------- */
2849 
2850 void
2851 edit_update_curs_row (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2852 {
2853     edit->curs_row = edit->buffer.curs_line - edit->start_line;
2854 }
2855 
2856 /* --------------------------------------------------------------------------------------------- */
2857 
2858 void
2859 edit_update_curs_col (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2860 {
2861     off_t b;
2862 
2863     b = edit_buffer_get_current_bol (&edit->buffer);
2864     edit->curs_col = (long) edit_move_forward3 (edit, b, 0, edit->buffer.curs1);
2865 }
2866 
2867 /* --------------------------------------------------------------------------------------------- */
2868 
2869 long
2870 edit_get_curs_col (const WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2871 {
2872     return edit->curs_col;
2873 }
2874 
2875 /* --------------------------------------------------------------------------------------------- */
2876 /** moves the display start position up by i lines */
2877 
2878 void
2879 edit_scroll_upward (WEdit *edit, long i)
     /* [previous][next][first][last][top][bottom][index][help]  */
2880 {
2881     long lines_above = edit->start_line;
2882 
2883     if (i > lines_above)
2884         i = lines_above;
2885     if (i != 0)
2886     {
2887         edit->start_line -= i;
2888         edit->start_display =
2889             edit_buffer_get_backward_offset (&edit->buffer, edit->start_display, i);
2890         edit->force |= REDRAW_PAGE;
2891         edit->force &= (0xfff - REDRAW_CHAR_ONLY);
2892     }
2893     edit_update_curs_row (edit);
2894 }
2895 
2896 /* --------------------------------------------------------------------------------------------- */
2897 
2898 void
2899 edit_scroll_downward (WEdit *edit, long i)
     /* [previous][next][first][last][top][bottom][index][help]  */
2900 {
2901     long lines_below;
2902 
2903     lines_below = edit->buffer.lines - edit->start_line - (WIDGET (edit)->rect.lines - 1);
2904     if (lines_below > 0)
2905     {
2906         if (i > lines_below)
2907             i = lines_below;
2908         edit->start_line += i;
2909         edit->start_display =
2910             edit_buffer_get_forward_offset (&edit->buffer, edit->start_display, i, 0);
2911         edit->force |= REDRAW_PAGE;
2912         edit->force &= (0xfff - REDRAW_CHAR_ONLY);
2913     }
2914     edit_update_curs_row (edit);
2915 }
2916 
2917 /* --------------------------------------------------------------------------------------------- */
2918 
2919 void
2920 edit_scroll_right (WEdit *edit, long i)
     /* [previous][next][first][last][top][bottom][index][help]  */
2921 {
2922     edit->force |= REDRAW_PAGE;
2923     edit->force &= (0xfff - REDRAW_CHAR_ONLY);
2924     edit->start_col -= i;
2925 }
2926 
2927 /* --------------------------------------------------------------------------------------------- */
2928 
2929 void
2930 edit_scroll_left (WEdit *edit, long i)
     /* [previous][next][first][last][top][bottom][index][help]  */
2931 {
2932     if (edit->start_col)
2933     {
2934         edit->start_col += i;
2935         if (edit->start_col > 0)
2936             edit->start_col = 0;
2937         edit->force |= REDRAW_PAGE;
2938         edit->force &= (0xfff - REDRAW_CHAR_ONLY);
2939     }
2940 }
2941 
2942 /* --------------------------------------------------------------------------------------------- */
2943 /* high level cursor movement commands */
2944 /* --------------------------------------------------------------------------------------------- */
2945 
2946 void
2947 edit_move_to_prev_col (WEdit *edit, off_t p)
     /* [previous][next][first][last][top][bottom][index][help]  */
2948 {
2949     long prev = edit->prev_col;
2950     long over = edit->over_col;
2951     off_t b;
2952 
2953     edit_cursor_move (edit,
2954                       edit_move_forward3 (edit, p, prev + edit->over_col, 0) - edit->buffer.curs1);
2955 
2956     if (edit_options.cursor_beyond_eol)
2957     {
2958         off_t e;
2959         long line_len;
2960 
2961         b = edit_buffer_get_current_bol (&edit->buffer);
2962         e = edit_buffer_get_current_eol (&edit->buffer);
2963         line_len = (long) edit_move_forward3 (edit, b, 0, e);
2964         if (line_len < prev + edit->over_col)
2965         {
2966             edit->over_col = prev + over - line_len;
2967             edit->prev_col = line_len;
2968             edit->curs_col = line_len;
2969         }
2970         else
2971         {
2972             edit->curs_col = prev + over;
2973             edit->prev_col = edit->curs_col;
2974             edit->over_col = 0;
2975         }
2976     }
2977     else
2978     {
2979         edit->over_col = 0;
2980         if (edit_options.fake_half_tabs && is_in_indent (&edit->buffer))
2981         {
2982             long fake_half_tabs;
2983 
2984             edit_update_curs_col (edit);
2985 
2986             fake_half_tabs = HALF_TAB_SIZE * space_width;
2987             if (fake_half_tabs != 0 && edit->curs_col % fake_half_tabs != 0)
2988             {
2989                 long q;
2990 
2991                 q = edit->curs_col;
2992                 edit->curs_col -= (edit->curs_col % fake_half_tabs);
2993                 p = edit_buffer_get_current_bol (&edit->buffer);
2994                 b = edit_move_forward3 (edit, p, edit->curs_col, 0);
2995                 edit_cursor_move (edit, b - edit->buffer.curs1);
2996                 if (!left_of_four_spaces (edit))
2997                 {
2998                     b = edit_move_forward3 (edit, p, q, 0);
2999                     edit_cursor_move (edit, b - edit->buffer.curs1);
3000                 }
3001             }
3002         }
3003     }
3004 }
3005 
3006 /* --------------------------------------------------------------------------------------------- */
3007 /** check whether line in editor is blank or not
3008  *
3009  * @param edit editor object
3010  * @param line number of line
3011  *
3012  * @return TRUE if line in blank, FALSE otherwise
3013  */
3014 
3015 gboolean
3016 edit_line_is_blank (WEdit *edit, long line)
     /* [previous][next][first][last][top][bottom][index][help]  */
3017 {
3018     return is_blank (&edit->buffer, edit_find_line (edit, line));
3019 }
3020 
3021 /* --------------------------------------------------------------------------------------------- */
3022 /** move cursor to line 'line' */
3023 
3024 void
3025 edit_move_to_line (WEdit *e, long line)
     /* [previous][next][first][last][top][bottom][index][help]  */
3026 {
3027     if (line < e->buffer.curs_line)
3028         edit_move_up (e, e->buffer.curs_line - line, FALSE);
3029     else
3030         edit_move_down (e, line - e->buffer.curs_line, FALSE);
3031     edit_scroll_screen_over_cursor (e);
3032 }
3033 
3034 /* --------------------------------------------------------------------------------------------- */
3035 /** scroll window so that first visible line is 'line' */
3036 
3037 void
3038 edit_move_display (WEdit *e, long line)
     /* [previous][next][first][last][top][bottom][index][help]  */
3039 {
3040     if (line < e->start_line)
3041         edit_scroll_upward (e, e->start_line - line);
3042     else
3043         edit_scroll_downward (e, line - e->start_line);
3044 }
3045 
3046 /* --------------------------------------------------------------------------------------------- */
3047 /** save markers onto undo stack */
3048 
3049 void
3050 edit_push_markers (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3051 {
3052     edit_push_undo_action (edit, MARK_1 + edit->mark1);
3053     edit_push_undo_action (edit, MARK_2 + edit->mark2);
3054     edit_push_undo_action (edit, MARK_CURS + edit->end_mark_curs);
3055 }
3056 
3057 /* --------------------------------------------------------------------------------------------- */
3058 
3059 void
3060 edit_set_markers (WEdit *edit, off_t m1, off_t m2, long c1, long c2)
     /* [previous][next][first][last][top][bottom][index][help]  */
3061 {
3062     edit->mark1 = m1;
3063     edit->mark2 = m2;
3064     edit->column1 = c1;
3065     edit->column2 = c2;
3066 }
3067 
3068 /* --------------------------------------------------------------------------------------------- */
3069 /**
3070    if mark2 is -1 then marking is from mark1 to the cursor.
3071    Otherwise its between the markers. This handles this.
3072    Returns FALSE if no text is marked.
3073  */
3074 
3075 gboolean
3076 eval_marks (WEdit *edit, off_t *start_mark, off_t *end_mark)
     /* [previous][next][first][last][top][bottom][index][help]  */
3077 {
3078     long end_mark_curs;
3079 
3080     if (edit->mark1 == edit->mark2)
3081     {
3082         *start_mark = *end_mark = 0;
3083         edit->column2 = edit->column1 = 0;
3084         return FALSE;
3085     }
3086 
3087     if (edit->end_mark_curs < 0)
3088         end_mark_curs = edit->buffer.curs1;
3089     else
3090         end_mark_curs = edit->end_mark_curs;
3091 
3092     if (edit->mark2 >= 0)
3093     {
3094         *start_mark = MIN (edit->mark1, edit->mark2);
3095         *end_mark = MAX (edit->mark1, edit->mark2);
3096     }
3097     else
3098     {
3099         *start_mark = MIN (edit->mark1, end_mark_curs);
3100         *end_mark = MAX (edit->mark1, end_mark_curs);
3101         edit->column2 = edit->curs_col + edit->over_col;
3102     }
3103 
3104     if (edit->column_highlight != 0
3105         && ((edit->mark1 > end_mark_curs && edit->column1 < edit->column2)
3106             || (edit->mark1 < end_mark_curs && edit->column1 > edit->column2)))
3107     {
3108         off_t start_bol, start_eol;
3109         off_t end_bol, end_eol;
3110         long col1, col2;
3111         off_t diff1, diff2;
3112 
3113         start_bol = edit_buffer_get_bol (&edit->buffer, *start_mark);
3114         start_eol = edit_buffer_get_eol (&edit->buffer, start_bol - 1) + 1;
3115         end_bol = edit_buffer_get_bol (&edit->buffer, *end_mark);
3116         end_eol = edit_buffer_get_eol (&edit->buffer, *end_mark);
3117         col1 = MIN (edit->column1, edit->column2);
3118         col2 = MAX (edit->column1, edit->column2);
3119 
3120         diff1 = edit_move_forward3 (edit, start_bol, col2, 0)
3121             - edit_move_forward3 (edit, start_bol, col1, 0);
3122         diff2 = edit_move_forward3 (edit, end_bol, col2, 0)
3123             - edit_move_forward3 (edit, end_bol, col1, 0);
3124 
3125         *start_mark -= diff1;
3126         *end_mark += diff2;
3127         *start_mark = MAX (*start_mark, start_eol);
3128         *end_mark = MIN (*end_mark, end_eol);
3129     }
3130 
3131     return TRUE;
3132 }
3133 
3134 /* --------------------------------------------------------------------------------------------- */
3135 /** highlight marker toggle */
3136 
3137 void
3138 edit_mark_cmd (WEdit *edit, gboolean unmark)
     /* [previous][next][first][last][top][bottom][index][help]  */
3139 {
3140     edit_push_markers (edit);
3141     if (unmark)
3142     {
3143         edit_set_markers (edit, 0, 0, 0, 0);
3144         edit->force |= REDRAW_PAGE;
3145     }
3146     else if (edit->mark2 >= 0)
3147     {
3148         edit->end_mark_curs = -1;
3149         edit_set_markers (edit, edit->buffer.curs1, -1, edit->curs_col + edit->over_col,
3150                           edit->curs_col + edit->over_col);
3151         edit->force |= REDRAW_PAGE;
3152     }
3153     else
3154     {
3155         edit->end_mark_curs = edit->buffer.curs1;
3156         edit_set_markers (edit, edit->mark1, edit->buffer.curs1, edit->column1,
3157                           edit->curs_col + edit->over_col);
3158     }
3159 }
3160 
3161 /* --------------------------------------------------------------------------------------------- */
3162 /** highlight the word under cursor */
3163 
3164 void
3165 edit_mark_current_word_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3166 {
3167     long pos;
3168 
3169     for (pos = edit->buffer.curs1; pos != 0; pos--)
3170     {
3171         int c1, c2;
3172 
3173         c1 = edit_buffer_get_byte (&edit->buffer, pos);
3174         c2 = edit_buffer_get_byte (&edit->buffer, pos - 1);
3175         if (!isspace (c1) && isspace (c2))
3176             break;
3177         if ((my_type_of (c1) & my_type_of (c2)) == 0)
3178             break;
3179     }
3180     edit->mark1 = pos;
3181 
3182     for (; pos < edit->buffer.size; pos++)
3183     {
3184         int c1, c2;
3185 
3186         c1 = edit_buffer_get_byte (&edit->buffer, pos);
3187         c2 = edit_buffer_get_byte (&edit->buffer, pos + 1);
3188         if (!isspace (c1) && isspace (c2))
3189             break;
3190         if ((my_type_of (c1) & my_type_of (c2)) == 0)
3191             break;
3192     }
3193     edit->mark2 = MIN (pos + 1, edit->buffer.size);
3194 
3195     edit->force |= REDRAW_LINE_ABOVE | REDRAW_AFTER_CURSOR;
3196 }
3197 
3198 /* --------------------------------------------------------------------------------------------- */
3199 
3200 void
3201 edit_mark_current_line_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3202 {
3203     edit->mark1 = edit_buffer_get_current_bol (&edit->buffer);
3204     edit->mark2 = edit_buffer_get_current_eol (&edit->buffer);
3205 
3206     edit->force |= REDRAW_LINE_ABOVE | REDRAW_AFTER_CURSOR;
3207 }
3208 
3209 /* --------------------------------------------------------------------------------------------- */
3210 
3211 void
3212 edit_delete_line (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3213 {
3214     /*
3215      * Delete right part of the line.
3216      * Note that edit_buffer_get_byte() returns '\n' when byte position is
3217      *   beyond EOF.
3218      */
3219     while (edit_buffer_get_current_byte (&edit->buffer) != '\n')
3220         (void) edit_delete (edit, TRUE);
3221 
3222     /*
3223      * Delete '\n' char.
3224      * Note that edit_delete() will not corrupt anything if called while
3225      *   cursor position is EOF.
3226      */
3227     (void) edit_delete (edit, TRUE);
3228 
3229     /*
3230      * Delete left part of the line.
3231      * Note, that edit_buffer_get_byte() returns '\n' when byte position is < 0.
3232      */
3233     while (edit_buffer_get_previous_byte (&edit->buffer) != '\n')
3234         (void) edit_backspace (edit, TRUE);
3235 }
3236 
3237 /* --------------------------------------------------------------------------------------------- */
3238 
3239 void
3240 edit_push_key_press (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3241 {
3242     edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
3243     if (edit->mark2 == -1)
3244     {
3245         edit_push_undo_action (edit, MARK_1 + edit->mark1);
3246         edit_push_undo_action (edit, MARK_CURS + edit->end_mark_curs);
3247     }
3248 }
3249 
3250 /* --------------------------------------------------------------------------------------------- */
3251 
3252 void
3253 edit_find_bracket (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3254 {
3255     edit->bracket = edit_get_bracket (edit, 1, 10000);
3256     if (edit->last_bracket != edit->bracket)
3257         edit->force |= REDRAW_PAGE;
3258     edit->last_bracket = edit->bracket;
3259 }
3260 
3261 /* --------------------------------------------------------------------------------------------- */
3262 /**
3263  * This executes a command as though the user initiated it through a key
3264  * press.  Callback with MSG_KEY as a message calls this after
3265  * translating the key press.  This function can be used to pass any
3266  * command to the editor.  Note that the screen wouldn't update
3267  * automatically.  Either of command or char_for_insertion must be
3268  * passed as -1.  Commands are executed, and char_for_insertion is
3269  * inserted at the cursor.
3270  */
3271 
3272 void
3273 edit_execute_key_command (WEdit *edit, long command, int char_for_insertion)
     /* [previous][next][first][last][top][bottom][index][help]  */
3274 {
3275     if (command == CK_MacroStartRecord || command == CK_RepeatStartRecord
3276         || (macro_index < 0
3277             && (command == CK_MacroStartStopRecord || command == CK_RepeatStartStopRecord)))
3278     {
3279         macro_index = 0;
3280         edit->force |= REDRAW_CHAR_ONLY | REDRAW_LINE;
3281         return;
3282     }
3283     if (macro_index != -1)
3284     {
3285         edit->force |= REDRAW_COMPLETELY;
3286         if (command == CK_MacroStopRecord || command == CK_MacroStartStopRecord)
3287         {
3288             edit_store_macro_cmd (edit);
3289             macro_index = -1;
3290             return;
3291         }
3292         if (command == CK_RepeatStopRecord || command == CK_RepeatStartStopRecord)
3293         {
3294             edit_repeat_macro_cmd (edit);
3295             macro_index = -1;
3296             return;
3297         }
3298     }
3299 
3300     if (macro_index >= 0 && macro_index < MAX_MACRO_LENGTH - 1)
3301     {
3302         record_macro_buf[macro_index].action = command;
3303         record_macro_buf[macro_index++].ch = char_for_insertion;
3304     }
3305     // record the beginning of a set of editing actions initiated by a key press
3306     if (command != CK_Undo && command != CK_ExtendedKeyMap)
3307         edit_push_key_press (edit);
3308 
3309     edit_execute_cmd (edit, command, char_for_insertion);
3310     if (edit->column_highlight != 0)
3311         edit->force |= REDRAW_PAGE;
3312 }
3313 
3314 /* --------------------------------------------------------------------------------------------- */
3315 /**
3316    This executes a command at a lower level than macro recording.
3317    It also does not push a key_press onto the undo stack. This means
3318    that if it is called many times, a single undo command will undo
3319    all of them. It also does not check for the Undo command.
3320  */
3321 void
3322 edit_execute_cmd (WEdit *edit, long command, int char_for_insertion)
     /* [previous][next][first][last][top][bottom][index][help]  */
3323 {
3324     WRect *w = &WIDGET (edit)->rect;
3325 
3326     if (command == CK_WindowFullscreen)
3327     {
3328         edit_toggle_fullscreen (edit);
3329         return;
3330     }
3331 
3332     // handle window state
3333     if (edit_handle_move_resize (edit, command))
3334         return;
3335 
3336     edit->force |= REDRAW_LINE;
3337 
3338     /* The next key press will unhighlight the found string, so update
3339      * the whole page */
3340     if (edit->found_len != 0 || edit->column_highlight != 0)
3341         edit->force |= REDRAW_PAGE;
3342 
3343     switch (command)
3344     {
3345         // a mark command with shift-arrow
3346     case CK_MarkLeft:
3347     case CK_MarkRight:
3348     case CK_MarkToWordBegin:
3349     case CK_MarkToWordEnd:
3350     case CK_MarkToHome:
3351     case CK_MarkToEnd:
3352     case CK_MarkUp:
3353     case CK_MarkDown:
3354     case CK_MarkPageUp:
3355     case CK_MarkPageDown:
3356     case CK_MarkToFileBegin:
3357     case CK_MarkToFileEnd:
3358     case CK_MarkToPageBegin:
3359     case CK_MarkToPageEnd:
3360     case CK_MarkScrollUp:
3361     case CK_MarkScrollDown:
3362     case CK_MarkParagraphUp:
3363     case CK_MarkParagraphDown:
3364         // a mark command with alt-arrow
3365     case CK_MarkColumnPageUp:
3366     case CK_MarkColumnPageDown:
3367     case CK_MarkColumnLeft:
3368     case CK_MarkColumnRight:
3369     case CK_MarkColumnUp:
3370     case CK_MarkColumnDown:
3371     case CK_MarkColumnScrollUp:
3372     case CK_MarkColumnScrollDown:
3373     case CK_MarkColumnParagraphUp:
3374     case CK_MarkColumnParagraphDown:
3375         edit->column_highlight = 0;
3376         if (edit->highlight == 0 || (edit->mark2 != -1 && edit->mark1 != edit->mark2))
3377         {
3378             edit_mark_cmd (edit, TRUE);   // clear
3379             edit_mark_cmd (edit, FALSE);  // marking on
3380         }
3381         edit->highlight = 1;
3382         break;
3383 
3384         // any other command
3385     default:
3386         if (edit->highlight != 0)
3387             edit_mark_cmd (edit, FALSE);  // clear
3388         edit->highlight = 0;
3389     }
3390 
3391     // first check for undo
3392     if (command == CK_Undo)
3393     {
3394         edit->redo_stack_reset = 0;
3395         edit_group_undo (edit);
3396         edit->found_len = 0;
3397         edit->prev_col = edit_get_col (edit);
3398         edit->search_start = edit->buffer.curs1;
3399         return;
3400     }
3401     //  check for redo
3402     if (command == CK_Redo)
3403     {
3404         edit->redo_stack_reset = 0;
3405         edit_do_redo (edit);
3406         edit->found_len = 0;
3407         edit->prev_col = edit_get_col (edit);
3408         edit->search_start = edit->buffer.curs1;
3409         return;
3410     }
3411 
3412     edit->redo_stack_reset = 1;
3413 
3414     // An ordinary key press
3415     if (char_for_insertion >= 0)
3416     {
3417         // if non persistent selection and text selected
3418         if (!edit_options.persistent_selections && edit->mark1 != edit->mark2)
3419             edit_block_delete_cmd (edit);
3420 
3421         if (edit->overwrite != 0)
3422         {
3423             // remove char only one time, after input first byte, multibyte chars
3424             if (!mc_global.utf8_display || edit->charpoint == 0)
3425                 if (edit_buffer_get_current_byte (&edit->buffer) != '\n')
3426                     edit_delete (edit, FALSE);
3427         }
3428         if (edit_options.cursor_beyond_eol && edit->over_col > 0)
3429             edit_insert_over (edit);
3430         /**
3431            Encode 8-bit input as UTF-8, if display (locale) is *not* UTF-8,
3432            *but* source encoding *is* set to UTF-8; see ticket #3843 for the details.
3433         */
3434         if (char_for_insertion > 127 && str_isutf8 (get_codepage_id (mc_global.source_codepage))
3435             && !mc_global.utf8_display)
3436         {
3437             unsigned char str[UTF8_CHAR_LEN + 1];
3438             size_t i;
3439             int res;
3440 
3441             res = g_unichar_to_utf8 (char_for_insertion, (char *) str);
3442             if (res == 0)
3443             {
3444                 str[0] = '.';
3445                 str[1] = '\0';
3446             }
3447             else
3448                 str[res] = '\0';
3449 
3450             for (i = 0; i <= UTF8_CHAR_LEN && str[i] != '\0'; i++)
3451             {
3452                 char_for_insertion = str[i];
3453                 edit_insert (edit, char_for_insertion);
3454             }
3455         }
3456         else
3457             edit_insert (edit, char_for_insertion);
3458 
3459         if (edit_options.auto_para_formatting)
3460         {
3461             format_paragraph (edit, FALSE);
3462             edit->force |= REDRAW_PAGE;
3463         }
3464         else
3465             check_and_wrap_line (edit);
3466         edit->found_len = 0;
3467         edit->prev_col = edit_get_col (edit);
3468         edit->search_start = edit->buffer.curs1;
3469         edit_find_bracket (edit);
3470         return;
3471     }
3472 
3473     switch (command)
3474     {
3475     case CK_TopOnScreen:
3476     case CK_BottomOnScreen:
3477     case CK_Top:
3478     case CK_Bottom:
3479     case CK_PageUp:
3480     case CK_PageDown:
3481     case CK_Home:
3482     case CK_End:
3483     case CK_Up:
3484     case CK_Down:
3485     case CK_Left:
3486     case CK_Right:
3487     case CK_WordLeft:
3488     case CK_WordRight:
3489         if (!edit_options.persistent_selections && edit->mark2 >= 0)
3490         {
3491             if (edit->column_highlight != 0)
3492                 edit_push_undo_action (edit, COLUMN_ON);
3493             edit->column_highlight = 0;
3494             edit_mark_cmd (edit, TRUE);
3495         }
3496         break;
3497     default:
3498         break;
3499     }
3500 
3501     switch (command)
3502     {
3503     case CK_TopOnScreen:
3504     case CK_BottomOnScreen:
3505     case CK_MarkToPageBegin:
3506     case CK_MarkToPageEnd:
3507     case CK_Up:
3508     case CK_Down:
3509     case CK_WordLeft:
3510     case CK_WordRight:
3511     case CK_MarkToWordBegin:
3512     case CK_MarkToWordEnd:
3513     case CK_MarkUp:
3514     case CK_MarkDown:
3515     case CK_MarkColumnUp:
3516     case CK_MarkColumnDown:
3517         if (edit->mark2 == -1)
3518             break;  // marking is following the cursor: may need to highlight a whole line
3519         MC_FALLTHROUGH;
3520     case CK_Left:
3521     case CK_Right:
3522     case CK_MarkLeft:
3523     case CK_MarkRight:
3524         edit->force |= REDRAW_CHAR_ONLY;
3525         break;
3526     default:
3527         break;
3528     }
3529 
3530     // basic cursor key commands
3531     switch (command)
3532     {
3533     case CK_BackSpace:
3534         // if non persistent selection and text selected
3535         if (!edit_options.persistent_selections && edit->mark1 != edit->mark2)
3536             edit_block_delete_cmd (edit);
3537         else if (edit_options.cursor_beyond_eol && edit->over_col > 0)
3538             edit->over_col--;
3539         else if (edit_options.backspace_through_tabs && is_in_indent (&edit->buffer))
3540         {
3541             while (edit_buffer_get_previous_byte (&edit->buffer) != '\n' && edit->buffer.curs1 > 0)
3542                 edit_backspace (edit, TRUE);
3543         }
3544         else if (edit_options.fake_half_tabs && is_in_indent (&edit->buffer)
3545                  && right_of_four_spaces (edit))
3546         {
3547             int i;
3548 
3549             for (i = 0; i < HALF_TAB_SIZE; i++)
3550                 edit_backspace (edit, TRUE);
3551         }
3552         else
3553             edit_backspace (edit, FALSE);
3554         break;
3555     case CK_Delete:
3556         // if non persistent selection and text selected
3557         if (!edit_options.persistent_selections && edit->mark1 != edit->mark2)
3558             edit_block_delete_cmd (edit);
3559         else
3560         {
3561             if (edit_options.cursor_beyond_eol && edit->over_col > 0)
3562                 edit_insert_over (edit);
3563 
3564             if (edit_options.fake_half_tabs && is_in_indent (&edit->buffer)
3565                 && left_of_four_spaces (edit))
3566             {
3567                 int i;
3568 
3569                 for (i = 1; i <= HALF_TAB_SIZE; i++)
3570                     edit_delete (edit, TRUE);
3571             }
3572             else
3573                 edit_delete (edit, FALSE);
3574         }
3575         break;
3576     case CK_DeleteToWordBegin:
3577         edit->over_col = 0;
3578         edit_left_delete_word (edit);
3579         break;
3580     case CK_DeleteToWordEnd:
3581         if (edit_options.cursor_beyond_eol && edit->over_col > 0)
3582             edit_insert_over (edit);
3583 
3584         edit_right_delete_word (edit);
3585         break;
3586     case CK_DeleteLine:
3587         edit_delete_line (edit);
3588         break;
3589     case CK_DeleteToHome:
3590         edit_delete_to_line_begin (edit);
3591         break;
3592     case CK_DeleteToEnd:
3593         edit_delete_to_line_end (edit);
3594         break;
3595     case CK_Enter:
3596         edit->over_col = 0;
3597         if (edit_options.auto_para_formatting)
3598         {
3599             edit_double_newline (edit);
3600             if (edit_options.return_does_auto_indent && !bracketed_pasting_in_progress)
3601                 edit_auto_indent (edit);
3602             format_paragraph (edit, FALSE);
3603         }
3604         else
3605         {
3606             edit_insert (edit, '\n');
3607             if (edit_options.return_does_auto_indent && !bracketed_pasting_in_progress)
3608                 edit_auto_indent (edit);
3609         }
3610         break;
3611     case CK_Return:
3612         edit_insert (edit, '\n');
3613         break;
3614 
3615     case CK_MarkColumnPageUp:
3616         edit->column_highlight = 1;
3617         MC_FALLTHROUGH;
3618     case CK_PageUp:
3619     case CK_MarkPageUp:
3620         edit_move_up (edit, w->lines - (edit->fullscreen ? 1 : 2), TRUE);
3621         break;
3622     case CK_MarkColumnPageDown:
3623         edit->column_highlight = 1;
3624         MC_FALLTHROUGH;
3625     case CK_PageDown:
3626     case CK_MarkPageDown:
3627         edit_move_down (edit, w->lines - (edit->fullscreen ? 1 : 2), TRUE);
3628         break;
3629     case CK_MarkColumnLeft:
3630         edit->column_highlight = 1;
3631         MC_FALLTHROUGH;
3632     case CK_Left:
3633     case CK_MarkLeft:
3634         if (edit_options.fake_half_tabs && is_in_indent (&edit->buffer)
3635             && right_of_four_spaces (edit))
3636         {
3637             if (edit_options.cursor_beyond_eol && edit->over_col > 0)
3638                 edit->over_col--;
3639             else
3640                 edit_cursor_move (edit, -HALF_TAB_SIZE);
3641             edit->force &= (0xFFF - REDRAW_CHAR_ONLY);
3642         }
3643         else
3644             edit_left_char_move_cmd (edit);
3645         break;
3646     case CK_MarkColumnRight:
3647         edit->column_highlight = 1;
3648         MC_FALLTHROUGH;
3649     case CK_Right:
3650     case CK_MarkRight:
3651         if (edit_options.fake_half_tabs && is_in_indent (&edit->buffer)
3652             && left_of_four_spaces (edit))
3653         {
3654             edit_cursor_move (edit, HALF_TAB_SIZE);
3655             edit->force &= (0xFFF - REDRAW_CHAR_ONLY);
3656         }
3657         else
3658             edit_right_char_move_cmd (edit);
3659         break;
3660     case CK_TopOnScreen:
3661     case CK_MarkToPageBegin:
3662         edit_begin_page (edit);
3663         break;
3664     case CK_BottomOnScreen:
3665     case CK_MarkToPageEnd:
3666         edit_end_page (edit);
3667         break;
3668     case CK_WordLeft:
3669     case CK_MarkToWordBegin:
3670         edit->over_col = 0;
3671         edit_left_word_move_cmd (edit);
3672         break;
3673     case CK_WordRight:
3674     case CK_MarkToWordEnd:
3675         edit->over_col = 0;
3676         edit_right_word_move_cmd (edit);
3677         break;
3678     case CK_MarkColumnUp:
3679         edit->column_highlight = 1;
3680         MC_FALLTHROUGH;
3681     case CK_Up:
3682     case CK_MarkUp:
3683         edit_move_up (edit, 1, FALSE);
3684         break;
3685     case CK_MarkColumnDown:
3686         edit->column_highlight = 1;
3687         MC_FALLTHROUGH;
3688     case CK_Down:
3689     case CK_MarkDown:
3690         edit_move_down (edit, 1, FALSE);
3691         break;
3692     case CK_MarkColumnParagraphUp:
3693         edit->column_highlight = 1;
3694         MC_FALLTHROUGH;
3695     case CK_ParagraphUp:
3696     case CK_MarkParagraphUp:
3697         edit_move_up_paragraph (edit, FALSE);
3698         break;
3699     case CK_MarkColumnParagraphDown:
3700         edit->column_highlight = 1;
3701         MC_FALLTHROUGH;
3702     case CK_ParagraphDown:
3703     case CK_MarkParagraphDown:
3704         edit_move_down_paragraph (edit, FALSE);
3705         break;
3706     case CK_MarkColumnScrollUp:
3707         edit->column_highlight = 1;
3708         MC_FALLTHROUGH;
3709     case CK_ScrollUp:
3710     case CK_MarkScrollUp:
3711         edit_move_up (edit, 1, TRUE);
3712         break;
3713     case CK_MarkColumnScrollDown:
3714         edit->column_highlight = 1;
3715         MC_FALLTHROUGH;
3716     case CK_ScrollDown:
3717     case CK_MarkScrollDown:
3718         edit_move_down (edit, 1, TRUE);
3719         break;
3720     case CK_Home:
3721     case CK_MarkToHome:
3722         edit_cursor_to_bol (edit);
3723         break;
3724     case CK_End:
3725     case CK_MarkToEnd:
3726         edit_cursor_to_eol (edit);
3727         break;
3728     case CK_Tab:
3729         // if text marked shift block
3730         if (edit->mark1 != edit->mark2 && !edit_options.persistent_selections)
3731         {
3732             if (edit->mark2 < 0)
3733                 edit_mark_cmd (edit, FALSE);
3734             edit_move_block_to_right (edit);
3735         }
3736         else
3737         {
3738             if (edit_options.cursor_beyond_eol)
3739                 edit_insert_over (edit);
3740             edit_tab_cmd (edit);
3741             if (edit_options.auto_para_formatting)
3742             {
3743                 format_paragraph (edit, FALSE);
3744                 edit->force |= REDRAW_PAGE;
3745             }
3746             else
3747                 check_and_wrap_line (edit);
3748         }
3749         break;
3750 
3751     case CK_InsertOverwrite:
3752         edit->overwrite = !edit->overwrite;
3753         break;
3754 
3755     case CK_Mark:
3756         if (edit->mark2 >= 0)
3757         {
3758             if (edit->column_highlight != 0)
3759                 edit_push_undo_action (edit, COLUMN_ON);
3760             edit->column_highlight = 0;
3761         }
3762         edit_mark_cmd (edit, FALSE);
3763         break;
3764     case CK_MarkColumn:
3765         if (edit->column_highlight == 0)
3766             edit_push_undo_action (edit, COLUMN_OFF);
3767         edit->column_highlight = 1;
3768         edit_mark_cmd (edit, FALSE);
3769         break;
3770     case CK_MarkAll:
3771         edit_set_markers (edit, 0, edit->buffer.size, 0, 0);
3772         edit->force |= REDRAW_PAGE;
3773         break;
3774     case CK_Unmark:
3775         if (edit->column_highlight != 0)
3776             edit_push_undo_action (edit, COLUMN_ON);
3777         edit->column_highlight = 0;
3778         edit_mark_cmd (edit, TRUE);
3779         break;
3780     case CK_MarkWord:
3781         if (edit->column_highlight != 0)
3782             edit_push_undo_action (edit, COLUMN_ON);
3783         edit->column_highlight = 0;
3784         edit_mark_current_word_cmd (edit);
3785         break;
3786     case CK_MarkLine:
3787         if (edit->column_highlight != 0)
3788             edit_push_undo_action (edit, COLUMN_ON);
3789         edit->column_highlight = 0;
3790         edit_mark_current_line_cmd (edit);
3791         break;
3792 
3793     case CK_Bookmark:
3794         book_mark_clear (edit, edit->buffer.curs_line, BOOK_MARK_FOUND_COLOR);
3795         if (book_mark_query_color (edit, edit->buffer.curs_line, BOOK_MARK_COLOR))
3796             book_mark_clear (edit, edit->buffer.curs_line, BOOK_MARK_COLOR);
3797         else
3798             book_mark_insert (edit, edit->buffer.curs_line, BOOK_MARK_COLOR);
3799         break;
3800     case CK_BookmarkFlush:
3801         book_mark_flush (edit, BOOK_MARK_COLOR);
3802         book_mark_flush (edit, BOOK_MARK_FOUND_COLOR);
3803         edit->force |= REDRAW_PAGE;
3804         break;
3805     case CK_BookmarkNext:
3806         if (edit->book_mark != NULL)
3807         {
3808             edit_book_mark_t *p;
3809 
3810             p = book_mark_find (edit, edit->buffer.curs_line);
3811             if (p->next != NULL)
3812             {
3813                 p = p->next;
3814                 if (p->line >= edit->start_line + w->lines || p->line < edit->start_line)
3815                     edit_move_display (edit, p->line - w->lines / 2);
3816                 edit_move_to_line (edit, p->line);
3817             }
3818         }
3819         break;
3820     case CK_BookmarkPrev:
3821         if (edit->book_mark != NULL)
3822         {
3823             edit_book_mark_t *p;
3824 
3825             p = book_mark_find (edit, edit->buffer.curs_line);
3826             while (p->line == edit->buffer.curs_line)
3827                 if (p->prev != NULL)
3828                     p = p->prev;
3829             if (p->line >= 0)
3830             {
3831                 if (p->line >= edit->start_line + w->lines || p->line < edit->start_line)
3832                     edit_move_display (edit, p->line - w->lines / 2);
3833                 edit_move_to_line (edit, p->line);
3834             }
3835         }
3836         break;
3837 
3838     case CK_Top:
3839     case CK_MarkToFileBegin:
3840         edit_move_to_top (edit);
3841         break;
3842     case CK_Bottom:
3843     case CK_MarkToFileEnd:
3844         edit_move_to_bottom (edit);
3845         break;
3846 
3847     case CK_Copy:
3848         if (edit_options.cursor_beyond_eol && edit->over_col > 0)
3849             edit_insert_over (edit);
3850         edit_block_copy_cmd (edit);
3851         break;
3852     case CK_Remove:
3853         edit_block_delete_cmd (edit);
3854         break;
3855     case CK_Move:
3856         edit_block_move_cmd (edit);
3857         break;
3858 
3859     case CK_BlockShiftLeft:
3860         if (edit->mark1 != edit->mark2)
3861             edit_move_block_to_left (edit);
3862         break;
3863     case CK_BlockShiftRight:
3864         if (edit->mark1 != edit->mark2)
3865             edit_move_block_to_right (edit);
3866         break;
3867     case CK_Store:
3868         edit_copy_to_X_buf_cmd (edit);
3869         break;
3870     case CK_Cut:
3871         edit_cut_to_X_buf_cmd (edit);
3872         break;
3873     case CK_Paste:
3874         // if non persistent selection and text selected
3875         if (!edit_options.persistent_selections && edit->mark1 != edit->mark2)
3876             edit_block_delete_cmd (edit);
3877         if (edit_options.cursor_beyond_eol && edit->over_col > 0)
3878             edit_insert_over (edit);
3879         edit_paste_from_X_buf_cmd (edit);
3880         if (!edit_options.persistent_selections && edit->mark2 >= 0)
3881         {
3882             if (edit->column_highlight != 0)
3883                 edit_push_undo_action (edit, COLUMN_ON);
3884             edit->column_highlight = 0;
3885             edit_mark_cmd (edit, TRUE);
3886         }
3887         break;
3888     case CK_History:
3889         edit_paste_from_history (edit);
3890         break;
3891 
3892     case CK_SaveAs:
3893         edit_save_as_cmd (edit);
3894         break;
3895     case CK_Save:
3896         edit_save_confirm_cmd (edit);
3897         break;
3898     case CK_BlockSave:
3899         edit_save_block_cmd (edit);
3900         break;
3901     case CK_InsertFile:
3902         edit_insert_file_cmd (edit);
3903         break;
3904 
3905     case CK_FilePrev:
3906         edit_load_back_cmd (edit);
3907         break;
3908     case CK_FileNext:
3909         edit_load_forward_cmd (edit);
3910         break;
3911 
3912     case CK_SyntaxChoose:
3913         edit_syntax_dialog (edit);
3914         break;
3915 
3916     case CK_Search:
3917         edit_search_cmd (edit, FALSE);
3918         break;
3919     case CK_SearchContinue:
3920         edit_search_cmd (edit, TRUE);
3921         break;
3922     case CK_Replace:
3923         edit_replace_cmd (edit, FALSE);
3924         break;
3925     case CK_ReplaceContinue:
3926         edit_replace_cmd (edit, TRUE);
3927         break;
3928     case CK_Complete:
3929         // if text marked shift block
3930         if (edit->mark1 != edit->mark2 && !edit_options.persistent_selections)
3931             edit_move_block_to_left (edit);
3932         else
3933             edit_complete_word_cmd (edit);
3934         break;
3935     case CK_Find:
3936         edit_get_match_keyword_cmd (edit);
3937         break;
3938 
3939 #ifdef HAVE_ASPELL
3940     case CK_SpellCheckCurrentWord:
3941         edit_suggest_current_word (edit);
3942         break;
3943     case CK_SpellCheck:
3944         edit_spellcheck_file (edit);
3945         break;
3946     case CK_SpellCheckSelectLang:
3947         edit_set_spell_lang ();
3948         break;
3949 #endif
3950 
3951     case CK_Date:
3952     {
3953         char s[BUF_MEDIUM];
3954         // fool gcc to prevent a Y2K warning
3955         char time_format[] = "_c";
3956         time_format[0] = '%';
3957 
3958         FMT_LOCALTIME_CURRENT (s, sizeof (s), time_format);
3959         edit_print_string (edit, s);
3960         edit->force |= REDRAW_PAGE;
3961     }
3962     break;
3963     case CK_Goto:
3964         edit_goto_cmd (edit);
3965         break;
3966     case CK_ParagraphFormat:
3967         format_paragraph (edit, TRUE);
3968         edit->force |= REDRAW_PAGE;
3969         break;
3970     case CK_MacroDelete:
3971         edit_delete_macro_cmd (edit);
3972         break;
3973     case CK_MatchBracket:
3974         edit_goto_matching_bracket (edit);
3975         break;
3976     case CK_UserMenu:
3977         edit_user_menu (edit, NULL, -1);
3978         break;
3979     case CK_Sort:
3980         edit_sort_cmd (edit);
3981         break;
3982     case CK_ExternalCommand:
3983         edit_ext_cmd (edit);
3984         break;
3985     case CK_EditMail:
3986         edit_mail_dialog (edit);
3987         break;
3988     case CK_SelectCodepage:
3989         edit_select_codepage_cmd (edit);
3990         break;
3991     case CK_InsertLiteral:
3992         edit_insert_literal_cmd (edit);
3993         break;
3994     case CK_MacroStartStopRecord:
3995         edit_begin_end_macro_cmd (edit);
3996         break;
3997     case CK_RepeatStartStopRecord:
3998         edit_begin_end_repeat_cmd (edit);
3999         break;
4000     case CK_ExtendedKeyMap:
4001         WIDGET (edit)->ext_mode = TRUE;
4002         break;
4003     default:
4004         break;
4005     }
4006 
4007     // CK_PipeBlock
4008     if ((command / CK_PipeBlock (0)) == 1)
4009         edit_block_process_cmd (edit, command - CK_PipeBlock (0));
4010 
4011     // keys which must set the col position, and the search vars
4012     switch (command)
4013     {
4014     case CK_Search:
4015     case CK_SearchContinue:
4016     case CK_Replace:
4017     case CK_ReplaceContinue:
4018     case CK_Complete:
4019         edit->prev_col = edit_get_col (edit);
4020         break;
4021     case CK_Up:
4022     case CK_MarkUp:
4023     case CK_MarkColumnUp:
4024     case CK_Down:
4025     case CK_MarkDown:
4026     case CK_MarkColumnDown:
4027     case CK_PageUp:
4028     case CK_MarkPageUp:
4029     case CK_MarkColumnPageUp:
4030     case CK_PageDown:
4031     case CK_MarkPageDown:
4032     case CK_MarkColumnPageDown:
4033     case CK_Top:
4034     case CK_MarkToFileBegin:
4035     case CK_Bottom:
4036     case CK_MarkToFileEnd:
4037     case CK_ParagraphUp:
4038     case CK_MarkParagraphUp:
4039     case CK_MarkColumnParagraphUp:
4040     case CK_ParagraphDown:
4041     case CK_MarkParagraphDown:
4042     case CK_MarkColumnParagraphDown:
4043     case CK_ScrollUp:
4044     case CK_MarkScrollUp:
4045     case CK_MarkColumnScrollUp:
4046     case CK_ScrollDown:
4047     case CK_MarkScrollDown:
4048     case CK_MarkColumnScrollDown:
4049         edit->search_start = edit->buffer.curs1;
4050         edit->found_len = 0;
4051         break;
4052     default:
4053         edit->found_len = 0;
4054         edit->prev_col = edit_get_col (edit);
4055         edit->search_start = edit->buffer.curs1;
4056     }
4057     edit_find_bracket (edit);
4058 
4059     if (edit_options.auto_para_formatting)
4060     {
4061         switch (command)
4062         {
4063         case CK_BackSpace:
4064         case CK_Delete:
4065         case CK_DeleteToWordBegin:
4066         case CK_DeleteToWordEnd:
4067         case CK_DeleteToHome:
4068         case CK_DeleteToEnd:
4069             format_paragraph (edit, FALSE);
4070             edit->force |= REDRAW_PAGE;
4071             break;
4072         default:
4073             break;
4074         }
4075     }
4076 }
4077 
4078 /* --------------------------------------------------------------------------------------------- */
4079 
4080 void
4081 edit_stack_init (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
4082 {
4083     for (edit_stack_iterator = 0; edit_stack_iterator < MAX_HISTORY_MOVETO; edit_stack_iterator++)
4084         edit_arg_init (&edit_history_moveto[edit_stack_iterator], NULL, -1);
4085 
4086     edit_stack_iterator = 0;
4087 }
4088 
4089 /* --------------------------------------------------------------------------------------------- */
4090 
4091 void
4092 edit_stack_free (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         vfs_path_free (edit_history_moveto[edit_stack_iterator].file_vpath, TRUE);
4096 }
4097 
4098 /* --------------------------------------------------------------------------------------------- */
4099 /** move i lines */
4100 
4101 void
4102 edit_move_up (WEdit *edit, long i, gboolean do_scroll)
     /* [previous][next][first][last][top][bottom][index][help]  */
4103 {
4104     edit_move_updown (edit, i, do_scroll, TRUE);
4105 }
4106 
4107 /* --------------------------------------------------------------------------------------------- */
4108 /** move i lines */
4109 
4110 void
4111 edit_move_down (WEdit *edit, long i, gboolean do_scroll)
     /* [previous][next][first][last][top][bottom][index][help]  */
4112 {
4113     edit_move_updown (edit, i, do_scroll, FALSE);
4114 }
4115 
4116 /* --------------------------------------------------------------------------------------------- */
4117 /**
4118  * Create edit_arg_t object from vfs_path_t object and the line number.
4119  *
4120  * @param file_vpath  file path object
4121  * @param line_number line number. If value is 0, try to restore saved position.
4122  * @return edit_arg_t object
4123  */
4124 
4125 edit_arg_t *
4126 edit_arg_vpath_new (vfs_path_t *file_vpath, long line_number)
     /* [previous][next][first][last][top][bottom][index][help]  */
4127 {
4128     edit_arg_t *arg;
4129 
4130     arg = g_new (edit_arg_t, 1);
4131     arg->file_vpath = file_vpath;
4132     arg->line_number = line_number;
4133 
4134     return arg;
4135 }
4136 
4137 /* --------------------------------------------------------------------------------------------- */
4138 /**
4139  * Create edit_arg_t object from file name and the line number.
4140  *
4141  * @param file_name   file name
4142  * @param line_number line number. If value is 0, try to restore saved position.
4143  * @return edit_arg_t object
4144  */
4145 
4146 edit_arg_t *
4147 edit_arg_new (const char *file_name, long line_number)
     /* [previous][next][first][last][top][bottom][index][help]  */
4148 {
4149     return edit_arg_vpath_new (vfs_path_from_str (file_name), line_number);
4150 }
4151 
4152 /* --------------------------------------------------------------------------------------------- */
4153 /**
4154  * Initialize edit_arg_t object.
4155  *
4156  * @param arg  edit_arg_t object
4157  * @param vpath vfs_path_t object
4158  * @param line line number
4159  */
4160 
4161 void
4162 edit_arg_init (edit_arg_t *arg, vfs_path_t *vpath, long line)
     /* [previous][next][first][last][top][bottom][index][help]  */
4163 {
4164     arg->file_vpath = (vfs_path_t *) vpath;
4165     arg->line_number = line;
4166 }
4167 
4168 /* --------------------------------------------------------------------------------------------- */
4169 /**
4170  * Apply new values to edit_arg_t object members.
4171  *
4172  * @param arg  edit_arg_t object
4173  * @param vpath vfs_path_t object
4174  * @param line line number
4175  */
4176 
4177 void
4178 edit_arg_assign (edit_arg_t *arg, vfs_path_t *vpath, long line)
     /* [previous][next][first][last][top][bottom][index][help]  */
4179 {
4180     vfs_path_free (arg->file_vpath, TRUE);
4181     edit_arg_init (arg, vpath, line);
4182 }
4183 
4184 /* --------------------------------------------------------------------------------------------- */
4185 /**
4186  * Free the edit_arg_t object.
4187  *
4188  * @param arg edit_arg_t object
4189  */
4190 
4191 void
4192 edit_arg_free (edit_arg_t *arg)
     /* [previous][next][first][last][top][bottom][index][help]  */
4193 {
4194     vfs_path_free (arg->file_vpath, TRUE);
4195     g_free (arg);
4196 }
4197 
4198 /* --------------------------------------------------------------------------------------------- */
4199 
4200 const char *
4201 edit_get_file_name (const WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
4202 {
4203     return vfs_path_as_str (edit->filename_vpath);
4204 }
4205 
4206 /* --------------------------------------------------------------------------------------------- */

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