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

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