root/src/editor/editcmd.c

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

DEFINITIONS

This source file includes following definitions.
  1. edit_search_status_update_cb
  2. edit_save_mode_callback
  3. edit_save_file
  4. edit_check_newline
  5. edit_get_save_file_as
  6. edit_save_cmd
  7. edit_load_file_from_filename
  8. edit_delete_column_of_text
  9. edit_block_delete
  10. edit_search_get_current_end_line_char
  11. edit_get_search_line_type
  12. edit_calculate_start_of_next_line
  13. edit_calculate_end_of_previous_line
  14. edit_calculate_start_of_previous_line
  15. edit_calculate_start_of_current_line
  16. edit_search_fix_search_start_if_selection
  17. editcmd_find
  18. edit_replace_cmd__conv_to_display
  19. edit_replace_cmd__conv_to_input
  20. edit_show_search_error
  21. edit_do_search
  22. edit_search
  23. edit_get_block
  24. edit_save_block_to_clip_file
  25. pipe_mail
  26. edit_find_word_start
  27. edit_collect_completions_get_current_word
  28. edit_collect_completions
  29. edit_insert_column_of_text
  30. edit_macro_comparator
  31. edit_macro_sort_by_hotkey
  32. edit_get_macro
  33. edit_delete_macro
  34. edit_syntax_onoff_cb
  35. edit_complete_word_insert_recoded_completion
  36. edit_refresh_cmd
  37. edit_syntax_onoff_cmd
  38. edit_show_tabs_tws_cmd
  39. edit_show_margin_cmd
  40. edit_show_numbers_cmd
  41. edit_save_mode_cmd
  42. edit_set_filename
  43. edit_save_as_cmd
  44. edit_delete_macro_cmd
  45. edit_execute_macro
  46. edit_store_macro_cmd
  47. edit_repeat_macro_cmd
  48. edit_load_macro_cmd
  49. edit_save_confirm_cmd
  50. edit_load_cmd
  51. edit_load_file_from_history
  52. edit_load_syntax_file
  53. edit_load_menu_file
  54. edit_close_cmd
  55. eval_marks
  56. edit_block_copy_cmd
  57. edit_block_move_cmd
  58. edit_block_delete_cmd
  59. edit_replace_cmd
  60. edit_search_cmd_callback
  61. edit_search_update_callback
  62. edit_search_cmd
  63. edit_ok_to_exit
  64. edit_save_block
  65. edit_paste_from_history
  66. edit_copy_to_X_buf_cmd
  67. edit_cut_to_X_buf_cmd
  68. edit_paste_from_X_buf_cmd
  69. edit_goto_cmd
  70. edit_save_block_cmd
  71. edit_insert_file_cmd
  72. edit_sort_cmd
  73. edit_ext_cmd
  74. edit_block_process_cmd
  75. edit_mail_dialog
  76. edit_complete_word_cmd
  77. edit_select_codepage_cmd
  78. edit_insert_literal_cmd
  79. edit_begin_end_macro_cmd
  80. edit_begin_end_repeat_cmd
  81. edit_load_forward_cmd
  82. edit_load_back_cmd
  83. edit_get_match_keyword_cmd
  84. edit_suggest_current_word
  85. edit_spellcheck_file
  86. edit_set_spell_lang

   1 /*
   2    Editor high level editing commands
   3 
   4    Copyright (C) 1996-2020
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Paul Sheer, 1996, 1997
   9    Andrew Borodin <aborodin@vmail.ru>, 2012-2014
  10    Ilia Maslakov <il.smind@gmail.com>, 2012
  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 high level editing commands
  30  *  \author Paul Sheer
  31  *  \date 1996, 1997
  32  */
  33 
  34 /* #define PIPE_BLOCKS_SO_READ_BYTE_BY_BYTE */
  35 
  36 #include <config.h>
  37 
  38 #include <ctype.h>
  39 #include <stdio.h>
  40 #include <stdarg.h>
  41 #include <sys/types.h>
  42 #include <unistd.h>
  43 #include <string.h>
  44 #include <errno.h>
  45 #include <sys/stat.h>
  46 #include <stdlib.h>
  47 
  48 #include "lib/global.h"
  49 #include "lib/tty/tty.h"
  50 #include "lib/tty/key.h"        /* XCTRL */
  51 #include "lib/mcconfig.h"
  52 #include "lib/skin.h"
  53 #include "lib/strutil.h"        /* utf string functions */
  54 #include "lib/fileloc.h"
  55 #include "lib/lock.h"
  56 #include "lib/util.h"           /* tilde_expand() */
  57 #include "lib/vfs/vfs.h"
  58 #include "lib/widget.h"
  59 #include "lib/event.h"          /* mc_event_raise() */
  60 #ifdef HAVE_CHARSET
  61 #include "lib/charsets.h"
  62 #endif
  63 
  64 #include "src/history.h"
  65 #include "src/file_history.h"   /* show_file_history() */
  66 #include "src/setup.h"          /* option_tab_spacing */
  67 #ifdef HAVE_CHARSET
  68 #include "src/selcodepage.h"
  69 #endif
  70 #include "src/keybind-defaults.h"
  71 #include "src/util.h"           /* check_for_default() */
  72 
  73 #include "edit-impl.h"
  74 #include "editwidget.h"
  75 #include "editcmd_dialogs.h"
  76 #ifdef HAVE_ASPELL
  77 #include "spell.h"
  78 #include "spell_dialogs.h"
  79 #endif
  80 #include "etags.h"
  81 
  82 /*** global variables ****************************************************************************/
  83 
  84 /* search and replace: */
  85 int search_create_bookmark = FALSE;
  86 
  87 /* queries on a save */
  88 gboolean edit_confirm_save = TRUE;
  89 
  90 /* whether we need to drop selection on copy to buffer */
  91 gboolean option_drop_selection_on_copy = TRUE;
  92 
  93 /*** file scope macro definitions ****************************************************************/
  94 
  95 #define space_width 1
  96 
  97 #define TEMP_BUF_LEN 1024
  98 
  99 #define MAX_WORD_COMPLETIONS 100        /* in listbox */
 100 
 101 /*** file scope type declarations ****************************************************************/
 102 
 103 typedef struct
 104 {
 105     simple_status_msg_t status_msg;     /* base class */
 106 
 107     gboolean first;
 108     WEdit *edit;
 109     off_t offset;
 110 } edit_search_status_msg_t;
 111 
 112 /*** file scope variables ************************************************************************/
 113 
 114 static unsigned long edit_save_mode_radio_id, edit_save_mode_input_id;
 115 
 116 /* --------------------------------------------------------------------------------------------- */
 117 /*** file scope functions ************************************************************************/
 118 /* --------------------------------------------------------------------------------------------- */
 119 
 120 static int
 121 edit_search_status_update_cb (status_msg_t * sm)
     /* [previous][next][first][last][top][bottom][index][help]  */
 122 {
 123     simple_status_msg_t *ssm = SIMPLE_STATUS_MSG (sm);
 124     edit_search_status_msg_t *esm = (edit_search_status_msg_t *) sm;
 125     Widget *wd = WIDGET (sm->dlg);
 126 
 127     if (verbose)
 128         label_set_textv (ssm->label, _("Searching %s: %3d%%"), esm->edit->last_search_string,
 129                          edit_buffer_calc_percent (&esm->edit->buffer, esm->offset));
 130     else
 131         label_set_textv (ssm->label, _("Searching %s"), esm->edit->last_search_string);
 132 
 133     if (esm->first)
 134     {
 135         int wd_width;
 136         Widget *lw = WIDGET (ssm->label);
 137 
 138         wd_width = MAX (wd->cols, lw->cols + 6);
 139         widget_set_size (wd, wd->y, wd->x, wd->lines, wd_width);
 140         widget_set_size (lw, lw->y, wd->x + (wd->cols - lw->cols) / 2, lw->lines, lw->cols);
 141         esm->first = FALSE;
 142     }
 143 
 144     return status_msg_common_update (sm);
 145 }
 146 
 147 /* --------------------------------------------------------------------------------------------- */
 148 
 149 static cb_ret_t
 150 edit_save_mode_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 151 {
 152     switch (msg)
 153     {
 154     case MSG_CHANGED_FOCUS:
 155         if (sender != NULL && sender->id == edit_save_mode_radio_id)
 156         {
 157             Widget *ww;
 158 
 159             ww = widget_find_by_id (w, edit_save_mode_input_id);
 160             widget_disable (ww, RADIO (sender)->sel != 2);
 161             return MSG_HANDLED;
 162         }
 163         return MSG_NOT_HANDLED;
 164 
 165     default:
 166         return dlg_default_callback (w, sender, msg, parm, data);
 167     }
 168 }
 169 
 170 /* --------------------------------------------------------------------------------------------- */
 171 
 172 /*  If 0 (quick save) then  a) create/truncate <filename> file,
 173    b) save to <filename>;
 174    if 1 (safe save) then   a) save to <tempnam>,
 175    b) rename <tempnam> to <filename>;
 176    if 2 (do backups) then  a) save to <tempnam>,
 177    b) rename <filename> to <filename.backup_ext>,
 178    c) rename <tempnam> to <filename>. */
 179 
 180 /* returns 0 on error, -1 on abort */
 181 
 182 static int
 183 edit_save_file (WEdit * edit, const vfs_path_t * filename_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 184 {
 185     char *p;
 186     gchar *tmp;
 187     off_t filelen = 0;
 188     int this_save_mode, rv, fd = -1;
 189     vfs_path_t *real_filename_vpath;
 190     vfs_path_t *savename_vpath = NULL;
 191     const char *start_filename;
 192     const vfs_path_element_t *vpath_element;
 193     struct stat sb;
 194 
 195     vpath_element = vfs_path_get_by_index (filename_vpath, 0);
 196     if (vpath_element == NULL)
 197         return 0;
 198 
 199     start_filename = vpath_element->path;
 200     if (*start_filename == '\0')
 201         return 0;
 202 
 203     if (!IS_PATH_SEP (*start_filename) && edit->dir_vpath != NULL)
 204         real_filename_vpath = vfs_path_append_vpath_new (edit->dir_vpath, filename_vpath, NULL);
 205     else
 206         real_filename_vpath = vfs_path_clone (filename_vpath);
 207 
 208     this_save_mode = option_save_mode;
 209     if (this_save_mode != EDIT_QUICK_SAVE)
 210     {
 211         if (!vfs_file_is_local (real_filename_vpath))
 212             /* The file does not exists yet, so no safe save or backup are necessary. */
 213             this_save_mode = EDIT_QUICK_SAVE;
 214         else
 215         {
 216             fd = mc_open (real_filename_vpath, O_RDONLY | O_BINARY);
 217             if (fd == -1)
 218                 /* The file does not exists yet, so no safe save or backup are necessary. */
 219                 this_save_mode = EDIT_QUICK_SAVE;
 220         }
 221 
 222         if (fd != -1)
 223             mc_close (fd);
 224     }
 225 
 226     rv = mc_stat (real_filename_vpath, &sb);
 227     if (rv == 0)
 228     {
 229         if (this_save_mode == EDIT_QUICK_SAVE && !edit->skip_detach_prompt && sb.st_nlink > 1)
 230         {
 231             rv = edit_query_dialog3 (_("Warning"),
 232                                      _("File has hard-links. Detach before saving?"),
 233                                      _("&Yes"), _("&No"), _("&Cancel"));
 234             switch (rv)
 235             {
 236             case 0:
 237                 this_save_mode = EDIT_SAFE_SAVE;
 238                 MC_FALLTHROUGH;
 239             case 1:
 240                 edit->skip_detach_prompt = 1;
 241                 break;
 242             default:
 243                 vfs_path_free (real_filename_vpath);
 244                 return -1;
 245             }
 246         }
 247 
 248         /* Prevent overwriting changes from other editor sessions. */
 249         if (edit->stat1.st_mtime != 0 && edit->stat1.st_mtime != sb.st_mtime)
 250         {
 251             /* The default action is "Cancel". */
 252             query_set_sel (1);
 253 
 254             rv = edit_query_dialog2 (_("Warning"),
 255                                      _("The file has been modified in the meantime. Save anyway?"),
 256                                      _("&Yes"), _("&Cancel"));
 257             if (rv != 0)
 258             {
 259                 vfs_path_free (real_filename_vpath);
 260                 return -1;
 261             }
 262         }
 263     }
 264 
 265     if (this_save_mode == EDIT_QUICK_SAVE)
 266         savename_vpath = vfs_path_clone (real_filename_vpath);
 267     else
 268     {
 269         char *savedir, *saveprefix;
 270 
 271         savedir = vfs_path_tokens_get (real_filename_vpath, 0, -1);
 272         if (savedir == NULL)
 273             savedir = g_strdup (".");
 274 
 275         /* Token-related function never return leading slash, so we need add it manually */
 276         saveprefix = mc_build_filename (PATH_SEP_STR, savedir, "cooledit", (char *) NULL);
 277         g_free (savedir);
 278         fd = mc_mkstemps (&savename_vpath, saveprefix, NULL);
 279         g_free (saveprefix);
 280         if (savename_vpath == NULL)
 281         {
 282             vfs_path_free (real_filename_vpath);
 283             return 0;
 284         }
 285         /* FIXME:
 286          * Close for now because mc_mkstemps use pure open system call
 287          * to create temporary file and it needs to be reopened by
 288          * VFS-aware mc_open().
 289          */
 290         close (fd);
 291     }
 292 
 293     (void) mc_chown (savename_vpath, edit->stat1.st_uid, edit->stat1.st_gid);
 294     (void) mc_chmod (savename_vpath, edit->stat1.st_mode);
 295 
 296     fd = mc_open (savename_vpath, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY, edit->stat1.st_mode);
 297     if (fd == -1)
 298         goto error_save;
 299 
 300     /* pipe save */
 301     p = edit_get_write_filter (savename_vpath, real_filename_vpath);
 302     if (p != NULL)
 303     {
 304         FILE *file;
 305 
 306         mc_close (fd);
 307         file = (FILE *) popen (p, "w");
 308 
 309         if (file != NULL)
 310         {
 311             filelen = edit_write_stream (edit, file);
 312 #if 1
 313             pclose (file);
 314 #else
 315             if (pclose (file) != 0)
 316             {
 317                 tmp = g_strdup_printf (_("Error writing to pipe: %s"), p);
 318                 edit_error_dialog (_("Error"), tmp);
 319                 g_free (tmp);
 320                 g_free (p);
 321                 goto error_save;
 322             }
 323 #endif
 324         }
 325         else
 326         {
 327             tmp = g_strdup_printf (_("Cannot open pipe for writing: %s"), p);
 328             edit_error_dialog (_("Error"), get_sys_error (tmp));
 329             g_free (p);
 330             g_free (tmp);
 331             goto error_save;
 332         }
 333         g_free (p);
 334     }
 335     else if (edit->lb == LB_ASIS)
 336     {                           /* do not change line breaks */
 337         filelen = edit_buffer_write_file (&edit->buffer, fd);
 338 
 339         if (filelen != edit->buffer.size)
 340         {
 341             mc_close (fd);
 342             goto error_save;
 343         }
 344 
 345         if (mc_close (fd) != 0)
 346             goto error_save;
 347 
 348         /* Update the file information, especially the mtime. */
 349         if (mc_stat (savename_vpath, &edit->stat1) == -1)
 350             goto error_save;
 351     }
 352     else
 353     {                           /* change line breaks */
 354         FILE *file;
 355         const vfs_path_element_t *path_element;
 356 
 357         mc_close (fd);
 358 
 359         path_element = vfs_path_get_by_index (savename_vpath, -1);
 360         file = (FILE *) fopen (path_element->path, "w");
 361         if (file != NULL)
 362         {
 363             filelen = edit_write_stream (edit, file);
 364             fclose (file);
 365         }
 366         else
 367         {
 368             char *msg;
 369 
 370             msg = g_strdup_printf (_("Cannot open file for writing: %s"), path_element->path);
 371             edit_error_dialog (_("Error"), msg);
 372             g_free (msg);
 373             goto error_save;
 374         }
 375     }
 376 
 377     if (filelen != edit->buffer.size)
 378         goto error_save;
 379 
 380     if (this_save_mode == EDIT_DO_BACKUP)
 381     {
 382         char *tmp_store_filename;
 383         vfs_path_element_t *last_vpath_element;
 384         vfs_path_t *tmp_vpath;
 385         gboolean ok;
 386 
 387         g_assert (option_backup_ext != NULL);
 388 
 389         /* add backup extension to the path */
 390         tmp_vpath = vfs_path_clone (real_filename_vpath);
 391         last_vpath_element = (vfs_path_element_t *) vfs_path_get_by_index (tmp_vpath, -1);
 392         tmp_store_filename = last_vpath_element->path;
 393         last_vpath_element->path = g_strdup_printf ("%s%s", tmp_store_filename, option_backup_ext);
 394         g_free (tmp_store_filename);
 395 
 396         ok = (mc_rename (real_filename_vpath, tmp_vpath) != -1);
 397         vfs_path_free (tmp_vpath);
 398         if (!ok)
 399             goto error_save;
 400     }
 401 
 402     if (this_save_mode != EDIT_QUICK_SAVE && mc_rename (savename_vpath, real_filename_vpath) == -1)
 403         goto error_save;
 404 
 405     vfs_path_free (real_filename_vpath);
 406     vfs_path_free (savename_vpath);
 407     return 1;
 408   error_save:
 409     /*  FIXME: Is this safe ?
 410      *  if (this_save_mode != EDIT_QUICK_SAVE)
 411      *      mc_unlink (savename);
 412      */
 413     vfs_path_free (real_filename_vpath);
 414     vfs_path_free (savename_vpath);
 415     return 0;
 416 }
 417 
 418 /* --------------------------------------------------------------------------------------------- */
 419 
 420 static gboolean
 421 edit_check_newline (const edit_buffer_t * buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 422 {
 423     return !(option_check_nl_at_eof && buf->size > 0
 424              && edit_buffer_get_byte (buf, buf->size - 1) != '\n'
 425              && edit_query_dialog2 (_("Warning"),
 426                                     _("The file you are saving does not end with a newline."),
 427                                     _("C&ontinue"), _("&Cancel")) != 0);
 428 }
 429 
 430 /* --------------------------------------------------------------------------------------------- */
 431 
 432 static vfs_path_t *
 433 edit_get_save_file_as (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 434 {
 435     static LineBreaks cur_lb = LB_ASIS;
 436     char *filename_res;
 437     vfs_path_t *ret_vpath = NULL;
 438 
 439     const char *lb_names[LB_NAMES] = {
 440         N_("&Do not change"),
 441         N_("&Unix format (LF)"),
 442         N_("&Windows/DOS format (CR LF)"),
 443         N_("&Macintosh format (CR)")
 444     };
 445 
 446     quick_widget_t quick_widgets[] = {
 447         /* *INDENT-OFF* */
 448         QUICK_LABELED_INPUT (N_("Enter file name:"), input_label_above,
 449                              vfs_path_as_str (edit->filename_vpath), "save-as",
 450                              &filename_res, NULL, FALSE, FALSE, INPUT_COMPLETE_FILENAMES),
 451         QUICK_SEPARATOR (TRUE),
 452         QUICK_LABEL (N_("Change line breaks to:"), NULL),
 453         QUICK_RADIO (LB_NAMES, lb_names, (int *) &cur_lb, NULL),
 454         QUICK_BUTTONS_OK_CANCEL,
 455         QUICK_END
 456         /* *INDENT-ON* */
 457     };
 458 
 459     quick_dialog_t qdlg = {
 460         -1, -1, 64,
 461         N_("Save As"), "[Save File As]",
 462         quick_widgets, NULL, NULL
 463     };
 464 
 465     if (quick_dialog (&qdlg) != B_CANCEL)
 466     {
 467         char *fname;
 468 
 469         edit->lb = cur_lb;
 470         fname = tilde_expand (filename_res);
 471         g_free (filename_res);
 472         ret_vpath = vfs_path_from_str (fname);
 473         g_free (fname);
 474     }
 475 
 476     return ret_vpath;
 477 }
 478 
 479 /* --------------------------------------------------------------------------------------------- */
 480 
 481 /** returns TRUE on success */
 482 
 483 static gboolean
 484 edit_save_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 485 {
 486     int res, save_lock = 0;
 487 
 488     if (!edit->locked && !edit->delete_file)
 489         save_lock = lock_file (edit->filename_vpath);
 490     res = edit_save_file (edit, edit->filename_vpath);
 491 
 492     /* Maintain modify (not save) lock on failure */
 493     if ((res > 0 && edit->locked) || save_lock)
 494         edit->locked = unlock_file (edit->filename_vpath);
 495 
 496     /* On failure try 'save as', it does locking on its own */
 497     if (res == 0)
 498         return edit_save_as_cmd (edit);
 499 
 500     if (res > 0)
 501     {
 502         edit->delete_file = 0;
 503         edit->modified = 0;
 504     }
 505 
 506     edit->force |= REDRAW_COMPLETELY;
 507 
 508     return TRUE;
 509 }
 510 
 511 /* --------------------------------------------------------------------------------------------- */
 512 /**
 513  * Load file content
 514  *
 515  * @param h screen the owner of editor window
 516  * @param vpath vfs file path
 517  * @return TRUE if file content was successfully loaded, FALSE otherwise
 518  */
 519 
 520 static inline gboolean
 521 edit_load_file_from_filename (WDialog * h, const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 522 {
 523     Widget *w = WIDGET (h);
 524 
 525     return edit_add_window (h, w->y + 1, w->x, w->lines - 2, w->cols, vpath, 0);
 526 }
 527 
 528 /* --------------------------------------------------------------------------------------------- */
 529 
 530 static void
 531 edit_delete_column_of_text (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 532 {
 533     off_t m1, m2;
 534     off_t n;
 535     long b, c, d;
 536 
 537     eval_marks (edit, &m1, &m2);
 538     n = edit_buffer_get_forward_offset (&edit->buffer, m1, 0, m2) + 1;
 539     c = (long) edit_move_forward3 (edit, edit_buffer_get_bol (&edit->buffer, m1), 0, m1);
 540     d = (long) edit_move_forward3 (edit, edit_buffer_get_bol (&edit->buffer, m2), 0, m2);
 541     b = MAX (MIN (c, d), MIN (edit->column1, edit->column2));
 542     c = MAX (c, MAX (edit->column1, edit->column2));
 543 
 544     while (n-- != 0)
 545     {
 546         off_t r, p, q;
 547 
 548         r = edit_buffer_get_current_bol (&edit->buffer);
 549         p = edit_move_forward3 (edit, r, b, 0);
 550         q = edit_move_forward3 (edit, r, c, 0);
 551         p = MAX (p, m1);
 552         q = MIN (q, m2);
 553         edit_cursor_move (edit, p - edit->buffer.curs1);
 554         /* delete line between margins */
 555         for (; q > p; q--)
 556             if (edit_buffer_get_current_byte (&edit->buffer) != '\n')
 557                 edit_delete (edit, TRUE);
 558 
 559         /* move to next line except on the last delete */
 560         if (n != 0)
 561             edit_cursor_move (edit,
 562                               edit_buffer_get_forward_offset (&edit->buffer, edit->buffer.curs1, 1,
 563                                                               0) - edit->buffer.curs1);
 564     }
 565 }
 566 
 567 /* --------------------------------------------------------------------------------------------- */
 568 /** if success return 0 */
 569 
 570 static int
 571 edit_block_delete (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 572 {
 573     off_t start_mark, end_mark;
 574     off_t curs_pos;
 575     long curs_line, c1, c2;
 576 
 577     if (!eval_marks (edit, &start_mark, &end_mark))
 578         return 0;
 579 
 580     if (edit->column_highlight && edit->mark2 < 0)
 581         edit_mark_cmd (edit, FALSE);
 582 
 583     /* Warning message with a query to continue or cancel the operation */
 584     if ((end_mark - start_mark) > option_max_undo / 2 &&
 585         edit_query_dialog2 (_("Warning"),
 586                             ("Block is large, you may not be able to undo this action"),
 587                             _("C&ontinue"), _("&Cancel")) != 0)
 588         return 1;
 589 
 590     c1 = MIN (edit->column1, edit->column2);
 591     c2 = MAX (edit->column1, edit->column2);
 592     edit->column1 = c1;
 593     edit->column2 = c2;
 594 
 595     edit_push_markers (edit);
 596 
 597     curs_line = edit->buffer.curs_line;
 598 
 599     curs_pos = edit->curs_col + edit->over_col;
 600 
 601     /* move cursor to start of selection */
 602     edit_cursor_move (edit, start_mark - edit->buffer.curs1);
 603     edit_scroll_screen_over_cursor (edit);
 604 
 605     if (start_mark < end_mark)
 606     {
 607         if (edit->column_highlight)
 608         {
 609             off_t line_width;
 610 
 611             if (edit->mark2 < 0)
 612                 edit_mark_cmd (edit, FALSE);
 613             edit_delete_column_of_text (edit);
 614             /* move cursor to the saved position */
 615             edit_move_to_line (edit, curs_line);
 616             /* calculate line width and cursor position before cut */
 617             line_width = edit_move_forward3 (edit, edit_buffer_get_current_bol (&edit->buffer), 0,
 618                                              edit_buffer_get_current_eol (&edit->buffer));
 619             if (option_cursor_beyond_eol && curs_pos > line_width)
 620                 edit->over_col = curs_pos - line_width;
 621         }
 622         else
 623         {
 624             off_t count;
 625 
 626             for (count = start_mark; count < end_mark; count++)
 627                 edit_delete (edit, TRUE);
 628         }
 629     }
 630 
 631     edit_set_markers (edit, 0, 0, 0, 0);
 632     edit->force |= REDRAW_PAGE;
 633 
 634     return 0;
 635 }
 636 
 637 /* --------------------------------------------------------------------------------------------- */
 638 /**
 639  * Get EOL symbol for searching.
 640  *
 641  * @param edit editor object
 642  * @return EOL symbol
 643  */
 644 
 645 static inline char
 646 edit_search_get_current_end_line_char (const WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 647 {
 648     switch (edit->lb)
 649     {
 650     case LB_MAC:
 651         return '\r';
 652     default:
 653         return '\n';
 654     }
 655 }
 656 
 657 /* --------------------------------------------------------------------------------------------- */
 658 /**
 659  * Checking if search condition have BOL(^) or EOL ($) regexp special characters.
 660  *
 661  * @param search search object
 662  * @return result of checks.
 663  */
 664 
 665 static edit_search_line_t
 666 edit_get_search_line_type (mc_search_t * search)
     /* [previous][next][first][last][top][bottom][index][help]  */
 667 {
 668     edit_search_line_t search_line_type = 0;
 669 
 670     if (search->search_type != MC_SEARCH_T_REGEX)
 671         return search_line_type;
 672 
 673     if (*search->original == '^')
 674         search_line_type |= AT_START_LINE;
 675 
 676     if (search->original[search->original_len - 1] == '$')
 677         search_line_type |= AT_END_LINE;
 678     return search_line_type;
 679 }
 680 
 681 /* --------------------------------------------------------------------------------------------- */
 682 /**
 683  * Calculating the start position of next line.
 684  *
 685  * @param buf               editor buffer object
 686  * @param current_pos       current position
 687  * @param max_pos           max position
 688  * @param end_string_symbol end of line symbol
 689  * @return start position of next line
 690  */
 691 
 692 static off_t
 693 edit_calculate_start_of_next_line (const edit_buffer_t * buf, off_t current_pos, off_t max_pos,
     /* [previous][next][first][last][top][bottom][index][help]  */
 694                                    char end_string_symbol)
 695 {
 696     off_t i;
 697 
 698     for (i = current_pos; i < max_pos; i++)
 699     {
 700         current_pos++;
 701         if (edit_buffer_get_byte (buf, i) == end_string_symbol)
 702             break;
 703     }
 704 
 705     return current_pos;
 706 }
 707 
 708 /* --------------------------------------------------------------------------------------------- */
 709 /**
 710  * Calculating the end position of previous line.
 711  *
 712  * @param buf               editor buffer object
 713  * @param current_pos       current position
 714  * @param end_string_symbol end of line symbol
 715  * @return end position of previous line
 716  */
 717 
 718 static off_t
 719 edit_calculate_end_of_previous_line (const edit_buffer_t * buf, off_t current_pos,
     /* [previous][next][first][last][top][bottom][index][help]  */
 720                                      char end_string_symbol)
 721 {
 722     off_t i;
 723 
 724     for (i = current_pos - 1; i >= 0; i--)
 725         if (edit_buffer_get_byte (buf, i) == end_string_symbol)
 726             break;
 727 
 728     return i;
 729 }
 730 
 731 /* --------------------------------------------------------------------------------------------- */
 732 /**
 733  * Calculating the start position of previous line.
 734  *
 735  * @param buf               editor buffer object
 736  * @param current_pos       current position
 737  * @param end_string_symbol end of line symbol
 738  * @return start position of previous line
 739  */
 740 
 741 static inline off_t
 742 edit_calculate_start_of_previous_line (const edit_buffer_t * buf, off_t current_pos,
     /* [previous][next][first][last][top][bottom][index][help]  */
 743                                        char end_string_symbol)
 744 {
 745     current_pos = edit_calculate_end_of_previous_line (buf, current_pos, end_string_symbol);
 746     current_pos = edit_calculate_end_of_previous_line (buf, current_pos, end_string_symbol);
 747 
 748     return (current_pos + 1);
 749 }
 750 
 751 /* --------------------------------------------------------------------------------------------- */
 752 /**
 753  * Calculating the start position of current line.
 754  *
 755  * @param buf               editor buffer object
 756  * @param current_pos       current position
 757  * @param end_string_symbol end of line symbol
 758  * @return start position of current line
 759  */
 760 
 761 static inline off_t
 762 edit_calculate_start_of_current_line (const edit_buffer_t * buf, off_t current_pos,
     /* [previous][next][first][last][top][bottom][index][help]  */
 763                                       char end_string_symbol)
 764 {
 765     current_pos = edit_calculate_end_of_previous_line (buf, current_pos, end_string_symbol);
 766 
 767     return (current_pos + 1);
 768 }
 769 
 770 /* --------------------------------------------------------------------------------------------- */
 771 /**
 772  * Fixing (if needed) search start position if 'only in selection' option present.
 773  *
 774  * @param edit              editor object
 775  */
 776 
 777 static void
 778 edit_search_fix_search_start_if_selection (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 779 {
 780     off_t start_mark = 0;
 781     off_t end_mark = 0;
 782 
 783     if (!edit_search_options.only_in_selection)
 784         return;
 785 
 786     if (!eval_marks (edit, &start_mark, &end_mark))
 787         return;
 788 
 789     if (edit_search_options.backwards)
 790     {
 791         if (edit->search_start > end_mark || edit->search_start <= start_mark)
 792             edit->search_start = end_mark;
 793     }
 794     else
 795     {
 796         if (edit->search_start < start_mark || edit->search_start >= end_mark)
 797             edit->search_start = start_mark;
 798     }
 799 }
 800 
 801 /* --------------------------------------------------------------------------------------------- */
 802 
 803 static gboolean
 804 editcmd_find (edit_search_status_msg_t * esm, gsize * len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 805 {
 806     WEdit *edit = esm->edit;
 807     off_t search_start = edit->search_start;
 808     off_t search_end;
 809     off_t start_mark = 0;
 810     off_t end_mark = edit->buffer.size;
 811     char end_string_symbol;
 812 
 813     end_string_symbol = edit_search_get_current_end_line_char (edit);
 814 
 815     /* prepare for search */
 816     if (edit_search_options.only_in_selection)
 817     {
 818         if (!eval_marks (edit, &start_mark, &end_mark))
 819         {
 820             mc_search_set_error (edit->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
 821             return FALSE;
 822         }
 823 
 824         /* fix the start and the end of search block positions */
 825         if ((edit->search_line_type & AT_START_LINE) != 0
 826             && (start_mark != 0
 827                 || edit_buffer_get_byte (&edit->buffer, start_mark - 1) != end_string_symbol))
 828             start_mark =
 829                 edit_calculate_start_of_next_line (&edit->buffer, start_mark, edit->buffer.size,
 830                                                    end_string_symbol);
 831 
 832         if ((edit->search_line_type & AT_END_LINE) != 0
 833             && (end_mark - 1 != edit->buffer.size
 834                 || edit_buffer_get_byte (&edit->buffer, end_mark) != end_string_symbol))
 835             end_mark =
 836                 edit_calculate_end_of_previous_line (&edit->buffer, end_mark, end_string_symbol);
 837 
 838         if (start_mark >= end_mark)
 839         {
 840             mc_search_set_error (edit->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
 841             return FALSE;
 842         }
 843     }
 844     else if (edit_search_options.backwards)
 845         end_mark = MAX (1, edit->buffer.curs1) - 1;
 846 
 847     /* search */
 848     if (edit_search_options.backwards)
 849     {
 850         /* backward search */
 851         search_end = end_mark;
 852 
 853         if ((edit->search_line_type & AT_START_LINE) != 0)
 854             search_start =
 855                 edit_calculate_start_of_current_line (&edit->buffer, search_start,
 856                                                       end_string_symbol);
 857 
 858         while (search_start >= start_mark)
 859         {
 860             gboolean ok;
 861 
 862             if (search_end > (off_t) (search_start + edit->search->original_len)
 863                 && mc_search_is_fixed_search_str (edit->search))
 864                 search_end = search_start + edit->search->original_len;
 865 
 866             ok = mc_search_run (edit->search, (void *) esm, search_start, search_end, len);
 867 
 868             if (ok && edit->search->normal_offset == search_start)
 869                 return TRUE;
 870 
 871             /* We abort the search in case of a pattern error, or if the user aborts
 872                the search. In other words: in all cases except "string not found". */
 873             if (!ok && edit->search->error != MC_SEARCH_E_NOTFOUND)
 874                 return FALSE;
 875 
 876             if ((edit->search_line_type & AT_START_LINE) != 0)
 877                 search_start =
 878                     edit_calculate_start_of_previous_line (&edit->buffer, search_start,
 879                                                            end_string_symbol);
 880             else
 881                 search_start--;
 882         }
 883 
 884         mc_search_set_error (edit->search, MC_SEARCH_E_NOTFOUND, "%s", _(STR_E_NOTFOUND));
 885         return FALSE;
 886     }
 887 
 888     /* forward search */
 889     if ((edit->search_line_type & AT_START_LINE) != 0 && search_start != start_mark)
 890         search_start =
 891             edit_calculate_start_of_next_line (&edit->buffer, search_start, end_mark,
 892                                                end_string_symbol);
 893 
 894     return mc_search_run (edit->search, (void *) esm, search_start, end_mark, len);
 895 }
 896 
 897 /* --------------------------------------------------------------------------------------------- */
 898 
 899 static char *
 900 edit_replace_cmd__conv_to_display (const char *str)
     /* [previous][next][first][last][top][bottom][index][help]  */
 901 {
 902 #ifdef HAVE_CHARSET
 903     GString *tmp;
 904 
 905     tmp = str_convert_to_display (str);
 906     if (tmp != NULL)
 907     {
 908         if (tmp->len != 0)
 909             return g_string_free (tmp, FALSE);
 910         g_string_free (tmp, TRUE);
 911     }
 912 #endif
 913     return g_strdup (str);
 914 }
 915 
 916 /* --------------------------------------------------------------------------------------------- */
 917 
 918 static char *
 919 edit_replace_cmd__conv_to_input (char *str)
     /* [previous][next][first][last][top][bottom][index][help]  */
 920 {
 921 #ifdef HAVE_CHARSET
 922     GString *tmp;
 923 
 924     tmp = str_convert_to_input (str);
 925     if (tmp->len != 0)
 926         return g_string_free (tmp, FALSE);
 927     g_string_free (tmp, TRUE);
 928 #endif
 929     return g_strdup (str);
 930 }
 931 
 932 /* --------------------------------------------------------------------------------------------- */
 933 
 934 static void
 935 edit_show_search_error (const WEdit * edit, const char *title)
     /* [previous][next][first][last][top][bottom][index][help]  */
 936 {
 937     if (edit->search->error == MC_SEARCH_E_NOTFOUND)
 938         edit_query_dialog (title, _(STR_E_NOTFOUND));
 939     else if (edit->search->error_str != NULL)
 940         edit_query_dialog (title, edit->search->error_str);
 941 }
 942 
 943 /* --------------------------------------------------------------------------------------------- */
 944 
 945 static void
 946 edit_do_search (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 947 {
 948     edit_search_status_msg_t esm;
 949     gsize len = 0;
 950 
 951     if (edit->search == NULL)
 952         edit->search_start = edit->buffer.curs1;
 953 
 954     edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
 955 
 956     esm.first = TRUE;
 957     esm.edit = edit;
 958     esm.offset = edit->search_start;
 959 
 960     status_msg_init (STATUS_MSG (&esm), _("Search"), 1.0, simple_status_msg_init_cb,
 961                      edit_search_status_update_cb, NULL);
 962 
 963     if (search_create_bookmark)
 964     {
 965         gboolean found = FALSE;
 966         long l = 0, l_last = -1;
 967         long q = 0;
 968 
 969         search_create_bookmark = FALSE;
 970         book_mark_flush (edit, -1);
 971 
 972         while (mc_search_run (edit->search, (void *) &esm, q, edit->buffer.size, &len))
 973         {
 974             if (!found)
 975                 edit->search_start = edit->search->normal_offset;
 976             found = TRUE;
 977 
 978             l += edit_buffer_count_lines (&edit->buffer, q, edit->search->normal_offset);
 979             if (l != l_last)
 980                 book_mark_insert (edit, l, BOOK_MARK_FOUND_COLOR);
 981             l_last = l;
 982             q = edit->search->normal_offset + 1;
 983         }
 984 
 985         if (!found)
 986             edit_error_dialog (_("Search"), _(STR_E_NOTFOUND));
 987         else
 988             edit_cursor_move (edit, edit->search_start - edit->buffer.curs1);
 989     }
 990     else
 991     {
 992         if (edit->found_len != 0 && edit->search_start == edit->found_start + 1
 993             && edit_search_options.backwards)
 994             edit->search_start--;
 995 
 996         if (edit->found_len != 0 && edit->search_start == edit->found_start - 1
 997             && !edit_search_options.backwards)
 998             edit->search_start++;
 999 
1000         if (editcmd_find (&esm, &len))
1001         {
1002             edit->found_start = edit->search_start = edit->search->normal_offset;
1003             edit->found_len = len;
1004             edit->over_col = 0;
1005             edit_cursor_move (edit, edit->search_start - edit->buffer.curs1);
1006             edit_scroll_screen_over_cursor (edit);
1007             if (edit_search_options.backwards)
1008                 edit->search_start--;
1009             else
1010                 edit->search_start++;
1011         }
1012         else
1013         {
1014             edit->search_start = edit->buffer.curs1;
1015             edit_show_search_error (edit, _("Search"));
1016         }
1017     }
1018 
1019     status_msg_deinit (STATUS_MSG (&esm));
1020 
1021     edit->force |= REDRAW_COMPLETELY;
1022     edit_scroll_screen_over_cursor (edit);
1023 }
1024 
1025 /* --------------------------------------------------------------------------------------------- */
1026 
1027 static void
1028 edit_search (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1029 {
1030     if (editcmd_dialog_search_show (edit))
1031     {
1032         edit->search_line_type = edit_get_search_line_type (edit->search);
1033         edit_search_fix_search_start_if_selection (edit);
1034         edit_do_search (edit);
1035     }
1036 }
1037 
1038 /* --------------------------------------------------------------------------------------------- */
1039 /** Return a null terminated length of text. Result must be g_free'd */
1040 
1041 static unsigned char *
1042 edit_get_block (WEdit * edit, off_t start, off_t finish, off_t * l)
     /* [previous][next][first][last][top][bottom][index][help]  */
1043 {
1044     unsigned char *s, *r;
1045 
1046     r = s = g_malloc0 (finish - start + 1);
1047 
1048     if (edit->column_highlight)
1049     {
1050         *l = 0;
1051 
1052         /* copy from buffer, excluding chars that are out of the column 'margins' */
1053         for (; start < finish; start++)
1054         {
1055             int c;
1056             off_t x;
1057 
1058             x = edit_move_forward3 (edit, edit_buffer_get_bol (&edit->buffer, start), 0, start);
1059             c = edit_buffer_get_byte (&edit->buffer, start);
1060             if ((x >= edit->column1 && x < edit->column2)
1061                 || (x >= edit->column2 && x < edit->column1) || c == '\n')
1062             {
1063                 *s++ = c;
1064                 (*l)++;
1065             }
1066         }
1067     }
1068     else
1069     {
1070         *l = finish - start;
1071 
1072         for (; start < finish; start++)
1073             *s++ = edit_buffer_get_byte (&edit->buffer, start);
1074     }
1075 
1076     *s = '\0';
1077 
1078     return r;
1079 }
1080 
1081 /* --------------------------------------------------------------------------------------------- */
1082 /** copies a block to clipboard file */
1083 
1084 static gboolean
1085 edit_save_block_to_clip_file (WEdit * edit, off_t start, off_t finish)
     /* [previous][next][first][last][top][bottom][index][help]  */
1086 {
1087     gboolean ret;
1088     gchar *tmp;
1089 
1090     tmp = mc_config_get_full_path (EDIT_HOME_CLIP_FILE);
1091     ret = edit_save_block (edit, tmp, start, finish);
1092     g_free (tmp);
1093 
1094     return ret;
1095 }
1096 
1097 /* --------------------------------------------------------------------------------------------- */
1098 
1099 static void
1100 pipe_mail (const edit_buffer_t * buf, char *to, char *subject, char *cc)
     /* [previous][next][first][last][top][bottom][index][help]  */
1101 {
1102     FILE *p = 0;
1103     char *s;
1104 
1105     to = name_quote (to, FALSE);
1106     subject = name_quote (subject, FALSE);
1107     cc = name_quote (cc, FALSE);
1108     s = g_strconcat ("mail -s ", subject, *cc ? " -c " : "", cc, " ", to, (char *) NULL);
1109     g_free (to);
1110     g_free (subject);
1111     g_free (cc);
1112 
1113     if (s != NULL)
1114     {
1115         p = popen (s, "w");
1116         g_free (s);
1117     }
1118 
1119     if (p != NULL)
1120     {
1121         off_t i;
1122 
1123         for (i = 0; i < buf->size; i++)
1124             if (fputc (edit_buffer_get_byte (buf, i), p) < 0)
1125                 break;
1126         pclose (p);
1127     }
1128 }
1129 
1130 /* --------------------------------------------------------------------------------------------- */
1131 /** find first character of current word */
1132 
1133 static gboolean
1134 edit_find_word_start (const edit_buffer_t * buf, off_t * word_start, gsize * word_len)
     /* [previous][next][first][last][top][bottom][index][help]  */
1135 {
1136     int c;
1137     off_t i;
1138 
1139     /* return if at begin of file */
1140     if (buf->curs1 <= 0)
1141         return FALSE;
1142 
1143     c = edit_buffer_get_previous_byte (buf);
1144     /* return if not at end or in word */
1145     if (is_break_char (c))
1146         return FALSE;
1147 
1148     /* search start of word to be completed */
1149     for (i = 1;; i++)
1150     {
1151         int last;
1152 
1153         last = c;
1154         c = edit_buffer_get_byte (buf, buf->curs1 - i - 1);
1155 
1156         if (is_break_char (c))
1157         {
1158             /* return if word starts with digit */
1159             if (isdigit (last))
1160                 return FALSE;
1161 
1162             break;
1163         }
1164     }
1165 
1166     /* success */
1167     *word_start = buf->curs1 - i;       /* start found */
1168     *word_len = (gsize) i;
1169 
1170     return TRUE;
1171 }
1172 
1173 /* --------------------------------------------------------------------------------------------- */
1174 /**
1175  * Get current word under cursor
1176  *
1177  * @param esm status message window
1178  * @param srch mc_search object
1179  * @param word_start start word position
1180  *
1181  * @return newly allocated string or NULL if no any words under cursor
1182  */
1183 
1184 static char *
1185 edit_collect_completions_get_current_word (edit_search_status_msg_t * esm, mc_search_t * srch,
     /* [previous][next][first][last][top][bottom][index][help]  */
1186                                            off_t word_start)
1187 {
1188     WEdit *edit = esm->edit;
1189     gsize len = 0;
1190     off_t i;
1191     GString *temp;
1192 
1193     if (!mc_search_run (srch, (void *) esm, word_start, edit->buffer.size, &len))
1194         return NULL;
1195 
1196     temp = g_string_sized_new (len);
1197 
1198     for (i = 0; i < (off_t) len; i++)
1199     {
1200         int chr;
1201 
1202         chr = edit_buffer_get_byte (&edit->buffer, word_start + i);
1203         if (!isspace (chr))
1204             g_string_append_c (temp, chr);
1205     }
1206 
1207     return g_string_free (temp, temp->len == 0);
1208 }
1209 
1210 /* --------------------------------------------------------------------------------------------- */
1211 /** collect the possible completions */
1212 
1213 static gsize
1214 edit_collect_completions (WEdit * edit, off_t word_start, gsize word_len,
     /* [previous][next][first][last][top][bottom][index][help]  */
1215                           char *match_expr, GString ** compl, gsize * num)
1216 {
1217     gsize len = 0;
1218     gsize max_len = 0;
1219     gsize i;
1220     int skip;
1221     GString *temp;
1222     mc_search_t *srch;
1223     off_t last_byte, start = -1;
1224     char *current_word;
1225     gboolean entire_file;
1226     edit_search_status_msg_t esm;
1227 
1228 #ifdef HAVE_CHARSET
1229     srch = mc_search_new (match_expr, cp_source);
1230 #else
1231     srch = mc_search_new (match_expr, NULL);
1232 #endif
1233     if (srch == NULL)
1234         return 0;
1235 
1236     entire_file =
1237         mc_config_get_bool (mc_global.main_config, CONFIG_APP_SECTION,
1238                             "editor_wordcompletion_collect_entire_file", 0);
1239 
1240     last_byte = entire_file ? edit->buffer.size : word_start;
1241 
1242     srch->search_type = MC_SEARCH_T_REGEX;
1243     srch->is_case_sensitive = TRUE;
1244     srch->search_fn = edit_search_cmd_callback;
1245     srch->update_fn = edit_search_update_callback;
1246 
1247     esm.first = TRUE;
1248     esm.edit = edit;
1249     esm.offset = entire_file ? 0 : word_start;
1250 
1251     status_msg_init (STATUS_MSG (&esm), _("Collect completions"), 1.0, simple_status_msg_init_cb,
1252                      edit_search_status_update_cb, NULL);
1253 
1254     current_word = edit_collect_completions_get_current_word (&esm, srch, word_start);
1255 
1256     temp = g_string_new ("");
1257 
1258     /* collect max MAX_WORD_COMPLETIONS completions */
1259     while (mc_search_run (srch, (void *) &esm, start + 1, last_byte, &len))
1260     {
1261         g_string_set_size (temp, 0);
1262         start = srch->normal_offset;
1263 
1264         /* add matched completion if not yet added */
1265         for (i = 0; i < len; i++)
1266         {
1267             skip = edit_buffer_get_byte (&edit->buffer, start + i);
1268             if (isspace (skip))
1269                 continue;
1270 
1271             /* skip current word */
1272             if (start + (off_t) i == word_start)
1273                 break;
1274 
1275             g_string_append_c (temp, skip);
1276         }
1277 
1278         if (temp->len == 0)
1279             continue;
1280 
1281         if (current_word != NULL && strcmp (current_word, temp->str) == 0)
1282             continue;
1283 
1284         skip = 0;
1285 
1286         for (i = 0; i < *num; i++)
1287         {
1288             if (strncmp
1289                 ((char *) &compl[i]->str[word_len],
1290                  (char *) &temp->str[word_len], MAX (len, compl[i]->len) - word_len) == 0)
1291             {
1292                 GString *this = compl[i];
1293 
1294                 for (++i; i < *num; i++)
1295                     compl[i - 1] = compl[i];
1296                 compl[*num - 1] = this;
1297 
1298                 skip = 1;
1299                 break;          /* skip it, already added */
1300             }
1301         }
1302         if (skip != 0)
1303             continue;
1304 
1305         if (*num == MAX_WORD_COMPLETIONS)
1306         {
1307             g_string_free (compl[0], TRUE);
1308             for (i = 1; i < *num; i++)
1309                 compl[i - 1] = compl[i];
1310             (*num)--;
1311         }
1312 #ifdef HAVE_CHARSET
1313         {
1314             GString *recoded;
1315 
1316             recoded = str_convert_to_display (temp->str);
1317             if (recoded->len != 0)
1318                 g_string_assign (temp, recoded->str);
1319 
1320             g_string_free (recoded, TRUE);
1321         }
1322 #endif
1323         compl[(*num)++] = g_string_new_len (temp->str, temp->len);
1324         start += len;
1325 
1326         /* note the maximal length needed for the completion dialog */
1327         if (len > max_len)
1328             max_len = len;
1329     }
1330 
1331     status_msg_deinit (STATUS_MSG (&esm));
1332     mc_search_free (srch);
1333     g_string_free (temp, TRUE);
1334     g_free (current_word);
1335 
1336     return max_len;
1337 }
1338 
1339 /* --------------------------------------------------------------------------------------------- */
1340 
1341 static void
1342 edit_insert_column_of_text (WEdit * edit, unsigned char *data, off_t size, long width,
     /* [previous][next][first][last][top][bottom][index][help]  */
1343                             off_t * start_pos, off_t * end_pos, long *col1, long *col2)
1344 {
1345     off_t i, cursor;
1346     long col;
1347 
1348     cursor = edit->buffer.curs1;
1349     col = edit_get_col (edit);
1350 
1351     for (i = 0; i < size; i++)
1352     {
1353         if (data[i] != '\n')
1354             edit_insert (edit, data[i]);
1355         else
1356         {                       /* fill in and move to next line */
1357             long l;
1358             off_t p;
1359 
1360             if (edit_buffer_get_current_byte (&edit->buffer) != '\n')
1361             {
1362                 for (l = width - (edit_get_col (edit) - col); l > 0; l -= space_width)
1363                     edit_insert (edit, ' ');
1364             }
1365             for (p = edit->buffer.curs1;; p++)
1366             {
1367                 if (p == edit->buffer.size)
1368                 {
1369                     edit_cursor_move (edit, edit->buffer.size - edit->buffer.curs1);
1370                     edit_insert_ahead (edit, '\n');
1371                     p++;
1372                     break;
1373                 }
1374                 if (edit_buffer_get_byte (&edit->buffer, p) == '\n')
1375                 {
1376                     p++;
1377                     break;
1378                 }
1379             }
1380             edit_cursor_move (edit, edit_move_forward3 (edit, p, col, 0) - edit->buffer.curs1);
1381 
1382             for (l = col - edit_get_col (edit); l >= space_width; l -= space_width)
1383                 edit_insert (edit, ' ');
1384         }
1385     }
1386 
1387     *col1 = col;
1388     *col2 = col + width;
1389     *start_pos = cursor;
1390     *end_pos = edit->buffer.curs1;
1391     edit_cursor_move (edit, cursor - edit->buffer.curs1);
1392 }
1393 
1394 /* --------------------------------------------------------------------------------------------- */
1395 
1396 static int
1397 edit_macro_comparator (gconstpointer * macro1, gconstpointer * macro2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1398 {
1399     const macros_t *m1 = (const macros_t *) macro1;
1400     const macros_t *m2 = (const macros_t *) macro2;
1401 
1402     return m1->hotkey - m2->hotkey;
1403 }
1404 
1405 /* --------------------------------------------------------------------------------------------- */
1406 
1407 static void
1408 edit_macro_sort_by_hotkey (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1409 {
1410     if (macros_list != NULL && macros_list->len != 0)
1411         g_array_sort (macros_list, (GCompareFunc) edit_macro_comparator);
1412 }
1413 
1414 /* --------------------------------------------------------------------------------------------- */
1415 
1416 static gboolean
1417 edit_get_macro (WEdit * edit, int hotkey, const macros_t ** macros, guint * indx)
     /* [previous][next][first][last][top][bottom][index][help]  */
1418 {
1419     const macros_t *array_start = &g_array_index (macros_list, struct macros_t, 0);
1420     macros_t *result;
1421     macros_t search_macro;
1422 
1423     (void) edit;
1424 
1425     search_macro.hotkey = hotkey;
1426     result = bsearch (&search_macro, macros_list->data, macros_list->len,
1427                       sizeof (macros_t), (GCompareFunc) edit_macro_comparator);
1428 
1429     if (result != NULL && result->macro != NULL)
1430     {
1431         *indx = (result - array_start);
1432         *macros = result;
1433         return TRUE;
1434     }
1435     *indx = 0;
1436     return FALSE;
1437 }
1438 
1439 /* --------------------------------------------------------------------------------------------- */
1440 /** returns FALSE on error */
1441 
1442 static gboolean
1443 edit_delete_macro (WEdit * edit, int hotkey)
     /* [previous][next][first][last][top][bottom][index][help]  */
1444 {
1445     mc_config_t *macros_config = NULL;
1446     const char *section_name = "editor";
1447     gchar *macros_fname;
1448     guint indx;
1449     char *skeyname;
1450     const macros_t *macros = NULL;
1451 
1452     /* clear array of actions for current hotkey */
1453     while (edit_get_macro (edit, hotkey, &macros, &indx))
1454     {
1455         if (macros->macro != NULL)
1456             g_array_free (macros->macro, TRUE);
1457         macros = NULL;
1458         g_array_remove_index (macros_list, indx);
1459         edit_macro_sort_by_hotkey ();
1460     }
1461 
1462     macros_fname = mc_config_get_full_path (MC_MACRO_FILE);
1463     macros_config = mc_config_init (macros_fname, FALSE);
1464     g_free (macros_fname);
1465 
1466     if (macros_config == NULL)
1467         return FALSE;
1468 
1469     skeyname = lookup_key_by_code (hotkey);
1470     while (mc_config_del_key (macros_config, section_name, skeyname))
1471         ;
1472     g_free (skeyname);
1473     mc_config_save_file (macros_config, NULL);
1474     mc_config_deinit (macros_config);
1475     return TRUE;
1476 }
1477 
1478 /* --------------------------------------------------------------------------------------------- */
1479 /**
1480  * Callback for the iteration of objects in the 'editors' array.
1481  * Toggle syntax highlighting in editor object.
1482  *
1483  * @param data      probably WEdit object
1484  * @param user_data unused
1485  */
1486 
1487 static void
1488 edit_syntax_onoff_cb (void *data, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help]  */
1489 {
1490     (void) user_data;
1491 
1492     if (edit_widget_is_editor (CONST_WIDGET (data)))
1493     {
1494         WEdit *edit = (WEdit *) data;
1495 
1496         if (option_syntax_highlighting)
1497             edit_load_syntax (edit, NULL, edit->syntax_type);
1498         edit->force |= REDRAW_PAGE;
1499     }
1500 }
1501 
1502 /* --------------------------------------------------------------------------------------------- */
1503 /**
1504  * Insert autocompleted word into editor.
1505  *
1506  * @param edit       editor object
1507  * @param completion word for completion
1508  * @param word_len   offset from beginning for insert
1509  */
1510 
1511 static void
1512 edit_complete_word_insert_recoded_completion (WEdit * edit, char *completion, gsize word_len)
     /* [previous][next][first][last][top][bottom][index][help]  */
1513 {
1514 #ifdef HAVE_CHARSET
1515     GString *temp;
1516 
1517     temp = str_convert_to_input (completion);
1518 
1519     for (completion = temp->str + word_len; *completion != '\0'; completion++)
1520         edit_insert (edit, *completion);
1521     g_string_free (temp, TRUE);
1522 #else
1523     for (completion += word_len; *completion != '\0'; completion++)
1524         edit_insert (edit, *completion);
1525 #endif
1526 }
1527 
1528 /* --------------------------------------------------------------------------------------------- */
1529 /*** public functions ****************************************************************************/
1530 /* --------------------------------------------------------------------------------------------- */
1531 
1532 void
1533 edit_refresh_cmd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1534 {
1535     tty_clear_screen ();
1536     repaint_screen ();
1537     tty_keypad (TRUE);
1538 }
1539 
1540 /* --------------------------------------------------------------------------------------------- */
1541 /**
1542  * Toggle syntax highlighting in all editor windows.
1543  *
1544  * @param h root widget for all windows
1545  */
1546 
1547 void
1548 edit_syntax_onoff_cmd (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1549 {
1550     option_syntax_highlighting = !option_syntax_highlighting;
1551     g_list_foreach (GROUP (h)->widgets, edit_syntax_onoff_cb, NULL);
1552     widget_draw (WIDGET (h));
1553 }
1554 
1555 /* --------------------------------------------------------------------------------------------- */
1556 /**
1557  * Toggle tabs showing in all editor windows.
1558  *
1559  * @param h root widget for all windows
1560  */
1561 
1562 void
1563 edit_show_tabs_tws_cmd (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1564 {
1565     enable_show_tabs_tws = !enable_show_tabs_tws;
1566     widget_draw (WIDGET (h));
1567 }
1568 
1569 /* --------------------------------------------------------------------------------------------- */
1570 /**
1571  * Toggle right margin showing in all editor windows.
1572  *
1573  * @param h root widget for all windows
1574  */
1575 
1576 void
1577 edit_show_margin_cmd (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1578 {
1579     show_right_margin = !show_right_margin;
1580     widget_draw (WIDGET (h));
1581 }
1582 
1583 /* --------------------------------------------------------------------------------------------- */
1584 /**
1585  * Toggle line numbers showing in all editor windows.
1586  *
1587  * @param h root widget for all windows
1588  */
1589 
1590 void
1591 edit_show_numbers_cmd (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1592 {
1593     option_line_state = !option_line_state;
1594     option_line_state_width = option_line_state ? LINE_STATE_WIDTH : 0;
1595     widget_draw (WIDGET (h));
1596 }
1597 
1598 /* --------------------------------------------------------------------------------------------- */
1599 
1600 void
1601 edit_save_mode_cmd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1602 {
1603     char *str_result = NULL;
1604 
1605     const char *str[] = {
1606         N_("&Quick save"),
1607         N_("&Safe save"),
1608         N_("&Do backups with following extension:")
1609     };
1610 
1611 #ifdef ENABLE_NLS
1612     size_t i;
1613 
1614     for (i = 0; i < 3; i++)
1615         str[i] = _(str[i]);
1616 #endif
1617 
1618     g_assert (option_backup_ext != NULL);
1619 
1620     {
1621         quick_widget_t quick_widgets[] = {
1622             /* *INDENT-OFF* */
1623             QUICK_RADIO (3, str, &option_save_mode, &edit_save_mode_radio_id),
1624             QUICK_INPUT (option_backup_ext, "edit-backup-ext", &str_result,
1625                          &edit_save_mode_input_id, FALSE, FALSE, INPUT_COMPLETE_NONE),
1626             QUICK_SEPARATOR (TRUE),
1627             QUICK_CHECKBOX (N_("Check &POSIX new line"), &option_check_nl_at_eof, NULL),
1628             QUICK_BUTTONS_OK_CANCEL,
1629             QUICK_END
1630             /* *INDENT-ON* */
1631         };
1632 
1633         quick_dialog_t qdlg = {
1634             -1, -1, 38,
1635             N_("Edit Save Mode"), "[Edit Save Mode]",
1636             quick_widgets, edit_save_mode_callback, NULL
1637         };
1638 
1639         if (quick_dialog (&qdlg) != B_CANCEL)
1640         {
1641             g_free (option_backup_ext);
1642             option_backup_ext = str_result;
1643         }
1644     }
1645 }
1646 
1647 /* --------------------------------------------------------------------------------------------- */
1648 
1649 void
1650 edit_set_filename (WEdit * edit, const vfs_path_t * name_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1651 {
1652     vfs_path_free (edit->filename_vpath);
1653     edit->filename_vpath = vfs_path_clone (name_vpath);
1654 
1655     if (edit->dir_vpath == NULL)
1656         edit->dir_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
1657 }
1658 
1659 /* --------------------------------------------------------------------------------------------- */
1660 /* Here we want to warn the users of overwriting an existing file,
1661    but only if they have made a change to the filename */
1662 /* returns TRUE on success */
1663 gboolean
1664 edit_save_as_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1665 {
1666     /* This heads the 'Save As' dialog box */
1667     vfs_path_t *exp_vpath;
1668     int save_lock = 0;
1669     gboolean different_filename = FALSE;
1670     gboolean ret = FALSE;
1671 
1672     if (!edit_check_newline (&edit->buffer))
1673         return FALSE;
1674 
1675     exp_vpath = edit_get_save_file_as (edit);
1676     edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
1677 
1678     if (exp_vpath != NULL && vfs_path_len (exp_vpath) != 0)
1679     {
1680         int rv;
1681 
1682         if (!vfs_path_equal (edit->filename_vpath, exp_vpath))
1683         {
1684             int file;
1685             struct stat sb;
1686 
1687             if (mc_stat (exp_vpath, &sb) == 0 && !S_ISREG (sb.st_mode))
1688             {
1689                 edit_error_dialog (_("Save as"),
1690                                    get_sys_error (_
1691                                                   ("Cannot save: destination is not a regular file")));
1692                 goto ret;
1693             }
1694 
1695             different_filename = TRUE;
1696             file = mc_open (exp_vpath, O_RDONLY | O_BINARY);
1697 
1698             if (file == -1)
1699                 edit->stat1.st_mode |= S_IWUSR;
1700             else
1701             {
1702                 /* the file exists */
1703                 mc_close (file);
1704                 /* Overwrite the current file or cancel the operation */
1705                 if (edit_query_dialog2
1706                     (_("Warning"),
1707                      _("A file already exists with this name"), _("&Overwrite"), _("&Cancel")))
1708                     goto ret;
1709             }
1710 
1711             save_lock = lock_file (exp_vpath);
1712         }
1713         else if (!edit->locked && !edit->delete_file)
1714             /* filenames equal, check if already locked */
1715             save_lock = lock_file (exp_vpath);
1716 
1717         if (different_filename)
1718             /* Allow user to write into saved (under another name) file
1719              * even if original file had r/o user permissions. */
1720             edit->stat1.st_mode |= S_IWRITE;
1721 
1722         rv = edit_save_file (edit, exp_vpath);
1723         switch (rv)
1724         {
1725         case 1:
1726             /* Successful, so unlock both files */
1727             if (different_filename)
1728             {
1729                 if (save_lock)
1730                     unlock_file (exp_vpath);
1731                 if (edit->locked)
1732                     edit->locked = unlock_file (edit->filename_vpath);
1733             }
1734             else if (edit->locked || save_lock)
1735                 edit->locked = unlock_file (edit->filename_vpath);
1736 
1737             edit_set_filename (edit, exp_vpath);
1738             if (edit->lb != LB_ASIS)
1739                 edit_reload (edit, exp_vpath);
1740             edit->modified = 0;
1741             edit->delete_file = 0;
1742             if (different_filename)
1743                 edit_load_syntax (edit, NULL, edit->syntax_type);
1744             ret = TRUE;
1745             break;
1746 
1747         default:
1748             edit_error_dialog (_("Save as"), get_sys_error (_("Cannot save file")));
1749             MC_FALLTHROUGH;
1750 
1751         case -1:
1752             /* Failed, so maintain modify (not save) lock */
1753             if (save_lock)
1754                 unlock_file (exp_vpath);
1755             break;
1756         }
1757     }
1758 
1759   ret:
1760     vfs_path_free (exp_vpath);
1761     edit->force |= REDRAW_COMPLETELY;
1762     return ret;
1763 }
1764 
1765 /* {{{ Macro stuff starts here */
1766 /* --------------------------------------------------------------------------------------------- */
1767 
1768 void
1769 edit_delete_macro_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1770 {
1771     int hotkey;
1772 
1773     hotkey = editcmd_dialog_raw_key_query (_("Delete macro"), _("Press macro hotkey:"), TRUE);
1774 
1775     if (hotkey != 0 && !edit_delete_macro (edit, hotkey))
1776         message (D_ERROR, _("Delete macro"), _("Macro not deleted"));
1777 }
1778 
1779 /* --------------------------------------------------------------------------------------------- */
1780 
1781 /** returns FALSE on error */
1782 gboolean
1783 edit_execute_macro (WEdit * edit, int hotkey)
     /* [previous][next][first][last][top][bottom][index][help]  */
1784 {
1785     gboolean res = FALSE;
1786 
1787     if (hotkey != 0)
1788     {
1789         const macros_t *macros;
1790         guint indx;
1791 
1792         if (edit_get_macro (edit, hotkey, &macros, &indx) &&
1793             macros->macro != NULL && macros->macro->len != 0)
1794         {
1795             guint i;
1796 
1797             edit->force |= REDRAW_PAGE;
1798 
1799             for (i = 0; i < macros->macro->len; i++)
1800             {
1801                 const macro_action_t *m_act;
1802 
1803                 m_act = &g_array_index (macros->macro, struct macro_action_t, i);
1804                 edit_execute_cmd (edit, m_act->action, m_act->ch);
1805                 res = TRUE;
1806             }
1807         }
1808     }
1809 
1810     return res;
1811 }
1812 
1813 /* --------------------------------------------------------------------------------------------- */
1814 
1815 /** returns FALSE on error */
1816 gboolean
1817 edit_store_macro_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1818 {
1819     int i;
1820     int hotkey;
1821     GString *marcros_string;
1822     mc_config_t *macros_config = NULL;
1823     const char *section_name = "editor";
1824     gchar *macros_fname;
1825     GArray *macros;             /* current macro */
1826     int tmp_act;
1827     gboolean have_macro = FALSE;
1828     char *skeyname = NULL;
1829 
1830     hotkey =
1831         editcmd_dialog_raw_key_query (_("Save macro"), _("Press the macro's new hotkey:"), TRUE);
1832     if (hotkey == ESC_CHAR)
1833         return FALSE;
1834 
1835     tmp_act = keybind_lookup_keymap_command (WIDGET (edit)->keymap, hotkey);
1836 
1837     /* return FALSE if try assign macro into restricted hotkeys */
1838     if (tmp_act == CK_MacroStartRecord
1839         || tmp_act == CK_MacroStopRecord || tmp_act == CK_MacroStartStopRecord)
1840         return FALSE;
1841 
1842     edit_delete_macro (edit, hotkey);
1843 
1844     macros_fname = mc_config_get_full_path (MC_MACRO_FILE);
1845     macros_config = mc_config_init (macros_fname, FALSE);
1846     g_free (macros_fname);
1847 
1848     if (macros_config == NULL)
1849         return FALSE;
1850 
1851     edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
1852 
1853     marcros_string = g_string_sized_new (250);
1854     macros = g_array_new (TRUE, FALSE, sizeof (macro_action_t));
1855 
1856     skeyname = lookup_key_by_code (hotkey);
1857 
1858     for (i = 0; i < macro_index; i++)
1859     {
1860         macro_action_t m_act;
1861         const char *action_name;
1862 
1863         action_name = keybind_lookup_actionname (record_macro_buf[i].action);
1864 
1865         if (action_name == NULL)
1866             break;
1867 
1868         m_act.action = record_macro_buf[i].action;
1869         m_act.ch = record_macro_buf[i].ch;
1870         g_array_append_val (macros, m_act);
1871         have_macro = TRUE;
1872         g_string_append_printf (marcros_string, "%s:%i;", action_name,
1873                                 (int) record_macro_buf[i].ch);
1874     }
1875 
1876     if (!have_macro)
1877         mc_config_del_key (macros_config, section_name, skeyname);
1878     else
1879     {
1880         macros_t macro;
1881 
1882         macro.hotkey = hotkey;
1883         macro.macro = macros;
1884         g_array_append_val (macros_list, macro);
1885         mc_config_set_string (macros_config, section_name, skeyname, marcros_string->str);
1886     }
1887 
1888     g_free (skeyname);
1889     edit_macro_sort_by_hotkey ();
1890 
1891     g_string_free (marcros_string, TRUE);
1892     mc_config_save_file (macros_config, NULL);
1893     mc_config_deinit (macros_config);
1894     return TRUE;
1895 }
1896 
1897  /* --------------------------------------------------------------------------------------------- */
1898 
1899 gboolean
1900 edit_repeat_macro_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1901 {
1902     int i, j;
1903     char *f;
1904     long count_repeat;
1905     char *error = NULL;
1906 
1907     f = input_dialog (_("Repeat last commands"), _("Repeat times:"), MC_HISTORY_EDIT_REPEAT, NULL,
1908                       INPUT_COMPLETE_NONE);
1909     if (f == NULL || *f == '\0')
1910     {
1911         g_free (f);
1912         return FALSE;
1913     }
1914 
1915     count_repeat = strtol (f, &error, 0);
1916 
1917     if (*error != '\0')
1918     {
1919         g_free (f);
1920         return FALSE;
1921     }
1922 
1923     g_free (f);
1924 
1925     edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
1926     edit->force |= REDRAW_PAGE;
1927 
1928     for (j = 0; j < count_repeat; j++)
1929         for (i = 0; i < macro_index; i++)
1930             edit_execute_cmd (edit, record_macro_buf[i].action, record_macro_buf[i].ch);
1931 
1932     edit_update_screen (edit);
1933     return TRUE;
1934 }
1935 
1936 /* --------------------------------------------------------------------------------------------- */
1937 /** return FALSE on error */
1938 
1939 gboolean
1940 edit_load_macro_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1941 {
1942     mc_config_t *macros_config = NULL;
1943     gchar **profile_keys, **keys;
1944     gchar **values, **curr_values;
1945     const char *section_name = "editor";
1946     gchar *macros_fname;
1947 
1948     (void) edit;
1949 
1950     if (macros_list == NULL || macros_list->len != 0)
1951         return FALSE;
1952 
1953     macros_fname = mc_config_get_full_path (MC_MACRO_FILE);
1954     macros_config = mc_config_init (macros_fname, TRUE);
1955     g_free (macros_fname);
1956 
1957     if (macros_config == NULL)
1958         return FALSE;
1959 
1960     keys = mc_config_get_keys (macros_config, section_name, NULL);
1961 
1962     for (profile_keys = keys; *profile_keys != NULL; profile_keys++)
1963     {
1964         int hotkey;
1965         gboolean have_macro = FALSE;
1966         GArray *macros;
1967         macros_t macro;
1968 
1969         macros = g_array_new (TRUE, FALSE, sizeof (macro_action_t));
1970         values = mc_config_get_string_list (macros_config, section_name, *profile_keys, NULL);
1971         hotkey = lookup_key (*profile_keys, NULL);
1972 
1973         for (curr_values = values; *curr_values != NULL && *curr_values[0] != '\0'; curr_values++)
1974         {
1975             char **macro_pair = NULL;
1976 
1977             macro_pair = g_strsplit (*curr_values, ":", 2);
1978             if (macro_pair != NULL)
1979             {
1980                 macro_action_t m_act;
1981                 if (macro_pair[0] == NULL || macro_pair[0][0] == '\0')
1982                     m_act.action = 0;
1983                 else
1984                 {
1985                     m_act.action = keybind_lookup_action (macro_pair[0]);
1986                     MC_PTR_FREE (macro_pair[0]);
1987                 }
1988                 if (macro_pair[1] == NULL || macro_pair[1][0] == '\0')
1989                     m_act.ch = -1;
1990                 else
1991                 {
1992                     m_act.ch = strtol (macro_pair[1], NULL, 0);
1993                     MC_PTR_FREE (macro_pair[1]);
1994                 }
1995                 if (m_act.action != 0)
1996                 {
1997                     /* a shell command */
1998                     if ((m_act.action / CK_PipeBlock (0)) == 1)
1999                     {
2000                         m_act.action = CK_PipeBlock (0) + (m_act.ch > 0 ? m_act.ch : 0);
2001                         m_act.ch = -1;
2002                     }
2003                     g_array_append_val (macros, m_act);
2004                     have_macro = TRUE;
2005                 }
2006                 g_strfreev (macro_pair);
2007                 macro_pair = NULL;
2008             }
2009         }
2010 
2011         if (have_macro)
2012         {
2013             macro.hotkey = hotkey;
2014             macro.macro = macros;
2015             g_array_append_val (macros_list, macro);
2016         }
2017 
2018         g_strfreev (values);
2019     }
2020 
2021     g_strfreev (keys);
2022     mc_config_deinit (macros_config);
2023     edit_macro_sort_by_hotkey ();
2024 
2025     return TRUE;
2026 }
2027 
2028 /* }}} Macro stuff end here */
2029 
2030 /* --------------------------------------------------------------------------------------------- */
2031 /** returns TRUE on success */
2032 
2033 gboolean
2034 edit_save_confirm_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2035 {
2036     if (edit->filename_vpath == NULL)
2037         return edit_save_as_cmd (edit);
2038 
2039     if (!edit_check_newline (&edit->buffer))
2040         return FALSE;
2041 
2042     if (edit_confirm_save)
2043     {
2044         char *f;
2045         gboolean ok;
2046 
2047         f = g_strdup_printf (_("Confirm save file: \"%s\""),
2048                              vfs_path_as_str (edit->filename_vpath));
2049         ok = (edit_query_dialog2 (_("Save file"), f, _("&Save"), _("&Cancel")) == 0);
2050         g_free (f);
2051         if (!ok)
2052             return FALSE;
2053     }
2054 
2055     return edit_save_cmd (edit);
2056 }
2057 
2058 /* --------------------------------------------------------------------------------------------- */
2059 /**
2060   * Ask file to edit and load it.
2061   *
2062   * @return TRUE on success or cancel of ask.
2063   */
2064 
2065 gboolean
2066 edit_load_cmd (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
2067 {
2068     char *exp;
2069     gboolean ret = TRUE;        /* possible cancel */
2070 
2071     exp = input_expand_dialog (_("Load"), _("Enter file name:"),
2072                                MC_HISTORY_EDIT_LOAD, INPUT_LAST_TEXT,
2073                                INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD);
2074 
2075     if (exp != NULL && *exp != '\0')
2076     {
2077         vfs_path_t *exp_vpath;
2078 
2079         exp_vpath = vfs_path_from_str (exp);
2080         ret = edit_load_file_from_filename (h, exp_vpath);
2081         vfs_path_free (exp_vpath);
2082     }
2083 
2084     g_free (exp);
2085 
2086     return ret;
2087 }
2088 
2089 /* --------------------------------------------------------------------------------------------- */
2090 /**
2091   * Show history od edited or viewed files and open selected file.
2092   *
2093   * @return TRUE on success, FALSE otherwise.
2094   */
2095 
2096 gboolean
2097 edit_load_file_from_history (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
2098 {
2099     char *exp;
2100     int action;
2101     gboolean ret = TRUE;        /* possible cancel */
2102 
2103     exp = show_file_history (CONST_WIDGET (h), &action);
2104     if (exp != NULL && (action == CK_Edit || action == CK_Enter))
2105     {
2106         vfs_path_t *exp_vpath;
2107 
2108         exp_vpath = vfs_path_from_str (exp);
2109         ret = edit_load_file_from_filename (h, exp_vpath);
2110         vfs_path_free (exp_vpath);
2111     }
2112 
2113     g_free (exp);
2114 
2115     return ret;
2116 }
2117 
2118 /* --------------------------------------------------------------------------------------------- */
2119 /**
2120   * Load syntax file to edit.
2121   *
2122   * @return TRUE on success
2123   */
2124 
2125 gboolean
2126 edit_load_syntax_file (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
2127 {
2128     vfs_path_t *extdir_vpath;
2129     int dir = 0;
2130     gboolean ret = FALSE;
2131 
2132     if (geteuid () == 0)
2133         dir = query_dialog (_("Syntax file edit"),
2134                             _("Which syntax file you want to edit?"), D_NORMAL, 2,
2135                             _("&User"), _("&System wide"));
2136 
2137     extdir_vpath =
2138         vfs_path_build_filename (mc_global.sysconfig_dir, "syntax", "Syntax", (char *) NULL);
2139     if (!exist_file (vfs_path_get_last_path_str (extdir_vpath)))
2140     {
2141         vfs_path_free (extdir_vpath);
2142         extdir_vpath =
2143             vfs_path_build_filename (mc_global.share_data_dir, "syntax", "Syntax", (char *) NULL);
2144     }
2145 
2146     if (dir == 0)
2147     {
2148         vfs_path_t *user_syntax_file_vpath;
2149 
2150         user_syntax_file_vpath = mc_config_get_full_vpath (EDIT_HOME_SYNTAX_FILE);
2151         check_for_default (extdir_vpath, user_syntax_file_vpath);
2152         ret = edit_load_file_from_filename (h, user_syntax_file_vpath);
2153         vfs_path_free (user_syntax_file_vpath);
2154     }
2155     else if (dir == 1)
2156         ret = edit_load_file_from_filename (h, extdir_vpath);
2157 
2158     vfs_path_free (extdir_vpath);
2159 
2160     return ret;
2161 }
2162 
2163 /* --------------------------------------------------------------------------------------------- */
2164 /**
2165   * Load menu file to edit.
2166   *
2167   * @return TRUE on success
2168   */
2169 
2170 gboolean
2171 edit_load_menu_file (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
2172 {
2173     vfs_path_t *buffer_vpath;
2174     vfs_path_t *menufile_vpath;
2175     int dir;
2176     gboolean ret;
2177 
2178     query_set_sel (1);
2179     dir = query_dialog (_("Menu edit"),
2180                         _("Which menu file do you want to edit?"), D_NORMAL,
2181                         geteuid () != 0 ? 2 : 3, _("&Local"), _("&User"), _("&System wide"));
2182 
2183     menufile_vpath =
2184         vfs_path_build_filename (mc_global.sysconfig_dir, EDIT_GLOBAL_MENU, (char *) NULL);
2185     if (!exist_file (vfs_path_get_last_path_str (menufile_vpath)))
2186     {
2187         vfs_path_free (menufile_vpath);
2188         menufile_vpath =
2189             vfs_path_build_filename (mc_global.share_data_dir, EDIT_GLOBAL_MENU, (char *) NULL);
2190     }
2191 
2192     switch (dir)
2193     {
2194     case 0:
2195         buffer_vpath = vfs_path_from_str (EDIT_LOCAL_MENU);
2196         check_for_default (menufile_vpath, buffer_vpath);
2197         chmod (vfs_path_get_last_path_str (buffer_vpath), 0600);
2198         break;
2199 
2200     case 1:
2201         buffer_vpath = mc_config_get_full_vpath (EDIT_HOME_MENU);
2202         check_for_default (menufile_vpath, buffer_vpath);
2203         break;
2204 
2205     case 2:
2206         buffer_vpath =
2207             vfs_path_build_filename (mc_global.sysconfig_dir, EDIT_GLOBAL_MENU, (char *) NULL);
2208         if (!exist_file (vfs_path_get_last_path_str (buffer_vpath)))
2209         {
2210             vfs_path_free (buffer_vpath);
2211             buffer_vpath =
2212                 vfs_path_build_filename (mc_global.share_data_dir, EDIT_GLOBAL_MENU, (char *) NULL);
2213         }
2214         break;
2215 
2216     default:
2217         vfs_path_free (menufile_vpath);
2218         return FALSE;
2219     }
2220 
2221     ret = edit_load_file_from_filename (h, buffer_vpath);
2222 
2223     vfs_path_free (buffer_vpath);
2224     vfs_path_free (menufile_vpath);
2225 
2226     return ret;
2227 }
2228 
2229 /* --------------------------------------------------------------------------------------------- */
2230 /**
2231   * Close window with opened file.
2232   *
2233   * @return TRUE if file was closed.
2234   */
2235 
2236 gboolean
2237 edit_close_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2238 {
2239     gboolean ret;
2240 
2241     ret = (edit != NULL) && edit_ok_to_exit (edit);
2242 
2243     if (ret)
2244     {
2245         Widget *w = WIDGET (edit);
2246         WGroup *g = w->owner;
2247 
2248         if (edit->locked != 0)
2249             unlock_file (edit->filename_vpath);
2250 
2251         group_remove_widget (w);
2252         widget_destroy (w);
2253 
2254         if (edit_widget_is_editor (CONST_WIDGET (g->current->data)))
2255             edit = (WEdit *) (g->current->data);
2256         else
2257         {
2258             edit = find_editor (DIALOG (g));
2259             if (edit != NULL)
2260                 widget_select (w);
2261         }
2262     }
2263 
2264     if (edit != NULL)
2265         edit->force |= REDRAW_COMPLETELY;
2266 
2267     return ret;
2268 }
2269 
2270 /* --------------------------------------------------------------------------------------------- */
2271 /**
2272    if mark2 is -1 then marking is from mark1 to the cursor.
2273    Otherwise its between the markers. This handles this.
2274    Returns FALSE if no text is marked.
2275  */
2276 
2277 gboolean
2278 eval_marks (WEdit * edit, off_t * start_mark, off_t * end_mark)
     /* [previous][next][first][last][top][bottom][index][help]  */
2279 {
2280     long end_mark_curs;
2281 
2282     if (edit->mark1 == edit->mark2)
2283     {
2284         *start_mark = *end_mark = 0;
2285         edit->column2 = edit->column1 = 0;
2286         return FALSE;
2287     }
2288 
2289     if (edit->end_mark_curs < 0)
2290         end_mark_curs = edit->buffer.curs1;
2291     else
2292         end_mark_curs = edit->end_mark_curs;
2293 
2294     if (edit->mark2 >= 0)
2295     {
2296         *start_mark = MIN (edit->mark1, edit->mark2);
2297         *end_mark = MAX (edit->mark1, edit->mark2);
2298     }
2299     else
2300     {
2301         *start_mark = MIN (edit->mark1, end_mark_curs);
2302         *end_mark = MAX (edit->mark1, end_mark_curs);
2303         edit->column2 = edit->curs_col + edit->over_col;
2304     }
2305 
2306     if (edit->column_highlight
2307         && ((edit->mark1 > end_mark_curs && edit->column1 < edit->column2)
2308             || (edit->mark1 < end_mark_curs && edit->column1 > edit->column2)))
2309     {
2310         off_t start_bol, start_eol;
2311         off_t end_bol, end_eol;
2312         long col1, col2;
2313         off_t diff1, diff2;
2314 
2315         start_bol = edit_buffer_get_bol (&edit->buffer, *start_mark);
2316         start_eol = edit_buffer_get_eol (&edit->buffer, start_bol - 1) + 1;
2317         end_bol = edit_buffer_get_bol (&edit->buffer, *end_mark);
2318         end_eol = edit_buffer_get_eol (&edit->buffer, *end_mark);
2319         col1 = MIN (edit->column1, edit->column2);
2320         col2 = MAX (edit->column1, edit->column2);
2321 
2322         diff1 = edit_move_forward3 (edit, start_bol, col2, 0) -
2323             edit_move_forward3 (edit, start_bol, col1, 0);
2324         diff2 = edit_move_forward3 (edit, end_bol, col2, 0) -
2325             edit_move_forward3 (edit, end_bol, col1, 0);
2326 
2327         *start_mark -= diff1;
2328         *end_mark += diff2;
2329         *start_mark = MAX (*start_mark, start_eol);
2330         *end_mark = MIN (*end_mark, end_eol);
2331     }
2332 
2333     return TRUE;
2334 }
2335 
2336 /* --------------------------------------------------------------------------------------------- */
2337 
2338 void
2339 edit_block_copy_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2340 {
2341     off_t start_mark, end_mark, current = edit->buffer.curs1;
2342     off_t mark1 = 0, mark2 = 0;
2343     long c1 = 0, c2 = 0;
2344     off_t size;
2345     unsigned char *copy_buf;
2346 
2347     edit_update_curs_col (edit);
2348     if (!eval_marks (edit, &start_mark, &end_mark))
2349         return;
2350 
2351     copy_buf = edit_get_block (edit, start_mark, end_mark, &size);
2352 
2353     /* all that gets pushed are deletes hence little space is used on the stack */
2354 
2355     edit_push_markers (edit);
2356 
2357     if (edit->column_highlight)
2358     {
2359         long col_delta;
2360 
2361         col_delta = labs (edit->column2 - edit->column1);
2362         edit_insert_column_of_text (edit, copy_buf, size, col_delta, &mark1, &mark2, &c1, &c2);
2363     }
2364     else
2365     {
2366         int size_orig = size;
2367 
2368         while (size-- != 0)
2369             edit_insert_ahead (edit, copy_buf[size]);
2370 
2371         /* Place cursor at the end of text selection */
2372         if (option_cursor_after_inserted_block)
2373             edit_cursor_move (edit, size_orig);
2374     }
2375 
2376     g_free (copy_buf);
2377     edit_scroll_screen_over_cursor (edit);
2378 
2379     if (edit->column_highlight)
2380         edit_set_markers (edit, edit->buffer.curs1, mark2, c1, c2);
2381     else if (start_mark < current && end_mark > current)
2382         edit_set_markers (edit, start_mark, end_mark + end_mark - start_mark, 0, 0);
2383 
2384     edit->force |= REDRAW_PAGE;
2385 }
2386 
2387 
2388 /* --------------------------------------------------------------------------------------------- */
2389 
2390 void
2391 edit_block_move_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2392 {
2393     off_t current;
2394     unsigned char *copy_buf = NULL;
2395     off_t start_mark, end_mark;
2396 
2397     if (!eval_marks (edit, &start_mark, &end_mark))
2398         return;
2399 
2400     if (!edit->column_highlight && edit->buffer.curs1 > start_mark && edit->buffer.curs1 < end_mark)
2401         return;
2402 
2403     if (edit->mark2 < 0)
2404         edit_mark_cmd (edit, FALSE);
2405     edit_push_markers (edit);
2406 
2407     if (edit->column_highlight)
2408     {
2409         off_t mark1, mark2;
2410         off_t size;
2411         long c1, c2, b_width;
2412         long x, x2;
2413 
2414         c1 = MIN (edit->column1, edit->column2);
2415         c2 = MAX (edit->column1, edit->column2);
2416         b_width = c2 - c1;
2417 
2418         edit_update_curs_col (edit);
2419 
2420         x = edit->curs_col;
2421         x2 = x + edit->over_col;
2422 
2423         /* do nothing when cursor inside first line of selected area */
2424         if ((edit_buffer_get_eol (&edit->buffer, edit->buffer.curs1) ==
2425              edit_buffer_get_eol (&edit->buffer, start_mark)) && x2 > c1 && x2 <= c2)
2426             return;
2427 
2428         if (edit->buffer.curs1 > start_mark
2429             && edit->buffer.curs1 < edit_buffer_get_eol (&edit->buffer, end_mark))
2430         {
2431             if (x > c2)
2432                 x -= b_width;
2433             else if (x > c1 && x <= c2)
2434                 x = c1;
2435         }
2436         /* save current selection into buffer */
2437         copy_buf = edit_get_block (edit, start_mark, end_mark, &size);
2438 
2439         /* remove current selection */
2440         edit_block_delete_cmd (edit);
2441 
2442         edit->over_col = MAX (0, edit->over_col - b_width);
2443         /* calculate the cursor pos after delete block */
2444         current = edit_move_forward3 (edit, edit_buffer_get_current_bol (&edit->buffer), x, 0);
2445         edit_cursor_move (edit, current - edit->buffer.curs1);
2446         edit_scroll_screen_over_cursor (edit);
2447 
2448         /* add TWS if need before block insertion */
2449         if (option_cursor_beyond_eol && edit->over_col > 0)
2450             edit_insert_over (edit);
2451 
2452         edit_insert_column_of_text (edit, copy_buf, size, b_width, &mark1, &mark2, &c1, &c2);
2453         edit_set_markers (edit, mark1, mark2, c1, c2);
2454     }
2455     else
2456     {
2457         off_t count, count_orig;
2458 
2459         current = edit->buffer.curs1;
2460         copy_buf = g_malloc0 (end_mark - start_mark);
2461         edit_cursor_move (edit, start_mark - edit->buffer.curs1);
2462         edit_scroll_screen_over_cursor (edit);
2463 
2464         for (count = start_mark; count < end_mark; count++)
2465             copy_buf[end_mark - count - 1] = edit_delete (edit, TRUE);
2466 
2467         edit_scroll_screen_over_cursor (edit);
2468         edit_cursor_move (edit,
2469                           current - edit->buffer.curs1 -
2470                           (((current - edit->buffer.curs1) > 0) ? end_mark - start_mark : 0));
2471         edit_scroll_screen_over_cursor (edit);
2472         count_orig = count;
2473         while (count-- > start_mark)
2474             edit_insert_ahead (edit, copy_buf[end_mark - count - 1]);
2475 
2476         edit_set_markers (edit, edit->buffer.curs1, edit->buffer.curs1 + end_mark - start_mark, 0,
2477                           0);
2478 
2479         /* Place cursor at the end of text selection */
2480         if (option_cursor_after_inserted_block)
2481             edit_cursor_move (edit, count_orig - start_mark);
2482     }
2483 
2484     edit_scroll_screen_over_cursor (edit);
2485     g_free (copy_buf);
2486     edit->force |= REDRAW_PAGE;
2487 }
2488 
2489 /* --------------------------------------------------------------------------------------------- */
2490 /** returns 1 if canceelled by user */
2491 
2492 int
2493 edit_block_delete_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2494 {
2495     off_t start_mark, end_mark;
2496 
2497     if (eval_marks (edit, &start_mark, &end_mark))
2498         return edit_block_delete (edit);
2499 
2500     edit_delete_line (edit);
2501 
2502     return 0;
2503 }
2504 
2505 /* --------------------------------------------------------------------------------------------- */
2506 /** call with edit = 0 before shutdown to close memory leaks */
2507 
2508 void
2509 edit_replace_cmd (WEdit * edit, gboolean again)
     /* [previous][next][first][last][top][bottom][index][help]  */
2510 {
2511     /* 1 = search string, 2 = replace with */
2512     static char *saved1 = NULL; /* saved default[123] */
2513     static char *saved2 = NULL;
2514     char *input1 = NULL;        /* user input from the dialog */
2515     char *input2 = NULL;
2516     GString *input2_str = NULL;
2517     char *disp1 = NULL;
2518     char *disp2 = NULL;
2519     long times_replaced = 0;
2520     gboolean once_found = FALSE;
2521     edit_search_status_msg_t esm;
2522 
2523     if (edit == NULL)
2524     {
2525         MC_PTR_FREE (saved1);
2526         MC_PTR_FREE (saved2);
2527         return;
2528     }
2529 
2530     edit->force |= REDRAW_COMPLETELY;
2531 
2532     if (again && saved1 == NULL && saved2 == NULL)
2533         again = FALSE;
2534 
2535     if (again)
2536     {
2537         input1 = g_strdup (saved1 != NULL ? saved1 : "");
2538         input2 = g_strdup (saved2 != NULL ? saved2 : "");
2539     }
2540     else
2541     {
2542         char *tmp_inp1, *tmp_inp2;
2543 
2544         disp1 = edit_replace_cmd__conv_to_display (saved1 != NULL ? saved1 : "");
2545         disp2 = edit_replace_cmd__conv_to_display (saved2 != NULL ? saved2 : "");
2546 
2547         edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
2548 
2549         editcmd_dialog_replace_show (edit, disp1, disp2, &input1, &input2);
2550 
2551         g_free (disp1);
2552         g_free (disp2);
2553 
2554         if (input1 == NULL || *input1 == '\0')
2555         {
2556             edit->force = REDRAW_COMPLETELY;
2557             goto cleanup;
2558         }
2559 
2560         tmp_inp1 = input1;
2561         tmp_inp2 = input2;
2562         input1 = edit_replace_cmd__conv_to_input (input1);
2563         input2 = edit_replace_cmd__conv_to_input (input2);
2564         g_free (tmp_inp1);
2565         g_free (tmp_inp2);
2566 
2567         g_free (saved1);
2568         saved1 = g_strdup (input1);
2569         g_free (saved2);
2570         saved2 = g_strdup (input2);
2571 
2572         mc_search_free (edit->search);
2573         edit->search = NULL;
2574     }
2575 
2576     input2_str = g_string_new (input2);
2577 
2578     if (edit->search == NULL)
2579     {
2580 #ifdef HAVE_CHARSET
2581         edit->search = mc_search_new (input1, cp_source);
2582 #else
2583         edit->search = mc_search_new (input1, NULL);
2584 #endif
2585         if (edit->search == NULL)
2586         {
2587             edit->search_start = edit->buffer.curs1;
2588             goto cleanup;
2589         }
2590         edit->search->search_type = edit_search_options.type;
2591 #ifdef HAVE_CHARSET
2592         edit->search->is_all_charsets = edit_search_options.all_codepages;
2593 #endif
2594         edit->search->is_case_sensitive = edit_search_options.case_sens;
2595         edit->search->whole_words = edit_search_options.whole_words;
2596         edit->search->search_fn = edit_search_cmd_callback;
2597         edit->search->update_fn = edit_search_update_callback;
2598         edit->search_line_type = edit_get_search_line_type (edit->search);
2599         edit_search_fix_search_start_if_selection (edit);
2600     }
2601 
2602     if (edit->found_len != 0 && edit->search_start == edit->found_start + 1
2603         && edit_search_options.backwards)
2604         edit->search_start--;
2605 
2606     if (edit->found_len != 0 && edit->search_start == edit->found_start - 1
2607         && !edit_search_options.backwards)
2608         edit->search_start++;
2609 
2610     esm.first = TRUE;
2611     esm.edit = edit;
2612     esm.offset = edit->search_start;
2613 
2614     status_msg_init (STATUS_MSG (&esm), _("Search"), 1.0, simple_status_msg_init_cb,
2615                      edit_search_status_update_cb, NULL);
2616 
2617     do
2618     {
2619         gsize len = 0;
2620 
2621         if (!editcmd_find (&esm, &len))
2622         {
2623             if (!(edit->search->error == MC_SEARCH_E_OK ||
2624                   (once_found && edit->search->error == MC_SEARCH_E_NOTFOUND)))
2625                 edit_show_search_error (edit, _("Search"));
2626             break;
2627         }
2628 
2629         once_found = TRUE;
2630 
2631         edit->search_start = edit->search->normal_offset;
2632         /* returns negative on not found or error in pattern */
2633 
2634         if (edit->search_start >= 0 && edit->search_start < edit->buffer.size)
2635         {
2636             gsize i;
2637             GString *repl_str;
2638 
2639             edit->found_start = edit->search_start;
2640             i = edit->found_len = len;
2641 
2642             edit_cursor_move (edit, edit->search_start - edit->buffer.curs1);
2643             edit_scroll_screen_over_cursor (edit);
2644 
2645             if (edit->replace_mode == 0)
2646             {
2647                 long l;
2648                 int prompt;
2649 
2650                 l = edit->curs_row - WIDGET (edit)->lines / 3;
2651                 if (l > 0)
2652                     edit_scroll_downward (edit, l);
2653                 if (l < 0)
2654                     edit_scroll_upward (edit, -l);
2655 
2656                 edit_scroll_screen_over_cursor (edit);
2657                 edit->force |= REDRAW_PAGE;
2658                 edit_render_keypress (edit);
2659 
2660                 /*so that undo stops at each query */
2661                 edit_push_key_press (edit);
2662                 /* and prompt 2/3 down */
2663                 disp1 = edit_replace_cmd__conv_to_display (saved1);
2664                 disp2 = edit_replace_cmd__conv_to_display (saved2);
2665                 prompt = editcmd_dialog_replace_prompt_show (edit, disp1, disp2, -1, -1);
2666                 g_free (disp1);
2667                 g_free (disp2);
2668 
2669                 if (prompt == B_REPLACE_ALL)
2670                     edit->replace_mode = 1;
2671                 else if (prompt == B_SKIP_REPLACE)
2672                 {
2673                     if (edit_search_options.backwards)
2674                         edit->search_start--;
2675                     else
2676                         edit->search_start++;
2677                     continue;   /* loop */
2678                 }
2679                 else if (prompt == B_CANCEL)
2680                 {
2681                     edit->replace_mode = -1;
2682                     break;      /* loop */
2683                 }
2684             }
2685 
2686             repl_str = mc_search_prepare_replace_str (edit->search, input2_str);
2687 
2688             if (edit->search->error != MC_SEARCH_E_OK)
2689             {
2690                 edit_show_search_error (edit, _("Replace"));
2691                 if (repl_str != NULL)
2692                     g_string_free (repl_str, TRUE);
2693                 break;
2694             }
2695 
2696             /* delete then insert new */
2697             for (i = 0; i < len; i++)
2698                 edit_delete (edit, TRUE);
2699 
2700             for (i = 0; i < repl_str->len; i++)
2701                 edit_insert (edit, repl_str->str[i]);
2702 
2703             edit->found_len = repl_str->len;
2704             g_string_free (repl_str, TRUE);
2705             times_replaced++;
2706 
2707             /* so that we don't find the same string again */
2708             if (edit_search_options.backwards)
2709                 edit->search_start--;
2710             else
2711             {
2712                 edit->search_start += edit->found_len + (len == 0 ? 1 : 0);
2713 
2714                 if (edit->search_start >= edit->buffer.size)
2715                     break;
2716             }
2717 
2718             edit_scroll_screen_over_cursor (edit);
2719         }
2720         else
2721         {
2722             /* try and find from right here for next search */
2723             edit->search_start = edit->buffer.curs1;
2724             edit_update_curs_col (edit);
2725 
2726             edit->force |= REDRAW_PAGE;
2727             edit_render_keypress (edit);
2728 
2729             if (times_replaced == 0)
2730                 query_dialog (_("Replace"), _(STR_E_NOTFOUND), D_NORMAL, 1, _("&OK"));
2731             break;
2732         }
2733     }
2734     while (edit->replace_mode >= 0);
2735 
2736     status_msg_deinit (STATUS_MSG (&esm));
2737     edit_scroll_screen_over_cursor (edit);
2738     edit->force |= REDRAW_COMPLETELY;
2739     edit_render_keypress (edit);
2740 
2741     if (edit->replace_mode == 1 && times_replaced != 0)
2742         message (D_NORMAL, _("Replace"), _("%ld replacements made"), times_replaced);
2743 
2744   cleanup:
2745     g_free (input1);
2746     g_free (input2);
2747     if (input2_str != NULL)
2748         g_string_free (input2_str, TRUE);
2749 }
2750 
2751 /* --------------------------------------------------------------------------------------------- */
2752 
2753 mc_search_cbret_t
2754 edit_search_cmd_callback (const void *user_data, gsize char_offset, int *current_char)
     /* [previous][next][first][last][top][bottom][index][help]  */
2755 {
2756     WEdit *edit = ((const edit_search_status_msg_t *) user_data)->edit;
2757 
2758     *current_char = edit_buffer_get_byte (&edit->buffer, (off_t) char_offset);
2759 
2760     return MC_SEARCH_CB_OK;
2761 }
2762 
2763 /* --------------------------------------------------------------------------------------------- */
2764 
2765 mc_search_cbret_t
2766 edit_search_update_callback (const void *user_data, gsize char_offset)
     /* [previous][next][first][last][top][bottom][index][help]  */
2767 {
2768     status_msg_t *sm = STATUS_MSG (user_data);
2769 
2770     ((edit_search_status_msg_t *) sm)->offset = (off_t) char_offset;
2771 
2772     return (sm->update (sm) == B_CANCEL ? MC_SEARCH_CB_ABORT : MC_SEARCH_CB_OK);
2773 }
2774 
2775 /* --------------------------------------------------------------------------------------------- */
2776 
2777 void
2778 edit_search_cmd (WEdit * edit, gboolean again)
     /* [previous][next][first][last][top][bottom][index][help]  */
2779 {
2780     if (edit == NULL)
2781         return;
2782 
2783     if (!again)
2784         edit_search (edit);
2785     else if (edit->last_search_string != NULL)
2786         edit_do_search (edit);
2787     else
2788     {
2789         /* find last search string in history */
2790         GList *history;
2791 
2792         history = mc_config_history_get (MC_HISTORY_SHARED_SEARCH);
2793         if (history != NULL && history->data != NULL)
2794         {
2795             edit->last_search_string = (char *) history->data;
2796             history->data = NULL;
2797             history = g_list_first (history);
2798             g_list_free_full (history, g_free);
2799 
2800 #ifdef HAVE_CHARSET
2801             edit->search = mc_search_new (edit->last_search_string, cp_source);
2802 #else
2803             edit->search = mc_search_new (edit->last_search_string, NULL);
2804 #endif
2805             if (edit->search == NULL)
2806             {
2807                 /* if not... then ask for an expression */
2808                 MC_PTR_FREE (edit->last_search_string);
2809                 edit_search (edit);
2810             }
2811             else
2812             {
2813                 edit->search->search_type = edit_search_options.type;
2814 #ifdef HAVE_CHARSET
2815                 edit->search->is_all_charsets = edit_search_options.all_codepages;
2816 #endif
2817                 edit->search->is_case_sensitive = edit_search_options.case_sens;
2818                 edit->search->whole_words = edit_search_options.whole_words;
2819                 edit->search->search_fn = edit_search_cmd_callback;
2820                 edit->search->update_fn = edit_search_update_callback;
2821                 edit->search_line_type = edit_get_search_line_type (edit->search);
2822                 edit_do_search (edit);
2823             }
2824         }
2825         else
2826         {
2827             /* if not... then ask for an expression */
2828             MC_PTR_FREE (edit->last_search_string);
2829             edit_search (edit);
2830         }
2831     }
2832 }
2833 
2834 /* --------------------------------------------------------------------------------------------- */
2835 /**
2836   * Check if it's OK to close the file. If there are unsaved changes, ask user.
2837   *
2838   * @return TRUE if it's OK to exit, FALSE to continue editing.
2839   */
2840 
2841 gboolean
2842 edit_ok_to_exit (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2843 {
2844     const char *fname = N_("[NoName]");
2845     char *msg;
2846     int act;
2847 
2848     if (!edit->modified)
2849         return TRUE;
2850 
2851     if (edit->filename_vpath != NULL)
2852         fname = vfs_path_as_str (edit->filename_vpath);
2853 #ifdef ENABLE_NLS
2854     else
2855         fname = _(fname);
2856 #endif
2857 
2858     if (!mc_global.midnight_shutdown)
2859     {
2860         query_set_sel (2);
2861 
2862         msg = g_strdup_printf (_("File %s was modified.\nSave before close?"), fname);
2863         act = edit_query_dialog3 (_("Close file"), msg, _("&Yes"), _("&No"), _("&Cancel"));
2864     }
2865     else
2866     {
2867         msg = g_strdup_printf (_("Midnight Commander is being shut down.\nSave modified file %s?"),
2868                                fname);
2869         act = edit_query_dialog2 (_("Quit"), msg, _("&Yes"), _("&No"));
2870 
2871         /* Esc is No */
2872         if (act == -1)
2873             act = 1;
2874     }
2875 
2876     g_free (msg);
2877 
2878     switch (act)
2879     {
2880     case 0:                    /* Yes */
2881         if (!mc_global.midnight_shutdown && !edit_check_newline (&edit->buffer))
2882             return FALSE;
2883         edit_push_markers (edit);
2884         edit_set_markers (edit, 0, 0, 0, 0);
2885         if (!edit_save_cmd (edit) || mc_global.midnight_shutdown)
2886             return mc_global.midnight_shutdown;
2887         break;
2888     case 1:                    /* No */
2889     default:
2890         break;
2891     case 2:                    /* Cancel quit */
2892     case -1:                   /* Esc */
2893         return FALSE;
2894     }
2895 
2896     return TRUE;
2897 }
2898 
2899 /* --------------------------------------------------------------------------------------------- */
2900 /** save block, returns TRUE on success */
2901 
2902 gboolean
2903 edit_save_block (WEdit * edit, const char *filename, off_t start, off_t finish)
     /* [previous][next][first][last][top][bottom][index][help]  */
2904 {
2905     int file;
2906     off_t len = 1;
2907     vfs_path_t *vpath;
2908 
2909     vpath = vfs_path_from_str (filename);
2910     file = mc_open (vpath, O_CREAT | O_WRONLY | O_TRUNC,
2911                     S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY);
2912     vfs_path_free (vpath);
2913     if (file == -1)
2914         return FALSE;
2915 
2916     if (edit->column_highlight)
2917     {
2918         int r;
2919 
2920         r = mc_write (file, VERTICAL_MAGIC, sizeof (VERTICAL_MAGIC));
2921         if (r > 0)
2922         {
2923             unsigned char *block, *p;
2924 
2925             p = block = edit_get_block (edit, start, finish, &len);
2926             while (len != 0)
2927             {
2928                 r = mc_write (file, p, len);
2929                 if (r < 0)
2930                     break;
2931                 p += r;
2932                 len -= r;
2933             }
2934             g_free (block);
2935         }
2936     }
2937     else
2938     {
2939         unsigned char *buf;
2940         off_t i = start;
2941 
2942         len = finish - start;
2943         buf = g_malloc0 (TEMP_BUF_LEN);
2944         while (start != finish)
2945         {
2946             off_t end;
2947 
2948             end = MIN (finish, start + TEMP_BUF_LEN);
2949             for (; i < end; i++)
2950                 buf[i - start] = edit_buffer_get_byte (&edit->buffer, i);
2951             len -= mc_write (file, (char *) buf, end - start);
2952             start = end;
2953         }
2954         g_free (buf);
2955     }
2956     mc_close (file);
2957 
2958     return (len == 0);
2959 }
2960 
2961 /* --------------------------------------------------------------------------------------------- */
2962 
2963 void
2964 edit_paste_from_history (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2965 {
2966     (void) edit;
2967     edit_error_dialog (_("Error"), _("This function is not implemented"));
2968 }
2969 
2970 /* --------------------------------------------------------------------------------------------- */
2971 
2972 gboolean
2973 edit_copy_to_X_buf_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2974 {
2975     off_t start_mark, end_mark;
2976 
2977     if (!eval_marks (edit, &start_mark, &end_mark))
2978         return TRUE;
2979 
2980     if (!edit_save_block_to_clip_file (edit, start_mark, end_mark))
2981     {
2982         edit_error_dialog (_("Copy to clipboard"), get_sys_error (_("Unable to save to file")));
2983         return FALSE;
2984     }
2985     /* try use external clipboard utility */
2986     mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
2987 
2988     if (option_drop_selection_on_copy)
2989         edit_mark_cmd (edit, TRUE);
2990 
2991     return TRUE;
2992 }
2993 
2994 /* --------------------------------------------------------------------------------------------- */
2995 
2996 gboolean
2997 edit_cut_to_X_buf_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2998 {
2999     off_t start_mark, end_mark;
3000 
3001     if (!eval_marks (edit, &start_mark, &end_mark))
3002         return TRUE;
3003 
3004     if (!edit_save_block_to_clip_file (edit, start_mark, end_mark))
3005     {
3006         edit_error_dialog (_("Cut to clipboard"), _("Unable to save to file"));
3007         return FALSE;
3008     }
3009     /* try use external clipboard utility */
3010     mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
3011 
3012     edit_block_delete_cmd (edit);
3013     edit_mark_cmd (edit, TRUE);
3014 
3015     return TRUE;
3016 }
3017 
3018 /* --------------------------------------------------------------------------------------------- */
3019 
3020 gboolean
3021 edit_paste_from_X_buf_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3022 {
3023     vfs_path_t *tmp;
3024     gboolean ret;
3025 
3026     /* try use external clipboard utility */
3027     mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_from_ext_clip", NULL);
3028     tmp = mc_config_get_full_vpath (EDIT_HOME_CLIP_FILE);
3029     ret = (edit_insert_file (edit, tmp) >= 0);
3030     vfs_path_free (tmp);
3031 
3032     return ret;
3033 }
3034 
3035 /* --------------------------------------------------------------------------------------------- */
3036 /**
3037  * Ask user for the line and go to that line.
3038  * Negative numbers mean line from the end (i.e. -1 is the last line).
3039  */
3040 
3041 void
3042 edit_goto_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3043 {
3044     static gboolean first_run = TRUE;
3045 
3046     char *f;
3047     long l;
3048     char *error;
3049 
3050     f = input_dialog (_("Goto line"), _("Enter line:"), MC_HISTORY_EDIT_GOTO_LINE,
3051                       first_run ? NULL : INPUT_LAST_TEXT, INPUT_COMPLETE_NONE);
3052     if (f == NULL || *f == '\0')
3053     {
3054         g_free (f);
3055         return;
3056     }
3057 
3058     l = strtol (f, &error, 0);
3059     if (*error != '\0')
3060     {
3061         g_free (f);
3062         return;
3063     }
3064 
3065     if (l < 0)
3066         l = edit->buffer.lines + l + 2;
3067 
3068     edit_move_display (edit, l - WIDGET (edit)->lines / 2 - 1);
3069     edit_move_to_line (edit, l - 1);
3070     edit->force |= REDRAW_COMPLETELY;
3071 
3072     g_free (f);
3073     first_run = FALSE;
3074 }
3075 
3076 /* --------------------------------------------------------------------------------------------- */
3077 /** Return TRUE on success */
3078 
3079 gboolean
3080 edit_save_block_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3081 {
3082     off_t start_mark, end_mark;
3083     char *exp, *tmp;
3084     gboolean ret = FALSE;
3085 
3086     if (!eval_marks (edit, &start_mark, &end_mark))
3087         return TRUE;
3088 
3089     tmp = mc_config_get_full_path (EDIT_HOME_CLIP_FILE);
3090     exp =
3091         input_expand_dialog (_("Save block"), _("Enter file name:"),
3092                              MC_HISTORY_EDIT_SAVE_BLOCK, tmp, INPUT_COMPLETE_FILENAMES);
3093     g_free (tmp);
3094     edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
3095 
3096     if (exp != NULL && *exp != '\0')
3097     {
3098         if (edit_save_block (edit, exp, start_mark, end_mark))
3099             ret = TRUE;
3100         else
3101             edit_error_dialog (_("Save block"), get_sys_error (_("Cannot save file")));
3102 
3103         edit->force |= REDRAW_COMPLETELY;
3104     }
3105 
3106     g_free (exp);
3107 
3108     return ret;
3109 }
3110 
3111 /* --------------------------------------------------------------------------------------------- */
3112 
3113 /** returns TRUE on success */
3114 gboolean
3115 edit_insert_file_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3116 {
3117     char *tmp;
3118     char *exp;
3119     gboolean ret = FALSE;
3120 
3121     tmp = mc_config_get_full_path (EDIT_HOME_CLIP_FILE);
3122     exp = input_expand_dialog (_("Insert file"), _("Enter file name:"),
3123                                MC_HISTORY_EDIT_INSERT_FILE, tmp, INPUT_COMPLETE_FILENAMES);
3124     g_free (tmp);
3125 
3126     edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
3127 
3128     if (exp != NULL && *exp != '\0')
3129     {
3130         vfs_path_t *exp_vpath;
3131 
3132         exp_vpath = vfs_path_from_str (exp);
3133         ret = (edit_insert_file (edit, exp_vpath) >= 0);
3134         vfs_path_free (exp_vpath);
3135 
3136         if (!ret)
3137             edit_error_dialog (_("Insert file"), get_sys_error (_("Cannot insert file")));
3138     }
3139 
3140     g_free (exp);
3141 
3142     edit->force |= REDRAW_COMPLETELY;
3143     return ret;
3144 }
3145 
3146 /* --------------------------------------------------------------------------------------------- */
3147 /** sorts a block, returns -1 on system fail, 1 on cancel and 0 on success */
3148 
3149 int
3150 edit_sort_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3151 {
3152     char *exp, *tmp, *tmp_edit_block_name, *tmp_edit_temp_name;
3153     off_t start_mark, end_mark;
3154     int e;
3155 
3156     if (!eval_marks (edit, &start_mark, &end_mark))
3157     {
3158         edit_error_dialog (_("Sort block"), _("You must first highlight a block of text"));
3159         return 0;
3160     }
3161 
3162     tmp = mc_config_get_full_path (EDIT_HOME_BLOCK_FILE);
3163     edit_save_block (edit, tmp, start_mark, end_mark);
3164     g_free (tmp);
3165 
3166     exp = input_dialog (_("Run sort"),
3167                         _("Enter sort options (see manpage) separated by whitespace:"),
3168                         MC_HISTORY_EDIT_SORT, INPUT_LAST_TEXT, INPUT_COMPLETE_NONE);
3169 
3170     if (exp == NULL)
3171         return 1;
3172 
3173     tmp_edit_block_name = mc_config_get_full_path (EDIT_HOME_BLOCK_FILE);
3174     tmp_edit_temp_name = mc_config_get_full_path (EDIT_HOME_TEMP_FILE);
3175     tmp =
3176         g_strconcat (" sort ", exp, " ", tmp_edit_block_name,
3177                      " > ", tmp_edit_temp_name, (char *) NULL);
3178     g_free (tmp_edit_temp_name);
3179     g_free (tmp_edit_block_name);
3180     g_free (exp);
3181 
3182     e = system (tmp);
3183     g_free (tmp);
3184     if (e != 0)
3185     {
3186         if (e == -1 || e == 127)
3187             edit_error_dialog (_("Sort"), get_sys_error (_("Cannot execute sort command")));
3188         else
3189         {
3190             char q[8];
3191 
3192             sprintf (q, "%d ", e);
3193             tmp = g_strdup_printf (_("Sort returned non-zero: %s"), q);
3194             edit_error_dialog (_("Sort"), tmp);
3195             g_free (tmp);
3196         }
3197 
3198         return -1;
3199     }
3200 
3201     edit->force |= REDRAW_COMPLETELY;
3202 
3203     if (edit_block_delete_cmd (edit))
3204         return 1;
3205 
3206     {
3207         vfs_path_t *tmp_vpath;
3208 
3209         tmp_vpath = mc_config_get_full_vpath (EDIT_HOME_TEMP_FILE);
3210         edit_insert_file (edit, tmp_vpath);
3211         vfs_path_free (tmp_vpath);
3212     }
3213 
3214     return 0;
3215 }
3216 
3217 /* --------------------------------------------------------------------------------------------- */
3218 /**
3219  * Ask user for a command, execute it and paste its output back to the
3220  * editor.
3221  */
3222 
3223 int
3224 edit_ext_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3225 {
3226     char *exp, *tmp, *tmp_edit_temp_file;
3227     int e;
3228 
3229     exp =
3230         input_dialog (_("Paste output of external command"),
3231                       _("Enter shell command(s):"), MC_HISTORY_EDIT_PASTE_EXTCMD, INPUT_LAST_TEXT,
3232                       INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES
3233                       | INPUT_COMPLETE_HOSTNAMES | INPUT_COMPLETE_CD | INPUT_COMPLETE_COMMANDS |
3234                       INPUT_COMPLETE_SHELL_ESC);
3235 
3236     if (!exp)
3237         return 1;
3238 
3239     tmp_edit_temp_file = mc_config_get_full_path (EDIT_HOME_TEMP_FILE);
3240     tmp = g_strconcat (exp, " > ", tmp_edit_temp_file, (char *) NULL);
3241     g_free (tmp_edit_temp_file);
3242     e = system (tmp);
3243     g_free (tmp);
3244     g_free (exp);
3245 
3246     if (e != 0)
3247     {
3248         edit_error_dialog (_("External command"), get_sys_error (_("Cannot execute command")));
3249         return -1;
3250     }
3251 
3252     edit->force |= REDRAW_COMPLETELY;
3253 
3254     {
3255         vfs_path_t *tmp_vpath;
3256 
3257         tmp_vpath = mc_config_get_full_vpath (EDIT_HOME_TEMP_FILE);
3258         edit_insert_file (edit, tmp_vpath);
3259         vfs_path_free (tmp_vpath);
3260     }
3261 
3262     return 0;
3263 }
3264 
3265 /* --------------------------------------------------------------------------------------------- */
3266 /** if block is 1, a block must be highlighted and the shell command
3267    processes it. If block is 0 the shell command is a straight system
3268    command, that just produces some output which is to be inserted */
3269 
3270 void
3271 edit_block_process_cmd (WEdit * edit, int macro_number)
     /* [previous][next][first][last][top][bottom][index][help]  */
3272 {
3273     char *fname;
3274     char *macros_fname = NULL;
3275 
3276     fname = g_strdup_printf ("%s.%i.sh", EDIT_HOME_MACRO_FILE, macro_number);
3277     macros_fname = g_build_filename (mc_config_get_data_path (), fname, (char *) NULL);
3278     user_menu (edit, macros_fname, 0);
3279     g_free (fname);
3280     g_free (macros_fname);
3281     edit->force |= REDRAW_COMPLETELY;
3282 }
3283 
3284 /* --------------------------------------------------------------------------------------------- */
3285 
3286 void
3287 edit_mail_dialog (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3288 {
3289     char *mail_to, *mail_subject, *mail_cc;
3290 
3291     quick_widget_t quick_widgets[] = {
3292         /* *INDENT-OFF* */
3293         QUICK_LABEL (N_("mail -s <subject> -c <cc> <to>"), NULL),
3294         QUICK_LABELED_INPUT (N_("To"), input_label_above,
3295                              INPUT_LAST_TEXT, "mail-dlg-input-3",
3296                              &mail_to, NULL, FALSE, FALSE, INPUT_COMPLETE_USERNAMES),
3297         QUICK_LABELED_INPUT (N_("Subject"), input_label_above,
3298                               INPUT_LAST_TEXT, "mail-dlg-input-2",
3299                               &mail_subject, NULL, FALSE, FALSE, INPUT_COMPLETE_NONE),
3300         QUICK_LABELED_INPUT (N_("Copies to"), input_label_above,
3301                              INPUT_LAST_TEXT, "mail-dlg-input",
3302                              &mail_cc, NULL, FALSE, FALSE, INPUT_COMPLETE_USERNAMES),
3303         QUICK_BUTTONS_OK_CANCEL,
3304         QUICK_END
3305         /* *INDENT-ON* */
3306     };
3307 
3308     quick_dialog_t qdlg = {
3309         -1, -1, 50,
3310         N_("Mail"), "[Input Line Keys]",
3311         quick_widgets, NULL, NULL
3312     };
3313 
3314     if (quick_dialog (&qdlg) != B_CANCEL)
3315     {
3316         pipe_mail (&edit->buffer, mail_to, mail_subject, mail_cc);
3317         g_free (mail_to);
3318         g_free (mail_subject);
3319         g_free (mail_cc);
3320     }
3321 }
3322 
3323 /* --------------------------------------------------------------------------------------------- */
3324 
3325 /*******************/
3326 /* Word Completion */
3327 /*******************/
3328 
3329 /**
3330  * Complete current word using regular expression search
3331  * backwards beginning at the current cursor position.
3332  */
3333 
3334 void
3335 edit_complete_word_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3336 {
3337     gsize i, max_len, word_len = 0, num_compl = 0;
3338     off_t word_start = 0;
3339     GString *match_expr;
3340     GString *compl[MAX_WORD_COMPLETIONS];       /* completions */
3341 
3342     /* search start of word to be completed */
3343     if (!edit_find_word_start (&edit->buffer, &word_start, &word_len))
3344         return;
3345 
3346     /* prepare match expression */
3347     /* match_expr = g_strdup_printf ("\\b%.*s[a-zA-Z_0-9]+", word_len, bufpos); */
3348     match_expr = g_string_new ("(^|\\s+|\\b)");
3349     for (i = 0; i < word_len; i++)
3350         g_string_append_c (match_expr, edit_buffer_get_byte (&edit->buffer, word_start + i));
3351     g_string_append (match_expr,
3352                      "[^\\s\\.=\\+\\[\\]\\(\\)\\,\\;\\:\\\"\\'\\-\\?\\/\\|\\\\\\{\\}\\*\\&\\^\\%%\\$#@\\!]+");
3353 
3354     /* collect the possible completions              */
3355     /* start search from begin to end of file */
3356     max_len =
3357         edit_collect_completions (edit, word_start, word_len, match_expr->str, (GString **) & compl,
3358                                   &num_compl);
3359 
3360     if (num_compl > 0)
3361     {
3362         /* insert completed word if there is only one match */
3363         if (num_compl == 1)
3364             edit_complete_word_insert_recoded_completion (edit, compl[0]->str, word_len);
3365         /* more than one possible completion => ask the user */
3366         else
3367         {
3368             char *curr_compl;
3369 
3370             /* !!! usually only a beep is expected and when <ALT-TAB> is !!! */
3371             /* !!! pressed again the selection dialog pops up, but that  !!! */
3372             /* !!! seems to require a further internal state             !!! */
3373             /*tty_beep (); */
3374 
3375             /* let the user select the preferred completion */
3376             curr_compl = editcmd_dialog_completion_show (edit, max_len,
3377                                                          (GString **) & compl, num_compl);
3378 
3379             if (curr_compl != NULL)
3380             {
3381                 edit_complete_word_insert_recoded_completion (edit, curr_compl, word_len);
3382                 g_free (curr_compl);
3383             }
3384         }
3385     }
3386 
3387     g_string_free (match_expr, TRUE);
3388     /* release memory before return */
3389     for (i = 0; i < num_compl; i++)
3390         g_string_free (compl[i], TRUE);
3391 }
3392 
3393 /* --------------------------------------------------------------------------------------------- */
3394 
3395 #ifdef HAVE_CHARSET
3396 void
3397 edit_select_codepage_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3398 {
3399     if (do_select_codepage ())
3400         edit_set_codeset (edit);
3401 
3402     edit->force = REDRAW_PAGE;
3403     widget_draw (WIDGET (edit));
3404 }
3405 #endif
3406 
3407 /* --------------------------------------------------------------------------------------------- */
3408 
3409 void
3410 edit_insert_literal_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3411 {
3412     int char_for_insertion;
3413 
3414     char_for_insertion = editcmd_dialog_raw_key_query (_("Insert literal"),
3415                                                        _("Press any key:"), FALSE);
3416     edit_execute_key_command (edit, -1, ascii_alpha_to_cntrl (char_for_insertion));
3417 }
3418 
3419 /* --------------------------------------------------------------------------------------------- */
3420 
3421 void
3422 edit_begin_end_macro_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3423 {
3424     /* edit is a pointer to the widget */
3425     if (edit != NULL)
3426     {
3427         long command = macro_index < 0 ? CK_MacroStartRecord : CK_MacroStopRecord;
3428 
3429         edit_execute_key_command (edit, command, -1);
3430     }
3431 }
3432 
3433  /* --------------------------------------------------------------------------------------------- */
3434 
3435 void
3436 edit_begin_end_repeat_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3437 {
3438     /* edit is a pointer to the widget */
3439     if (edit != NULL)
3440     {
3441         long command = macro_index < 0 ? CK_RepeatStartRecord : CK_RepeatStopRecord;
3442 
3443         edit_execute_key_command (edit, command, -1);
3444     }
3445 }
3446 
3447 /* --------------------------------------------------------------------------------------------- */
3448 
3449 gboolean
3450 edit_load_forward_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3451 {
3452     if (edit->modified
3453         && edit_query_dialog2 (_("Warning"),
3454                                _("Current text was modified without a file save.\n"
3455                                  "Continue discards these changes."), _("C&ontinue"),
3456                                _("&Cancel")) == 1)
3457     {
3458         edit->force |= REDRAW_COMPLETELY;
3459         return TRUE;
3460     }
3461 
3462     if (edit_stack_iterator + 1 >= MAX_HISTORY_MOVETO)
3463         return FALSE;
3464 
3465     if (edit_history_moveto[edit_stack_iterator + 1].line < 1)
3466         return FALSE;
3467 
3468     edit_stack_iterator++;
3469     if (edit_history_moveto[edit_stack_iterator].filename_vpath != NULL)
3470         return edit_reload_line (edit, edit_history_moveto[edit_stack_iterator].filename_vpath,
3471                                  edit_history_moveto[edit_stack_iterator].line);
3472 
3473     return FALSE;
3474 }
3475 
3476 /* --------------------------------------------------------------------------------------------- */
3477 
3478 gboolean
3479 edit_load_back_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3480 {
3481     if (edit->modified
3482         && edit_query_dialog2 (_("Warning"),
3483                                _("Current text was modified without a file save.\n"
3484                                  "Continue discards these changes."), _("C&ontinue"),
3485                                _("&Cancel")) == 1)
3486     {
3487         edit->force |= REDRAW_COMPLETELY;
3488         return TRUE;
3489     }
3490 
3491     /* we are in the bottom of the stack, NO WAY! */
3492     if (edit_stack_iterator == 0)
3493         return FALSE;
3494 
3495     edit_stack_iterator--;
3496     if (edit_history_moveto[edit_stack_iterator].filename_vpath != NULL)
3497         return edit_reload_line (edit, edit_history_moveto[edit_stack_iterator].filename_vpath,
3498                                  edit_history_moveto[edit_stack_iterator].line);
3499 
3500     return FALSE;
3501 }
3502 
3503 /* --------------------------------------------------------------------------------------------- */
3504 
3505 void
3506 edit_get_match_keyword_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3507 {
3508     gsize word_len = 0, max_len = 0;
3509     int num_def = 0;
3510     gsize i;
3511     off_t word_start = 0;
3512     GString *match_expr;
3513     char *path = NULL;
3514     char *ptr = NULL;
3515     char *tagfile = NULL;
3516 
3517     etags_hash_t def_hash[MAX_DEFINITIONS];
3518 
3519     for (i = 0; i < MAX_DEFINITIONS; i++)
3520         def_hash[i].filename = NULL;
3521 
3522     /* search start of word to be completed */
3523     if (!edit_find_word_start (&edit->buffer, &word_start, &word_len))
3524         return;
3525 
3526     /* prepare match expression */
3527     match_expr = g_string_sized_new (word_len);
3528     for (i = 0; i < word_len; i++)
3529         g_string_append_c (match_expr, edit_buffer_get_byte (&edit->buffer, word_start + i));
3530 
3531     ptr = g_get_current_dir ();
3532     path = g_strconcat (ptr, PATH_SEP_STR, (char *) NULL);
3533     g_free (ptr);
3534 
3535     /* Recursive search file 'TAGS' in parent dirs */
3536     do
3537     {
3538         ptr = g_path_get_dirname (path);
3539         g_free (path);
3540         path = ptr;
3541         g_free (tagfile);
3542         tagfile = mc_build_filename (path, TAGS_NAME, (char *) NULL);
3543         if (tagfile != NULL && exist_file (tagfile))
3544             break;
3545     }
3546     while (strcmp (path, PATH_SEP_STR) != 0);
3547 
3548     if (tagfile != NULL)
3549     {
3550         num_def =
3551             etags_set_definition_hash (tagfile, path, match_expr->str, (etags_hash_t *) & def_hash);
3552         g_free (tagfile);
3553     }
3554     g_free (path);
3555 
3556     max_len = MAX_WIDTH_DEF_DIALOG;
3557     word_len = 0;
3558     if (num_def > 0)
3559         editcmd_dialog_select_definition_show (edit, match_expr->str, max_len, word_len,
3560                                                (etags_hash_t *) & def_hash, num_def);
3561     g_string_free (match_expr, TRUE);
3562 }
3563 
3564 /* --------------------------------------------------------------------------------------------- */
3565 
3566 #ifdef HAVE_ASPELL
3567 int
3568 edit_suggest_current_word (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3569 {
3570     gsize cut_len = 0;
3571     gsize word_len = 0;
3572     off_t word_start = 0;
3573     int retval = B_SKIP_WORD;
3574     GString *match_word;
3575 
3576     /* search start of word to spell check */
3577     match_word = edit_buffer_get_word_from_pos (&edit->buffer, edit->buffer.curs1, &word_start,
3578                                                 &cut_len);
3579     word_len = match_word->len;
3580 
3581 #ifdef HAVE_CHARSET
3582     if (mc_global.source_codepage >= 0 && mc_global.source_codepage != mc_global.display_codepage)
3583     {
3584         GString *tmp_word;
3585 
3586         tmp_word = str_convert_to_display (match_word->str);
3587         g_string_free (match_word, TRUE);
3588         match_word = tmp_word;
3589     }
3590 #endif
3591     if (!aspell_check (match_word->str, (int) word_len))
3592     {
3593         GArray *suggest;
3594         unsigned int res;
3595         guint i;
3596 
3597         suggest = g_array_new (TRUE, FALSE, sizeof (char *));
3598 
3599         res = aspell_suggest (suggest, match_word->str, (int) word_len);
3600         if (res != 0)
3601         {
3602             char *new_word = NULL;
3603 
3604             edit->found_start = word_start;
3605             edit->found_len = word_len;
3606             edit->force |= REDRAW_PAGE;
3607             edit_scroll_screen_over_cursor (edit);
3608             edit_render_keypress (edit);
3609 
3610             retval = spell_dialog_spell_suggest_show (edit, match_word->str, &new_word, suggest);
3611             edit_cursor_move (edit, word_len - cut_len);
3612 
3613             if (retval == B_ENTER && new_word != NULL)
3614             {
3615                 char *cp_word;
3616 
3617 #ifdef HAVE_CHARSET
3618                 if (mc_global.source_codepage >= 0 &&
3619                     (mc_global.source_codepage != mc_global.display_codepage))
3620                 {
3621                     GString *tmp_word;
3622 
3623                     tmp_word = str_convert_to_input (new_word);
3624                     g_free (new_word);
3625                     new_word = g_string_free (tmp_word, FALSE);
3626                 }
3627 #endif
3628                 cp_word = new_word;
3629                 for (i = 0; i < word_len; i++)
3630                     edit_backspace (edit, TRUE);
3631                 for (; *new_word; new_word++)
3632                     edit_insert (edit, *new_word);
3633                 g_free (cp_word);
3634             }
3635             else if (retval == B_ADD_WORD)
3636                 aspell_add_to_dict (match_word->str, (int) word_len);
3637         }
3638 
3639         for (i = 0; i < suggest->len; i++)
3640         {
3641             char *cur_sugg_word;
3642 
3643             cur_sugg_word = g_array_index (suggest, char *, i);
3644             g_free (cur_sugg_word);
3645         }
3646         g_array_free (suggest, TRUE);
3647         edit->found_start = 0;
3648         edit->found_len = 0;
3649     }
3650 
3651     g_string_free (match_word, TRUE);
3652 
3653     return retval;
3654 }
3655 
3656 /* --------------------------------------------------------------------------------------------- */
3657 
3658 void
3659 edit_spellcheck_file (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
3660 {
3661     if (edit->buffer.curs_line > 0)
3662     {
3663         edit_cursor_move (edit, -edit->buffer.curs1);
3664         edit_move_to_prev_col (edit, 0);
3665         edit_update_curs_row (edit);
3666     }
3667 
3668     do
3669     {
3670         int c1, c2;
3671 
3672         c2 = edit_buffer_get_current_byte (&edit->buffer);
3673 
3674         do
3675         {
3676             if (edit->buffer.curs1 >= edit->buffer.size)
3677                 return;
3678 
3679             c1 = c2;
3680             edit_cursor_move (edit, 1);
3681             c2 = edit_buffer_get_current_byte (&edit->buffer);
3682         }
3683         while (is_break_char (c1) || is_break_char (c2));
3684     }
3685     while (edit_suggest_current_word (edit) != B_CANCEL);
3686 }
3687 
3688 /* --------------------------------------------------------------------------------------------- */
3689 
3690 void
3691 edit_set_spell_lang (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
3692 {
3693     GArray *lang_list;
3694 
3695     lang_list = g_array_new (TRUE, FALSE, sizeof (char *));
3696     if (aspell_get_lang_list (lang_list) != 0)
3697     {
3698         char *lang;
3699 
3700         lang = spell_dialog_lang_list_show (lang_list);
3701         if (lang != NULL)
3702         {
3703             (void) aspell_set_lang (lang);
3704             g_free (lang);
3705         }
3706     }
3707     aspell_array_clean (lang_list);
3708 }
3709 #endif /* HAVE_ASPELL */
3710 
3711 /* --------------------------------------------------------------------------------------------- */

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