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

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