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-2025
   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     char *s = NULL;
 627 
 628     to = name_quote (to, FALSE);
 629     if (to != NULL)
 630     {
 631         subject = name_quote (subject, FALSE);
 632         if (subject != NULL)
 633         {
 634             cc = name_quote (cc, FALSE);
 635             if (cc == NULL)
 636                 s = g_strdup_printf ("mail -s %s %s", subject, to);
 637             else
 638             {
 639                 s = g_strdup_printf ("mail -s %s -c %s %s", subject, cc, to);
 640                 g_free (cc);
 641             }
 642 
 643             g_free (subject);
 644         }
 645 
 646         g_free (to);
 647     }
 648 
 649     if (s != NULL)
 650     {
 651         p = popen (s, "w");
 652         g_free (s);
 653     }
 654 
 655     if (p != NULL)
 656     {
 657         off_t i;
 658 
 659         for (i = 0; i < buf->size; i++)
 660             if (fputc (edit_buffer_get_byte (buf, i), p) < 0)
 661                 break;
 662         pclose (p);
 663     }
 664 }
 665 
 666 /* --------------------------------------------------------------------------------------------- */
 667 
 668 static void
 669 edit_append_spaces_at_eol (WEdit *edit, const long col, const long width)
     /* [previous][next][first][last][top][bottom][index][help]  */
 670 {
 671     if (edit_buffer_get_current_byte (&edit->buffer) != '\n')
 672         for (long l = width - (edit_get_col (edit) - col); l > 0; l--)
 673             edit_insert (edit, ' ');
 674 }
 675 
 676 /* --------------------------------------------------------------------------------------------- */
 677 
 678 MC_TESTABLE void
 679 edit_insert_column_of_text (WEdit *edit, GString *data, long width, off_t *start_pos,
     /* [previous][next][first][last][top][bottom][index][help]  */
 680                             off_t *end_pos, long *col1, long *col2)
 681 {
 682     off_t cursor;
 683     long col;
 684 
 685     cursor = edit->buffer.curs1;
 686     col = edit_get_col (edit);
 687 
 688     for (gsize i = 0; i < data->len; i++)
 689     {
 690         if (data->str[i] != '\n')
 691             edit_insert (edit, data->str[i]);
 692         else
 693         {  // fill in and move to next line
 694             off_t p;
 695 
 696             edit_append_spaces_at_eol (edit, col, width);
 697 
 698             for (p = edit->buffer.curs1;; p++)
 699             {
 700                 if (p == edit->buffer.size)
 701                 {
 702                     edit_cursor_move (edit, edit->buffer.size - edit->buffer.curs1);
 703                     edit_insert_ahead (edit, '\n');
 704                     p++;
 705                     break;
 706                 }
 707                 if (edit_buffer_get_byte (&edit->buffer, p) == '\n')
 708                 {
 709                     p++;
 710                     break;
 711                 }
 712             }
 713             edit_cursor_move (edit, edit_move_forward3 (edit, p, col, 0) - edit->buffer.curs1);
 714 
 715             for (long l = col - edit_get_col (edit); l >= 1; l--)
 716                 edit_insert (edit, ' ');
 717         }
 718     }
 719 
 720     // add spaces at the end of the last line if this line is short
 721     edit_append_spaces_at_eol (edit, col, width);
 722 
 723     *col1 = col;
 724     *col2 = col + width;
 725     *start_pos = cursor;
 726     *end_pos = edit->buffer.curs1;
 727     edit_cursor_move (edit, cursor - edit->buffer.curs1);
 728 }
 729 
 730 /* --------------------------------------------------------------------------------------------- */
 731 /**
 732  * Callback for the iteration of objects in the 'editors' array.
 733  * Toggle syntax highlighting in editor object.
 734  *
 735  * @param data      probably WEdit object
 736  * @param user_data unused
 737  */
 738 
 739 static void
 740 edit_syntax_onoff_cb (void *data, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 741 {
 742     (void) user_data;
 743 
 744     if (edit_widget_is_editor (CONST_WIDGET (data)))
 745     {
 746         WEdit *edit = EDIT (data);
 747 
 748         if (edit_options.syntax_highlighting)
 749             edit_load_syntax (edit, NULL, edit->syntax_type);
 750         edit->force |= REDRAW_PAGE;
 751     }
 752 }
 753 
 754 /* --------------------------------------------------------------------------------------------- */
 755 
 756 static cb_ret_t
 757 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]  */
 758 {
 759     WDialog *h = DIALOG (w);
 760 
 761     switch (msg)
 762     {
 763     case MSG_KEY:
 764         h->ret_value = parm;
 765         dlg_close (h);
 766         return MSG_HANDLED;
 767     default:
 768         return dlg_default_callback (w, sender, msg, parm, data);
 769     }
 770 }
 771 
 772 /* --------------------------------------------------------------------------------------------- */
 773 
 774 static gboolean
 775 editcmd_check_and_create_user_syntax_directory (const vfs_path_t *user_syntax_file_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 776 {
 777     gboolean ret;
 778     struct stat st;
 779 
 780     ret = mc_stat (user_syntax_file_vpath, &st) == 0;
 781     if (!ret)
 782     {
 783         // file doesn't exist -- check why
 784         const char *user_syntax_file_path = vfs_path_as_str (user_syntax_file_vpath);
 785 
 786         // check directory
 787         const char *user_syntax_file_basename = x_basename (user_syntax_file_path);
 788         char *user_syntax_dir;
 789         vfs_path_t *user_syntax_vpath;
 790 
 791         user_syntax_dir =
 792             g_strndup (user_syntax_file_path, user_syntax_file_basename - user_syntax_file_path);
 793         user_syntax_vpath = vfs_path_from_str (user_syntax_dir);
 794 
 795         ret = mc_stat (user_syntax_vpath, &st) == 0;
 796         if (!ret)
 797             ret = mc_mkdir (user_syntax_vpath, 0700) == 0;
 798         else if (!S_ISDIR (st.st_mode))
 799         {
 800             ret = FALSE;
 801             // set for file_error_message()
 802             errno = EEXIST;
 803         }
 804 
 805         if (!ret)
 806             file_error_message (_ ("Cannot create directory\n%s"), user_syntax_dir);
 807 
 808         vfs_path_free (user_syntax_vpath, TRUE);
 809         g_free (user_syntax_dir);
 810     }
 811 
 812     return ret;
 813 }
 814 
 815 /* --------------------------------------------------------------------------------------------- */
 816 /*** public functions ****************************************************************************/
 817 /* --------------------------------------------------------------------------------------------- */
 818 
 819 void
 820 edit_refresh_cmd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 821 {
 822     tty_clear_screen ();
 823     repaint_screen ();
 824     tty_keypad (TRUE);
 825 }
 826 
 827 /* --------------------------------------------------------------------------------------------- */
 828 /**
 829  * Toggle syntax highlighting in all editor windows.
 830  *
 831  * @param h root widget for all windows
 832  */
 833 
 834 void
 835 edit_syntax_onoff_cmd (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 836 {
 837     edit_options.syntax_highlighting = !edit_options.syntax_highlighting;
 838     g_list_foreach (GROUP (h)->widgets, edit_syntax_onoff_cb, NULL);
 839     widget_draw (WIDGET (h));
 840 }
 841 
 842 /* --------------------------------------------------------------------------------------------- */
 843 /**
 844  * Toggle tabs showing in all editor windows.
 845  *
 846  * @param h root widget for all windows
 847  */
 848 
 849 void
 850 edit_show_tabs_tws_cmd (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 851 {
 852     enable_show_tabs_tws = !enable_show_tabs_tws;
 853     widget_draw (WIDGET (h));
 854 }
 855 
 856 /* --------------------------------------------------------------------------------------------- */
 857 /**
 858  * Toggle right margin showing in all editor windows.
 859  *
 860  * @param h root widget for all windows
 861  */
 862 
 863 void
 864 edit_show_margin_cmd (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 865 {
 866     edit_options.show_right_margin = !edit_options.show_right_margin;
 867     widget_draw (WIDGET (h));
 868 }
 869 
 870 /* --------------------------------------------------------------------------------------------- */
 871 /**
 872  * Toggle line numbers showing in all editor windows.
 873  *
 874  * @param h root widget for all windows
 875  */
 876 
 877 void
 878 edit_show_numbers_cmd (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 879 {
 880     edit_options.line_state = !edit_options.line_state;
 881     edit_options.line_state_width = edit_options.line_state ? LINE_STATE_WIDTH : 0;
 882     widget_draw (WIDGET (h));
 883 }
 884 
 885 /* --------------------------------------------------------------------------------------------- */
 886 
 887 void
 888 edit_save_mode_cmd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 889 {
 890     char *str_result = NULL;
 891 
 892     const char *str[] = {
 893         _ ("&Quick save"),
 894         _ ("&Safe save"),
 895         _ ("&Do backups with following extension:"),
 896     };
 897 
 898     g_assert (edit_options.backup_ext != NULL);
 899 
 900     {
 901         quick_widget_t quick_widgets[] = {
 902             QUICK_RADIO (3, str, &edit_options.save_mode, &edit_save_mode_radio_id),
 903             QUICK_INPUT (edit_options.backup_ext, "edit-backup-ext", &str_result,
 904                          &edit_save_mode_input_id, FALSE, FALSE, INPUT_COMPLETE_NONE),
 905             QUICK_SEPARATOR (TRUE),
 906             QUICK_CHECKBOX (_ ("Check &POSIX new line"), &edit_options.check_nl_at_eof, NULL),
 907             QUICK_BUTTONS_OK_CANCEL,
 908             QUICK_END,
 909         };
 910 
 911         WRect r = { -1, -1, 0, 38 };
 912 
 913         quick_dialog_t qdlg = {
 914             .rect = r,
 915             .title = _ ("Edit Save Mode"),
 916             .help = "[Edit Save Mode]",
 917             .widgets = quick_widgets,
 918             .callback = edit_save_mode_callback,
 919             .mouse_callback = NULL,
 920         };
 921 
 922         if (quick_dialog (&qdlg) != B_CANCEL)
 923         {
 924             g_free (edit_options.backup_ext);
 925             edit_options.backup_ext = str_result;
 926         }
 927     }
 928 }
 929 
 930 /* --------------------------------------------------------------------------------------------- */
 931 
 932 void
 933 edit_set_filename (WEdit *edit, const vfs_path_t *name_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 934 {
 935     vfs_path_free (edit->filename_vpath, TRUE);
 936     edit->filename_vpath = vfs_path_clone (name_vpath);
 937 
 938     if (edit->dir_vpath == NULL)
 939         edit->dir_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
 940 }
 941 
 942 /* --------------------------------------------------------------------------------------------- */
 943 /* Here we want to warn the users of overwriting an existing file,
 944    but only if they have made a change to the filename */
 945 /* returns TRUE on success */
 946 gboolean
 947 edit_save_as_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 948 {
 949     // This heads the 'Save As' dialog box
 950     vfs_path_t *exp_vpath;
 951     int save_lock = 0;
 952     gboolean different_filename = FALSE;
 953     gboolean ret = FALSE;
 954 
 955     if (!edit_check_newline (&edit->buffer))
 956         return FALSE;
 957 
 958     exp_vpath = edit_get_save_file_as (edit);
 959     edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
 960 
 961     if (exp_vpath != NULL && vfs_path_len (exp_vpath) != 0)
 962     {
 963         int rv;
 964 
 965         if (!vfs_path_equal (edit->filename_vpath, exp_vpath))
 966         {
 967             int file;
 968             struct stat sb;
 969 
 970             if (mc_stat (exp_vpath, &sb) == 0 && !S_ISREG (sb.st_mode))
 971             {
 972                 file_error_message (_ ("Cannot save\n%s:\nnot a regular file"),
 973                                     vfs_path_as_str (exp_vpath));
 974                 goto ret;
 975             }
 976 
 977             different_filename = TRUE;
 978             file = mc_open (exp_vpath, O_RDONLY | O_BINARY);
 979 
 980             if (file == -1)
 981                 edit->stat1.st_mode |= S_IWUSR;
 982             else
 983             {
 984                 // the file exists
 985                 mc_close (file);
 986                 // Overwrite the current file or cancel the operation
 987                 if (edit_query_dialog2 (_ ("Warning"), _ ("A file already exists with this name"),
 988                                         _ ("&Overwrite"), _ ("&Cancel")))
 989                     goto ret;
 990             }
 991 
 992             save_lock = lock_file (exp_vpath);
 993         }
 994         else if (edit->locked == 0 && edit->delete_file == 0)
 995             // filenames equal, check if already locked
 996             save_lock = lock_file (exp_vpath);
 997 
 998         if (different_filename)
 999             /* Allow user to write into saved (under another name) file
1000              * even if original file had r/o user permissions. */
1001             edit->stat1.st_mode |= S_IWUSR;
1002 
1003         rv = edit_save_file (edit, exp_vpath);
1004         switch (rv)
1005         {
1006         case 1:
1007             // Successful, so unlock both files
1008             if (different_filename)
1009             {
1010                 if (save_lock != 0)
1011                     unlock_file (exp_vpath);
1012                 if (edit->locked != 0)
1013                     edit->locked = unlock_file (edit->filename_vpath);
1014             }
1015             else if (edit->locked != 0 || save_lock != 0)
1016                 edit->locked = unlock_file (edit->filename_vpath);
1017 
1018             edit_set_filename (edit, exp_vpath);
1019             if (edit->lb != LB_ASIS)
1020                 edit_reload (edit, exp_vpath);
1021             edit->modified = 0;
1022             edit->delete_file = 0;
1023             if (different_filename)
1024                 edit_load_syntax (edit, NULL, edit->syntax_type);
1025             ret = TRUE;
1026             break;
1027 
1028         default:
1029             message (D_ERROR, MSG_ERROR, "%s", _ ("Cannot save file"));
1030             MC_FALLTHROUGH;
1031 
1032         case -1:
1033             // Failed, so maintain modify (not save) lock
1034             if (save_lock != 0)
1035                 unlock_file (exp_vpath);
1036             break;
1037         }
1038     }
1039 
1040 ret:
1041     vfs_path_free (exp_vpath, TRUE);
1042     edit->force |= REDRAW_COMPLETELY;
1043     return ret;
1044 }
1045 
1046 /* --------------------------------------------------------------------------------------------- */
1047 /** returns TRUE on success */
1048 
1049 gboolean
1050 edit_save_confirm_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1051 {
1052     if (edit->filename_vpath == NULL)
1053         return edit_save_as_cmd (edit);
1054 
1055     if (!edit_check_newline (&edit->buffer))
1056         return FALSE;
1057 
1058     if (edit_options.confirm_save)
1059     {
1060         char *f;
1061         gboolean ok;
1062 
1063         f = g_strdup_printf (_ ("Confirm save file: \"%s\""),
1064                              vfs_path_as_str (edit->filename_vpath));
1065         ok = (edit_query_dialog2 (_ ("Save file"), f, _ ("&Save"), _ ("&Cancel")) == 0);
1066         g_free (f);
1067         if (!ok)
1068             return FALSE;
1069     }
1070 
1071     return edit_save_cmd (edit);
1072 }
1073 
1074 /* --------------------------------------------------------------------------------------------- */
1075 /**
1076  * Ask file to edit and load it.
1077  *
1078  * @return TRUE on success or cancel of ask.
1079  */
1080 
1081 gboolean
1082 edit_load_cmd (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1083 {
1084     char *exp;
1085     gboolean ret = TRUE;  // possible cancel
1086 
1087     exp = input_expand_dialog (_ ("Load"), _ ("Enter file name:"), MC_HISTORY_EDIT_LOAD,
1088                                INPUT_LAST_TEXT, INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD);
1089 
1090     if (exp != NULL && *exp != '\0')
1091     {
1092         vfs_path_t *exp_vpath;
1093         edit_arg_t arg;
1094 
1095         exp_vpath = vfs_path_from_str (exp);
1096         edit_arg_init (&arg, exp_vpath, 0);
1097         ret = edit_load_file_from_filename (h, &arg);
1098         vfs_path_free (exp_vpath, TRUE);
1099     }
1100 
1101     g_free (exp);
1102 
1103     return ret;
1104 }
1105 
1106 /* --------------------------------------------------------------------------------------------- */
1107 /**
1108  * Load file content
1109  *
1110  * @param h screen the owner of editor window
1111  * @param vpath vfs file path
1112  * @param line line number
1113  *
1114  * @return TRUE if file content was successfully loaded, FALSE otherwise
1115  */
1116 
1117 gboolean
1118 edit_load_file_from_filename (WDialog *h, const edit_arg_t *arg)
     /* [previous][next][first][last][top][bottom][index][help]  */
1119 {
1120     WRect r = WIDGET (h)->rect;
1121 
1122     rect_grow (&r, -1, 0);
1123 
1124     return edit_add_window (h, &r, arg);
1125 }
1126 
1127 /* --------------------------------------------------------------------------------------------- */
1128 /**
1129  * Show history od edited or viewed files and open selected file.
1130  *
1131  * @return TRUE on success, FALSE otherwise.
1132  */
1133 
1134 gboolean
1135 edit_load_file_from_history (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1136 {
1137     char *exp;
1138     int action;
1139     gboolean ret = TRUE;  // possible cancel
1140 
1141     exp = show_file_history (CONST_WIDGET (h), &action);
1142     if (exp != NULL && (action == CK_Edit || action == CK_Enter))
1143     {
1144         vfs_path_t *exp_vpath;
1145         edit_arg_t arg;
1146 
1147         exp_vpath = vfs_path_from_str (exp);
1148         edit_arg_init (&arg, exp_vpath, 0);
1149         ret = edit_load_file_from_filename (h, &arg);
1150         vfs_path_free (exp_vpath, TRUE);
1151     }
1152 
1153     g_free (exp);
1154 
1155     return ret;
1156 }
1157 
1158 /* --------------------------------------------------------------------------------------------- */
1159 /**
1160  * Load syntax file to edit.
1161  *
1162  * @return TRUE on success
1163  */
1164 
1165 gboolean
1166 edit_load_syntax_file (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1167 {
1168     vfs_path_t *extdir_vpath;
1169     int dir = 0;
1170     edit_arg_t arg;
1171     gboolean ret = FALSE;
1172 
1173     if (geteuid () == 0)
1174         dir = query_dialog (_ ("Syntax file edit"), _ ("Which syntax file you want to edit?"),
1175                             D_NORMAL, 2, _ ("&User"), _ ("&System wide"));
1176 
1177     extdir_vpath =
1178         vfs_path_build_filename (mc_global.sysconfig_dir, EDIT_SYNTAX_FILE, (char *) NULL);
1179     if (!exist_file (vfs_path_get_last_path_str (extdir_vpath)))
1180     {
1181         vfs_path_free (extdir_vpath, TRUE);
1182         extdir_vpath =
1183             vfs_path_build_filename (mc_global.share_data_dir, EDIT_SYNTAX_FILE, (char *) NULL);
1184     }
1185 
1186     if (dir == 0)
1187     {
1188         vfs_path_t *user_syntax_file_vpath;
1189 
1190         user_syntax_file_vpath = mc_config_get_full_vpath (EDIT_SYNTAX_FILE);
1191 
1192         if (editcmd_check_and_create_user_syntax_directory (user_syntax_file_vpath))
1193         {
1194             check_for_default (extdir_vpath, user_syntax_file_vpath);
1195             edit_arg_init (&arg, user_syntax_file_vpath, 0);
1196             ret = edit_load_file_from_filename (h, &arg);
1197         }
1198 
1199         vfs_path_free (user_syntax_file_vpath, TRUE);
1200     }
1201     else if (dir == 1)
1202     {
1203         edit_arg_init (&arg, extdir_vpath, 0);
1204         ret = edit_load_file_from_filename (h, &arg);
1205     }
1206 
1207     vfs_path_free (extdir_vpath, TRUE);
1208 
1209     return ret;
1210 }
1211 
1212 /* --------------------------------------------------------------------------------------------- */
1213 /**
1214  * Load menu file to edit.
1215  *
1216  * @return TRUE on success
1217  */
1218 
1219 gboolean
1220 edit_load_menu_file (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1221 {
1222     vfs_path_t *buffer_vpath;
1223     vfs_path_t *menufile_vpath;
1224     int dir;
1225     edit_arg_t arg;
1226     gboolean ret;
1227 
1228     query_set_sel (1);
1229     dir = query_dialog (_ ("Menu edit"), _ ("Which menu file do you want to edit?"), D_NORMAL,
1230                         geteuid () != 0 ? 2 : 3, _ ("&Local"), _ ("&User"), _ ("&System wide"));
1231 
1232     menufile_vpath =
1233         vfs_path_build_filename (mc_global.sysconfig_dir, EDIT_GLOBAL_MENU, (char *) NULL);
1234     if (!exist_file (vfs_path_get_last_path_str (menufile_vpath)))
1235     {
1236         vfs_path_free (menufile_vpath, TRUE);
1237         menufile_vpath =
1238             vfs_path_build_filename (mc_global.share_data_dir, EDIT_GLOBAL_MENU, (char *) NULL);
1239     }
1240 
1241     switch (dir)
1242     {
1243     case 0:
1244         buffer_vpath = vfs_path_from_str (EDIT_LOCAL_MENU);
1245         check_for_default (menufile_vpath, buffer_vpath);
1246         chmod (vfs_path_get_last_path_str (buffer_vpath), 0600);
1247         break;
1248 
1249     case 1:
1250         buffer_vpath = mc_config_get_full_vpath (EDIT_HOME_MENU);
1251         check_for_default (menufile_vpath, buffer_vpath);
1252         break;
1253 
1254     case 2:
1255         buffer_vpath =
1256             vfs_path_build_filename (mc_global.sysconfig_dir, EDIT_GLOBAL_MENU, (char *) NULL);
1257         if (!exist_file (vfs_path_get_last_path_str (buffer_vpath)))
1258         {
1259             vfs_path_free (buffer_vpath, TRUE);
1260             buffer_vpath =
1261                 vfs_path_build_filename (mc_global.share_data_dir, EDIT_GLOBAL_MENU, (char *) NULL);
1262         }
1263         break;
1264 
1265     default:
1266         vfs_path_free (menufile_vpath, TRUE);
1267         return FALSE;
1268     }
1269 
1270     edit_arg_init (&arg, buffer_vpath, 0);
1271     ret = edit_load_file_from_filename (h, &arg);
1272 
1273     vfs_path_free (buffer_vpath, TRUE);
1274     vfs_path_free (menufile_vpath, TRUE);
1275 
1276     return ret;
1277 }
1278 
1279 /* --------------------------------------------------------------------------------------------- */
1280 /**
1281  * Close window with opened file.
1282  *
1283  * @return TRUE if file was closed.
1284  */
1285 
1286 gboolean
1287 edit_close_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1288 {
1289     gboolean ret;
1290 
1291     ret = (edit != NULL) && edit_ok_to_quit (edit);
1292 
1293     if (ret)
1294     {
1295         Widget *w = WIDGET (edit);
1296         WGroup *g = w->owner;
1297 
1298         if (edit->locked != 0)
1299             edit->locked = unlock_file (edit->filename_vpath);
1300 
1301         group_remove_widget (w);
1302         widget_destroy (w);
1303 
1304         if (edit_widget_is_editor (CONST_WIDGET (g->current->data)))
1305             edit = EDIT (g->current->data);
1306         else
1307         {
1308             edit = edit_find_editor (DIALOG (g));
1309             if (edit != NULL)
1310                 widget_select (WIDGET (edit));
1311         }
1312     }
1313 
1314     if (edit != NULL)
1315         edit->force |= REDRAW_COMPLETELY;
1316 
1317     return ret;
1318 }
1319 
1320 /* --------------------------------------------------------------------------------------------- */
1321 
1322 void
1323 edit_block_copy_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1324 {
1325     off_t start_mark, end_mark, current = edit->buffer.curs1;
1326     off_t mark1 = 0, mark2 = 0;
1327     long c1 = 0, c2 = 0;
1328     GString *copy_buf;
1329 
1330     edit_update_curs_col (edit);
1331     if (!eval_marks (edit, &start_mark, &end_mark))
1332         return;
1333 
1334     copy_buf = edit_get_block (edit, start_mark, end_mark);
1335 
1336     // all that gets pushed are deletes hence little space is used on the stack
1337 
1338     edit_push_markers (edit);
1339 
1340     if (edit->column_highlight)
1341     {
1342         long col_delta;
1343 
1344         col_delta = labs (edit->column2 - edit->column1);
1345         edit_insert_column_of_text (edit, copy_buf, col_delta, &mark1, &mark2, &c1, &c2);
1346     }
1347     else
1348     {
1349         for (gsize i = copy_buf->len; i != 0; i--)
1350             edit_insert_ahead (edit, copy_buf->str[i - 1]);
1351 
1352         // Place cursor at the end of text selection
1353         if (edit_options.cursor_after_inserted_block)
1354             edit_cursor_move (edit, copy_buf->len);
1355     }
1356 
1357     g_string_free (copy_buf, TRUE);
1358     edit_scroll_screen_over_cursor (edit);
1359 
1360     if (edit->column_highlight)
1361         edit_set_markers (edit, edit->buffer.curs1, mark2, c1, c2);
1362     else if (start_mark < current && end_mark > current)
1363         edit_set_markers (edit, start_mark, end_mark + end_mark - start_mark, 0, 0);
1364 
1365     edit->force |= REDRAW_PAGE;
1366 }
1367 
1368 /* --------------------------------------------------------------------------------------------- */
1369 
1370 void
1371 edit_block_move_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1372 {
1373     off_t current;
1374     off_t start_mark, end_mark;
1375 
1376     if (!eval_marks (edit, &start_mark, &end_mark))
1377         return;
1378 
1379     if (!edit->column_highlight && edit->buffer.curs1 > start_mark && edit->buffer.curs1 < end_mark)
1380         return;
1381 
1382     if (edit->mark2 < 0)
1383         edit_mark_cmd (edit, FALSE);
1384     edit_push_markers (edit);
1385 
1386     if (edit->column_highlight)
1387     {
1388         off_t mark1, mark2;
1389         long c1, c2, b_width;
1390         long x, x2;
1391         off_t b1, b2;
1392         GString *copy_buf;
1393 
1394         c1 = MIN (edit->column1, edit->column2);
1395         c2 = MAX (edit->column1, edit->column2);
1396         b_width = c2 - c1;
1397 
1398         edit_update_curs_col (edit);
1399 
1400         x = edit->curs_col;
1401         x2 = x + edit->over_col;
1402 
1403         // do nothing when cursor inside first line of selected area
1404         b1 = edit_buffer_get_eol (&edit->buffer, edit->buffer.curs1);
1405         b2 = edit_buffer_get_eol (&edit->buffer, start_mark);
1406         if (b1 == b2 && x2 > c1 && x2 <= c2)
1407             return;
1408 
1409         if (edit->buffer.curs1 > start_mark
1410             && edit->buffer.curs1 < edit_buffer_get_eol (&edit->buffer, end_mark))
1411         {
1412             if (x > c2)
1413                 x -= b_width;
1414             else if (x > c1 && x <= c2)
1415                 x = c1;
1416         }
1417         // save current selection into buffer
1418         copy_buf = edit_get_block (edit, start_mark, end_mark);
1419 
1420         // remove current selection
1421         edit_block_delete_cmd (edit);
1422 
1423         edit->over_col = MAX (0, edit->over_col - b_width);
1424         // calculate the cursor pos after delete block
1425         b1 = edit_buffer_get_current_bol (&edit->buffer);
1426         current = edit_move_forward3 (edit, b1, x, 0);
1427         edit_cursor_move (edit, current - edit->buffer.curs1);
1428         edit_scroll_screen_over_cursor (edit);
1429 
1430         // add TWS if need before block insertion
1431         if (edit_options.cursor_beyond_eol && edit->over_col > 0)
1432             edit_insert_over (edit);
1433 
1434         edit_insert_column_of_text (edit, copy_buf, b_width, &mark1, &mark2, &c1, &c2);
1435         edit_set_markers (edit, mark1, mark2, c1, c2);
1436         g_string_free (copy_buf, TRUE);
1437     }
1438     else
1439     {
1440         unsigned char *copy_buf;
1441         off_t count, count_orig;
1442         off_t x;
1443 
1444         current = edit->buffer.curs1;
1445         copy_buf = g_malloc0 (end_mark - start_mark);
1446         edit_cursor_move (edit, start_mark - edit->buffer.curs1);
1447         edit_scroll_screen_over_cursor (edit);
1448 
1449         for (count = start_mark; count < end_mark; count++)
1450             copy_buf[end_mark - count - 1] = edit_delete (edit, TRUE);
1451 
1452         edit_scroll_screen_over_cursor (edit);
1453         x = current > edit->buffer.curs1 ? end_mark - start_mark : 0;
1454         edit_cursor_move (edit, current - edit->buffer.curs1 - x);
1455         edit_scroll_screen_over_cursor (edit);
1456         count_orig = count;
1457         while (count-- > start_mark)
1458             edit_insert_ahead (edit, copy_buf[end_mark - count - 1]);
1459         g_free (copy_buf);
1460 
1461         edit_set_markers (edit, edit->buffer.curs1, edit->buffer.curs1 + end_mark - start_mark, 0,
1462                           0);
1463 
1464         // Place cursor at the end of text selection
1465         if (edit_options.cursor_after_inserted_block)
1466             edit_cursor_move (edit, count_orig - start_mark);
1467     }
1468 
1469     edit_scroll_screen_over_cursor (edit);
1470     edit->force |= REDRAW_PAGE;
1471 }
1472 
1473 /* --------------------------------------------------------------------------------------------- */
1474 /** returns FALSE if canceelled by user */
1475 
1476 gboolean
1477 edit_block_delete_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1478 {
1479     off_t start_mark, end_mark;
1480 
1481     if (eval_marks (edit, &start_mark, &end_mark))
1482         return edit_block_delete (edit, start_mark, end_mark);
1483 
1484     edit_delete_line (edit);
1485 
1486     return TRUE;
1487 }
1488 
1489 /* --------------------------------------------------------------------------------------------- */
1490 /**
1491  * Check if it's OK to close the file. If there are unsaved changes, ask user.
1492  *
1493  * @return TRUE if it's OK to exit, FALSE to continue editing.
1494  */
1495 
1496 gboolean
1497 edit_ok_to_quit (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1498 {
1499     const char *fname;
1500     char *msg;
1501     int act;
1502 
1503     if (edit->modified == 0)
1504         return TRUE;
1505 
1506     if (edit->filename_vpath != NULL)
1507         fname = vfs_path_as_str (edit->filename_vpath);
1508     else
1509         fname = _ ("[NoName]");
1510 
1511     if (!mc_global.midnight_shutdown)
1512     {
1513         query_set_sel (2);
1514 
1515         msg = g_strdup_printf (_ ("File %s was modified.\nSave before close?"), fname);
1516         act = edit_query_dialog3 (_ ("Close file"), msg, _ ("&Yes"), _ ("&No"), _ ("&Cancel"));
1517     }
1518     else
1519     {
1520         msg = g_strdup_printf (_ ("%s is being shut down.\nSave modified file %s?"), PACKAGE_NAME,
1521                                fname);
1522         act = edit_query_dialog2 (_ ("Quit"), msg, _ ("&Yes"), _ ("&No"));
1523 
1524         // Esc is No
1525         if (act == -1)
1526             act = 1;
1527     }
1528 
1529     g_free (msg);
1530 
1531     switch (act)
1532     {
1533     case 0:  // Yes
1534         if (!mc_global.midnight_shutdown && !edit_check_newline (&edit->buffer))
1535             return FALSE;
1536         edit_push_markers (edit);
1537         edit_set_markers (edit, 0, 0, 0, 0);
1538         if (!edit_save_cmd (edit) || mc_global.midnight_shutdown)
1539             return mc_global.midnight_shutdown;
1540         break;
1541     case 1:  // No
1542     default:
1543         break;
1544     case 2:   // Cancel
1545     case -1:  // Esc
1546         return FALSE;
1547     }
1548 
1549     return TRUE;
1550 }
1551 
1552 /* --------------------------------------------------------------------------------------------- */
1553 /** save block, returns TRUE on success */
1554 
1555 gboolean
1556 edit_save_block (WEdit *edit, const char *filename, off_t start, off_t finish)
     /* [previous][next][first][last][top][bottom][index][help]  */
1557 {
1558     int file;
1559     vfs_path_t *vpath;
1560     off_t len = 1;
1561 
1562     vpath = vfs_path_from_str (filename);
1563     file = mc_open (vpath, O_CREAT | O_WRONLY | O_TRUNC,
1564                     S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY);
1565     vfs_path_free (vpath, TRUE);
1566     if (file == -1)
1567         return FALSE;
1568 
1569     if (edit->column_highlight)
1570     {
1571         int r;
1572 
1573         r = mc_write (file, VERTICAL_MAGIC, sizeof (VERTICAL_MAGIC));
1574         if (r > 0)
1575         {
1576             GString *block;
1577             char *p;
1578 
1579             block = edit_get_block (edit, start, finish);
1580             p = block->str;
1581             len = block->len;
1582 
1583             while (len != 0)
1584             {
1585                 r = mc_write (file, p, len);
1586                 if (r < 0)
1587                     break;
1588                 p += r;
1589                 len -= r;
1590             }
1591 
1592             g_string_free (block, TRUE);
1593         }
1594     }
1595     else
1596     {
1597         unsigned char *buf;
1598         off_t i = start;
1599 
1600         len = finish - start;
1601         buf = g_malloc0 (TEMP_BUF_LEN);
1602         while (start != finish)
1603         {
1604             off_t end;
1605 
1606             end = MIN (finish, start + TEMP_BUF_LEN);
1607             for (; i < end; i++)
1608                 buf[i - start] = edit_buffer_get_byte (&edit->buffer, i);
1609             len -= mc_write (file, (char *) buf, end - start);
1610             start = end;
1611         }
1612         g_free (buf);
1613     }
1614     mc_close (file);
1615 
1616     return (len == 0);
1617 }
1618 
1619 /* --------------------------------------------------------------------------------------------- */
1620 
1621 void
1622 edit_paste_from_history (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1623 {
1624     (void) edit;
1625 
1626     message (D_ERROR, MSG_ERROR, "%s", _ ("This function is not implemented"));
1627 }
1628 
1629 /* --------------------------------------------------------------------------------------------- */
1630 
1631 gboolean
1632 edit_copy_to_X_buf_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1633 {
1634     off_t start_mark, end_mark;
1635 
1636     if (!eval_marks (edit, &start_mark, &end_mark))
1637         return TRUE;
1638 
1639     if (!edit_save_block_to_clip_file (edit, start_mark, end_mark))
1640     {
1641         message (D_ERROR, MSG_ERROR, "%s", _ ("Unable to save to file"));
1642         return FALSE;
1643     }
1644     // try use external clipboard utility
1645     mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
1646 
1647     if (edit_options.drop_selection_on_copy)
1648         edit_mark_cmd (edit, TRUE);
1649 
1650     return TRUE;
1651 }
1652 
1653 /* --------------------------------------------------------------------------------------------- */
1654 
1655 gboolean
1656 edit_cut_to_X_buf_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1657 {
1658     off_t start_mark, end_mark;
1659 
1660     if (!eval_marks (edit, &start_mark, &end_mark))
1661         return TRUE;
1662 
1663     if (!edit_save_block_to_clip_file (edit, start_mark, end_mark))
1664     {
1665         message (D_ERROR, MSG_ERROR, "%s", _ ("Unable to save to file"));
1666         return FALSE;
1667     }
1668     // try use external clipboard utility
1669     mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
1670 
1671     edit_block_delete_cmd (edit);
1672     edit_mark_cmd (edit, TRUE);
1673 
1674     return TRUE;
1675 }
1676 
1677 /* --------------------------------------------------------------------------------------------- */
1678 
1679 gboolean
1680 edit_paste_from_X_buf_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1681 {
1682     vfs_path_t *tmp;
1683     gboolean ret;
1684 
1685     // try use external clipboard utility
1686     mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_from_ext_clip", NULL);
1687     tmp = mc_config_get_full_vpath (EDIT_HOME_CLIP_FILE);
1688     ret = (edit_insert_file (edit, tmp) >= 0);
1689     vfs_path_free (tmp, TRUE);
1690 
1691     return ret;
1692 }
1693 
1694 /* --------------------------------------------------------------------------------------------- */
1695 /**
1696  * Ask user for the line and go to that line.
1697  * Negative numbers mean line from the end (i.e. -1 is the last line).
1698  */
1699 
1700 void
1701 edit_goto_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1702 {
1703     static gboolean first_run = TRUE;
1704 
1705     char *f;
1706     long l;
1707     char *error;
1708 
1709     f = input_dialog (_ ("Goto line"), _ ("Enter line:"), MC_HISTORY_EDIT_GOTO_LINE,
1710                       first_run ? NULL : INPUT_LAST_TEXT, INPUT_COMPLETE_NONE);
1711     if (f == NULL || *f == '\0')
1712     {
1713         g_free (f);
1714         return;
1715     }
1716 
1717     l = strtol (f, &error, 0);
1718     if (*error != '\0')
1719     {
1720         g_free (f);
1721         return;
1722     }
1723 
1724     if (l < 0)
1725         l = edit->buffer.lines + l + 2;
1726 
1727     edit_move_display (edit, l - WIDGET (edit)->rect.lines / 2 - 1);
1728     edit_move_to_line (edit, l - 1);
1729     edit->force |= REDRAW_COMPLETELY;
1730 
1731     g_free (f);
1732     first_run = FALSE;
1733 }
1734 
1735 /* --------------------------------------------------------------------------------------------- */
1736 /** Return TRUE on success */
1737 
1738 gboolean
1739 edit_save_block_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1740 {
1741     off_t start_mark, end_mark;
1742     char *exp, *tmp;
1743     gboolean ret = FALSE;
1744 
1745     if (!eval_marks (edit, &start_mark, &end_mark))
1746         return TRUE;
1747 
1748     tmp = mc_config_get_full_path (EDIT_HOME_CLIP_FILE);
1749     exp = input_expand_dialog (_ ("Save block"), _ ("Enter file name:"), MC_HISTORY_EDIT_SAVE_BLOCK,
1750                                tmp, INPUT_COMPLETE_FILENAMES);
1751     g_free (tmp);
1752     edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
1753 
1754     if (exp != NULL && *exp != '\0')
1755     {
1756         if (edit_save_block (edit, exp, start_mark, end_mark))
1757             ret = TRUE;
1758         else
1759             message (D_ERROR, MSG_ERROR, "%s", _ ("Cannot save block"));
1760 
1761         edit->force |= REDRAW_COMPLETELY;
1762     }
1763 
1764     g_free (exp);
1765 
1766     return ret;
1767 }
1768 
1769 /* --------------------------------------------------------------------------------------------- */
1770 
1771 /** returns TRUE on success */
1772 gboolean
1773 edit_insert_file_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1774 {
1775     char *tmp;
1776     char *exp;
1777     gboolean ret = FALSE;
1778 
1779     tmp = mc_config_get_full_path (EDIT_HOME_CLIP_FILE);
1780     exp = input_expand_dialog (_ ("Insert file"), _ ("Enter file name:"),
1781                                MC_HISTORY_EDIT_INSERT_FILE, tmp, INPUT_COMPLETE_FILENAMES);
1782     g_free (tmp);
1783 
1784     edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
1785 
1786     if (exp != NULL && *exp != '\0')
1787     {
1788         vfs_path_t *exp_vpath;
1789 
1790         exp_vpath = vfs_path_from_str (exp);
1791         ret = (edit_insert_file (edit, exp_vpath) >= 0);
1792         vfs_path_free (exp_vpath, TRUE);
1793 
1794         if (!ret)
1795             message (D_ERROR, MSG_ERROR, "%s", _ ("Cannot insert file"));
1796     }
1797 
1798     g_free (exp);
1799 
1800     edit->force |= REDRAW_COMPLETELY;
1801     return ret;
1802 }
1803 
1804 /* --------------------------------------------------------------------------------------------- */
1805 /** sorts a block, returns -1 on system fail, 1 on cancel and 0 on success */
1806 
1807 int
1808 edit_sort_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1809 {
1810     char *exp, *tmp, *tmp_edit_block_name, *tmp_edit_temp_name;
1811     off_t start_mark, end_mark;
1812     int e;
1813 
1814     if (!eval_marks (edit, &start_mark, &end_mark))
1815     {
1816         message (D_ERROR, MSG_ERROR, "%s", _ ("You must first highlight a block of text"));
1817         return 0;
1818     }
1819 
1820     tmp = mc_config_get_full_path (EDIT_HOME_BLOCK_FILE);
1821     edit_save_block (edit, tmp, start_mark, end_mark);
1822     g_free (tmp);
1823 
1824     exp = input_dialog (_ ("Run sort"),
1825                         _ ("Enter sort options (see sort(1) manpage) separated by whitespace:"),
1826                         MC_HISTORY_EDIT_SORT, INPUT_LAST_TEXT, INPUT_COMPLETE_NONE);
1827 
1828     if (exp == NULL)
1829         return 1;
1830 
1831     tmp_edit_block_name = mc_config_get_full_path (EDIT_HOME_BLOCK_FILE);
1832     tmp_edit_temp_name = mc_config_get_full_path (EDIT_HOME_TEMP_FILE);
1833     tmp = g_strconcat (" sort ", exp, " ", tmp_edit_block_name, " > ", tmp_edit_temp_name,
1834                        (char *) NULL);
1835     g_free (tmp_edit_temp_name);
1836     g_free (tmp_edit_block_name);
1837     g_free (exp);
1838 
1839     e = system (tmp);
1840     g_free (tmp);
1841     if (e != 0)
1842     {
1843         if (e == -1 || e == 127)
1844             message (D_ERROR, MSG_ERROR, "%s", _ ("Cannot execute sort command"));
1845         else
1846         {
1847             char q[8];
1848 
1849             sprintf (q, "%d ", e);
1850             message (D_ERROR, MSG_ERROR, _ ("Sort returned non-zero: %s"), q);
1851         }
1852 
1853         return -1;
1854     }
1855 
1856     edit->force |= REDRAW_COMPLETELY;
1857 
1858     if (!edit_block_delete_cmd (edit))
1859         return 1;
1860 
1861     {
1862         vfs_path_t *tmp_vpath;
1863 
1864         tmp_vpath = mc_config_get_full_vpath (EDIT_HOME_TEMP_FILE);
1865         edit_insert_file (edit, tmp_vpath);
1866         vfs_path_free (tmp_vpath, TRUE);
1867     }
1868 
1869     return 0;
1870 }
1871 
1872 /* --------------------------------------------------------------------------------------------- */
1873 /**
1874  * Ask user for a command, execute it and paste its output back to the
1875  * editor.
1876  */
1877 
1878 int
1879 edit_ext_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1880 {
1881     char *exp, *tmp, *tmp_edit_temp_file;
1882     int e;
1883 
1884     exp =
1885         input_dialog (_ ("Paste output of external command"), _ ("Enter shell command(s):"),
1886                       MC_HISTORY_EDIT_PASTE_EXTCMD, INPUT_LAST_TEXT,
1887                       INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES
1888                           | INPUT_COMPLETE_HOSTNAMES | INPUT_COMPLETE_CD | INPUT_COMPLETE_COMMANDS
1889                           | INPUT_COMPLETE_SHELL_ESC);
1890 
1891     if (!exp)
1892         return 1;
1893 
1894     tmp_edit_temp_file = mc_config_get_full_path (EDIT_HOME_TEMP_FILE);
1895     tmp = g_strconcat (exp, " > ", tmp_edit_temp_file, (char *) NULL);
1896     g_free (tmp_edit_temp_file);
1897     e = system (tmp);
1898     g_free (tmp);
1899     g_free (exp);
1900 
1901     if (e != 0)
1902     {
1903         message (D_ERROR, MSG_ERROR, "%s", _ ("Cannot execute external command"));
1904         return -1;
1905     }
1906 
1907     edit->force |= REDRAW_COMPLETELY;
1908 
1909     {
1910         vfs_path_t *tmp_vpath;
1911 
1912         tmp_vpath = mc_config_get_full_vpath (EDIT_HOME_TEMP_FILE);
1913         edit_insert_file (edit, tmp_vpath);
1914         vfs_path_free (tmp_vpath, TRUE);
1915     }
1916 
1917     return 0;
1918 }
1919 
1920 /* --------------------------------------------------------------------------------------------- */
1921 /** if block is 1, a block must be highlighted and the shell command
1922    processes it. If block is 0 the shell command is a straight system
1923    command, that just produces some output which is to be inserted */
1924 
1925 void
1926 edit_block_process_cmd (WEdit *edit, int macro_number)
     /* [previous][next][first][last][top][bottom][index][help]  */
1927 {
1928     char *fname;
1929     char *macros_fname = NULL;
1930 
1931     fname = g_strdup_printf ("%s.%i.sh", EDIT_HOME_MACRO_FILE, macro_number);
1932     macros_fname = g_build_filename (mc_config_get_data_path (), fname, (char *) NULL);
1933     edit_user_menu (edit, macros_fname, 0);
1934     g_free (fname);
1935     g_free (macros_fname);
1936     edit->force |= REDRAW_COMPLETELY;
1937 }
1938 
1939 /* --------------------------------------------------------------------------------------------- */
1940 
1941 void
1942 edit_mail_dialog (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1943 {
1944     char *mail_to = NULL;
1945     char *mail_subject = NULL;
1946     char *mail_cc = NULL;
1947 
1948     quick_widget_t quick_widgets[] = {
1949         QUICK_LABEL (_ ("mail -s <subject> -c <cc> <to>"), NULL),
1950         QUICK_LABELED_INPUT (_ ("To"), input_label_above, INPUT_LAST_TEXT, "mail-dlg-input-3",
1951                              &mail_to, NULL, FALSE, FALSE, INPUT_COMPLETE_USERNAMES),
1952         QUICK_LABELED_INPUT (_ ("Subject"), input_label_above, INPUT_LAST_TEXT, "mail-dlg-input-2",
1953                              &mail_subject, NULL, FALSE, FALSE, INPUT_COMPLETE_NONE),
1954         QUICK_LABELED_INPUT (_ ("Copies to"), input_label_above, INPUT_LAST_TEXT, "mail-dlg-input",
1955                              &mail_cc, NULL, FALSE, FALSE, INPUT_COMPLETE_USERNAMES),
1956         QUICK_BUTTONS_OK_CANCEL,
1957         QUICK_END,
1958     };
1959 
1960     WRect r = { -1, -1, 0, 50 };
1961 
1962     quick_dialog_t qdlg = {
1963         .rect = r,
1964         .title = _ ("Mail"),
1965         .help = "[Input Line Keys]",
1966         .widgets = quick_widgets,
1967         .callback = NULL,
1968         .mouse_callback = NULL,
1969     };
1970 
1971     if (quick_dialog (&qdlg) != B_CANCEL)
1972     {
1973         pipe_mail (&edit->buffer, mail_to, mail_subject, mail_cc);
1974         g_free (mail_to);
1975         g_free (mail_subject);
1976         g_free (mail_cc);
1977     }
1978 }
1979 
1980 /* --------------------------------------------------------------------------------------------- */
1981 
1982 void
1983 edit_select_codepage_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1984 {
1985     if (do_select_codepage ())
1986         edit_set_codeset (edit);
1987 
1988     edit->force = REDRAW_PAGE;
1989     widget_draw (WIDGET (edit));
1990 }
1991 
1992 /* --------------------------------------------------------------------------------------------- */
1993 
1994 void
1995 edit_insert_literal_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1996 {
1997     int char_for_insertion;
1998 
1999     char_for_insertion =
2000         editcmd_dialog_raw_key_query (_ ("Insert literal"), _ ("Press any key:"), FALSE);
2001     edit_execute_key_command (edit, -1, ascii_alpha_to_cntrl (char_for_insertion));
2002 }
2003 
2004 /* --------------------------------------------------------------------------------------------- */
2005 
2006 gboolean
2007 edit_load_forward_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2008 {
2009     if (edit->modified != 0
2010         && edit_query_dialog2 (_ ("Warning"),
2011                                _ ("Current text was modified without a file save.\n"
2012                                   "Continue discards these changes."),
2013                                _ ("C&ontinue"), _ ("&Cancel"))
2014             == 1)
2015     {
2016         edit->force |= REDRAW_COMPLETELY;
2017         return TRUE;
2018     }
2019 
2020     if (edit_stack_iterator + 1 >= MAX_HISTORY_MOVETO)
2021         return FALSE;
2022 
2023     if (edit_history_moveto[edit_stack_iterator + 1].line_number < 1)
2024         return FALSE;
2025 
2026     edit_stack_iterator++;
2027     if (edit_history_moveto[edit_stack_iterator].file_vpath != NULL)
2028         return edit_reload_line (edit, &edit_history_moveto[edit_stack_iterator]);
2029 
2030     return FALSE;
2031 }
2032 
2033 /* --------------------------------------------------------------------------------------------- */
2034 
2035 gboolean
2036 edit_load_back_cmd (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
2037 {
2038     if (edit->modified != 0
2039         && edit_query_dialog2 (_ ("Warning"),
2040                                _ ("Current text was modified without a file save.\n"
2041                                   "Continue discards these changes."),
2042                                _ ("C&ontinue"), _ ("&Cancel"))
2043             == 1)
2044     {
2045         edit->force |= REDRAW_COMPLETELY;
2046         return TRUE;
2047     }
2048 
2049     // we are in the bottom of the stack, NO WAY!
2050     if (edit_stack_iterator == 0)
2051         return FALSE;
2052 
2053     edit_stack_iterator--;
2054     if (edit_history_moveto[edit_stack_iterator].file_vpath != NULL)
2055         return edit_reload_line (edit, &edit_history_moveto[edit_stack_iterator]);
2056 
2057     return FALSE;
2058 }
2059 
2060 /* --------------------------------------------------------------------------------------------- */
2061 
2062 /* gets a raw key from the keyboard. Passing cancel = 1 draws
2063    a cancel button thus allowing c-c etc.  Alternatively, cancel = 0
2064    will return the next key pressed.  ctrl-a (=B_CANCEL), ctrl-g, ctrl-c,
2065    and Esc are cannot returned */
2066 
2067 int
2068 editcmd_dialog_raw_key_query (const char *heading, const char *query, gboolean cancel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2069 {
2070     int w, wq;
2071     int y = 2;
2072     WDialog *raw_dlg;
2073     WGroup *g;
2074 
2075     w = str_term_width1 (heading) + 6;
2076     wq = str_term_width1 (query);
2077     w = MAX (w, wq + 3 * 2 + 1 + 2);
2078 
2079     raw_dlg = dlg_create (TRUE, 0, 0, cancel ? 7 : 5, w, WPOS_CENTER | WPOS_TRYUP, FALSE,
2080                           dialog_colors, editcmd_dialog_raw_key_query_cb, NULL, NULL, heading);
2081     g = GROUP (raw_dlg);
2082     widget_want_tab (WIDGET (raw_dlg), TRUE);
2083 
2084     group_add_widget (g, label_new (y, 3, query));
2085     group_add_widget (
2086         g, input_new (y++, 3 + wq + 1, input_colors, w - (6 + wq + 1), "", 0, INPUT_COMPLETE_NONE));
2087     if (cancel)
2088     {
2089         group_add_widget (g, hline_new (y++, -1, -1));
2090         // Button w/o hotkey to allow use any key as raw or macro one
2091         group_add_widget_autopos (g, button_new (y, 1, B_CANCEL, NORMAL_BUTTON, _ ("Cancel"), NULL),
2092                                   WPOS_KEEP_TOP | WPOS_CENTER_HORZ, NULL);
2093     }
2094 
2095     w = dlg_run (raw_dlg);
2096     widget_destroy (WIDGET (raw_dlg));
2097 
2098     return (cancel && (w == ESC_CHAR || w == B_CANCEL)) ? 0 : w;
2099 }
2100 
2101 /* --------------------------------------------------------------------------------------------- */

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