Manual pages: mcmcdiffmceditmcview

root/src/editor/editcmd.c

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

DEFINITIONS

This source file includes following definitions.
  1. edit_save_mode_callback
  2. edit_save_file
  3. edit_check_newline
  4. edit_get_save_file_as
  5. edit_save_cmd
  6. edit_delete_column_of_text
  7. edit_block_delete
  8. edit_get_block
  9. edit_save_block_to_clip_file
  10. pipe_mail
  11. edit_append_spaces_at_eol
  12. edit_insert_column_of_text
  13. edit_syntax_onoff_cb
  14. editcmd_dialog_raw_key_query_cb
  15. editcmd_check_and_create_user_syntax_directory
  16. edit_refresh_cmd
  17. edit_syntax_onoff_cmd
  18. edit_show_tabs_tws_cmd
  19. edit_show_margin_cmd
  20. edit_show_numbers_cmd
  21. edit_save_mode_cmd
  22. edit_set_filename
  23. edit_save_as_cmd
  24. edit_save_confirm_cmd
  25. edit_load_cmd
  26. edit_load_file_from_filename
  27. edit_load_file_from_history
  28. edit_load_syntax_file
  29. edit_load_menu_file
  30. edit_close_cmd
  31. edit_block_copy_cmd
  32. edit_block_move_cmd
  33. edit_block_delete_cmd
  34. edit_ok_to_quit
  35. edit_save_block
  36. edit_paste_from_history
  37. edit_copy_to_X_buf_cmd
  38. edit_cut_to_X_buf_cmd
  39. edit_paste_from_X_buf_cmd
  40. edit_goto_cmd
  41. edit_save_block_cmd
  42. edit_insert_file_cmd
  43. edit_sort_cmd
  44. edit_ext_cmd
  45. edit_block_process_cmd
  46. edit_mail_dialog
  47. edit_select_codepage_cmd
  48. edit_insert_literal_cmd
  49. edit_load_forward_cmd
  50. edit_load_back_cmd
  51. editcmd_dialog_raw_key_query

   1 /*
   2    Editor high level editing commands
   3 
   4    Copyright (C) 1996-2026
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Paul Sheer, 1996, 1997
   9    Andrew Borodin <aborodin@vmail.ru>, 2012-2022
  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 <https://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 <sys/stat.h>
  45 #include <stdlib.h>
  46 #include <errno.h>
  47 
  48 #include "lib/global.h"
  49 #include "lib/tty/tty.h"
  50 #include "lib/tty/key.h"  // XCTRL
  51 #include "lib/strutil.h"  // utf string functions
  52 #include "lib/fileloc.h"
  53 #include "lib/lock.h"
  54 #include "lib/util.h"  // tilde_expand(), x_basename()
  55 #include "lib/vfs/vfs.h"
  56 #include "lib/widget.h"
  57 #include "lib/event.h"  // mc_event_raise()
  58 #include "lib/charsets.h"
  59 
  60 #include "src/history.h"
  61 #include "src/file_history.h"  // show_file_history()
  62 #include "src/selcodepage.h"
  63 #include "src/util.h"  // check_for_default(), file_error_message()
  64 
  65 #include "edit-impl.h"
  66 #include "editwidget.h"
  67 #include "editsearch.h"
  68 #include "etags.h"
  69 
  70 /*** global variables ****************************************************************************/
  71 
  72 /* search and replace: */
  73 int search_create_bookmark = FALSE;
  74 
  75 /*** file scope macro definitions ****************************************************************/
  76 
  77 #define TEMP_BUF_LEN 1024
  78 
  79 /*** file scope type declarations ****************************************************************/
  80 
  81 /*** forward declarations (file scope functions) *************************************************/
  82 
  83 MC_TESTABLE void edit_insert_column_of_text (WEdit *edit, GString *data, long width,
  84                                              off_t *start_pos, off_t *end_pos, long *col1,
  85                                              long *col2);
  86 
  87 /*** file scope variables ************************************************************************/
  88 
  89 static unsigned long edit_save_mode_radio_id, edit_save_mode_input_id;
  90 
  91 /* --------------------------------------------------------------------------------------------- */
  92 /*** file scope functions ************************************************************************/
  93 /* --------------------------------------------------------------------------------------------- */
  94 
  95 static cb_ret_t
  96 edit_save_mode_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
  97 {
  98     switch (msg)
  99     {
 100     case MSG_CHANGED_FOCUS:
 101         if (sender != NULL && sender->id == edit_save_mode_radio_id)
 102         {
 103             Widget *ww;
 104 
 105             ww = widget_find_by_id (w, edit_save_mode_input_id);
 106             widget_disable (ww, RADIO (sender)->sel != 2);
 107             return MSG_HANDLED;
 108         }
 109         return MSG_NOT_HANDLED;
 110 
 111     default:
 112         return dlg_default_callback (w, sender, msg, parm, data);
 113     }
 114 }
 115 
 116 /* --------------------------------------------------------------------------------------------- */
 117 
 118 /*  If 0 (quick save) then  a) create/truncate <filename> file,
 119    b) save to <filename>;
 120    if 1 (safe save) then   a) save to <tempnam>,
 121    b) rename <tempnam> to <filename>;
 122    if 2 (do backups) then  a) save to <tempnam>,
 123    b) rename <filename> to <filename.backup_ext>,
 124    c) rename <tempnam> to <filename>. */
 125 
 126 /* returns 0 on error, -1 on abort */
 127 
 128 static int
 129 edit_save_file (WEdit *edit, const vfs_path_t *filename_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 130 {
 131     char *p;
 132     off_t filelen = 0;
 133     int this_save_mode, rv, fd = -1;
 134     vfs_path_t *real_filename_vpath;
 135     vfs_path_t *savename_vpath = NULL;
 136     const char *start_filename;
 137     const vfs_path_element_t *vpath_element;
 138     struct stat sb;
 139 
 140     vpath_element = vfs_path_get_by_index (filename_vpath, 0);
 141     if (vpath_element == NULL)
 142         return 0;
 143 
 144     start_filename = vpath_element->path;
 145     if (*start_filename == '\0')
 146         return 0;
 147 
 148     if (!IS_PATH_SEP (*start_filename) && edit->dir_vpath != NULL)
 149         real_filename_vpath = vfs_path_append_vpath_new (edit->dir_vpath, filename_vpath, NULL);
 150     else
 151         real_filename_vpath = vfs_path_clone (filename_vpath);
 152 
 153     this_save_mode = edit_options.save_mode;
 154     if (this_save_mode != EDIT_QUICK_SAVE)
 155     {
 156         if (!vfs_file_is_local (real_filename_vpath))
 157             // The file does not exists yet, so no safe save or backup are necessary.
 158             this_save_mode = EDIT_QUICK_SAVE;
 159         else
 160         {
 161             fd = mc_open (real_filename_vpath, O_RDONLY | O_BINARY);
 162             if (fd == -1)
 163                 // The file does not exists yet, so no safe save or backup are necessary.
 164                 this_save_mode = EDIT_QUICK_SAVE;
 165         }
 166 
 167         if (fd != -1)
 168             mc_close (fd);
 169     }
 170 
 171     rv = mc_stat (real_filename_vpath, &sb);
 172     if (rv == 0)
 173     {
 174         if (this_save_mode == EDIT_QUICK_SAVE && edit->skip_detach_prompt == 0 && sb.st_nlink > 1)
 175         {
 176             rv =
 177                 edit_query_dialog3 (_ ("Warning"), _ ("File has hard-links. Detach before saving?"),
 178                                     _ ("&Yes"), _ ("&No"), _ ("&Cancel"));
 179             switch (rv)
 180             {
 181             case 0:
 182                 this_save_mode = EDIT_SAFE_SAVE;
 183                 MC_FALLTHROUGH;
 184             case 1:
 185                 edit->skip_detach_prompt = 1;
 186                 break;
 187             default:
 188                 vfs_path_free (real_filename_vpath, TRUE);
 189                 return -1;
 190             }
 191         }
 192 
 193         // Prevent overwriting changes from other editor sessions.
 194         if (edit->stat1.st_mtime != 0 && edit->stat1.st_mtime != sb.st_mtime)
 195         {
 196             // The default action is "Cancel".
 197             query_set_sel (1);
 198 
 199             rv = edit_query_dialog2 (_ ("Warning"),
 200                                      _ ("The file has been modified in the meantime. Save anyway?"),
 201                                      _ ("&Yes"), _ ("&Cancel"));
 202             if (rv != 0)
 203             {
 204                 vfs_path_free (real_filename_vpath, TRUE);
 205                 return -1;
 206             }
 207         }
 208     }
 209 
 210     if (this_save_mode == EDIT_QUICK_SAVE)
 211         savename_vpath = vfs_path_clone (real_filename_vpath);
 212     else
 213     {
 214         char *savedir, *saveprefix;
 215 
 216         savedir = vfs_path_tokens_get (real_filename_vpath, 0, -1);
 217         if (savedir == NULL)
 218             savedir = g_strdup (".");
 219 
 220         // Token-related function never return leading slash, so we need add it manually
 221         saveprefix = mc_build_filename (PATH_SEP_STR, savedir, "cooledit", (char *) NULL);
 222         g_free (savedir);
 223         fd = mc_mkstemps (&savename_vpath, saveprefix, NULL);
 224         g_free (saveprefix);
 225         if (savename_vpath == NULL)
 226         {
 227             vfs_path_free (real_filename_vpath, TRUE);
 228             return 0;
 229         }
 230         /* FIXME:
 231          * Close for now because mc_mkstemps use pure open system call
 232          * to create temporary file and it needs to be reopened by
 233          * VFS-aware mc_open().
 234          */
 235         close (fd);
 236     }
 237 
 238     (void) mc_chown (savename_vpath, edit->stat1.st_uid, edit->stat1.st_gid);
 239     (void) mc_chmod (savename_vpath, edit->stat1.st_mode);
 240     if (edit->attrs_ok)
 241         (void) mc_fsetflags (savename_vpath, edit->attrs);
 242 
 243     fd = mc_open (savename_vpath, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY, edit->stat1.st_mode);
 244     if (fd == -1)
 245         goto error_save;
 246 
 247     // pipe save
 248     p = edit_get_write_filter (savename_vpath, real_filename_vpath);
 249     if (p != NULL)
 250     {
 251         FILE *file;
 252 
 253         mc_close (fd);
 254         file = (FILE *) popen (p, "w");
 255 
 256         if (file != NULL)
 257         {
 258             filelen = edit_write_stream (edit, file);
 259 #if 1
 260             pclose (file);
 261 #else
 262             if (pclose (file) != 0)
 263             {
 264                 message (D_ERROR, MSG_ERROR, _ ("Error writing to pipe: %s"), p);
 265                 g_free (p);
 266                 goto error_save;
 267             }
 268 #endif
 269         }
 270         else
 271         {
 272             file_error_message (_ ("Cannot open pipe for reading\n%s"), p);
 273             g_free (p);
 274             goto error_save;
 275         }
 276         g_free (p);
 277     }
 278     else if (edit->lb == LB_ASIS)
 279     {  // do not change line breaks
 280         filelen = edit_buffer_write_file (&edit->buffer, fd);
 281 
 282         if (filelen != edit->buffer.size)
 283         {
 284             mc_close (fd);
 285             goto error_save;
 286         }
 287 
 288         if (mc_close (fd) != 0)
 289             goto error_save;
 290 
 291         // Update the file information, especially the mtime.
 292         if (mc_stat (savename_vpath, &edit->stat1) == -1)
 293             goto error_save;
 294     }
 295     else
 296     {  // change line breaks
 297         FILE *file;
 298         const char *savename;
 299 
 300         mc_close (fd);
 301 
 302         savename = vfs_path_get_last_path_str (savename_vpath);
 303         file = (FILE *) fopen (savename, "w");
 304         if (file != NULL)
 305         {
 306             filelen = edit_write_stream (edit, file);
 307             fclose (file);
 308         }
 309         else
 310         {
 311             file_error_message (_ ("Cannot open file\n%s\nfor writing"), savename);
 312             goto error_save;
 313         }
 314     }
 315 
 316     if (filelen != edit->buffer.size)
 317         goto error_save;
 318 
 319     if (this_save_mode == EDIT_DO_BACKUP)
 320     {
 321         char *tmp_store_filename;
 322         vfs_path_element_t *last_vpath_element;
 323         vfs_path_t *tmp_vpath;
 324         gboolean ok;
 325 
 326         g_assert (edit_options.backup_ext != NULL);
 327 
 328         // add backup extension to the path
 329         tmp_vpath = vfs_path_clone (real_filename_vpath);
 330         last_vpath_element = (vfs_path_element_t *) vfs_path_get_by_index (tmp_vpath, -1);
 331         tmp_store_filename = last_vpath_element->path;
 332         last_vpath_element->path =
 333             g_strdup_printf ("%s%s", tmp_store_filename, edit_options.backup_ext);
 334         g_free (tmp_store_filename);
 335 
 336         ok = (mc_rename (real_filename_vpath, tmp_vpath) != -1);
 337         vfs_path_free (tmp_vpath, TRUE);
 338         if (!ok)
 339             goto error_save;
 340     }
 341 
 342     if (this_save_mode != EDIT_QUICK_SAVE && mc_rename (savename_vpath, real_filename_vpath) == -1)
 343         goto error_save;
 344 
 345     vfs_path_free (real_filename_vpath, TRUE);
 346     vfs_path_free (savename_vpath, TRUE);
 347     return 1;
 348 error_save:
 349     /*  FIXME: Is this safe ?
 350      *  if (this_save_mode != EDIT_QUICK_SAVE)
 351      *      mc_unlink (savename);
 352      */
 353     vfs_path_free (real_filename_vpath, TRUE);
 354     vfs_path_free (savename_vpath, TRUE);
 355     return 0;
 356 }
 357 
 358 /* --------------------------------------------------------------------------------------------- */
 359 
 360 static gboolean
 361 edit_check_newline (const edit_buffer_t *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 362 {
 363     return !(edit_options.check_nl_at_eof && buf->size > 0
 364              && edit_buffer_get_byte (buf, buf->size - 1) != '\n'
 365              && edit_query_dialog2 (_ ("Warning"),
 366                                     _ ("The file you are saving does not end with a newline."),
 367                                     _ ("C&ontinue"), _ ("&Cancel"))
 368                  != 0);
 369 }
 370 
 371 /* --------------------------------------------------------------------------------------------- */
 372 
 373 static vfs_path_t *
 374 edit_get_save_file_as (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 375 {
 376     static LineBreaks cur_lb = LB_ASIS;
 377     char *filename_res = NULL;
 378     vfs_path_t *ret_vpath = NULL;
 379 
 380     const char *lb_names[LB_NAMES] = {
 381         _ ("&Do not change"),
 382         _ ("&Unix format (LF)"),
 383         _ ("&Windows/DOS format (CR LF)"),
 384         _ ("&Macintosh format (CR)"),
 385     };
 386 
 387     quick_widget_t quick_widgets[] = {
 388         QUICK_LABELED_INPUT (_ ("Enter file name:"), input_label_above,
 389                              vfs_path_as_str (edit->filename_vpath), "save-as", &filename_res, NULL,
 390                              FALSE, FALSE, INPUT_COMPLETE_FILENAMES),
 391         QUICK_SEPARATOR (TRUE),
 392         QUICK_LABEL (_ ("Change line breaks to:"), NULL),
 393         QUICK_RADIO (LB_NAMES, lb_names, (int *) &cur_lb, NULL),
 394         QUICK_BUTTONS_OK_CANCEL,
 395         QUICK_END,
 396     };
 397 
 398     WRect r = { -1, -1, 0, 64 };
 399 
 400     quick_dialog_t qdlg = {
 401         .rect = r,
 402         .title = _ ("Save As"),
 403         .help = "[Save File As]",
 404         .widgets = quick_widgets,
 405         .callback = NULL,
 406         .mouse_callback = NULL,
 407     };
 408 
 409     if (quick_dialog (&qdlg) != B_CANCEL)
 410     {
 411         char *fname;
 412 
 413         edit->lb = cur_lb;
 414         fname = tilde_expand (filename_res);
 415         g_free (filename_res);
 416         ret_vpath = vfs_path_from_str (fname);
 417         g_free (fname);
 418     }
 419 
 420     return ret_vpath;
 421 }
 422 
 423 /* --------------------------------------------------------------------------------------------- */
 424 
 425 /** returns TRUE on success */
 426 
 427 static gboolean
 428 edit_save_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 429 {
 430     int save_lock = 0;
 431 
 432     if (edit->locked == 0 && edit->delete_file == 0)
 433         save_lock = lock_file (edit->filename_vpath);
 434 
 435     const int res = edit_save_file (edit, edit->filename_vpath);
 436 
 437     // Maintain modify (not save) lock on failure
 438     if ((res > 0 && edit->locked != 0) || save_lock != 0)
 439         edit->locked = unlock_file (edit->filename_vpath);
 440 
 441     // On failure try 'save as', it does locking on its own
 442     if (res == 0)
 443         return edit_save_as_cmd (edit);
 444 
 445     if (res > 0)
 446     {
 447         edit->delete_file = 0;
 448         edit->modified = 0;
 449     }
 450 
 451     edit->force |= REDRAW_COMPLETELY;
 452 
 453     return TRUE;
 454 }
 455 
 456 /* --------------------------------------------------------------------------------------------- */
 457 
 458 static void
 459 edit_delete_column_of_text (WEdit *edit, off_t m1, off_t m2)
     /* [previous][next][first][last][top][bottom][index][help]  */
 460 {
 461     off_t n;
 462     off_t r;
 463     long b, c, d;
 464 
 465     n = edit_buffer_get_forward_offset (&edit->buffer, m1, 0, m2) + 1;
 466     r = edit_buffer_get_bol (&edit->buffer, m1);
 467     c = (long) edit_move_forward3 (edit, r, 0, m1);
 468     r = edit_buffer_get_bol (&edit->buffer, m2);
 469     d = (long) edit_move_forward3 (edit, r, 0, m2);
 470     b = MAX (MIN (c, d), MIN (edit->column1, edit->column2));
 471     c = MAX (c, MAX (edit->column1, edit->column2));
 472 
 473     while (n-- != 0)
 474     {
 475         off_t p, q;
 476 
 477         r = edit_buffer_get_current_bol (&edit->buffer);
 478         p = edit_move_forward3 (edit, r, b, 0);
 479         q = edit_move_forward3 (edit, r, c, 0);
 480         p = MAX (p, m1);
 481         q = MIN (q, m2);
 482         edit_cursor_move (edit, p - edit->buffer.curs1);
 483         // delete line between margins
 484         for (; q > p; q--)
 485             if (edit_buffer_get_current_byte (&edit->buffer) != '\n')
 486                 edit_delete (edit, TRUE);
 487 
 488         // move to next line except on the last delete
 489         if (n != 0)
 490         {
 491             r = edit_buffer_get_forward_offset (&edit->buffer, edit->buffer.curs1, 1, 0);
 492             edit_cursor_move (edit, r - edit->buffer.curs1);
 493         }
 494     }
 495 }
 496 
 497 /* --------------------------------------------------------------------------------------------- */
 498 /** if success return TRUE */
 499 
 500 static gboolean
 501 edit_block_delete (WEdit *edit, off_t start_mark, off_t end_mark)
     /* [previous][next][first][last][top][bottom][index][help]  */
 502 {
 503     off_t curs_pos;
 504     long curs_line, c1, c2;
 505 
 506     if (edit->column_highlight && edit->mark2 < 0)
 507         edit_mark_cmd (edit, FALSE);
 508 
 509     // Warning message with a query to continue or cancel the operation
 510     if ((end_mark - start_mark) > max_undo / 2
 511         && edit_query_dialog2 (_ ("Warning"),
 512                                ("Block is large, you may not be able to undo this action"),
 513                                _ ("C&ontinue"), _ ("&Cancel"))
 514             != 0)
 515         return FALSE;
 516 
 517     c1 = MIN (edit->column1, edit->column2);
 518     c2 = MAX (edit->column1, edit->column2);
 519     edit->column1 = c1;
 520     edit->column2 = c2;
 521 
 522     edit_push_markers (edit);
 523 
 524     curs_line = edit->buffer.curs_line;
 525 
 526     curs_pos = edit->curs_col + edit->over_col;
 527 
 528     // move cursor to start of selection
 529     edit_cursor_move (edit, start_mark - edit->buffer.curs1);
 530     edit_scroll_screen_over_cursor (edit);
 531 
 532     if (start_mark < end_mark)
 533     {
 534         if (edit->column_highlight)
 535         {
 536             off_t b, e;
 537             off_t line_width;
 538 
 539             if (edit->mark2 < 0)
 540                 edit_mark_cmd (edit, FALSE);
 541             edit_delete_column_of_text (edit, start_mark, end_mark);
 542             // move cursor to the saved position
 543             edit_move_to_line (edit, curs_line);
 544             // calculate line width and cursor position before cut
 545             b = edit_buffer_get_current_bol (&edit->buffer);
 546             e = edit_buffer_get_current_eol (&edit->buffer);
 547             line_width = edit_move_forward3 (edit, b, 0, e);
 548             if (edit_options.cursor_beyond_eol && curs_pos > line_width)
 549                 edit->over_col = curs_pos - line_width;
 550         }
 551         else
 552         {
 553             off_t count;
 554 
 555             for (count = start_mark; count < end_mark; count++)
 556                 edit_delete (edit, TRUE);
 557         }
 558     }
 559 
 560     edit_set_markers (edit, 0, 0, 0, 0);
 561     edit->force |= REDRAW_PAGE;
 562 
 563     return TRUE;
 564 }
 565 
 566 /* --------------------------------------------------------------------------------------------- */
 567 /** Return a null terminated length of text. Result must be g_free'd */
 568 
 569 static GString *
 570 edit_get_block (WEdit *edit, const off_t start, const off_t finish)
     /* [previous][next][first][last][top][bottom][index][help]  */
 571 {
 572     GString *r;
 573 
 574     r = g_string_sized_new (finish - start);
 575 
 576     if (edit->column_highlight)
 577     {
 578         // copy from buffer, excluding chars that are out of the column 'margins'
 579         for (off_t i = start; i < finish; i++)
 580         {
 581             off_t x;
 582 
 583             x = edit_buffer_get_bol (&edit->buffer, i);
 584             x = edit_move_forward3 (edit, x, 0, i);
 585 
 586             const int c = edit_buffer_get_byte (&edit->buffer, i);
 587 
 588             if ((x >= edit->column1 && x < edit->column2)
 589                 || (x >= edit->column2 && x < edit->column1) || c == '\n')
 590                 g_string_append_c (r, (gchar) c);
 591         }
 592     }
 593     else
 594         for (off_t i = start; i < finish; i++)
 595         {
 596             const int c = edit_buffer_get_byte (&edit->buffer, i);
 597 
 598             g_string_append_c (r, (gchar) c);
 599         }
 600 
 601     return r;
 602 }
 603 
 604 /* --------------------------------------------------------------------------------------------- */
 605 /** copies a block to clipboard file */
 606 
 607 static gboolean
 608 edit_save_block_to_clip_file (WEdit *edit, off_t start, off_t finish)
     /* [previous][next][first][last][top][bottom][index][help]  */
 609 {
 610     gboolean ret;
 611     gchar *tmp;
 612 
 613     tmp = mc_config_get_full_path (EDIT_HOME_CLIP_FILE);
 614     ret = edit_save_block (edit, tmp, start, finish);
 615     g_free (tmp);
 616 
 617     return ret;
 618 }
 619 
 620 /* --------------------------------------------------------------------------------------------- */
 621 
 622 static void
 623 pipe_mail (const edit_buffer_t *buf, char *to, char *subject, char *cc)
     /* [previous][next][first][last][top][bottom][index][help]  */
 624 {
 625     FILE *p = 0;
 626     GString *s = NULL;
 627     GString *to_quoted;
 628 
 629     to_quoted = name_quote (to, FALSE);
 630     if (to_quoted != NULL)
 631     {
 632         GString *subject_quoted;
 633 
 634         subject_quoted = name_quote (subject, FALSE);
 635         if (subject_quoted != NULL)
 636         {
 637             GString *cc_quoted;
 638 
 639             s = g_string_new ("mail -s ");
 640             mc_g_string_concat (s, subject_quoted);
 641             g_string_append_c (s, ' ');
 642 
 643             g_string_free (subject_quoted, TRUE);
 644 
 645             cc_quoted = name_quote (cc, FALSE);
 646             if (cc_quoted != NULL)
 647             {
 648                 g_string_append (s, "-c ");
 649                 mc_g_string_concat (s, cc_quoted);
 650                 g_string_append_c (s, ' ');
 651 
 652                 g_string_free (cc_quoted, TRUE);
 653             }
 654 
 655             mc_g_string_concat (s, to_quoted);
 656         }
 657 
 658         g_string_free (to_quoted, TRUE);
 659     }
 660 
 661     if (s != NULL)
 662     {
 663         p = popen (s->str, "w");
 664         g_string_free (s, TRUE);
 665     }
 666 
 667     if (p != NULL)
 668     {
 669         off_t i;
 670 
 671         for (i = 0; i < buf->size; i++)
 672             if (fputc (edit_buffer_get_byte (buf, i), p) < 0)
 673                 break;
 674         pclose (p);
 675     }
 676 }
 677 
 678 /* --------------------------------------------------------------------------------------------- */
 679 
 680 static void
 681 edit_append_spaces_at_eol (WEdit *edit, const long col, const long width)
     /* [previous][next][first][last][top][bottom][index][help]  */
 682 {
 683     if (edit_buffer_get_current_byte (&edit->buffer) != '\n')
 684         for (long l = width - (edit_get_col (edit) - col); l > 0; l--)
 685             edit_insert (edit, ' ');
 686 }
 687 
 688 /* --------------------------------------------------------------------------------------------- */
 689 
 690 MC_TESTABLE void
 691 edit_insert_column_of_text (WEdit *edit, GString *data, long width, off_t *start_pos,
     /* [previous][next][first][last][top][bottom][index][help]  */
 692                             off_t *end_pos, long *col1, long *col2)
 693 {
 694     off_t cursor;
 695     long col;
 696 
 697     cursor = edit->buffer.curs1;
 698     col = edit_get_col (edit);
 699 
 700     for (gsize i = 0; i < data->len; i++)
 701     {
 702         if (data->str[i] != '\n')
 703             edit_insert (edit, data->str[i]);
 704         else
 705         {  // fill in and move to next line
 706             off_t p;
 707 
 708             edit_append_spaces_at_eol (edit, col, width);
 709 
 710             for (p = edit->buffer.curs1;; p++)
 711             {
 712                 if (p == edit->buffer.size)
 713                 {
 714                     edit_cursor_move (edit, edit->buffer.size - edit->buffer.curs1);
 715                     edit_insert_ahead (edit, '\n');
 716                     p++;
 717                     break;
 718                 }
 719                 if (edit_buffer_get_byte (&edit->buffer, p) == '\n')
 720                 {
 721                     p++;
 722                     break;
 723                 }
 724             }
 725             edit_cursor_move (edit, edit_move_forward3 (edit, p, col, 0) - edit->buffer.curs1);
 726 
 727             for (long l = col - edit_get_col (edit); l >= 1; l--)
 728                 edit_insert (edit, ' ');
 729         }
 730     }
 731 
 732     // add spaces at the end of the last line if this line is short
 733     edit_append_spaces_at_eol (edit, col, width);
 734 
 735     *col1 = col;
 736     *col2 = col + width;
 737     *start_pos = cursor;
 738     *end_pos = edit->buffer.curs1;
 739     edit_cursor_move (edit, cursor - edit->buffer.curs1);
 740 }
 741 
 742 /* --------------------------------------------------------------------------------------------- */
 743 /**
 744  * Callback for the iteration of objects in the 'editors' array.
 745  * Toggle syntax highlighting in editor object.
 746  *
 747  * @param data      probably WEdit object
 748  * @param user_data unused
 749  */
 750 
 751 static void
 752 edit_syntax_onoff_cb (void *data, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 753 {
 754     (void) user_data;
 755 
 756     if (edit_widget_is_editor (CONST_WIDGET (data)))
 757     {
 758         WEdit *edit = EDIT (data);
 759 
 760         if (edit_options.syntax_highlighting)
 761             edit_load_syntax (edit, NULL, edit->syntax_type);
 762         edit->force |= REDRAW_PAGE;
 763     }
 764 }
 765 
 766 /* --------------------------------------------------------------------------------------------- */
 767 
 768 static cb_ret_t
 769 editcmd_dialog_raw_key_query_cb (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 770 {
 771     WDialog *h = DIALOG (w);
 772 
 773     switch (msg)
 774     {
 775     case MSG_KEY:
 776         h->ret_value = parm;
 777         dlg_close (h);
 778         return MSG_HANDLED;
 779     default:
 780         return dlg_default_callback (w, sender, msg, parm, data);
 781     }
 782 }
 783 
 784 /* --------------------------------------------------------------------------------------------- */
 785 
 786 static gboolean
 787 editcmd_check_and_create_user_syntax_directory (const vfs_path_t *user_syntax_file_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 788 {
 789     gboolean ret;
 790     struct stat st;
 791 
 792     ret = mc_stat (user_syntax_file_vpath, &st) == 0;
 793     if (!ret)
 794     {
 795         // file doesn't exist -- check why
 796         const char *user_syntax_file_path = vfs_path_as_str (user_syntax_file_vpath);
 797 
 798         // check directory
 799         const char *user_syntax_file_basename = x_basename (user_syntax_file_path);
 800         char *user_syntax_dir;
 801         vfs_path_t *user_syntax_vpath;
 802 
 803         user_syntax_dir =
 804             g_strndup (user_syntax_file_path, user_syntax_file_basename - user_syntax_file_path);
 805         user_syntax_vpath = vfs_path_from_str (user_syntax_dir);
 806 
 807         ret = mc_stat (user_syntax_vpath, &st) == 0;
 808         if (!ret)
 809             ret = mc_mkdir (user_syntax_vpath, 0700) == 0;
 810         else if (!S_ISDIR (st.st_mode))
 811         {
 812             ret = FALSE;
 813             // set for file_error_message()
 814             errno = EEXIST;
 815         }
 816 
 817         if (!ret)
 818             file_error_message (_ ("Cannot create directory\n%s"), user_syntax_dir);
 819 
 820         vfs_path_free (user_syntax_vpath, TRUE);
 821         g_free (user_syntax_dir);
 822     }
 823 
 824     return ret;
 825 }
 826 
 827 /* --------------------------------------------------------------------------------------------- */
 828 /*** public functions ****************************************************************************/
 829 /* --------------------------------------------------------------------------------------------- */
 830 
 831 void
 832 edit_refresh_cmd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 833 {
 834     tty_clear_screen ();
 835     repaint_screen ();
 836     tty_keypad (TRUE);
 837 }
 838 
 839 /* --------------------------------------------------------------------------------------------- */
 840 /**
 841  * Toggle syntax highlighting in all editor windows.
 842  *
 843  * @param h root widget for all windows
 844  */
 845 
 846 void
 847 edit_syntax_onoff_cmd (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 848 {
 849     edit_options.syntax_highlighting = !edit_options.syntax_highlighting;
 850     g_list_foreach (GROUP (h)->widgets, edit_syntax_onoff_cb, NULL);
 851     widget_draw (WIDGET (h));
 852 }
 853 
 854 /* --------------------------------------------------------------------------------------------- */
 855 /**
 856  * Toggle tabs showing in all editor windows.
 857  *
 858  * @param h root widget for all windows
 859  */
 860 
 861 void
 862 edit_show_tabs_tws_cmd (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 863 {
 864     enable_show_tabs_tws = !enable_show_tabs_tws;
 865     widget_draw (WIDGET (h));
 866 }
 867 
 868 /* --------------------------------------------------------------------------------------------- */
 869 /**
 870  * Toggle right margin showing in all editor windows.
 871  *
 872  * @param h root widget for all windows
 873  */
 874 
 875 void
 876 edit_show_margin_cmd (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 877 {
 878     edit_options.show_right_margin = !edit_options.show_right_margin;
 879     widget_draw (WIDGET (h));
 880 }
 881 
 882 /* --------------------------------------------------------------------------------------------- */
 883 /**
 884  * Toggle line numbers showing in all editor windows.
 885  *
 886  * @param h root widget for all windows
 887  */
 888 
 889 void
 890 edit_show_numbers_cmd (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 891 {
 892     edit_options.line_state = !edit_options.line_state;
 893     edit_options.line_state_width = edit_options.line_state ? LINE_STATE_WIDTH : 0;
 894     widget_draw (WIDGET (h));
 895 }
 896 
 897 /* --------------------------------------------------------------------------------------------- */
 898 
 899 void
 900 edit_save_mode_cmd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 901 {
 902     char *str_result = NULL;
 903 
 904     const char *str[] = {
 905         _ ("&Quick save"),
 906         _ ("&Safe save"),
 907         _ ("&Do backups with following extension:"),
 908     };
 909 
 910     g_assert (edit_options.backup_ext != NULL);
 911 
 912     {
 913         quick_widget_t quick_widgets[] = {
 914             QUICK_RADIO (3, str, &edit_options.save_mode, &edit_save_mode_radio_id),
 915             QUICK_INPUT (edit_options.backup_ext, "edit-backup-ext", &str_result,
 916                          &edit_save_mode_input_id, FALSE, FALSE, INPUT_COMPLETE_NONE),
 917             QUICK_SEPARATOR (TRUE),
 918             QUICK_CHECKBOX (_ ("Check &POSIX new line"), &edit_options.check_nl_at_eof, NULL),
 919             QUICK_BUTTONS_OK_CANCEL,
 920             QUICK_END,
 921         };
 922 
 923         WRect r = { -1, -1, 0, 38 };
 924 
 925         quick_dialog_t qdlg = {
 926             .rect = r,
 927             .title = _ ("Edit Save Mode"),
 928             .help = "[Edit Save Mode]",
 929             .widgets = quick_widgets,
 930             .callback = edit_save_mode_callback,
 931             .mouse_callback = NULL,
 932         };
 933 
 934         if (quick_dialog (&qdlg) != B_CANCEL)
 935         {
 936             g_free (edit_options.backup_ext);
 937             edit_options.backup_ext = str_result;
 938         }
 939     }
 940 }
 941 
 942 /* --------------------------------------------------------------------------------------------- */
 943 
 944 void
 945 edit_set_filename (WEdit *edit, const vfs_path_t *name_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 946 {
 947     vfs_path_free (edit->filename_vpath, TRUE);
 948     edit->filename_vpath = vfs_path_clone (name_vpath);
 949 
 950     if (edit->dir_vpath == NULL)
 951         edit->dir_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
 952 }
 953 
 954 /* --------------------------------------------------------------------------------------------- */
 955 /* Here we want to warn the users of overwriting an existing file,
 956    but only if they have made a change to the filename */
 957 /* returns TRUE on success */
 958 gboolean
 959 edit_save_as_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 960 {
 961     // This heads the 'Save As' dialog box
 962     vfs_path_t *exp_vpath;
 963     int save_lock = 0;
 964     gboolean different_filename = FALSE;
 965     gboolean ret = FALSE;
 966 
 967     if (!edit_check_newline (&edit->buffer))
 968         return FALSE;
 969 
 970     exp_vpath = edit_get_save_file_as (edit);
 971     edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
 972 
 973     if (exp_vpath != NULL && vfs_path_len (exp_vpath) != 0)
 974     {
 975         int rv;
 976 
 977         if (!vfs_path_equal (edit->filename_vpath, exp_vpath))
 978         {
 979             int file;
 980             struct stat sb;
 981 
 982             if (mc_stat (exp_vpath, &sb) == 0 && !S_ISREG (sb.st_mode))
 983             {
 984                 file_error_message (_ ("Cannot save\n%s:\nnot a regular file"),
 985                                     vfs_path_as_str (exp_vpath));
 986                 goto ret;
 987             }
 988 
 989             different_filename = TRUE;
 990             file = mc_open (exp_vpath, O_RDONLY | O_BINARY);
 991 
 992             if (file == -1)
 993                 edit->stat1.st_mode |= S_IWUSR;
 994             else
 995             {
 996                 // the file exists
 997                 mc_close (file);
 998                 // Overwrite the current file or cancel the operation
 999                 if (edit_query_dialog2 (_ ("Warning"), _ ("A file already exists with this name"),
1000                                         _ ("&Overwrite"), _ ("&Cancel")))
1001                     goto ret;
1002             }
1003 
1004             save_lock = lock_file (exp_vpath);
1005         }
1006         else if (edit->locked == 0 && edit->delete_file == 0)
1007             // filenames equal, check if already locked
1008             save_lock = lock_file (exp_vpath);
1009 
1010         if (different_filename)
1011             /* Allow user to write into saved (under another name) file
1012              * even if original file had r/o user permissions. */
1013             edit->stat1.st_mode |= S_IWUSR;
1014 
1015         rv = edit_save_file (edit, exp_vpath);
1016         switch (rv)
1017         {
1018         case 1:
1019             // Successful, so unlock both files
1020             if (different_filename)
1021             {
1022                 if (save_lock != 0)
1023                     unlock_file (exp_vpath);
1024                 if (edit->locked != 0)
1025                     edit->locked = unlock_file (edit->filename_vpath);
1026             }
1027             else if (edit->locked != 0 || save_lock != 0)
1028                 edit->locked = unlock_file (edit->filename_vpath);
1029 
1030             edit_set_filename (edit, exp_vpath);
1031             if (edit->lb != LB_ASIS)
1032                 edit_reload (edit, exp_vpath);
1033             edit->modified = 0;
1034             edit->delete_file = 0;
1035             if (different_filename)
1036                 edit_load_syntax (edit, NULL, edit->syntax_type);
1037             ret = TRUE;
1038             break;
1039 
1040         default:
1041             message (D_ERROR, MSG_ERROR, "%s", _ ("Cannot save file"));
1042             MC_FALLTHROUGH;
1043 
1044         case -1:
1045             // Failed, so maintain modify (not save) lock
1046             if (save_lock != 0)
1047                 unlock_file (exp_vpath);
1048             break;
1049         }
1050     }
1051 
1052 ret:
1053     vfs_path_free (exp_vpath, TRUE);
1054     edit->force |= REDRAW_COMPLETELY;
1055     return ret;
1056 }
1057 
1058 /* --------------------------------------------------------------------------------------------- */
1059 /** returns TRUE on success */
1060 
1061 gboolean
1062 edit_save_confirm_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1063 {
1064     if (edit->filename_vpath == NULL)
1065         return edit_save_as_cmd (edit);
1066 
1067     if (!edit_check_newline (&edit->buffer))
1068         return FALSE;
1069 
1070     if (edit_options.confirm_save)
1071     {
1072         char *f;
1073         gboolean ok;
1074 
1075         f = g_strdup_printf (_ ("Confirm save file: \"%s\""),
1076                              vfs_path_as_str (edit->filename_vpath));
1077         ok = (edit_query_dialog2 (_ ("Save file"), f, _ ("&Save"), _ ("&Cancel")) == 0);
1078         g_free (f);
1079         if (!ok)
1080             return FALSE;
1081     }
1082 
1083     return edit_save_cmd (edit);
1084 }
1085 
1086 /* --------------------------------------------------------------------------------------------- */
1087 /**
1088  * Ask file to edit and load it.
1089  *
1090  * @return TRUE on success or cancel of ask.
1091  */
1092 
1093 gboolean
1094 edit_load_cmd (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1095 {
1096     char *exp;
1097     gboolean ret = TRUE;  // possible cancel
1098 
1099     exp = input_expand_dialog (_ ("Load"), _ ("Enter file name:"), MC_HISTORY_EDIT_LOAD,
1100                                INPUT_LAST_TEXT, INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD);
1101 
1102     if (exp != NULL && *exp != '\0')
1103     {
1104         vfs_path_t *exp_vpath;
1105         edit_arg_t arg;
1106 
1107         exp_vpath = vfs_path_from_str (exp);
1108         edit_arg_init (&arg, exp_vpath, 0);
1109         ret = edit_load_file_from_filename (h, &arg);
1110         vfs_path_free (exp_vpath, TRUE);
1111     }
1112 
1113     g_free (exp);
1114 
1115     return ret;
1116 }
1117 
1118 /* --------------------------------------------------------------------------------------------- */
1119 /**
1120  * Load file content
1121  *
1122  * @param h screen the owner of editor window
1123  * @param vpath vfs file path
1124  * @param line line number
1125  *
1126  * @return TRUE if file content was successfully loaded, FALSE otherwise
1127  */
1128 
1129 gboolean
1130 edit_load_file_from_filename (WDialog *h, const edit_arg_t *arg)
     /* [previous][next][first][last][top][bottom][index][help]  */
1131 {
1132     WRect r = WIDGET (h)->rect;
1133 
1134     rect_grow (&r, -1, 0);
1135 
1136     return edit_add_window (h, &r, arg);
1137 }
1138 
1139 /* --------------------------------------------------------------------------------------------- */
1140 /**
1141  * Show history od edited or viewed files and open selected file.
1142  *
1143  * @return TRUE on success, FALSE otherwise.
1144  */
1145 
1146 gboolean
1147 edit_load_file_from_history (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1148 {
1149     char *exp;
1150     int action;
1151     gboolean ret = TRUE;  // possible cancel
1152 
1153     exp = show_file_history (CONST_WIDGET (h), &action);
1154     if (exp != NULL && (action == CK_Edit || action == CK_Enter))
1155     {
1156         vfs_path_t *exp_vpath;
1157         edit_arg_t arg;
1158 
1159         exp_vpath = vfs_path_from_str (exp);
1160         edit_arg_init (&arg, exp_vpath, 0);
1161         ret = edit_load_file_from_filename (h, &arg);
1162         vfs_path_free (exp_vpath, TRUE);
1163     }
1164 
1165     g_free (exp);
1166 
1167     return ret;
1168 }
1169 
1170 /* --------------------------------------------------------------------------------------------- */
1171 /**
1172  * Load syntax file to edit.
1173  *
1174  * @return TRUE on success
1175  */
1176 
1177 gboolean
1178 edit_load_syntax_file (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1179 {
1180     vfs_path_t *extdir_vpath;
1181     int dir = 0;
1182     edit_arg_t arg;
1183     gboolean ret = FALSE;
1184 
1185     if (geteuid () == 0)
1186         dir = query_dialog (_ ("Syntax file edit"), _ ("Which syntax file you want to edit?"),
1187                             D_NORMAL, 2, _ ("&User"), _ ("&System wide"));
1188 
1189     extdir_vpath =
1190         vfs_path_build_filename (mc_global.sysconfig_dir, EDIT_SYNTAX_FILE, (char *) NULL);
1191     if (!exist_file (vfs_path_get_last_path_str (extdir_vpath)))
1192     {
1193         vfs_path_free (extdir_vpath, TRUE);
1194         extdir_vpath =
1195             vfs_path_build_filename (mc_global.share_data_dir, EDIT_SYNTAX_FILE, (char *) NULL);
1196     }
1197 
1198     if (dir == 0)
1199     {
1200         vfs_path_t *user_syntax_file_vpath;
1201 
1202         user_syntax_file_vpath = mc_config_get_full_vpath (EDIT_SYNTAX_FILE);
1203 
1204         if (editcmd_check_and_create_user_syntax_directory (user_syntax_file_vpath))
1205         {
1206             check_for_default (extdir_vpath, user_syntax_file_vpath);
1207             edit_arg_init (&arg, user_syntax_file_vpath, 0);
1208             ret = edit_load_file_from_filename (h, &arg);
1209         }
1210 
1211         vfs_path_free (user_syntax_file_vpath, TRUE);
1212     }
1213     else if (dir == 1)
1214     {
1215         edit_arg_init (&arg, extdir_vpath, 0);
1216         ret = edit_load_file_from_filename (h, &arg);
1217     }
1218 
1219     vfs_path_free (extdir_vpath, TRUE);
1220 
1221     return ret;
1222 }
1223 
1224 /* --------------------------------------------------------------------------------------------- */
1225 /**
1226  * Load menu file to edit.
1227  *
1228  * @return TRUE on success
1229  */
1230 
1231 gboolean
1232 edit_load_menu_file (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1233 {
1234     vfs_path_t *buffer_vpath;
1235     vfs_path_t *menufile_vpath;
1236     int dir;
1237     edit_arg_t arg;
1238     gboolean ret;
1239 
1240     query_set_sel (1);
1241     dir = query_dialog (_ ("Menu edit"), _ ("Which menu file do you want to edit?"), D_NORMAL,
1242                         geteuid () != 0 ? 2 : 3, _ ("&Local"), _ ("&User"), _ ("&System wide"));
1243 
1244     menufile_vpath =
1245         vfs_path_build_filename (mc_global.sysconfig_dir, EDIT_GLOBAL_MENU, (char *) NULL);
1246     if (!exist_file (vfs_path_get_last_path_str (menufile_vpath)))
1247     {
1248         vfs_path_free (menufile_vpath, TRUE);
1249         menufile_vpath =
1250             vfs_path_build_filename (mc_global.share_data_dir, EDIT_GLOBAL_MENU, (char *) NULL);
1251     }
1252 
1253     switch (dir)
1254     {
1255     case 0:
1256         buffer_vpath = vfs_path_from_str (EDIT_LOCAL_MENU);
1257         check_for_default (menufile_vpath, buffer_vpath);
1258         chmod (vfs_path_get_last_path_str (buffer_vpath), 0600);
1259         break;
1260 
1261     case 1:
1262         buffer_vpath = mc_config_get_full_vpath (EDIT_HOME_MENU);
1263         check_for_default (menufile_vpath, buffer_vpath);
1264         break;
1265 
1266     case 2:
1267         buffer_vpath =
1268             vfs_path_build_filename (mc_global.sysconfig_dir, EDIT_GLOBAL_MENU, (char *) NULL);
1269         if (!exist_file (vfs_path_get_last_path_str (buffer_vpath)))
1270         {
1271             vfs_path_free (buffer_vpath, TRUE);
1272             buffer_vpath =
1273                 vfs_path_build_filename (mc_global.share_data_dir, EDIT_GLOBAL_MENU, (char *) NULL);
1274         }
1275         break;
1276 
1277     default:
1278         vfs_path_free (menufile_vpath, TRUE);
1279         return FALSE;
1280     }
1281 
1282     edit_arg_init (&arg, buffer_vpath, 0);
1283     ret = edit_load_file_from_filename (h, &arg);
1284 
1285     vfs_path_free (buffer_vpath, TRUE);
1286     vfs_path_free (menufile_vpath, TRUE);
1287 
1288     return ret;
1289 }
1290 
1291 /* --------------------------------------------------------------------------------------------- */
1292 /**
1293  * Close window with opened file.
1294  *
1295  * @return TRUE if file was closed.
1296  */
1297 
1298 gboolean
1299 edit_close_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1300 {
1301     gboolean ret;
1302 
1303     ret = (edit != NULL) && edit_ok_to_quit (edit);
1304 
1305     if (ret)
1306     {
1307         Widget *w = WIDGET (edit);
1308         WGroup *g = w->owner;
1309 
1310         if (edit->locked != 0)
1311             edit->locked = unlock_file (edit->filename_vpath);
1312 
1313         group_remove_widget (w);
1314         widget_destroy (w);
1315 
1316         if (edit_widget_is_editor (CONST_WIDGET (g->current->data)))
1317             edit = EDIT (g->current->data);
1318         else
1319         {
1320             edit = edit_find_editor (DIALOG (g));
1321             if (edit != NULL)
1322                 widget_select (WIDGET (edit));
1323         }
1324     }
1325 
1326     if (edit != NULL)
1327         edit->force |= REDRAW_COMPLETELY;
1328 
1329     return ret;
1330 }
1331 
1332 /* --------------------------------------------------------------------------------------------- */
1333 
1334 void
1335 edit_block_copy_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1336 {
1337     off_t start_mark, end_mark, current = edit->buffer.curs1;
1338     off_t mark1 = 0, mark2 = 0;
1339     long c1 = 0, c2 = 0;
1340     GString *copy_buf;
1341 
1342     edit_update_curs_col (edit);
1343     if (!eval_marks (edit, &start_mark, &end_mark))
1344         return;
1345 
1346     copy_buf = edit_get_block (edit, start_mark, end_mark);
1347 
1348     // all that gets pushed are deletes hence little space is used on the stack
1349 
1350     edit_push_markers (edit);
1351 
1352     if (edit->column_highlight)
1353     {
1354         long col_delta;
1355 
1356         col_delta = labs (edit->column2 - edit->column1);
1357         edit_insert_column_of_text (edit, copy_buf, col_delta, &mark1, &mark2, &c1, &c2);
1358     }
1359     else
1360     {
1361         for (gsize i = copy_buf->len; i != 0; i--)
1362             edit_insert_ahead (edit, copy_buf->str[i - 1]);
1363 
1364         // Place cursor at the end of text selection
1365         if (edit_options.cursor_after_inserted_block)
1366             edit_cursor_move (edit, copy_buf->len);
1367     }
1368 
1369     g_string_free (copy_buf, TRUE);
1370     edit_scroll_screen_over_cursor (edit);
1371 
1372     if (edit->column_highlight)
1373         edit_set_markers (edit, edit->buffer.curs1, mark2, c1, c2);
1374     else if (start_mark < current && end_mark > current)
1375         edit_set_markers (edit, start_mark, end_mark + end_mark - start_mark, 0, 0);
1376 
1377     edit->force |= REDRAW_PAGE;
1378 }
1379 
1380 /* --------------------------------------------------------------------------------------------- */
1381 
1382 void
1383 edit_block_move_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1384 {
1385     off_t current;
1386     off_t start_mark, end_mark;
1387 
1388     if (!eval_marks (edit, &start_mark, &end_mark))
1389         return;
1390 
1391     if (!edit->column_highlight && edit->buffer.curs1 > start_mark && edit->buffer.curs1 < end_mark)
1392         return;
1393 
1394     if (edit->mark2 < 0)
1395         edit_mark_cmd (edit, FALSE);
1396     edit_push_markers (edit);
1397 
1398     if (edit->column_highlight)
1399     {
1400         off_t mark1, mark2;
1401         long c1, c2, b_width;
1402         long x, x2;
1403         off_t b1, b2;
1404         GString *copy_buf;
1405 
1406         c1 = MIN (edit->column1, edit->column2);
1407         c2 = MAX (edit->column1, edit->column2);
1408         b_width = c2 - c1;
1409 
1410         edit_update_curs_col (edit);
1411 
1412         x = edit->curs_col;
1413         x2 = x + edit->over_col;
1414 
1415         // do nothing when cursor inside first line of selected area
1416         b1 = edit_buffer_get_eol (&edit->buffer, edit->buffer.curs1);
1417         b2 = edit_buffer_get_eol (&edit->buffer, start_mark);
1418         if (b1 == b2 && x2 > c1 && x2 <= c2)
1419             return;
1420 
1421         if (edit->buffer.curs1 > start_mark
1422             && edit->buffer.curs1 < edit_buffer_get_eol (&edit->buffer, end_mark))
1423         {
1424             if (x > c2)
1425                 x -= b_width;
1426             else if (x > c1 && x <= c2)
1427                 x = c1;
1428         }
1429         // save current selection into buffer
1430         copy_buf = edit_get_block (edit, start_mark, end_mark);
1431 
1432         // remove current selection
1433         edit_block_delete_cmd (edit);
1434 
1435         edit->over_col = MAX (0, edit->over_col - b_width);
1436         // calculate the cursor pos after delete block
1437         b1 = edit_buffer_get_current_bol (&edit->buffer);
1438         current = edit_move_forward3 (edit, b1, x, 0);
1439         edit_cursor_move (edit, current - edit->buffer.curs1);
1440         edit_scroll_screen_over_cursor (edit);
1441 
1442         // add TWS if need before block insertion
1443         if (edit_options.cursor_beyond_eol && edit->over_col > 0)
1444             edit_insert_over (edit);
1445 
1446         edit_insert_column_of_text (edit, copy_buf, b_width, &mark1, &mark2, &c1, &c2);
1447         edit_set_markers (edit, mark1, mark2, c1, c2);
1448         g_string_free (copy_buf, TRUE);
1449     }
1450     else
1451     {
1452         unsigned char *copy_buf;
1453         off_t count, count_orig;
1454         off_t x;
1455 
1456         current = edit->buffer.curs1;
1457         copy_buf = g_malloc0 (end_mark - start_mark);
1458         edit_cursor_move (edit, start_mark - edit->buffer.curs1);
1459         edit_scroll_screen_over_cursor (edit);
1460 
1461         for (count = start_mark; count < end_mark; count++)
1462             copy_buf[end_mark - count - 1] = edit_delete (edit, TRUE);
1463 
1464         edit_scroll_screen_over_cursor (edit);
1465         x = current > edit->buffer.curs1 ? end_mark - start_mark : 0;
1466         edit_cursor_move (edit, current - edit->buffer.curs1 - x);
1467         edit_scroll_screen_over_cursor (edit);
1468         count_orig = count;
1469         while (count-- > start_mark)
1470             edit_insert_ahead (edit, copy_buf[end_mark - count - 1]);
1471         g_free (copy_buf);
1472 
1473         edit_set_markers (edit, edit->buffer.curs1, edit->buffer.curs1 + end_mark - start_mark, 0,
1474                           0);
1475 
1476         // Place cursor at the end of text selection
1477         if (edit_options.cursor_after_inserted_block)
1478             edit_cursor_move (edit, count_orig - start_mark);
1479     }
1480 
1481     edit_scroll_screen_over_cursor (edit);
1482     edit->force |= REDRAW_PAGE;
1483 }
1484 
1485 /* --------------------------------------------------------------------------------------------- */
1486 /** returns FALSE if canceelled by user */
1487 
1488 gboolean
1489 edit_block_delete_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1490 {
1491     off_t start_mark, end_mark;
1492 
1493     if (eval_marks (edit, &start_mark, &end_mark))
1494         return edit_block_delete (edit, start_mark, end_mark);
1495 
1496     edit_delete_line (edit);
1497 
1498     return TRUE;
1499 }
1500 
1501 /* --------------------------------------------------------------------------------------------- */
1502 /**
1503  * Check if it's OK to close the file. If there are unsaved changes, ask user.
1504  *
1505  * @return TRUE if it's OK to exit, FALSE to continue editing.
1506  */
1507 
1508 gboolean
1509 edit_ok_to_quit (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1510 {
1511     const char *fname;
1512     char *msg;
1513     int act;
1514 
1515     if (edit->modified == 0)
1516         return TRUE;
1517 
1518     if (edit->filename_vpath != NULL)
1519         fname = vfs_path_as_str (edit->filename_vpath);
1520     else
1521         fname = _ ("[NoName]");
1522 
1523     if (!mc_global.midnight_shutdown)
1524     {
1525         query_set_sel (2);
1526 
1527         msg = g_strdup_printf (_ ("File %s was modified.\nSave before close?"), fname);
1528         act = edit_query_dialog3 (_ ("Close file"), msg, _ ("&Yes"), _ ("&No"), _ ("&Cancel"));
1529     }
1530     else
1531     {
1532         msg = g_strdup_printf (_ ("%s is being shut down.\nSave modified file %s?"), PACKAGE_NAME,
1533                                fname);
1534         act = edit_query_dialog2 (_ ("Quit"), msg, _ ("&Yes"), _ ("&No"));
1535 
1536         // Esc is No
1537         if (act == -1)
1538             act = 1;
1539     }
1540 
1541     g_free (msg);
1542 
1543     switch (act)
1544     {
1545     case 0:  // Yes
1546         if (!mc_global.midnight_shutdown && !edit_check_newline (&edit->buffer))
1547             return FALSE;
1548         edit_push_markers (edit);
1549         edit_set_markers (edit, 0, 0, 0, 0);
1550         if (!edit_save_cmd (edit) || mc_global.midnight_shutdown)
1551             return mc_global.midnight_shutdown;
1552         break;
1553     case 1:  // No
1554     default:
1555         break;
1556     case 2:   // Cancel
1557     case -1:  // Esc
1558         return FALSE;
1559     }
1560 
1561     return TRUE;
1562 }
1563 
1564 /* --------------------------------------------------------------------------------------------- */
1565 /** save block, returns TRUE on success */
1566 
1567 gboolean
1568 edit_save_block (WEdit *edit, const char *filename, off_t start, off_t finish)
     /* [previous][next][first][last][top][bottom][index][help]  */
1569 {
1570     int file;
1571     vfs_path_t *vpath;
1572     off_t len = 1;
1573 
1574     vpath = vfs_path_from_str (filename);
1575     file = mc_open (vpath, O_CREAT | O_WRONLY | O_TRUNC,
1576                     S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY);
1577     vfs_path_free (vpath, TRUE);
1578     if (file == -1)
1579         return FALSE;
1580 
1581     if (edit->column_highlight)
1582     {
1583         int r;
1584 
1585         r = mc_write (file, VERTICAL_MAGIC, sizeof (VERTICAL_MAGIC));
1586         if (r > 0)
1587         {
1588             GString *block;
1589             char *p;
1590 
1591             block = edit_get_block (edit, start, finish);
1592             p = block->str;
1593             len = block->len;
1594 
1595             while (len != 0)
1596             {
1597                 r = mc_write (file, p, len);
1598                 if (r < 0)
1599                     break;
1600                 p += r;
1601                 len -= r;
1602             }
1603 
1604             g_string_free (block, TRUE);
1605         }
1606     }
1607     else
1608     {
1609         unsigned char *buf;
1610         off_t i = start;
1611 
1612         len = finish - start;
1613         buf = g_malloc0 (TEMP_BUF_LEN);
1614         while (start != finish)
1615         {
1616             off_t end;
1617 
1618             end = MIN (finish, start + TEMP_BUF_LEN);
1619             for (; i < end; i++)
1620                 buf[i - start] = edit_buffer_get_byte (&edit->buffer, i);
1621             len -= mc_write (file, (char *) buf, end - start);
1622             start = end;
1623         }
1624         g_free (buf);
1625     }
1626     mc_close (file);
1627 
1628     return (len == 0);
1629 }
1630 
1631 /* --------------------------------------------------------------------------------------------- */
1632 
1633 void
1634 edit_paste_from_history (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1635 {
1636     (void) edit;
1637 
1638     message (D_ERROR, MSG_ERROR, "%s", _ ("This function is not implemented"));
1639 }
1640 
1641 /* --------------------------------------------------------------------------------------------- */
1642 
1643 gboolean
1644 edit_copy_to_X_buf_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1645 {
1646     off_t start_mark, end_mark;
1647 
1648     if (!eval_marks (edit, &start_mark, &end_mark))
1649         return TRUE;
1650 
1651     if (!edit_save_block_to_clip_file (edit, start_mark, end_mark))
1652     {
1653         message (D_ERROR, MSG_ERROR, "%s", _ ("Unable to save to file"));
1654         return FALSE;
1655     }
1656     // try use external clipboard utility
1657     mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
1658 
1659     if (edit_options.drop_selection_on_copy)
1660         edit_mark_cmd (edit, TRUE);
1661 
1662     return TRUE;
1663 }
1664 
1665 /* --------------------------------------------------------------------------------------------- */
1666 
1667 gboolean
1668 edit_cut_to_X_buf_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1669 {
1670     off_t start_mark, end_mark;
1671 
1672     if (!eval_marks (edit, &start_mark, &end_mark))
1673         return TRUE;
1674 
1675     if (!edit_save_block_to_clip_file (edit, start_mark, end_mark))
1676     {
1677         message (D_ERROR, MSG_ERROR, "%s", _ ("Unable to save to file"));
1678         return FALSE;
1679     }
1680     // try use external clipboard utility
1681     mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
1682 
1683     edit_block_delete_cmd (edit);
1684     edit_mark_cmd (edit, TRUE);
1685 
1686     return TRUE;
1687 }
1688 
1689 /* --------------------------------------------------------------------------------------------- */
1690 
1691 gboolean
1692 edit_paste_from_X_buf_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1693 {
1694     vfs_path_t *tmp;
1695     gboolean ret;
1696 
1697     // try use external clipboard utility
1698     mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_from_ext_clip", NULL);
1699     tmp = mc_config_get_full_vpath (EDIT_HOME_CLIP_FILE);
1700     ret = (edit_insert_file (edit, tmp) >= 0);
1701     vfs_path_free (tmp, TRUE);
1702 
1703     return ret;
1704 }
1705 
1706 /* --------------------------------------------------------------------------------------------- */
1707 /**
1708  * Ask user for the line and go to that line.
1709  * Negative numbers mean line from the end (i.e. -1 is the last line).
1710  */
1711 
1712 void
1713 edit_goto_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1714 {
1715     static gboolean first_run = TRUE;
1716 
1717     char *f;
1718     long l;
1719     char *error;
1720 
1721     f = input_dialog (_ ("Goto line"), _ ("Enter line:"), MC_HISTORY_EDIT_GOTO_LINE,
1722                       first_run ? NULL : INPUT_LAST_TEXT, INPUT_COMPLETE_NONE);
1723     if (f == NULL || *f == '\0')
1724     {
1725         g_free (f);
1726         return;
1727     }
1728 
1729     l = strtol (f, &error, 0);
1730     if (*error != '\0')
1731     {
1732         g_free (f);
1733         return;
1734     }
1735 
1736     if (l < 0)
1737         l = edit->buffer.lines + l + 2;
1738 
1739     edit_move_display (edit, l - WIDGET (edit)->rect.lines / 2 - 1);
1740     edit_move_to_line (edit, l - 1);
1741     edit->force |= REDRAW_COMPLETELY;
1742 
1743     g_free (f);
1744     first_run = FALSE;
1745 }
1746 
1747 /* --------------------------------------------------------------------------------------------- */
1748 /** Return TRUE on success */
1749 
1750 gboolean
1751 edit_save_block_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1752 {
1753     off_t start_mark, end_mark;
1754     char *exp, *tmp;
1755     gboolean ret = FALSE;
1756 
1757     if (!eval_marks (edit, &start_mark, &end_mark))
1758         return TRUE;
1759 
1760     tmp = mc_config_get_full_path (EDIT_HOME_CLIP_FILE);
1761     exp = input_expand_dialog (_ ("Save block"), _ ("Enter file name:"), MC_HISTORY_EDIT_SAVE_BLOCK,
1762                                tmp, INPUT_COMPLETE_FILENAMES);
1763     g_free (tmp);
1764     edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
1765 
1766     if (exp != NULL && *exp != '\0')
1767     {
1768         if (edit_save_block (edit, exp, start_mark, end_mark))
1769             ret = TRUE;
1770         else
1771             message (D_ERROR, MSG_ERROR, "%s", _ ("Cannot save block"));
1772 
1773         edit->force |= REDRAW_COMPLETELY;
1774     }
1775 
1776     g_free (exp);
1777 
1778     return ret;
1779 }
1780 
1781 /* --------------------------------------------------------------------------------------------- */
1782 
1783 /** returns TRUE on success */
1784 gboolean
1785 edit_insert_file_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1786 {
1787     char *tmp;
1788     char *exp;
1789     gboolean ret = FALSE;
1790 
1791     tmp = mc_config_get_full_path (EDIT_HOME_CLIP_FILE);
1792     exp = input_expand_dialog (_ ("Insert file"), _ ("Enter file name:"),
1793                                MC_HISTORY_EDIT_INSERT_FILE, tmp, INPUT_COMPLETE_FILENAMES);
1794     g_free (tmp);
1795 
1796     edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
1797 
1798     if (exp != NULL && *exp != '\0')
1799     {
1800         vfs_path_t *exp_vpath;
1801 
1802         exp_vpath = vfs_path_from_str (exp);
1803         ret = (edit_insert_file (edit, exp_vpath) >= 0);
1804         vfs_path_free (exp_vpath, TRUE);
1805 
1806         if (!ret)
1807             message (D_ERROR, MSG_ERROR, "%s", _ ("Cannot insert file"));
1808     }
1809 
1810     g_free (exp);
1811 
1812     edit->force |= REDRAW_COMPLETELY;
1813     return ret;
1814 }
1815 
1816 /* --------------------------------------------------------------------------------------------- */
1817 /** sorts a block, returns -1 on system fail, 1 on cancel and 0 on success */
1818 
1819 int
1820 edit_sort_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1821 {
1822     char *exp, *tmp, *tmp_edit_block_name, *tmp_edit_temp_name;
1823     off_t start_mark, end_mark;
1824     int e;
1825 
1826     if (!eval_marks (edit, &start_mark, &end_mark))
1827     {
1828         message (D_ERROR, MSG_ERROR, "%s", _ ("You must first highlight a block of text"));
1829         return 0;
1830     }
1831 
1832     tmp = mc_config_get_full_path (EDIT_HOME_BLOCK_FILE);
1833     edit_save_block (edit, tmp, start_mark, end_mark);
1834     g_free (tmp);
1835 
1836     exp = input_dialog (_ ("Run sort"),
1837                         _ ("Enter sort options (see sort(1) manpage) separated by whitespace:"),
1838                         MC_HISTORY_EDIT_SORT, INPUT_LAST_TEXT, INPUT_COMPLETE_NONE);
1839 
1840     if (exp == NULL)
1841         return 1;
1842 
1843     tmp_edit_block_name = mc_config_get_full_path (EDIT_HOME_BLOCK_FILE);
1844     tmp_edit_temp_name = mc_config_get_full_path (EDIT_HOME_TEMP_FILE);
1845     tmp = g_strconcat (" sort ", exp, " ", tmp_edit_block_name, " > ", tmp_edit_temp_name,
1846                        (char *) NULL);
1847     g_free (tmp_edit_temp_name);
1848     g_free (tmp_edit_block_name);
1849     g_free (exp);
1850 
1851     e = system (tmp);
1852     g_free (tmp);
1853     if (e != 0)
1854     {
1855         if (e == -1 || e == 127)
1856             message (D_ERROR, MSG_ERROR, "%s", _ ("Cannot execute sort command"));
1857         else
1858         {
1859             char q[8];
1860 
1861             sprintf (q, "%d ", e);
1862             message (D_ERROR, MSG_ERROR, _ ("Sort returned non-zero: %s"), q);
1863         }
1864 
1865         return -1;
1866     }
1867 
1868     edit->force |= REDRAW_COMPLETELY;
1869 
1870     if (!edit_block_delete_cmd (edit))
1871         return 1;
1872 
1873     {
1874         vfs_path_t *tmp_vpath;
1875 
1876         tmp_vpath = mc_config_get_full_vpath (EDIT_HOME_TEMP_FILE);
1877         edit_insert_file (edit, tmp_vpath);
1878         vfs_path_free (tmp_vpath, TRUE);
1879     }
1880 
1881     return 0;
1882 }
1883 
1884 /* --------------------------------------------------------------------------------------------- */
1885 /**
1886  * Ask user for a command, execute it and paste its output back to the
1887  * editor.
1888  */
1889 
1890 int
1891 edit_ext_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1892 {
1893     char *exp, *tmp, *tmp_edit_temp_file;
1894     int e;
1895 
1896     exp =
1897         input_dialog (_ ("Paste output of external command"), _ ("Enter shell command(s):"),
1898                       MC_HISTORY_EDIT_PASTE_EXTCMD, INPUT_LAST_TEXT,
1899                       INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES
1900                           | INPUT_COMPLETE_HOSTNAMES | INPUT_COMPLETE_CD | INPUT_COMPLETE_COMMANDS
1901                           | INPUT_COMPLETE_SHELL_ESC);
1902 
1903     if (!exp)
1904         return 1;
1905 
1906     tmp_edit_temp_file = mc_config_get_full_path (EDIT_HOME_TEMP_FILE);
1907     tmp = g_strconcat (exp, " > ", tmp_edit_temp_file, (char *) NULL);
1908     g_free (tmp_edit_temp_file);
1909     e = system (tmp);
1910     g_free (tmp);
1911     g_free (exp);
1912 
1913     if (e != 0)
1914     {
1915         message (D_ERROR, MSG_ERROR, "%s", _ ("Cannot execute external command"));
1916         return -1;
1917     }
1918 
1919     edit->force |= REDRAW_COMPLETELY;
1920 
1921     {
1922         vfs_path_t *tmp_vpath;
1923 
1924         tmp_vpath = mc_config_get_full_vpath (EDIT_HOME_TEMP_FILE);
1925         edit_insert_file (edit, tmp_vpath);
1926         vfs_path_free (tmp_vpath, TRUE);
1927     }
1928 
1929     return 0;
1930 }
1931 
1932 /* --------------------------------------------------------------------------------------------- */
1933 /** if block is 1, a block must be highlighted and the shell command
1934    processes it. If block is 0 the shell command is a straight system
1935    command, that just produces some output which is to be inserted */
1936 
1937 void
1938 edit_block_process_cmd (WEdit *edit, int macro_number)
     /* [previous][next][first][last][top][bottom][index][help]  */
1939 {
1940     char *fname;
1941     char *macros_fname = NULL;
1942 
1943     fname = g_strdup_printf ("%s.%i.sh", EDIT_HOME_MACRO_FILE, macro_number);
1944     macros_fname = g_build_filename (mc_config_get_data_path (), fname, (char *) NULL);
1945     edit_user_menu (edit, macros_fname, 0);
1946     g_free (fname);
1947     g_free (macros_fname);
1948     edit->force |= REDRAW_COMPLETELY;
1949 }
1950 
1951 /* --------------------------------------------------------------------------------------------- */
1952 
1953 void
1954 edit_mail_dialog (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1955 {
1956     char *mail_to = NULL;
1957     char *mail_subject = NULL;
1958     char *mail_cc = NULL;
1959 
1960     quick_widget_t quick_widgets[] = {
1961         QUICK_LABEL (_ ("mail -s <subject> -c <cc> <to>"), NULL),
1962         QUICK_LABELED_INPUT (_ ("To"), input_label_above, INPUT_LAST_TEXT, "mail-dlg-input-3",
1963                              &mail_to, NULL, FALSE, FALSE, INPUT_COMPLETE_USERNAMES),
1964         QUICK_LABELED_INPUT (_ ("Subject"), input_label_above, INPUT_LAST_TEXT, "mail-dlg-input-2",
1965                              &mail_subject, NULL, FALSE, FALSE, INPUT_COMPLETE_NONE),
1966         QUICK_LABELED_INPUT (_ ("Copies to"), input_label_above, INPUT_LAST_TEXT, "mail-dlg-input",
1967                              &mail_cc, NULL, FALSE, FALSE, INPUT_COMPLETE_USERNAMES),
1968         QUICK_BUTTONS_OK_CANCEL,
1969         QUICK_END,
1970     };
1971 
1972     WRect r = { -1, -1, 0, 50 };
1973 
1974     quick_dialog_t qdlg = {
1975         .rect = r,
1976         .title = _ ("Mail"),
1977         .help = "[Input Line Keys]",
1978         .widgets = quick_widgets,
1979         .callback = NULL,
1980         .mouse_callback = NULL,
1981     };
1982 
1983     if (quick_dialog (&qdlg) != B_CANCEL)
1984     {
1985         pipe_mail (&edit->buffer, mail_to, mail_subject, mail_cc);
1986         g_free (mail_to);
1987         g_free (mail_subject);
1988         g_free (mail_cc);
1989     }
1990 }
1991 
1992 /* --------------------------------------------------------------------------------------------- */
1993 
1994 void
1995 edit_select_codepage_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1996 {
1997     if (do_select_codepage ())
1998         edit_set_codeset (edit);
1999 
2000     edit->force = REDRAW_PAGE;
2001     widget_draw (WIDGET (edit));
2002 }
2003 
2004 /* --------------------------------------------------------------------------------------------- */
2005 
2006 void
2007 edit_insert_literal_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2008 {
2009     const int char_for_insertion =
2010         editcmd_dialog_raw_key_query (_ ("Insert literal"), _ ("Press any key:"), FALSE);
2011 
2012     const int control_code = keycode_to_cntrl (char_for_insertion);
2013 
2014     if (control_code != -1)
2015         edit_execute_key_command (edit, -1, control_code);
2016 }
2017 
2018 /* --------------------------------------------------------------------------------------------- */
2019 
2020 gboolean
2021 edit_load_forward_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2022 {
2023     if (edit->modified != 0
2024         && edit_query_dialog2 (_ ("Warning"),
2025                                _ ("Current text was modified without a file save.\n"
2026                                   "Continue discards these changes."),
2027                                _ ("C&ontinue"), _ ("&Cancel"))
2028             == 1)
2029     {
2030         edit->force |= REDRAW_COMPLETELY;
2031         return TRUE;
2032     }
2033 
2034     if (edit_stack_iterator + 1 >= MAX_HISTORY_MOVETO)
2035         return FALSE;
2036 
2037     if (edit_history_moveto[edit_stack_iterator + 1].line_number < 1)
2038         return FALSE;
2039 
2040     edit_stack_iterator++;
2041     if (edit_history_moveto[edit_stack_iterator].file_vpath != NULL)
2042         return edit_reload_line (edit, &edit_history_moveto[edit_stack_iterator]);
2043 
2044     return FALSE;
2045 }
2046 
2047 /* --------------------------------------------------------------------------------------------- */
2048 
2049 gboolean
2050 edit_load_back_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2051 {
2052     if (edit->modified != 0
2053         && edit_query_dialog2 (_ ("Warning"),
2054                                _ ("Current text was modified without a file save.\n"
2055                                   "Continue discards these changes."),
2056                                _ ("C&ontinue"), _ ("&Cancel"))
2057             == 1)
2058     {
2059         edit->force |= REDRAW_COMPLETELY;
2060         return TRUE;
2061     }
2062 
2063     // we are in the bottom of the stack, NO WAY!
2064     if (edit_stack_iterator == 0)
2065         return FALSE;
2066 
2067     edit_stack_iterator--;
2068     if (edit_history_moveto[edit_stack_iterator].file_vpath != NULL)
2069         return edit_reload_line (edit, &edit_history_moveto[edit_stack_iterator]);
2070 
2071     return FALSE;
2072 }
2073 
2074 /* --------------------------------------------------------------------------------------------- */
2075 
2076 /* gets a raw key from the keyboard. Passing cancel = 1 draws
2077    a cancel button thus allowing c-c etc.  Alternatively, cancel = 0
2078    will return the next key pressed.  ctrl-a (=B_CANCEL), ctrl-g, ctrl-c,
2079    and Esc are cannot returned */
2080 
2081 int
2082 editcmd_dialog_raw_key_query (const char *heading, const char *query, gboolean cancel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2083 {
2084     int w, wq;
2085     int y = 2;
2086     WDialog *raw_dlg;
2087     WGroup *g;
2088 
2089     w = str_term_width1 (heading) + 6;
2090     wq = str_term_width1 (query);
2091     w = MAX (w, wq + 3 * 2 + 1 + 2);
2092 
2093     raw_dlg = dlg_create (TRUE, 0, 0, cancel ? 7 : 5, w, WPOS_CENTER | WPOS_TRYUP, FALSE,
2094                           dialog_colors, editcmd_dialog_raw_key_query_cb, NULL, NULL, heading);
2095     g = GROUP (raw_dlg);
2096     widget_want_tab (WIDGET (raw_dlg), TRUE);
2097 
2098     group_add_widget (g, label_new (y, 3, query));
2099     group_add_widget (
2100         g, input_new (y++, 3 + wq + 1, input_colors, w - (6 + wq + 1), "", 0, INPUT_COMPLETE_NONE));
2101     if (cancel)
2102     {
2103         group_add_widget (g, hline_new (y++, -1, -1));
2104         // Button w/o hotkey to allow use any key as raw or macro one
2105         group_add_widget_autopos (g, button_new (y, 1, B_CANCEL, NORMAL_BUTTON, _ ("Cancel"), NULL),
2106                                   WPOS_KEEP_TOP | WPOS_CENTER_HORZ, NULL);
2107     }
2108 
2109     w = dlg_run (raw_dlg);
2110     widget_destroy (WIDGET (raw_dlg));
2111 
2112     return (cancel && (w == ESC_CHAR || w == B_CANCEL)) ? 0 : w;
2113 }
2114 
2115 /* --------------------------------------------------------------------------------------------- */

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