diff options
Diffstat (limited to 'src/editor/editcmd.c')
-rw-r--r-- | src/editor/editcmd.c | 2108 |
1 files changed, 2108 insertions, 0 deletions
diff --git a/src/editor/editcmd.c b/src/editor/editcmd.c new file mode 100644 index 0000000..de624f2 --- /dev/null +++ b/src/editor/editcmd.c @@ -0,0 +1,2108 @@ +/* + Editor high level editing commands + + Copyright (C) 1996-2023 + Free Software Foundation, Inc. + + Written by: + Paul Sheer, 1996, 1997 + Andrew Borodin <aborodin@vmail.ru>, 2012-2022 + Ilia Maslakov <il.smind@gmail.com>, 2012 + + This file is part of the Midnight Commander. + + The Midnight Commander is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + The Midnight Commander is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file + * \brief Source: editor high level editing commands + * \author Paul Sheer + * \date 1996, 1997 + */ + +/* #define PIPE_BLOCKS_SO_READ_BYTE_BY_BYTE */ + +#include <config.h> + +#include <ctype.h> +#include <stdio.h> +#include <stdarg.h> +#include <sys/types.h> +#include <unistd.h> +#include <string.h> +#include <sys/stat.h> +#include <stdlib.h> + +#include "lib/global.h" +#include "lib/tty/tty.h" +#include "lib/tty/key.h" /* XCTRL */ +#include "lib/strutil.h" /* utf string functions */ +#include "lib/fileloc.h" +#include "lib/lock.h" +#include "lib/util.h" /* tilde_expand() */ +#include "lib/vfs/vfs.h" +#include "lib/widget.h" +#include "lib/event.h" /* mc_event_raise() */ +#ifdef HAVE_CHARSET +#include "lib/charsets.h" +#endif + +#include "src/history.h" +#include "src/file_history.h" /* show_file_history() */ +#ifdef HAVE_CHARSET +#include "src/selcodepage.h" +#endif +#include "src/util.h" /* check_for_default() */ + +#include "edit-impl.h" +#include "editwidget.h" +#include "editsearch.h" +#include "etags.h" + +/*** global variables ****************************************************************************/ + +/* search and replace: */ +int search_create_bookmark = FALSE; + +/*** file scope macro definitions ****************************************************************/ + +#define space_width 1 + +#define TEMP_BUF_LEN 1024 + +/*** file scope type declarations ****************************************************************/ + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +static unsigned long edit_save_mode_radio_id, edit_save_mode_input_id; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static cb_ret_t +edit_save_mode_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) +{ + switch (msg) + { + case MSG_CHANGED_FOCUS: + if (sender != NULL && sender->id == edit_save_mode_radio_id) + { + Widget *ww; + + ww = widget_find_by_id (w, edit_save_mode_input_id); + widget_disable (ww, RADIO (sender)->sel != 2); + return MSG_HANDLED; + } + return MSG_NOT_HANDLED; + + default: + return dlg_default_callback (w, sender, msg, parm, data); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +/* If 0 (quick save) then a) create/truncate <filename> file, + b) save to <filename>; + if 1 (safe save) then a) save to <tempnam>, + b) rename <tempnam> to <filename>; + if 2 (do backups) then a) save to <tempnam>, + b) rename <filename> to <filename.backup_ext>, + c) rename <tempnam> to <filename>. */ + +/* returns 0 on error, -1 on abort */ + +static int +edit_save_file (WEdit * edit, const vfs_path_t * filename_vpath) +{ + char *p; + gchar *tmp; + off_t filelen = 0; + int this_save_mode, rv, fd = -1; + vfs_path_t *real_filename_vpath; + vfs_path_t *savename_vpath = NULL; + const char *start_filename; + const vfs_path_element_t *vpath_element; + struct stat sb; + + vpath_element = vfs_path_get_by_index (filename_vpath, 0); + if (vpath_element == NULL) + return 0; + + start_filename = vpath_element->path; + if (*start_filename == '\0') + return 0; + + if (!IS_PATH_SEP (*start_filename) && edit->dir_vpath != NULL) + real_filename_vpath = vfs_path_append_vpath_new (edit->dir_vpath, filename_vpath, NULL); + else + real_filename_vpath = vfs_path_clone (filename_vpath); + + this_save_mode = edit_options.save_mode; + if (this_save_mode != EDIT_QUICK_SAVE) + { + if (!vfs_file_is_local (real_filename_vpath)) + /* The file does not exists yet, so no safe save or backup are necessary. */ + this_save_mode = EDIT_QUICK_SAVE; + else + { + fd = mc_open (real_filename_vpath, O_RDONLY | O_BINARY); + if (fd == -1) + /* The file does not exists yet, so no safe save or backup are necessary. */ + this_save_mode = EDIT_QUICK_SAVE; + } + + if (fd != -1) + mc_close (fd); + } + + rv = mc_stat (real_filename_vpath, &sb); + if (rv == 0) + { + if (this_save_mode == EDIT_QUICK_SAVE && !edit->skip_detach_prompt && sb.st_nlink > 1) + { + rv = edit_query_dialog3 (_("Warning"), + _("File has hard-links. Detach before saving?"), + _("&Yes"), _("&No"), _("&Cancel")); + switch (rv) + { + case 0: + this_save_mode = EDIT_SAFE_SAVE; + MC_FALLTHROUGH; + case 1: + edit->skip_detach_prompt = 1; + break; + default: + vfs_path_free (real_filename_vpath, TRUE); + return -1; + } + } + + /* Prevent overwriting changes from other editor sessions. */ + if (edit->stat1.st_mtime != 0 && edit->stat1.st_mtime != sb.st_mtime) + { + /* The default action is "Cancel". */ + query_set_sel (1); + + rv = edit_query_dialog2 (_("Warning"), + _("The file has been modified in the meantime. Save anyway?"), + _("&Yes"), _("&Cancel")); + if (rv != 0) + { + vfs_path_free (real_filename_vpath, TRUE); + return -1; + } + } + } + + if (this_save_mode == EDIT_QUICK_SAVE) + savename_vpath = vfs_path_clone (real_filename_vpath); + else + { + char *savedir, *saveprefix; + + savedir = vfs_path_tokens_get (real_filename_vpath, 0, -1); + if (savedir == NULL) + savedir = g_strdup ("."); + + /* Token-related function never return leading slash, so we need add it manually */ + saveprefix = mc_build_filename (PATH_SEP_STR, savedir, "cooledit", (char *) NULL); + g_free (savedir); + fd = mc_mkstemps (&savename_vpath, saveprefix, NULL); + g_free (saveprefix); + if (savename_vpath == NULL) + { + vfs_path_free (real_filename_vpath, TRUE); + return 0; + } + /* FIXME: + * Close for now because mc_mkstemps use pure open system call + * to create temporary file and it needs to be reopened by + * VFS-aware mc_open(). + */ + close (fd); + } + + (void) mc_chown (savename_vpath, edit->stat1.st_uid, edit->stat1.st_gid); + (void) mc_chmod (savename_vpath, edit->stat1.st_mode); + + fd = mc_open (savename_vpath, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY, edit->stat1.st_mode); + if (fd == -1) + goto error_save; + + /* pipe save */ + p = edit_get_write_filter (savename_vpath, real_filename_vpath); + if (p != NULL) + { + FILE *file; + + mc_close (fd); + file = (FILE *) popen (p, "w"); + + if (file != NULL) + { + filelen = edit_write_stream (edit, file); +#if 1 + pclose (file); +#else + if (pclose (file) != 0) + { + tmp = g_strdup_printf (_("Error writing to pipe: %s"), p); + edit_error_dialog (_("Error"), tmp); + g_free (tmp); + g_free (p); + goto error_save; + } +#endif + } + else + { + tmp = g_strdup_printf (_("Cannot open pipe for writing: %s"), p); + edit_error_dialog (_("Error"), get_sys_error (tmp)); + g_free (p); + g_free (tmp); + goto error_save; + } + g_free (p); + } + else if (edit->lb == LB_ASIS) + { /* do not change line breaks */ + filelen = edit_buffer_write_file (&edit->buffer, fd); + + if (filelen != edit->buffer.size) + { + mc_close (fd); + goto error_save; + } + + if (mc_close (fd) != 0) + goto error_save; + + /* Update the file information, especially the mtime. */ + if (mc_stat (savename_vpath, &edit->stat1) == -1) + goto error_save; + } + else + { /* change line breaks */ + FILE *file; + const char *savename; + + mc_close (fd); + + savename = vfs_path_get_last_path_str (savename_vpath); + file = (FILE *) fopen (savename, "w"); + if (file != NULL) + { + filelen = edit_write_stream (edit, file); + fclose (file); + } + else + { + char *msg; + + msg = g_strdup_printf (_("Cannot open file for writing: %s"), savename); + edit_error_dialog (_("Error"), msg); + g_free (msg); + goto error_save; + } + } + + if (filelen != edit->buffer.size) + goto error_save; + + if (this_save_mode == EDIT_DO_BACKUP) + { + char *tmp_store_filename; + vfs_path_element_t *last_vpath_element; + vfs_path_t *tmp_vpath; + gboolean ok; + + g_assert (edit_options.backup_ext != NULL); + + /* add backup extension to the path */ + tmp_vpath = vfs_path_clone (real_filename_vpath); + last_vpath_element = (vfs_path_element_t *) vfs_path_get_by_index (tmp_vpath, -1); + tmp_store_filename = last_vpath_element->path; + last_vpath_element->path = + g_strdup_printf ("%s%s", tmp_store_filename, edit_options.backup_ext); + g_free (tmp_store_filename); + + ok = (mc_rename (real_filename_vpath, tmp_vpath) != -1); + vfs_path_free (tmp_vpath, TRUE); + if (!ok) + goto error_save; + } + + if (this_save_mode != EDIT_QUICK_SAVE && mc_rename (savename_vpath, real_filename_vpath) == -1) + goto error_save; + + vfs_path_free (real_filename_vpath, TRUE); + vfs_path_free (savename_vpath, TRUE); + return 1; + error_save: + /* FIXME: Is this safe ? + * if (this_save_mode != EDIT_QUICK_SAVE) + * mc_unlink (savename); + */ + vfs_path_free (real_filename_vpath, TRUE); + vfs_path_free (savename_vpath, TRUE); + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +edit_check_newline (const edit_buffer_t * buf) +{ + return !(edit_options.check_nl_at_eof && buf->size > 0 + && edit_buffer_get_byte (buf, buf->size - 1) != '\n' + && edit_query_dialog2 (_("Warning"), + _("The file you are saving does not end with a newline."), + _("C&ontinue"), _("&Cancel")) != 0); +} + +/* --------------------------------------------------------------------------------------------- */ + +static vfs_path_t * +edit_get_save_file_as (WEdit * edit) +{ + static LineBreaks cur_lb = LB_ASIS; + char *filename_res; + vfs_path_t *ret_vpath = NULL; + + const char *lb_names[LB_NAMES] = { + N_("&Do not change"), + N_("&Unix format (LF)"), + N_("&Windows/DOS format (CR LF)"), + N_("&Macintosh format (CR)") + }; + + quick_widget_t quick_widgets[] = { + /* *INDENT-OFF* */ + QUICK_LABELED_INPUT (N_("Enter file name:"), input_label_above, + vfs_path_as_str (edit->filename_vpath), "save-as", + &filename_res, NULL, FALSE, FALSE, INPUT_COMPLETE_FILENAMES), + QUICK_SEPARATOR (TRUE), + QUICK_LABEL (N_("Change line breaks to:"), NULL), + QUICK_RADIO (LB_NAMES, lb_names, (int *) &cur_lb, NULL), + QUICK_BUTTONS_OK_CANCEL, + QUICK_END + /* *INDENT-ON* */ + }; + + WRect r = { -1, -1, 0, 64 }; + + quick_dialog_t qdlg = { + r, N_("Save As"), "[Save File As]", + quick_widgets, NULL, NULL + }; + + if (quick_dialog (&qdlg) != B_CANCEL) + { + char *fname; + + edit->lb = cur_lb; + fname = tilde_expand (filename_res); + g_free (filename_res); + ret_vpath = vfs_path_from_str (fname); + g_free (fname); + } + + return ret_vpath; +} + +/* --------------------------------------------------------------------------------------------- */ + +/** returns TRUE on success */ + +static gboolean +edit_save_cmd (WEdit * edit) +{ + int res, save_lock = 0; + + if (!edit->locked && !edit->delete_file) + save_lock = lock_file (edit->filename_vpath); + res = edit_save_file (edit, edit->filename_vpath); + + /* Maintain modify (not save) lock on failure */ + if ((res > 0 && edit->locked) || save_lock) + edit->locked = unlock_file (edit->filename_vpath); + + /* On failure try 'save as', it does locking on its own */ + if (res == 0) + return edit_save_as_cmd (edit); + + if (res > 0) + { + edit->delete_file = 0; + edit->modified = 0; + } + + edit->force |= REDRAW_COMPLETELY; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +edit_delete_column_of_text (WEdit * edit) +{ + off_t m1, m2; + off_t n; + long b, c, d; + + eval_marks (edit, &m1, &m2); + n = edit_buffer_get_forward_offset (&edit->buffer, m1, 0, m2) + 1; + c = (long) edit_move_forward3 (edit, edit_buffer_get_bol (&edit->buffer, m1), 0, m1); + d = (long) edit_move_forward3 (edit, edit_buffer_get_bol (&edit->buffer, m2), 0, m2); + b = MAX (MIN (c, d), MIN (edit->column1, edit->column2)); + c = MAX (c, MAX (edit->column1, edit->column2)); + + while (n-- != 0) + { + off_t r, p, q; + + r = edit_buffer_get_current_bol (&edit->buffer); + p = edit_move_forward3 (edit, r, b, 0); + q = edit_move_forward3 (edit, r, c, 0); + p = MAX (p, m1); + q = MIN (q, m2); + edit_cursor_move (edit, p - edit->buffer.curs1); + /* delete line between margins */ + for (; q > p; q--) + if (edit_buffer_get_current_byte (&edit->buffer) != '\n') + edit_delete (edit, TRUE); + + /* move to next line except on the last delete */ + if (n != 0) + edit_cursor_move (edit, + edit_buffer_get_forward_offset (&edit->buffer, edit->buffer.curs1, 1, + 0) - edit->buffer.curs1); + } +} + +/* --------------------------------------------------------------------------------------------- */ +/** if success return 0 */ + +static int +edit_block_delete (WEdit * edit) +{ + off_t start_mark, end_mark; + off_t curs_pos; + long curs_line, c1, c2; + + if (!eval_marks (edit, &start_mark, &end_mark)) + return 0; + + if (edit->column_highlight && edit->mark2 < 0) + edit_mark_cmd (edit, FALSE); + + /* Warning message with a query to continue or cancel the operation */ + if ((end_mark - start_mark) > max_undo / 2 && + edit_query_dialog2 (_("Warning"), + ("Block is large, you may not be able to undo this action"), + _("C&ontinue"), _("&Cancel")) != 0) + return 1; + + c1 = MIN (edit->column1, edit->column2); + c2 = MAX (edit->column1, edit->column2); + edit->column1 = c1; + edit->column2 = c2; + + edit_push_markers (edit); + + curs_line = edit->buffer.curs_line; + + curs_pos = edit->curs_col + edit->over_col; + + /* move cursor to start of selection */ + edit_cursor_move (edit, start_mark - edit->buffer.curs1); + edit_scroll_screen_over_cursor (edit); + + if (start_mark < end_mark) + { + if (edit->column_highlight) + { + off_t line_width; + + if (edit->mark2 < 0) + edit_mark_cmd (edit, FALSE); + edit_delete_column_of_text (edit); + /* move cursor to the saved position */ + edit_move_to_line (edit, curs_line); + /* calculate line width and cursor position before cut */ + line_width = edit_move_forward3 (edit, edit_buffer_get_current_bol (&edit->buffer), 0, + edit_buffer_get_current_eol (&edit->buffer)); + if (edit_options.cursor_beyond_eol && curs_pos > line_width) + edit->over_col = curs_pos - line_width; + } + else + { + off_t count; + + for (count = start_mark; count < end_mark; count++) + edit_delete (edit, TRUE); + } + } + + edit_set_markers (edit, 0, 0, 0, 0); + edit->force |= REDRAW_PAGE; + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ +/** Return a null terminated length of text. Result must be g_free'd */ + +static unsigned char * +edit_get_block (WEdit * edit, off_t start, off_t finish, off_t * l) +{ + unsigned char *s, *r; + + r = s = g_malloc0 (finish - start + 1); + + if (edit->column_highlight) + { + *l = 0; + + /* copy from buffer, excluding chars that are out of the column 'margins' */ + for (; start < finish; start++) + { + int c; + off_t x; + + x = edit_move_forward3 (edit, edit_buffer_get_bol (&edit->buffer, start), 0, start); + c = edit_buffer_get_byte (&edit->buffer, start); + if ((x >= edit->column1 && x < edit->column2) + || (x >= edit->column2 && x < edit->column1) || c == '\n') + { + *s++ = c; + (*l)++; + } + } + } + else + { + *l = finish - start; + + for (; start < finish; start++) + *s++ = edit_buffer_get_byte (&edit->buffer, start); + } + + *s = '\0'; + + return r; +} + +/* --------------------------------------------------------------------------------------------- */ +/** copies a block to clipboard file */ + +static gboolean +edit_save_block_to_clip_file (WEdit * edit, off_t start, off_t finish) +{ + gboolean ret; + gchar *tmp; + + tmp = mc_config_get_full_path (EDIT_HOME_CLIP_FILE); + ret = edit_save_block (edit, tmp, start, finish); + g_free (tmp); + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +pipe_mail (const edit_buffer_t * buf, char *to, char *subject, char *cc) +{ + FILE *p = 0; + char *s; + + to = name_quote (to, FALSE); + subject = name_quote (subject, FALSE); + cc = name_quote (cc, FALSE); + s = g_strconcat ("mail -s ", subject, *cc ? " -c " : "", cc, " ", to, (char *) NULL); + g_free (to); + g_free (subject); + g_free (cc); + + if (s != NULL) + { + p = popen (s, "w"); + g_free (s); + } + + if (p != NULL) + { + off_t i; + + for (i = 0; i < buf->size; i++) + if (fputc (edit_buffer_get_byte (buf, i), p) < 0) + break; + pclose (p); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +edit_insert_column_of_text (WEdit * edit, unsigned char *data, off_t size, long width, + off_t * start_pos, off_t * end_pos, long *col1, long *col2) +{ + off_t i, cursor; + long col; + + cursor = edit->buffer.curs1; + col = edit_get_col (edit); + + for (i = 0; i < size; i++) + { + if (data[i] != '\n') + edit_insert (edit, data[i]); + else + { /* fill in and move to next line */ + long l; + off_t p; + + if (edit_buffer_get_current_byte (&edit->buffer) != '\n') + { + for (l = width - (edit_get_col (edit) - col); l > 0; l -= space_width) + edit_insert (edit, ' '); + } + for (p = edit->buffer.curs1;; p++) + { + if (p == edit->buffer.size) + { + edit_cursor_move (edit, edit->buffer.size - edit->buffer.curs1); + edit_insert_ahead (edit, '\n'); + p++; + break; + } + if (edit_buffer_get_byte (&edit->buffer, p) == '\n') + { + p++; + break; + } + } + edit_cursor_move (edit, edit_move_forward3 (edit, p, col, 0) - edit->buffer.curs1); + + for (l = col - edit_get_col (edit); l >= space_width; l -= space_width) + edit_insert (edit, ' '); + } + } + + *col1 = col; + *col2 = col + width; + *start_pos = cursor; + *end_pos = edit->buffer.curs1; + edit_cursor_move (edit, cursor - edit->buffer.curs1); +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Callback for the iteration of objects in the 'editors' array. + * Toggle syntax highlighting in editor object. + * + * @param data probably WEdit object + * @param user_data unused + */ + +static void +edit_syntax_onoff_cb (void *data, void *user_data) +{ + (void) user_data; + + if (edit_widget_is_editor (CONST_WIDGET (data))) + { + WEdit *edit = EDIT (data); + + if (edit_options.syntax_highlighting) + edit_load_syntax (edit, NULL, edit->syntax_type); + edit->force |= REDRAW_PAGE; + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static cb_ret_t +editcmd_dialog_raw_key_query_cb (Widget * w, Widget * sender, widget_msg_t msg, int parm, + void *data) +{ + WDialog *h = DIALOG (w); + + switch (msg) + { + case MSG_KEY: + h->ret_value = parm; + dlg_close (h); + return MSG_HANDLED; + default: + return dlg_default_callback (w, sender, msg, parm, data); + } +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +void +edit_refresh_cmd (void) +{ + tty_clear_screen (); + repaint_screen (); + tty_keypad (TRUE); +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Toggle syntax highlighting in all editor windows. + * + * @param h root widget for all windows + */ + +void +edit_syntax_onoff_cmd (WDialog * h) +{ + edit_options.syntax_highlighting = !edit_options.syntax_highlighting; + g_list_foreach (GROUP (h)->widgets, edit_syntax_onoff_cb, NULL); + widget_draw (WIDGET (h)); +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Toggle tabs showing in all editor windows. + * + * @param h root widget for all windows + */ + +void +edit_show_tabs_tws_cmd (WDialog * h) +{ + enable_show_tabs_tws = !enable_show_tabs_tws; + widget_draw (WIDGET (h)); +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Toggle right margin showing in all editor windows. + * + * @param h root widget for all windows + */ + +void +edit_show_margin_cmd (WDialog * h) +{ + edit_options.show_right_margin = !edit_options.show_right_margin; + widget_draw (WIDGET (h)); +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Toggle line numbers showing in all editor windows. + * + * @param h root widget for all windows + */ + +void +edit_show_numbers_cmd (WDialog * h) +{ + edit_options.line_state = !edit_options.line_state; + edit_options.line_state_width = edit_options.line_state ? LINE_STATE_WIDTH : 0; + widget_draw (WIDGET (h)); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +edit_save_mode_cmd (void) +{ + char *str_result = NULL; + + const char *str[] = { + N_("&Quick save"), + N_("&Safe save"), + N_("&Do backups with following extension:") + }; + +#ifdef ENABLE_NLS + size_t i; + + for (i = 0; i < 3; i++) + str[i] = _(str[i]); +#endif + + g_assert (edit_options.backup_ext != NULL); + + { + quick_widget_t quick_widgets[] = { + /* *INDENT-OFF* */ + QUICK_RADIO (3, str, &edit_options.save_mode, &edit_save_mode_radio_id), + QUICK_INPUT (edit_options.backup_ext, "edit-backup-ext", &str_result, + &edit_save_mode_input_id, FALSE, FALSE, INPUT_COMPLETE_NONE), + QUICK_SEPARATOR (TRUE), + QUICK_CHECKBOX (N_("Check &POSIX new line"), &edit_options.check_nl_at_eof, NULL), + QUICK_BUTTONS_OK_CANCEL, + QUICK_END + /* *INDENT-ON* */ + }; + + WRect r = { -1, -1, 0, 38 }; + + quick_dialog_t qdlg = { + r, N_("Edit Save Mode"), "[Edit Save Mode]", + quick_widgets, edit_save_mode_callback, NULL + }; + + if (quick_dialog (&qdlg) != B_CANCEL) + { + g_free (edit_options.backup_ext); + edit_options.backup_ext = str_result; + } + } +} + +/* --------------------------------------------------------------------------------------------- */ + +void +edit_set_filename (WEdit * edit, const vfs_path_t * name_vpath) +{ + vfs_path_free (edit->filename_vpath, TRUE); + edit->filename_vpath = vfs_path_clone (name_vpath); + + if (edit->dir_vpath == NULL) + edit->dir_vpath = vfs_path_clone (vfs_get_raw_current_dir ()); +} + +/* --------------------------------------------------------------------------------------------- */ +/* Here we want to warn the users of overwriting an existing file, + but only if they have made a change to the filename */ +/* returns TRUE on success */ +gboolean +edit_save_as_cmd (WEdit * edit) +{ + /* This heads the 'Save As' dialog box */ + vfs_path_t *exp_vpath; + int save_lock = 0; + gboolean different_filename = FALSE; + gboolean ret = FALSE; + + if (!edit_check_newline (&edit->buffer)) + return FALSE; + + exp_vpath = edit_get_save_file_as (edit); + edit_push_undo_action (edit, KEY_PRESS + edit->start_display); + + if (exp_vpath != NULL && vfs_path_len (exp_vpath) != 0) + { + int rv; + + if (!vfs_path_equal (edit->filename_vpath, exp_vpath)) + { + int file; + struct stat sb; + + if (mc_stat (exp_vpath, &sb) == 0 && !S_ISREG (sb.st_mode)) + { + edit_error_dialog (_("Save as"), + get_sys_error (_ + ("Cannot save: destination is not a regular file"))); + goto ret; + } + + different_filename = TRUE; + file = mc_open (exp_vpath, O_RDONLY | O_BINARY); + + if (file == -1) + edit->stat1.st_mode |= S_IWUSR; + else + { + /* the file exists */ + mc_close (file); + /* Overwrite the current file or cancel the operation */ + if (edit_query_dialog2 + (_("Warning"), + _("A file already exists with this name"), _("&Overwrite"), _("&Cancel"))) + goto ret; + } + + save_lock = lock_file (exp_vpath); + } + else if (!edit->locked && !edit->delete_file) + /* filenames equal, check if already locked */ + save_lock = lock_file (exp_vpath); + + if (different_filename) + /* Allow user to write into saved (under another name) file + * even if original file had r/o user permissions. */ + edit->stat1.st_mode |= S_IWUSR; + + rv = edit_save_file (edit, exp_vpath); + switch (rv) + { + case 1: + /* Successful, so unlock both files */ + if (different_filename) + { + if (save_lock) + unlock_file (exp_vpath); + if (edit->locked) + edit->locked = unlock_file (edit->filename_vpath); + } + else if (edit->locked || save_lock) + edit->locked = unlock_file (edit->filename_vpath); + + edit_set_filename (edit, exp_vpath); + if (edit->lb != LB_ASIS) + edit_reload (edit, exp_vpath); + edit->modified = 0; + edit->delete_file = 0; + if (different_filename) + edit_load_syntax (edit, NULL, edit->syntax_type); + ret = TRUE; + break; + + default: + edit_error_dialog (_("Save as"), get_sys_error (_("Cannot save file"))); + MC_FALLTHROUGH; + + case -1: + /* Failed, so maintain modify (not save) lock */ + if (save_lock) + unlock_file (exp_vpath); + break; + } + } + + ret: + vfs_path_free (exp_vpath, TRUE); + edit->force |= REDRAW_COMPLETELY; + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ +/** returns TRUE on success */ + +gboolean +edit_save_confirm_cmd (WEdit * edit) +{ + if (edit->filename_vpath == NULL) + return edit_save_as_cmd (edit); + + if (!edit_check_newline (&edit->buffer)) + return FALSE; + + if (edit_options.confirm_save) + { + char *f; + gboolean ok; + + f = g_strdup_printf (_("Confirm save file: \"%s\""), + vfs_path_as_str (edit->filename_vpath)); + ok = (edit_query_dialog2 (_("Save file"), f, _("&Save"), _("&Cancel")) == 0); + g_free (f); + if (!ok) + return FALSE; + } + + return edit_save_cmd (edit); +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Ask file to edit and load it. + * + * @return TRUE on success or cancel of ask. + */ + +gboolean +edit_load_cmd (WDialog * h) +{ + char *exp; + gboolean ret = TRUE; /* possible cancel */ + + exp = input_expand_dialog (_("Load"), _("Enter file name:"), + MC_HISTORY_EDIT_LOAD, INPUT_LAST_TEXT, + INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD); + + if (exp != NULL && *exp != '\0') + { + vfs_path_t *exp_vpath; + + exp_vpath = vfs_path_from_str (exp); + ret = edit_load_file_from_filename (h, exp_vpath, 0); + vfs_path_free (exp_vpath, TRUE); + } + + g_free (exp); + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Load file content + * + * @param h screen the owner of editor window + * @param vpath vfs file path + * @param line line number + * + * @return TRUE if file content was successfully loaded, FALSE otherwise + */ + +gboolean +edit_load_file_from_filename (WDialog * h, const vfs_path_t * vpath, long line) +{ + WRect r = WIDGET (h)->rect; + + rect_grow (&r, -1, 0); + + return edit_add_window (h, &r, vpath, line); +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Show history od edited or viewed files and open selected file. + * + * @return TRUE on success, FALSE otherwise. + */ + +gboolean +edit_load_file_from_history (WDialog * h) +{ + char *exp; + int action; + gboolean ret = TRUE; /* possible cancel */ + + exp = show_file_history (CONST_WIDGET (h), &action); + if (exp != NULL && (action == CK_Edit || action == CK_Enter)) + { + vfs_path_t *exp_vpath; + + exp_vpath = vfs_path_from_str (exp); + ret = edit_load_file_from_filename (h, exp_vpath, 0); + vfs_path_free (exp_vpath, TRUE); + } + + g_free (exp); + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Load syntax file to edit. + * + * @return TRUE on success + */ + +gboolean +edit_load_syntax_file (WDialog * h) +{ + vfs_path_t *extdir_vpath; + int dir = 0; + gboolean ret = FALSE; + + if (geteuid () == 0) + dir = query_dialog (_("Syntax file edit"), + _("Which syntax file you want to edit?"), D_NORMAL, 2, + _("&User"), _("&System wide")); + + extdir_vpath = + vfs_path_build_filename (mc_global.sysconfig_dir, EDIT_SYNTAX_FILE, (char *) NULL); + if (!exist_file (vfs_path_get_last_path_str (extdir_vpath))) + { + vfs_path_free (extdir_vpath, TRUE); + extdir_vpath = + vfs_path_build_filename (mc_global.share_data_dir, EDIT_SYNTAX_FILE, (char *) NULL); + } + + if (dir == 0) + { + vfs_path_t *user_syntax_file_vpath; + + user_syntax_file_vpath = mc_config_get_full_vpath (EDIT_SYNTAX_FILE); + check_for_default (extdir_vpath, user_syntax_file_vpath); + ret = edit_load_file_from_filename (h, user_syntax_file_vpath, 0); + vfs_path_free (user_syntax_file_vpath, TRUE); + } + else if (dir == 1) + ret = edit_load_file_from_filename (h, extdir_vpath, 0); + + vfs_path_free (extdir_vpath, TRUE); + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Load menu file to edit. + * + * @return TRUE on success + */ + +gboolean +edit_load_menu_file (WDialog * h) +{ + vfs_path_t *buffer_vpath; + vfs_path_t *menufile_vpath; + int dir; + gboolean ret; + + query_set_sel (1); + dir = query_dialog (_("Menu edit"), + _("Which menu file do you want to edit?"), D_NORMAL, + geteuid () != 0 ? 2 : 3, _("&Local"), _("&User"), _("&System wide")); + + menufile_vpath = + vfs_path_build_filename (mc_global.sysconfig_dir, EDIT_GLOBAL_MENU, (char *) NULL); + if (!exist_file (vfs_path_get_last_path_str (menufile_vpath))) + { + vfs_path_free (menufile_vpath, TRUE); + menufile_vpath = + vfs_path_build_filename (mc_global.share_data_dir, EDIT_GLOBAL_MENU, (char *) NULL); + } + + switch (dir) + { + case 0: + buffer_vpath = vfs_path_from_str (EDIT_LOCAL_MENU); + check_for_default (menufile_vpath, buffer_vpath); + chmod (vfs_path_get_last_path_str (buffer_vpath), 0600); + break; + + case 1: + buffer_vpath = mc_config_get_full_vpath (EDIT_HOME_MENU); + check_for_default (menufile_vpath, buffer_vpath); + break; + + case 2: + buffer_vpath = + vfs_path_build_filename (mc_global.sysconfig_dir, EDIT_GLOBAL_MENU, (char *) NULL); + if (!exist_file (vfs_path_get_last_path_str (buffer_vpath))) + { + vfs_path_free (buffer_vpath, TRUE); + buffer_vpath = + vfs_path_build_filename (mc_global.share_data_dir, EDIT_GLOBAL_MENU, (char *) NULL); + } + break; + + default: + vfs_path_free (menufile_vpath, TRUE); + return FALSE; + } + + ret = edit_load_file_from_filename (h, buffer_vpath, 0); + + vfs_path_free (buffer_vpath, TRUE); + vfs_path_free (menufile_vpath, TRUE); + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Close window with opened file. + * + * @return TRUE if file was closed. + */ + +gboolean +edit_close_cmd (WEdit * edit) +{ + gboolean ret; + + ret = (edit != NULL) && edit_ok_to_exit (edit); + + if (ret) + { + Widget *w = WIDGET (edit); + WGroup *g = w->owner; + + if (edit->locked != 0) + unlock_file (edit->filename_vpath); + + group_remove_widget (w); + widget_destroy (w); + + if (edit_widget_is_editor (CONST_WIDGET (g->current->data))) + edit = EDIT (g->current->data); + else + { + edit = edit_find_editor (DIALOG (g)); + if (edit != NULL) + widget_select (WIDGET (edit)); + } + } + + if (edit != NULL) + edit->force |= REDRAW_COMPLETELY; + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + if mark2 is -1 then marking is from mark1 to the cursor. + Otherwise its between the markers. This handles this. + Returns FALSE if no text is marked. + */ + +gboolean +eval_marks (WEdit * edit, off_t * start_mark, off_t * end_mark) +{ + long end_mark_curs; + + if (edit->mark1 == edit->mark2) + { + *start_mark = *end_mark = 0; + edit->column2 = edit->column1 = 0; + return FALSE; + } + + if (edit->end_mark_curs < 0) + end_mark_curs = edit->buffer.curs1; + else + end_mark_curs = edit->end_mark_curs; + + if (edit->mark2 >= 0) + { + *start_mark = MIN (edit->mark1, edit->mark2); + *end_mark = MAX (edit->mark1, edit->mark2); + } + else + { + *start_mark = MIN (edit->mark1, end_mark_curs); + *end_mark = MAX (edit->mark1, end_mark_curs); + edit->column2 = edit->curs_col + edit->over_col; + } + + if (edit->column_highlight + && ((edit->mark1 > end_mark_curs && edit->column1 < edit->column2) + || (edit->mark1 < end_mark_curs && edit->column1 > edit->column2))) + { + off_t start_bol, start_eol; + off_t end_bol, end_eol; + long col1, col2; + off_t diff1, diff2; + + start_bol = edit_buffer_get_bol (&edit->buffer, *start_mark); + start_eol = edit_buffer_get_eol (&edit->buffer, start_bol - 1) + 1; + end_bol = edit_buffer_get_bol (&edit->buffer, *end_mark); + end_eol = edit_buffer_get_eol (&edit->buffer, *end_mark); + col1 = MIN (edit->column1, edit->column2); + col2 = MAX (edit->column1, edit->column2); + + diff1 = edit_move_forward3 (edit, start_bol, col2, 0) - + edit_move_forward3 (edit, start_bol, col1, 0); + diff2 = edit_move_forward3 (edit, end_bol, col2, 0) - + edit_move_forward3 (edit, end_bol, col1, 0); + + *start_mark -= diff1; + *end_mark += diff2; + *start_mark = MAX (*start_mark, start_eol); + *end_mark = MIN (*end_mark, end_eol); + } + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +edit_block_copy_cmd (WEdit * edit) +{ + off_t start_mark, end_mark, current = edit->buffer.curs1; + off_t mark1 = 0, mark2 = 0; + long c1 = 0, c2 = 0; + off_t size; + unsigned char *copy_buf; + + edit_update_curs_col (edit); + if (!eval_marks (edit, &start_mark, &end_mark)) + return; + + copy_buf = edit_get_block (edit, start_mark, end_mark, &size); + + /* all that gets pushed are deletes hence little space is used on the stack */ + + edit_push_markers (edit); + + if (edit->column_highlight) + { + long col_delta; + + col_delta = labs (edit->column2 - edit->column1); + edit_insert_column_of_text (edit, copy_buf, size, col_delta, &mark1, &mark2, &c1, &c2); + } + else + { + int size_orig = size; + + while (size-- != 0) + edit_insert_ahead (edit, copy_buf[size]); + + /* Place cursor at the end of text selection */ + if (edit_options.cursor_after_inserted_block) + edit_cursor_move (edit, size_orig); + } + + g_free (copy_buf); + edit_scroll_screen_over_cursor (edit); + + if (edit->column_highlight) + edit_set_markers (edit, edit->buffer.curs1, mark2, c1, c2); + else if (start_mark < current && end_mark > current) + edit_set_markers (edit, start_mark, end_mark + end_mark - start_mark, 0, 0); + + edit->force |= REDRAW_PAGE; +} + + +/* --------------------------------------------------------------------------------------------- */ + +void +edit_block_move_cmd (WEdit * edit) +{ + off_t current; + unsigned char *copy_buf = NULL; + off_t start_mark, end_mark; + + if (!eval_marks (edit, &start_mark, &end_mark)) + return; + + if (!edit->column_highlight && edit->buffer.curs1 > start_mark && edit->buffer.curs1 < end_mark) + return; + + if (edit->mark2 < 0) + edit_mark_cmd (edit, FALSE); + edit_push_markers (edit); + + if (edit->column_highlight) + { + off_t mark1, mark2; + off_t size; + long c1, c2, b_width; + long x, x2; + + c1 = MIN (edit->column1, edit->column2); + c2 = MAX (edit->column1, edit->column2); + b_width = c2 - c1; + + edit_update_curs_col (edit); + + x = edit->curs_col; + x2 = x + edit->over_col; + + /* do nothing when cursor inside first line of selected area */ + if ((edit_buffer_get_eol (&edit->buffer, edit->buffer.curs1) == + edit_buffer_get_eol (&edit->buffer, start_mark)) && x2 > c1 && x2 <= c2) + return; + + if (edit->buffer.curs1 > start_mark + && edit->buffer.curs1 < edit_buffer_get_eol (&edit->buffer, end_mark)) + { + if (x > c2) + x -= b_width; + else if (x > c1 && x <= c2) + x = c1; + } + /* save current selection into buffer */ + copy_buf = edit_get_block (edit, start_mark, end_mark, &size); + + /* remove current selection */ + edit_block_delete_cmd (edit); + + edit->over_col = MAX (0, edit->over_col - b_width); + /* calculate the cursor pos after delete block */ + current = edit_move_forward3 (edit, edit_buffer_get_current_bol (&edit->buffer), x, 0); + edit_cursor_move (edit, current - edit->buffer.curs1); + edit_scroll_screen_over_cursor (edit); + + /* add TWS if need before block insertion */ + if (edit_options.cursor_beyond_eol && edit->over_col > 0) + edit_insert_over (edit); + + edit_insert_column_of_text (edit, copy_buf, size, b_width, &mark1, &mark2, &c1, &c2); + edit_set_markers (edit, mark1, mark2, c1, c2); + } + else + { + off_t count, count_orig; + + current = edit->buffer.curs1; + copy_buf = g_malloc0 (end_mark - start_mark); + edit_cursor_move (edit, start_mark - edit->buffer.curs1); + edit_scroll_screen_over_cursor (edit); + + for (count = start_mark; count < end_mark; count++) + copy_buf[end_mark - count - 1] = edit_delete (edit, TRUE); + + edit_scroll_screen_over_cursor (edit); + edit_cursor_move (edit, + current - edit->buffer.curs1 - + (((current - edit->buffer.curs1) > 0) ? end_mark - start_mark : 0)); + edit_scroll_screen_over_cursor (edit); + count_orig = count; + while (count-- > start_mark) + edit_insert_ahead (edit, copy_buf[end_mark - count - 1]); + + edit_set_markers (edit, edit->buffer.curs1, edit->buffer.curs1 + end_mark - start_mark, 0, + 0); + + /* Place cursor at the end of text selection */ + if (edit_options.cursor_after_inserted_block) + edit_cursor_move (edit, count_orig - start_mark); + } + + edit_scroll_screen_over_cursor (edit); + g_free (copy_buf); + edit->force |= REDRAW_PAGE; +} + +/* --------------------------------------------------------------------------------------------- */ +/** returns 1 if canceelled by user */ + +int +edit_block_delete_cmd (WEdit * edit) +{ + off_t start_mark, end_mark; + + if (eval_marks (edit, &start_mark, &end_mark)) + return edit_block_delete (edit); + + edit_delete_line (edit); + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Check if it's OK to close the file. If there are unsaved changes, ask user. + * + * @return TRUE if it's OK to exit, FALSE to continue editing. + */ + +gboolean +edit_ok_to_exit (WEdit * edit) +{ + const char *fname = N_("[NoName]"); + char *msg; + int act; + + if (!edit->modified) + return TRUE; + + if (edit->filename_vpath != NULL) + fname = vfs_path_as_str (edit->filename_vpath); +#ifdef ENABLE_NLS + else + fname = _(fname); +#endif + + if (!mc_global.midnight_shutdown) + { + query_set_sel (2); + + msg = g_strdup_printf (_("File %s was modified.\nSave before close?"), fname); + act = edit_query_dialog3 (_("Close file"), msg, _("&Yes"), _("&No"), _("&Cancel")); + } + else + { + msg = g_strdup_printf (_("Midnight Commander is being shut down.\nSave modified file %s?"), + fname); + act = edit_query_dialog2 (_("Quit"), msg, _("&Yes"), _("&No")); + + /* Esc is No */ + if (act == -1) + act = 1; + } + + g_free (msg); + + switch (act) + { + case 0: /* Yes */ + if (!mc_global.midnight_shutdown && !edit_check_newline (&edit->buffer)) + return FALSE; + edit_push_markers (edit); + edit_set_markers (edit, 0, 0, 0, 0); + if (!edit_save_cmd (edit) || mc_global.midnight_shutdown) + return mc_global.midnight_shutdown; + break; + case 1: /* No */ + default: + break; + case 2: /* Cancel quit */ + case -1: /* Esc */ + return FALSE; + } + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/** save block, returns TRUE on success */ + +gboolean +edit_save_block (WEdit * edit, const char *filename, off_t start, off_t finish) +{ + int file; + off_t len = 1; + vfs_path_t *vpath; + + vpath = vfs_path_from_str (filename); + file = mc_open (vpath, O_CREAT | O_WRONLY | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY); + vfs_path_free (vpath, TRUE); + if (file == -1) + return FALSE; + + if (edit->column_highlight) + { + int r; + + r = mc_write (file, VERTICAL_MAGIC, sizeof (VERTICAL_MAGIC)); + if (r > 0) + { + unsigned char *block, *p; + + p = block = edit_get_block (edit, start, finish, &len); + while (len != 0) + { + r = mc_write (file, p, len); + if (r < 0) + break; + p += r; + len -= r; + } + g_free (block); + } + } + else + { + unsigned char *buf; + off_t i = start; + + len = finish - start; + buf = g_malloc0 (TEMP_BUF_LEN); + while (start != finish) + { + off_t end; + + end = MIN (finish, start + TEMP_BUF_LEN); + for (; i < end; i++) + buf[i - start] = edit_buffer_get_byte (&edit->buffer, i); + len -= mc_write (file, (char *) buf, end - start); + start = end; + } + g_free (buf); + } + mc_close (file); + + return (len == 0); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +edit_paste_from_history (WEdit * edit) +{ + (void) edit; + edit_error_dialog (_("Error"), _("This function is not implemented")); +} + +/* --------------------------------------------------------------------------------------------- */ + +gboolean +edit_copy_to_X_buf_cmd (WEdit * edit) +{ + off_t start_mark, end_mark; + + if (!eval_marks (edit, &start_mark, &end_mark)) + return TRUE; + + if (!edit_save_block_to_clip_file (edit, start_mark, end_mark)) + { + edit_error_dialog (_("Copy to clipboard"), get_sys_error (_("Unable to save to file"))); + return FALSE; + } + /* try use external clipboard utility */ + mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL); + + if (edit_options.drop_selection_on_copy) + edit_mark_cmd (edit, TRUE); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +gboolean +edit_cut_to_X_buf_cmd (WEdit * edit) +{ + off_t start_mark, end_mark; + + if (!eval_marks (edit, &start_mark, &end_mark)) + return TRUE; + + if (!edit_save_block_to_clip_file (edit, start_mark, end_mark)) + { + edit_error_dialog (_("Cut to clipboard"), _("Unable to save to file")); + return FALSE; + } + /* try use external clipboard utility */ + mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL); + + edit_block_delete_cmd (edit); + edit_mark_cmd (edit, TRUE); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +gboolean +edit_paste_from_X_buf_cmd (WEdit * edit) +{ + vfs_path_t *tmp; + gboolean ret; + + /* try use external clipboard utility */ + mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_from_ext_clip", NULL); + tmp = mc_config_get_full_vpath (EDIT_HOME_CLIP_FILE); + ret = (edit_insert_file (edit, tmp) >= 0); + vfs_path_free (tmp, TRUE); + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Ask user for the line and go to that line. + * Negative numbers mean line from the end (i.e. -1 is the last line). + */ + +void +edit_goto_cmd (WEdit * edit) +{ + static gboolean first_run = TRUE; + + char *f; + long l; + char *error; + + f = input_dialog (_("Goto line"), _("Enter line:"), MC_HISTORY_EDIT_GOTO_LINE, + first_run ? NULL : INPUT_LAST_TEXT, INPUT_COMPLETE_NONE); + if (f == NULL || *f == '\0') + { + g_free (f); + return; + } + + l = strtol (f, &error, 0); + if (*error != '\0') + { + g_free (f); + return; + } + + if (l < 0) + l = edit->buffer.lines + l + 2; + + edit_move_display (edit, l - WIDGET (edit)->rect.lines / 2 - 1); + edit_move_to_line (edit, l - 1); + edit->force |= REDRAW_COMPLETELY; + + g_free (f); + first_run = FALSE; +} + +/* --------------------------------------------------------------------------------------------- */ +/** Return TRUE on success */ + +gboolean +edit_save_block_cmd (WEdit * edit) +{ + off_t start_mark, end_mark; + char *exp, *tmp; + gboolean ret = FALSE; + + if (!eval_marks (edit, &start_mark, &end_mark)) + return TRUE; + + tmp = mc_config_get_full_path (EDIT_HOME_CLIP_FILE); + exp = + input_expand_dialog (_("Save block"), _("Enter file name:"), + MC_HISTORY_EDIT_SAVE_BLOCK, tmp, INPUT_COMPLETE_FILENAMES); + g_free (tmp); + edit_push_undo_action (edit, KEY_PRESS + edit->start_display); + + if (exp != NULL && *exp != '\0') + { + if (edit_save_block (edit, exp, start_mark, end_mark)) + ret = TRUE; + else + edit_error_dialog (_("Save block"), get_sys_error (_("Cannot save file"))); + + edit->force |= REDRAW_COMPLETELY; + } + + g_free (exp); + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +/** returns TRUE on success */ +gboolean +edit_insert_file_cmd (WEdit * edit) +{ + char *tmp; + char *exp; + gboolean ret = FALSE; + + tmp = mc_config_get_full_path (EDIT_HOME_CLIP_FILE); + exp = input_expand_dialog (_("Insert file"), _("Enter file name:"), + MC_HISTORY_EDIT_INSERT_FILE, tmp, INPUT_COMPLETE_FILENAMES); + g_free (tmp); + + edit_push_undo_action (edit, KEY_PRESS + edit->start_display); + + if (exp != NULL && *exp != '\0') + { + vfs_path_t *exp_vpath; + + exp_vpath = vfs_path_from_str (exp); + ret = (edit_insert_file (edit, exp_vpath) >= 0); + vfs_path_free (exp_vpath, TRUE); + + if (!ret) + edit_error_dialog (_("Insert file"), get_sys_error (_("Cannot insert file"))); + } + + g_free (exp); + + edit->force |= REDRAW_COMPLETELY; + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ +/** sorts a block, returns -1 on system fail, 1 on cancel and 0 on success */ + +int +edit_sort_cmd (WEdit * edit) +{ + char *exp, *tmp, *tmp_edit_block_name, *tmp_edit_temp_name; + off_t start_mark, end_mark; + int e; + + if (!eval_marks (edit, &start_mark, &end_mark)) + { + edit_error_dialog (_("Sort block"), _("You must first highlight a block of text")); + return 0; + } + + tmp = mc_config_get_full_path (EDIT_HOME_BLOCK_FILE); + edit_save_block (edit, tmp, start_mark, end_mark); + g_free (tmp); + + exp = input_dialog (_("Run sort"), + _("Enter sort options (see sort(1) manpage) separated by whitespace:"), + MC_HISTORY_EDIT_SORT, INPUT_LAST_TEXT, INPUT_COMPLETE_NONE); + + if (exp == NULL) + return 1; + + tmp_edit_block_name = mc_config_get_full_path (EDIT_HOME_BLOCK_FILE); + tmp_edit_temp_name = mc_config_get_full_path (EDIT_HOME_TEMP_FILE); + tmp = + g_strconcat (" sort ", exp, " ", tmp_edit_block_name, + " > ", tmp_edit_temp_name, (char *) NULL); + g_free (tmp_edit_temp_name); + g_free (tmp_edit_block_name); + g_free (exp); + + e = system (tmp); + g_free (tmp); + if (e != 0) + { + if (e == -1 || e == 127) + edit_error_dialog (_("Sort"), get_sys_error (_("Cannot execute sort command"))); + else + { + char q[8]; + + sprintf (q, "%d ", e); + tmp = g_strdup_printf (_("Sort returned non-zero: %s"), q); + edit_error_dialog (_("Sort"), tmp); + g_free (tmp); + } + + return -1; + } + + edit->force |= REDRAW_COMPLETELY; + + if (edit_block_delete_cmd (edit)) + return 1; + + { + vfs_path_t *tmp_vpath; + + tmp_vpath = mc_config_get_full_vpath (EDIT_HOME_TEMP_FILE); + edit_insert_file (edit, tmp_vpath); + vfs_path_free (tmp_vpath, TRUE); + } + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Ask user for a command, execute it and paste its output back to the + * editor. + */ + +int +edit_ext_cmd (WEdit * edit) +{ + char *exp, *tmp, *tmp_edit_temp_file; + int e; + + exp = + input_dialog (_("Paste output of external command"), + _("Enter shell command(s):"), MC_HISTORY_EDIT_PASTE_EXTCMD, INPUT_LAST_TEXT, + INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES + | INPUT_COMPLETE_HOSTNAMES | INPUT_COMPLETE_CD | INPUT_COMPLETE_COMMANDS | + INPUT_COMPLETE_SHELL_ESC); + + if (!exp) + return 1; + + tmp_edit_temp_file = mc_config_get_full_path (EDIT_HOME_TEMP_FILE); + tmp = g_strconcat (exp, " > ", tmp_edit_temp_file, (char *) NULL); + g_free (tmp_edit_temp_file); + e = system (tmp); + g_free (tmp); + g_free (exp); + + if (e != 0) + { + edit_error_dialog (_("External command"), get_sys_error (_("Cannot execute command"))); + return -1; + } + + edit->force |= REDRAW_COMPLETELY; + + { + vfs_path_t *tmp_vpath; + + tmp_vpath = mc_config_get_full_vpath (EDIT_HOME_TEMP_FILE); + edit_insert_file (edit, tmp_vpath); + vfs_path_free (tmp_vpath, TRUE); + } + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ +/** if block is 1, a block must be highlighted and the shell command + processes it. If block is 0 the shell command is a straight system + command, that just produces some output which is to be inserted */ + +void +edit_block_process_cmd (WEdit * edit, int macro_number) +{ + char *fname; + char *macros_fname = NULL; + + fname = g_strdup_printf ("%s.%i.sh", EDIT_HOME_MACRO_FILE, macro_number); + macros_fname = g_build_filename (mc_config_get_data_path (), fname, (char *) NULL); + user_menu (edit, macros_fname, 0); + g_free (fname); + g_free (macros_fname); + edit->force |= REDRAW_COMPLETELY; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +edit_mail_dialog (WEdit * edit) +{ + char *mail_to, *mail_subject, *mail_cc; + + quick_widget_t quick_widgets[] = { + /* *INDENT-OFF* */ + QUICK_LABEL (N_("mail -s <subject> -c <cc> <to>"), NULL), + QUICK_LABELED_INPUT (N_("To"), input_label_above, + INPUT_LAST_TEXT, "mail-dlg-input-3", + &mail_to, NULL, FALSE, FALSE, INPUT_COMPLETE_USERNAMES), + QUICK_LABELED_INPUT (N_("Subject"), input_label_above, + INPUT_LAST_TEXT, "mail-dlg-input-2", + &mail_subject, NULL, FALSE, FALSE, INPUT_COMPLETE_NONE), + QUICK_LABELED_INPUT (N_("Copies to"), input_label_above, + INPUT_LAST_TEXT, "mail-dlg-input", + &mail_cc, NULL, FALSE, FALSE, INPUT_COMPLETE_USERNAMES), + QUICK_BUTTONS_OK_CANCEL, + QUICK_END + /* *INDENT-ON* */ + }; + + WRect r = { -1, -1, 0, 50 }; + + quick_dialog_t qdlg = { + r, N_("Mail"), "[Input Line Keys]", + quick_widgets, NULL, NULL + }; + + if (quick_dialog (&qdlg) != B_CANCEL) + { + pipe_mail (&edit->buffer, mail_to, mail_subject, mail_cc); + g_free (mail_to); + g_free (mail_subject); + g_free (mail_cc); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +#ifdef HAVE_CHARSET +void +edit_select_codepage_cmd (WEdit * edit) +{ + if (do_select_codepage ()) + edit_set_codeset (edit); + + edit->force = REDRAW_PAGE; + widget_draw (WIDGET (edit)); +} +#endif + +/* --------------------------------------------------------------------------------------------- */ + +void +edit_insert_literal_cmd (WEdit * edit) +{ + int char_for_insertion; + + char_for_insertion = editcmd_dialog_raw_key_query (_("Insert literal"), + _("Press any key:"), FALSE); + edit_execute_key_command (edit, -1, ascii_alpha_to_cntrl (char_for_insertion)); +} + +/* --------------------------------------------------------------------------------------------- */ + +gboolean +edit_load_forward_cmd (WEdit * edit) +{ + if (edit->modified + && edit_query_dialog2 (_("Warning"), + _("Current text was modified without a file save.\n" + "Continue discards these changes."), _("C&ontinue"), + _("&Cancel")) == 1) + { + edit->force |= REDRAW_COMPLETELY; + return TRUE; + } + + if (edit_stack_iterator + 1 >= MAX_HISTORY_MOVETO) + return FALSE; + + if (edit_history_moveto[edit_stack_iterator + 1].line < 1) + return FALSE; + + edit_stack_iterator++; + if (edit_history_moveto[edit_stack_iterator].filename_vpath != NULL) + return edit_reload_line (edit, edit_history_moveto[edit_stack_iterator].filename_vpath, + edit_history_moveto[edit_stack_iterator].line); + + return FALSE; +} + +/* --------------------------------------------------------------------------------------------- */ + +gboolean +edit_load_back_cmd (WEdit * edit) +{ + if (edit->modified + && edit_query_dialog2 (_("Warning"), + _("Current text was modified without a file save.\n" + "Continue discards these changes."), _("C&ontinue"), + _("&Cancel")) == 1) + { + edit->force |= REDRAW_COMPLETELY; + return TRUE; + } + + /* we are in the bottom of the stack, NO WAY! */ + if (edit_stack_iterator == 0) + return FALSE; + + edit_stack_iterator--; + if (edit_history_moveto[edit_stack_iterator].filename_vpath != NULL) + return edit_reload_line (edit, edit_history_moveto[edit_stack_iterator].filename_vpath, + edit_history_moveto[edit_stack_iterator].line); + + return FALSE; +} + +/* --------------------------------------------------------------------------------------------- */ + +/* gets a raw key from the keyboard. Passing cancel = 1 draws + a cancel button thus allowing c-c etc. Alternatively, cancel = 0 + will return the next key pressed. ctrl-a (=B_CANCEL), ctrl-g, ctrl-c, + and Esc are cannot returned */ + +int +editcmd_dialog_raw_key_query (const char *heading, const char *query, gboolean cancel) +{ + int w, wq; + int y = 2; + WDialog *raw_dlg; + WGroup *g; + + w = str_term_width1 (heading) + 6; + wq = str_term_width1 (query); + w = MAX (w, wq + 3 * 2 + 1 + 2); + + raw_dlg = + dlg_create (TRUE, 0, 0, cancel ? 7 : 5, w, WPOS_CENTER | WPOS_TRYUP, FALSE, dialog_colors, + editcmd_dialog_raw_key_query_cb, NULL, NULL, heading); + g = GROUP (raw_dlg); + widget_want_tab (WIDGET (raw_dlg), TRUE); + + group_add_widget (g, label_new (y, 3, query)); + group_add_widget (g, + input_new (y++, 3 + wq + 1, input_colors, w - (6 + wq + 1), "", 0, + INPUT_COMPLETE_NONE)); + if (cancel) + { + group_add_widget (g, hline_new (y++, -1, -1)); + /* Button w/o hotkey to allow use any key as raw or macro one */ + group_add_widget_autopos (g, button_new (y, 1, B_CANCEL, NORMAL_BUTTON, _("Cancel"), NULL), + WPOS_KEEP_TOP | WPOS_CENTER_HORZ, NULL); + } + + w = dlg_run (raw_dlg); + widget_destroy (WIDGET (raw_dlg)); + + return (cancel && (w == ESC_CHAR || w == B_CANCEL)) ? 0 : w; +} + +/* --------------------------------------------------------------------------------------------- */ |