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

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