Merge branch '4524_cleanup'
[midnight-commander.git] / src / filemanager / cmd.c
blob1ddc3f311eba701b7e82d1c35c448f0adf56560f
1 /*
2 Routines invoked by a function key
3 They normally operate on the current panel.
5 Copyright (C) 1994-2024
6 Free Software Foundation, Inc.
8 Written by:
9 Andrew Borodin <aborodin@vmail.ru>, 2013-2022
11 This file is part of the Midnight Commander.
13 The Midnight Commander is free software: you can redistribute it
14 and/or modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation, either version 3 of the License,
16 or (at your option) any later version.
18 The Midnight Commander is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 /** \file cmd.c
28 * \brief Source: routines invoked by a function key
30 * They normally operate on the current panel.
33 #include <config.h>
35 #include <errno.h>
36 #include <stdio.h>
37 #include <string.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #ifdef HAVE_MMAP
42 #include <sys/mman.h>
43 #endif
44 #ifdef ENABLE_VFS_NET
45 #include <netdb.h>
46 #endif
47 #include <unistd.h>
48 #include <stdlib.h>
49 #include <pwd.h>
50 #include <grp.h>
52 #include "lib/global.h"
54 #include "lib/tty/tty.h" /* LINES, tty_touch_screen() */
55 #include "lib/tty/key.h" /* ALT() macro */
56 #include "lib/mcconfig.h"
57 #include "lib/filehighlight.h" /* MC_FHL_INI_FILE */
58 #include "lib/vfs/vfs.h"
59 #include "lib/fileloc.h"
60 #include "lib/strutil.h"
61 #include "lib/file-entry.h"
62 #include "lib/util.h"
63 #include "lib/widget.h"
64 #include "lib/keybind.h" /* CK_Down, CK_History */
65 #include "lib/event.h" /* mc_event_raise() */
67 #include "src/setup.h"
68 #include "src/execute.h" /* toggle_panels() */
69 #include "src/history.h"
70 #include "src/usermenu.h" /* MC_GLOBAL_MENU */
71 #include "src/util.h" /* check_for_default() */
73 #include "src/viewer/mcviewer.h"
75 #ifdef USE_INTERNAL_EDIT
76 #include "src/editor/edit.h"
77 #endif
79 #ifdef USE_DIFF_VIEW
80 #include "src/diffviewer/ydiff.h"
81 #endif
83 #include "fileopctx.h"
84 #include "filenot.h"
85 #include "hotlist.h" /* hotlist_show() */
86 #include "tree.h" /* tree_chdir() */
87 #include "filemanager.h" /* change_panel() */
88 #include "command.h" /* cmdline */
89 #include "layout.h" /* get_current_type() */
90 #include "ext.h" /* regex_command() */
91 #include "boxes.h" /* cd_box() */
92 #include "dir.h"
93 #include "cd.h"
95 #include "cmd.h" /* Our definitions */
97 /*** global variables ****************************************************************************/
99 /*** file scope macro definitions ****************************************************************/
101 #ifdef HAVE_MMAP
102 #ifndef MAP_FILE
103 #define MAP_FILE 0
104 #endif
105 #endif /* HAVE_MMAP */
107 /*** file scope type declarations ****************************************************************/
109 enum CompareMode
111 compare_quick = 0,
112 compare_size_only,
113 compare_thourough
116 /*** forward declarations (file scope functions) *************************************************/
118 /*** file scope variables ************************************************************************/
120 #ifdef ENABLE_VFS_NET
121 static const char *machine_str = N_("Enter machine name (F1 for details):");
122 #endif /* ENABLE_VFS_NET */
124 /* --------------------------------------------------------------------------------------------- */
125 /*** file scope functions ************************************************************************/
126 /* --------------------------------------------------------------------------------------------- */
128 * Run viewer (internal or external) on the current file.
129 * If @plain_view is TRUE, force internal viewer and raw mode (used for F13).
131 static void
132 do_view_cmd (WPanel * panel, gboolean plain_view)
134 const file_entry_t *fe;
136 fe = panel_current_entry (panel);
138 /* Directories are viewed by changing to them */
139 if (S_ISDIR (fe->st.st_mode) || link_isdir (fe))
141 vfs_path_t *fname_vpath;
143 if (confirm_view_dir && (panel->marked != 0 || panel->dirs_marked != 0) &&
144 query_dialog (_("Confirmation"), _("Files tagged, want to cd?"), D_NORMAL, 2,
145 _("&Yes"), _("&No")) != 0)
146 return;
148 fname_vpath = vfs_path_from_str (fe->fname->str);
149 if (!panel_cd (panel, fname_vpath, cd_exact))
150 cd_error_message (fe->fname->str);
151 vfs_path_free (fname_vpath, TRUE);
153 else
155 vfs_path_t *filename_vpath;
157 filename_vpath = vfs_path_from_str (fe->fname->str);
158 view_file (filename_vpath, plain_view, use_internal_view);
159 vfs_path_free (filename_vpath, TRUE);
162 repaint_screen ();
165 /* --------------------------------------------------------------------------------------------- */
167 static inline void
168 do_edit (const vfs_path_t * what_vpath)
170 edit_file_at_line (what_vpath, use_internal_edit, 0);
173 /* --------------------------------------------------------------------------------------------- */
175 static int
176 compare_files (const vfs_path_t * vpath1, const vfs_path_t * vpath2, off_t size)
178 int file1;
179 int result = -1; /* Different by default */
181 if (size == 0)
182 return 0;
184 file1 = open (vfs_path_as_str (vpath1), O_RDONLY);
185 if (file1 >= 0)
187 int file2;
189 file2 = open (vfs_path_as_str (vpath2), O_RDONLY);
190 if (file2 >= 0)
192 #ifdef HAVE_MMAP
193 char *data1;
195 /* Ugly if jungle */
196 data1 = mmap (0, size, PROT_READ, MAP_FILE | MAP_PRIVATE, file1, 0);
197 if (data1 != (char *) -1)
199 char *data2;
201 data2 = mmap (0, size, PROT_READ, MAP_FILE | MAP_PRIVATE, file2, 0);
202 if (data2 != (char *) -1)
204 rotate_dash (TRUE);
205 result = memcmp (data1, data2, size);
206 munmap (data2, size);
208 munmap (data1, size);
210 #else
211 /* Don't have mmap() :( Even more ugly :) */
212 char buf1[BUFSIZ], buf2[BUFSIZ];
213 int n1, n2;
215 rotate_dash (TRUE);
218 while ((n1 = read (file1, buf1, sizeof (buf1))) == -1 && errno == EINTR)
220 while ((n2 = read (file2, buf2, sizeof (buf2))) == -1 && errno == EINTR)
223 while (n1 == n2 && n1 == sizeof (buf1) && memcmp (buf1, buf2, sizeof (buf1)) == 0);
224 result = (n1 != n2) || memcmp (buf1, buf2, n1);
225 #endif /* !HAVE_MMAP */
226 close (file2);
228 close (file1);
230 rotate_dash (FALSE);
232 return result;
235 /* --------------------------------------------------------------------------------------------- */
237 static void
238 compare_dir (WPanel * panel, const WPanel * other, enum CompareMode mode)
240 int i, j;
242 /* No marks by default */
243 panel->marked = 0;
244 panel->total = 0;
245 panel->dirs_marked = 0;
247 /* Handle all files in the panel */
248 for (i = 0; i < panel->dir.len; i++)
250 file_entry_t *source = &panel->dir.list[i];
251 const char *source_fname;
253 /* Default: unmarked */
254 file_mark (panel, i, 0);
256 /* Skip directories */
257 if (S_ISDIR (source->st.st_mode))
258 continue;
260 source_fname = source->fname->str;
261 if (panel->is_panelized)
262 source_fname = x_basename (source_fname);
264 /* Search the corresponding entry from the other panel */
265 for (j = 0; j < other->dir.len; j++)
267 const char *other_fname;
269 other_fname = other->dir.list[j].fname->str;
270 if (other->is_panelized)
271 other_fname = x_basename (other_fname);
273 if (strcmp (source_fname, other_fname) == 0)
274 break;
277 if (j >= other->dir.len)
278 /* Not found -> mark */
279 do_file_mark (panel, i, 1);
280 else
282 /* Found */
283 file_entry_t *target = &other->dir.list[j];
285 if (mode != compare_size_only)
286 /* Older version is not marked */
287 if (source->st.st_mtime < target->st.st_mtime)
288 continue;
290 /* Newer version with different size is marked */
291 if (source->st.st_size != target->st.st_size)
293 do_file_mark (panel, i, 1);
294 continue;
297 if (mode == compare_size_only)
298 continue;
300 if (mode == compare_quick)
302 /* Thorough compare off, compare only time stamps */
303 /* Mark newer version, don't mark version with the same date */
304 if (source->st.st_mtime > target->st.st_mtime)
305 do_file_mark (panel, i, 1);
307 continue;
310 /* Thorough compare on, do byte-by-byte comparison */
312 vfs_path_t *src_name, *dst_name;
314 src_name =
315 vfs_path_append_new (panel->cwd_vpath, source->fname->str, (char *) NULL);
316 dst_name =
317 vfs_path_append_new (other->cwd_vpath, target->fname->str, (char *) NULL);
318 if (compare_files (src_name, dst_name, source->st.st_size))
319 do_file_mark (panel, i, 1);
320 vfs_path_free (src_name, TRUE);
321 vfs_path_free (dst_name, TRUE);
324 } /* for (i ...) */
327 /* --------------------------------------------------------------------------------------------- */
329 static void
330 do_link (link_type_t link_type, const char *fname)
332 char *dest = NULL, *src = NULL;
333 vfs_path_t *dest_vpath = NULL;
335 if (link_type == LINK_HARDLINK)
337 vfs_path_t *fname_vpath;
339 src = g_strdup_printf (_("Link %s to:"), str_trunc (fname, 46));
340 dest =
341 input_expand_dialog (_("Link"), src, MC_HISTORY_FM_LINK, "", INPUT_COMPLETE_FILENAMES);
342 if (dest == NULL || *dest == '\0')
343 goto cleanup;
345 save_cwds_stat ();
347 fname_vpath = vfs_path_from_str (fname);
348 dest_vpath = vfs_path_from_str (dest);
349 if (mc_link (fname_vpath, dest_vpath) == -1)
350 message (D_ERROR, MSG_ERROR, _("link: %s"), unix_error_string (errno));
351 vfs_path_free (fname_vpath, TRUE);
353 else
355 vfs_path_t *s, *d;
357 /* suggest the full path for symlink, and either the full or
358 relative path to the file it points to */
359 s = vfs_path_append_new (current_panel->cwd_vpath, fname, (char *) NULL);
361 if (get_other_type () == view_listing)
362 d = vfs_path_append_new (other_panel->cwd_vpath, fname, (char *) NULL);
363 else
364 d = vfs_path_from_str (fname);
366 if (link_type == LINK_SYMLINK_RELATIVE)
368 char *s_str;
370 s_str = diff_two_paths (other_panel->cwd_vpath, s);
371 vfs_path_free (s, TRUE);
372 s = vfs_path_from_str_flags (s_str, VPF_NO_CANON);
373 g_free (s_str);
376 symlink_box (s, d, &dest, &src);
377 vfs_path_free (d, TRUE);
378 vfs_path_free (s, TRUE);
380 if (dest == NULL || *dest == '\0' || src == NULL || *src == '\0')
381 goto cleanup;
383 save_cwds_stat ();
385 dest_vpath = vfs_path_from_str_flags (dest, VPF_NO_CANON);
387 s = vfs_path_from_str (src);
388 if (mc_symlink (dest_vpath, s) == -1)
389 message (D_ERROR, MSG_ERROR, _("symlink: %s"), unix_error_string (errno));
390 vfs_path_free (s, TRUE);
393 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
394 repaint_screen ();
396 cleanup:
397 vfs_path_free (dest_vpath, TRUE);
398 g_free (src);
399 g_free (dest);
402 /* --------------------------------------------------------------------------------------------- */
404 #if defined(ENABLE_VFS_UNDELFS) || defined(ENABLE_VFS_NET)
405 static void
406 nice_cd (const char *text, const char *xtext, const char *help,
407 const char *history_name, const char *prefix, int to_home, gboolean strip_password)
409 char *machine;
410 char *cd_path;
412 machine =
413 input_dialog_help (text, xtext, help, history_name, INPUT_LAST_TEXT, strip_password,
414 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD | INPUT_COMPLETE_HOSTNAMES |
415 INPUT_COMPLETE_USERNAMES);
416 if (machine == NULL)
417 return;
419 to_home = 0; /* FIXME: how to solve going to home nicely? /~/ is
420 ugly as hell and leads to problems in vfs layer */
422 if (strncmp (prefix, machine, strlen (prefix)) == 0)
423 cd_path = g_strconcat (machine, to_home ? "/~/" : (char *) NULL, (char *) NULL);
424 else
425 cd_path = g_strconcat (prefix, machine, to_home ? "/~/" : (char *) NULL, (char *) NULL);
427 g_free (machine);
429 if (!IS_PATH_SEP (*cd_path))
431 char *tmp = cd_path;
433 cd_path = g_strconcat (PATH_SEP_STR, tmp, (char *) NULL);
434 g_free (tmp);
438 panel_view_mode_t save_type;
439 vfs_path_t *cd_vpath;
441 save_type = get_panel_type (MENU_PANEL_IDX);
443 if (save_type != view_listing)
444 create_panel (MENU_PANEL_IDX, view_listing);
446 cd_vpath = vfs_path_from_str_flags (cd_path, VPF_NO_CANON);
447 if (!panel_do_cd (MENU_PANEL, cd_vpath, cd_parse_command))
449 cd_error_message (cd_path);
451 if (save_type != view_listing)
452 create_panel (MENU_PANEL_IDX, save_type);
454 vfs_path_free (cd_vpath, TRUE);
456 g_free (cd_path);
458 /* In case of passive panel, restore current VFS directory that was changed in panel_do_cd() */
459 if (MENU_PANEL != current_panel)
460 (void) mc_chdir (current_panel->cwd_vpath);
462 #endif /* ENABLE_VFS_UNDELFS || ENABLE_VFS_NET */
464 /* --------------------------------------------------------------------------------------------- */
466 static void
467 configure_panel_listing (WPanel * p, int list_format, int brief_cols, gboolean use_msformat,
468 char **user, char **status)
470 p->user_mini_status = use_msformat;
471 p->list_format = list_format;
473 if (list_format == list_brief)
474 p->brief_cols = brief_cols;
476 if (list_format == list_user || use_msformat)
478 g_free (p->user_format);
479 p->user_format = *user;
480 *user = NULL;
482 g_free (p->user_status_format[list_format]);
483 p->user_status_format[list_format] = *status;
484 *status = NULL;
486 set_panel_formats (p);
489 set_panel_formats (p);
490 do_refresh ();
493 /* --------------------------------------------------------------------------------------------- */
495 static void
496 switch_to_listing (int panel_index)
498 if (get_panel_type (panel_index) != view_listing)
499 create_panel (panel_index, view_listing);
502 /* --------------------------------------------------------------------------------------------- */
503 /*** public functions ****************************************************************************/
504 /* --------------------------------------------------------------------------------------------- */
506 gboolean
507 view_file_at_line (const vfs_path_t * filename_vpath, gboolean plain_view, gboolean internal,
508 long start_line, off_t search_start, off_t search_end)
510 gboolean ret = TRUE;
512 if (plain_view)
514 mcview_mode_flags_t changed_flags;
516 mcview_clear_mode_flags (&changed_flags);
517 mcview_altered_flags.hex = FALSE;
518 mcview_altered_flags.magic = FALSE;
519 mcview_altered_flags.nroff = FALSE;
520 if (mcview_global_flags.hex)
521 changed_flags.hex = TRUE;
522 if (mcview_global_flags.magic)
523 changed_flags.magic = TRUE;
524 if (mcview_global_flags.nroff)
525 changed_flags.nroff = TRUE;
526 mcview_global_flags.hex = FALSE;
527 mcview_global_flags.magic = FALSE;
528 mcview_global_flags.nroff = FALSE;
530 ret = mcview_viewer (NULL, filename_vpath, start_line, search_start, search_end);
532 if (changed_flags.hex && !mcview_altered_flags.hex)
533 mcview_global_flags.hex = TRUE;
534 if (changed_flags.magic && !mcview_altered_flags.magic)
535 mcview_global_flags.magic = TRUE;
536 if (changed_flags.nroff && !mcview_altered_flags.nroff)
537 mcview_global_flags.nroff = TRUE;
539 dialog_switch_process_pending ();
541 else if (internal)
543 char view_entry[BUF_TINY];
545 if (start_line > 0)
546 g_snprintf (view_entry, sizeof (view_entry), "View:%ld", start_line);
547 else
548 strcpy (view_entry, "View");
550 ret = (regex_command (filename_vpath, view_entry) == 0);
551 if (ret)
553 ret = mcview_viewer (NULL, filename_vpath, start_line, search_start, search_end);
554 dialog_switch_process_pending ();
557 else
559 static const char *viewer = NULL;
561 if (viewer == NULL)
563 viewer = getenv ("VIEWER");
564 if (viewer == NULL)
565 viewer = getenv ("PAGER");
566 if (viewer == NULL)
567 viewer = "view";
570 execute_external_editor_or_viewer (viewer, filename_vpath, start_line);
573 return ret;
576 /* --------------------------------------------------------------------------------------------- */
577 /** view_file (filename, plain_view, internal)
579 * Inputs:
580 * filename_vpath: The file name to view
581 * plain_view: If set does not do any fancy pre-processing (no filtering) and
582 * always invokes the internal viewer.
583 * internal: If set uses the internal viewer, otherwise an external viewer.
586 gboolean
587 view_file (const vfs_path_t * filename_vpath, gboolean plain_view, gboolean internal)
589 return view_file_at_line (filename_vpath, plain_view, internal, 0, 0, 0);
593 /* --------------------------------------------------------------------------------------------- */
594 /** Run user's preferred viewer on the current file */
596 void
597 view_cmd (WPanel * panel)
599 do_view_cmd (panel, FALSE);
602 /* --------------------------------------------------------------------------------------------- */
603 /** Ask for file and run user's preferred viewer on it */
605 void
606 view_file_cmd (const WPanel * panel)
608 char *filename;
609 vfs_path_t *vpath;
611 filename =
612 input_expand_dialog (_("View file"), _("Filename:"),
613 MC_HISTORY_FM_VIEW_FILE, panel_current_entry (panel)->fname->str,
614 INPUT_COMPLETE_FILENAMES);
615 if (filename == NULL)
616 return;
618 vpath = vfs_path_from_str (filename);
619 g_free (filename);
620 view_file (vpath, FALSE, use_internal_view);
621 vfs_path_free (vpath, TRUE);
624 /* --------------------------------------------------------------------------------------------- */
625 /** Run plain internal viewer on the current file */
626 void
627 view_raw_cmd (WPanel * panel)
629 do_view_cmd (panel, TRUE);
632 /* --------------------------------------------------------------------------------------------- */
634 void
635 view_filtered_cmd (const WPanel * panel)
637 char *command;
638 const char *initial_command;
640 if (input_is_empty (cmdline))
641 initial_command = panel_current_entry (panel)->fname->str;
642 else
643 initial_command = input_get_ctext (cmdline);
645 command =
646 input_dialog (_("Filtered view"),
647 _("Filter command and arguments:"),
648 MC_HISTORY_FM_FILTERED_VIEW, initial_command,
649 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_COMMANDS);
651 if (command != NULL)
653 mcview_viewer (command, NULL, 0, 0, 0);
654 g_free (command);
655 dialog_switch_process_pending ();
659 /* --------------------------------------------------------------------------------------------- */
661 void
662 edit_file_at_line (const vfs_path_t * what_vpath, gboolean internal, long start_line)
665 #ifdef USE_INTERNAL_EDIT
666 if (internal)
668 const edit_arg_t arg = { (vfs_path_t *) what_vpath, start_line };
670 edit_file (&arg);
672 else
673 #endif /* USE_INTERNAL_EDIT */
675 static const char *editor = NULL;
677 (void) internal;
679 if (editor == NULL)
681 editor = getenv ("EDITOR");
682 if (editor == NULL)
683 editor = get_default_editor ();
686 execute_external_editor_or_viewer (editor, what_vpath, start_line);
689 if (mc_global.mc_run_mode == MC_RUN_FULL)
690 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
692 #ifdef USE_INTERNAL_EDIT
693 if (use_internal_edit)
694 dialog_switch_process_pending ();
695 else
696 #endif /* USE_INTERNAL_EDIT */
697 repaint_screen ();
700 /* --------------------------------------------------------------------------------------------- */
702 void
703 edit_cmd (const WPanel * panel)
705 vfs_path_t *fname;
707 fname = vfs_path_from_str (panel_current_entry (panel)->fname->str);
708 if (regex_command (fname, "Edit") == 0)
709 do_edit (fname);
710 vfs_path_free (fname, TRUE);
713 /* --------------------------------------------------------------------------------------------- */
715 #ifdef USE_INTERNAL_EDIT
716 void
717 edit_cmd_force_internal (const WPanel * panel)
719 vfs_path_t *fname;
721 fname = vfs_path_from_str (panel_current_entry (panel)->fname->str);
722 if (regex_command (fname, "Edit") == 0)
723 edit_file_at_line (fname, TRUE, 1);
724 vfs_path_free (fname, TRUE);
726 #endif
728 /* --------------------------------------------------------------------------------------------- */
730 void
731 edit_cmd_new (void)
733 vfs_path_t *fname_vpath = NULL;
735 if (editor_ask_filename_before_edit)
737 char *fname;
739 fname = input_expand_dialog (_("Edit file"), _("Enter file name:"),
740 MC_HISTORY_EDIT_LOAD, "", INPUT_COMPLETE_FILENAMES);
741 if (fname == NULL)
742 return;
744 if (*fname != '\0')
745 fname_vpath = vfs_path_from_str (fname);
747 g_free (fname);
750 #ifdef HAVE_CHARSET
751 mc_global.source_codepage = default_source_codepage;
752 #endif
753 do_edit (fname_vpath);
755 vfs_path_free (fname_vpath, TRUE);
758 /* --------------------------------------------------------------------------------------------- */
760 void
761 mkdir_cmd (WPanel * panel)
763 const file_entry_t *fe;
764 char *dir;
765 const char *name = "";
767 fe = panel_current_entry (panel);
769 /* If 'on' then automatically fills name with current item name */
770 if (auto_fill_mkdir_name && !DIR_IS_DOTDOT (fe->fname->str))
771 name = fe->fname->str;
773 dir =
774 input_expand_dialog (_("Create a new Directory"),
775 _("Enter directory name:"), MC_HISTORY_FM_MKDIR, name,
776 INPUT_COMPLETE_FILENAMES);
778 if (dir != NULL && *dir != '\0')
780 vfs_path_t *absdir;
782 if (IS_PATH_SEP (dir[0]) || dir[0] == '~')
783 absdir = vfs_path_from_str (dir);
784 else
786 /* possible escaped '~' */
787 /* allow create directory with name '~' */
788 char *tmpdir = dir;
790 if (dir[0] == '\\' && dir[1] == '~')
791 tmpdir = dir + 1;
793 absdir = vfs_path_append_new (panel->cwd_vpath, tmpdir, (char *) NULL);
796 save_cwds_stat ();
798 if (my_mkdir (absdir, 0777) != 0)
799 message (D_ERROR, MSG_ERROR, "%s", unix_error_string (errno));
800 else
802 update_panels (UP_OPTIMIZE, dir);
803 repaint_screen ();
804 select_item (panel);
807 vfs_path_free (absdir, TRUE);
809 g_free (dir);
812 /* --------------------------------------------------------------------------------------------- */
814 void
815 reread_cmd (void)
817 panel_update_flags_t flag = UP_ONLY_CURRENT;
819 if (get_current_type () == view_listing && get_other_type () == view_listing &&
820 vfs_path_equal (current_panel->cwd_vpath, other_panel->cwd_vpath))
821 flag = UP_OPTIMIZE;
823 update_panels (UP_RELOAD | flag, UP_KEEPSEL);
824 repaint_screen ();
827 /* --------------------------------------------------------------------------------------------- */
829 void
830 ext_cmd (void)
832 vfs_path_t *extdir_vpath;
833 int dir = 0;
835 if (geteuid () == 0)
836 dir = query_dialog (_("Extension file edit"),
837 _("Which extension file you want to edit?"), D_NORMAL, 2,
838 _("&User"), _("&System Wide"));
840 extdir_vpath = vfs_path_build_filename (mc_global.sysconfig_dir, MC_EXT_FILE, (char *) NULL);
842 if (dir == 0)
844 vfs_path_t *buffer_vpath;
846 buffer_vpath = mc_config_get_full_vpath (MC_EXT_FILE);
847 check_for_default (extdir_vpath, buffer_vpath);
848 do_edit (buffer_vpath);
849 vfs_path_free (buffer_vpath, TRUE);
851 else if (dir == 1)
853 if (!exist_file (vfs_path_get_last_path_str (extdir_vpath)))
855 vfs_path_free (extdir_vpath, TRUE);
856 extdir_vpath =
857 vfs_path_build_filename (mc_global.share_data_dir, MC_EXT_FILE, (char *) NULL);
859 do_edit (extdir_vpath);
862 vfs_path_free (extdir_vpath, TRUE);
863 flush_extension_file ();
866 /* --------------------------------------------------------------------------------------------- */
867 /** edit file menu for mc */
869 void
870 edit_mc_menu_cmd (void)
872 vfs_path_t *buffer_vpath;
873 vfs_path_t *menufile_vpath;
874 int dir = 0;
876 query_set_sel (1);
877 dir = query_dialog (_("Menu edit"),
878 _("Which menu file do you want to edit?"),
879 D_NORMAL, geteuid ()? 2 : 3, _("&Local"), _("&User"), _("&System Wide"));
881 menufile_vpath =
882 vfs_path_build_filename (mc_global.sysconfig_dir, MC_GLOBAL_MENU, (char *) NULL);
884 if (!exist_file (vfs_path_get_last_path_str (menufile_vpath)))
886 vfs_path_free (menufile_vpath, TRUE);
887 menufile_vpath =
888 vfs_path_build_filename (mc_global.share_data_dir, MC_GLOBAL_MENU, (char *) NULL);
891 switch (dir)
893 case 0:
894 buffer_vpath = vfs_path_from_str (MC_LOCAL_MENU);
895 check_for_default (menufile_vpath, buffer_vpath);
896 chmod (vfs_path_get_last_path_str (buffer_vpath), 0600);
897 break;
899 case 1:
900 buffer_vpath = mc_config_get_full_vpath (MC_USERMENU_FILE);
901 check_for_default (menufile_vpath, buffer_vpath);
902 break;
904 case 2:
905 buffer_vpath =
906 vfs_path_build_filename (mc_global.sysconfig_dir, MC_GLOBAL_MENU, (char *) NULL);
907 if (!exist_file (vfs_path_get_last_path_str (buffer_vpath)))
909 vfs_path_free (buffer_vpath, TRUE);
910 buffer_vpath =
911 vfs_path_build_filename (mc_global.share_data_dir, MC_GLOBAL_MENU, (char *) NULL);
913 break;
915 default:
916 vfs_path_free (menufile_vpath, TRUE);
917 return;
920 do_edit (buffer_vpath);
922 vfs_path_free (buffer_vpath, TRUE);
923 vfs_path_free (menufile_vpath, TRUE);
926 /* --------------------------------------------------------------------------------------------- */
928 void
929 edit_fhl_cmd (void)
931 vfs_path_t *fhlfile_vpath = NULL;
932 int dir = 0;
934 if (geteuid () == 0)
935 dir = query_dialog (_("Highlighting groups file edit"),
936 _("Which highlighting file you want to edit?"), D_NORMAL, 2,
937 _("&User"), _("&System Wide"));
939 fhlfile_vpath =
940 vfs_path_build_filename (mc_global.sysconfig_dir, MC_FHL_INI_FILE, (char *) NULL);
942 if (dir == 0)
944 vfs_path_t *buffer_vpath;
946 buffer_vpath = mc_config_get_full_vpath (MC_FHL_INI_FILE);
947 check_for_default (fhlfile_vpath, buffer_vpath);
948 do_edit (buffer_vpath);
949 vfs_path_free (buffer_vpath, TRUE);
951 else if (dir == 1)
953 if (!exist_file (vfs_path_get_last_path_str (fhlfile_vpath)))
955 vfs_path_free (fhlfile_vpath, TRUE);
956 fhlfile_vpath =
957 vfs_path_build_filename (mc_global.sysconfig_dir, MC_FHL_INI_FILE, (char *) NULL);
959 do_edit (fhlfile_vpath);
962 vfs_path_free (fhlfile_vpath, TRUE);
963 /* refresh highlighting rules */
964 mc_fhl_free (&mc_filehighlight);
965 mc_filehighlight = mc_fhl_new (TRUE);
968 /* --------------------------------------------------------------------------------------------- */
970 void
971 hotlist_cmd (WPanel * panel)
973 char *target;
975 target = hotlist_show (LIST_HOTLIST, panel);
976 if (target == NULL)
977 return;
979 if (get_current_type () == view_tree)
981 vfs_path_t *vpath;
983 vpath = vfs_path_from_str (target);
984 tree_chdir (the_tree, vpath);
985 vfs_path_free (vpath, TRUE);
987 else
989 vfs_path_t *deprecated_vpath;
990 const char *deprecated_path;
992 deprecated_vpath = vfs_path_from_str_flags (target, VPF_USE_DEPRECATED_PARSER);
993 deprecated_path = vfs_path_as_str (deprecated_vpath);
994 cd_to (deprecated_path);
995 vfs_path_free (deprecated_vpath, TRUE);
998 g_free (target);
1001 /* --------------------------------------------------------------------------------------------- */
1003 #ifdef ENABLE_VFS
1004 void
1005 vfs_list (WPanel * panel)
1007 char *target;
1008 vfs_path_t *target_vpath;
1010 target = hotlist_show (LIST_VFSLIST, panel);
1011 if (target == NULL)
1012 return;
1014 target_vpath = vfs_path_from_str (target);
1015 if (!panel_cd (current_panel, target_vpath, cd_exact))
1016 cd_error_message (target);
1017 vfs_path_free (target_vpath, TRUE);
1018 g_free (target);
1020 #endif /* ENABLE_VFS */
1022 /* --------------------------------------------------------------------------------------------- */
1024 void
1025 compare_dirs_cmd (void)
1027 int choice;
1028 enum CompareMode thorough_flag;
1030 choice =
1031 query_dialog (_("Compare directories"),
1032 _("Select compare method:"), D_NORMAL, 4,
1033 _("&Quick"), _("&Size only"), _("&Thorough"), _("&Cancel"));
1035 if (choice < 0 || choice > 2)
1036 return;
1038 thorough_flag = choice;
1040 if (get_current_type () == view_listing && get_other_type () == view_listing)
1042 compare_dir (current_panel, other_panel, thorough_flag);
1043 compare_dir (other_panel, current_panel, thorough_flag);
1045 else
1046 message (D_ERROR, MSG_ERROR,
1047 _("Both panels should be in the listing mode\nto use this command"));
1050 /* --------------------------------------------------------------------------------------------- */
1052 #ifdef USE_DIFF_VIEW
1053 void
1054 diff_view_cmd (void)
1056 /* both panels must be in the list mode */
1057 if (get_current_type () == view_listing && get_other_type () == view_listing)
1059 if (get_current_index () == 0)
1060 dview_diff_cmd (current_panel, other_panel);
1061 else
1062 dview_diff_cmd (other_panel, current_panel);
1064 if (mc_global.mc_run_mode == MC_RUN_FULL)
1065 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
1067 dialog_switch_process_pending ();
1070 #endif
1072 /* --------------------------------------------------------------------------------------------- */
1074 void
1075 swap_cmd (void)
1077 swap_panels ();
1078 tty_touch_screen ();
1079 repaint_screen ();
1082 /* --------------------------------------------------------------------------------------------- */
1084 void
1085 link_cmd (link_type_t link_type)
1087 const char *filename;
1089 filename = panel_current_entry (current_panel)->fname->str;
1090 if (filename != NULL)
1091 do_link (link_type, filename);
1094 /* --------------------------------------------------------------------------------------------- */
1096 void
1097 edit_symlink_cmd (void)
1099 const file_entry_t *fe;
1100 const char *p;
1102 fe = panel_current_entry (current_panel);
1103 p = fe->fname->str;
1105 if (!S_ISLNK (fe->st.st_mode))
1106 message (D_ERROR, MSG_ERROR, _("'%s' is not a symbolic link"), p);
1107 else
1109 char buffer[MC_MAXPATHLEN];
1110 int i;
1112 i = readlink (p, buffer, sizeof (buffer) - 1);
1113 if (i > 0)
1115 char *q, *dest;
1117 buffer[i] = '\0';
1119 q = g_strdup_printf (_("Symlink '%s\' points to:"), str_trunc (p, 32));
1120 dest =
1121 input_expand_dialog (_("Edit symlink"), q, MC_HISTORY_FM_EDIT_LINK, buffer,
1122 INPUT_COMPLETE_FILENAMES);
1123 g_free (q);
1125 if (dest != NULL && *dest != '\0' && strcmp (buffer, dest) != 0)
1127 vfs_path_t *p_vpath;
1129 p_vpath = vfs_path_from_str (p);
1131 save_cwds_stat ();
1133 if (mc_unlink (p_vpath) == -1)
1134 message (D_ERROR, MSG_ERROR, _("edit symlink, unable to remove %s: %s"), p,
1135 unix_error_string (errno));
1136 else
1138 vfs_path_t *dest_vpath;
1140 dest_vpath = vfs_path_from_str_flags (dest, VPF_NO_CANON);
1141 if (mc_symlink (dest_vpath, p_vpath) == -1)
1142 message (D_ERROR, MSG_ERROR, _("edit symlink: %s"),
1143 unix_error_string (errno));
1144 vfs_path_free (dest_vpath, TRUE);
1147 vfs_path_free (p_vpath, TRUE);
1149 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
1150 repaint_screen ();
1153 g_free (dest);
1158 /* --------------------------------------------------------------------------------------------- */
1160 void
1161 help_cmd (void)
1163 ev_help_t event_data = { NULL, NULL };
1165 if (current_panel->quick_search.active)
1166 event_data.node = "[Quick search]";
1167 else
1168 event_data.node = "[main]";
1170 mc_event_raise (MCEVENT_GROUP_CORE, "help", &event_data);
1173 /* --------------------------------------------------------------------------------------------- */
1175 void
1176 user_file_menu_cmd (void)
1178 (void) user_menu_cmd (NULL, NULL, -1);
1181 /* --------------------------------------------------------------------------------------------- */
1183 #ifdef ENABLE_VFS_FTP
1184 void
1185 ftplink_cmd (void)
1187 nice_cd (_("FTP to machine"), _(machine_str),
1188 "[FTP File System]", ":ftplink_cmd: FTP to machine ", "ftp://", 1, TRUE);
1190 #endif /* ENABLE_VFS_FTP */
1192 /* --------------------------------------------------------------------------------------------- */
1194 #ifdef ENABLE_VFS_SFTP
1195 void
1196 sftplink_cmd (void)
1198 nice_cd (_("SFTP to machine"), _(machine_str),
1199 "[SFTP (SSH File Transfer Protocol) filesystem]",
1200 ":sftplink_cmd: SFTP to machine ", "sftp://", 1, TRUE);
1202 #endif /* ENABLE_VFS_SFTP */
1204 /* --------------------------------------------------------------------------------------------- */
1206 #ifdef ENABLE_VFS_SHELL
1207 void
1208 shelllink_cmd (void)
1210 nice_cd (_("Shell link to machine"), _(machine_str),
1211 "[FIle transfer over SHell filesystem]", ":fishlink_cmd: Shell link to machine ",
1212 "sh://", 1, TRUE);
1214 #endif /* ENABLE_VFS_SHELL */
1216 /* --------------------------------------------------------------------------------------------- */
1218 #ifdef ENABLE_VFS_UNDELFS
1219 void
1220 undelete_cmd (void)
1222 nice_cd (_("Undelete files on an ext2 file system"),
1223 _("Enter device (without /dev/) to undelete\nfiles on: (F1 for details)"),
1224 "[Undelete File System]", ":undelete_cmd: Undel on ext2 fs ", "undel://", 0, FALSE);
1226 #endif /* ENABLE_VFS_UNDELFS */
1228 /* --------------------------------------------------------------------------------------------- */
1230 void
1231 quick_cd_cmd (WPanel * panel)
1233 char *p;
1235 p = cd_box (panel);
1236 if (p != NULL && *p != '\0')
1237 cd_to (p);
1238 g_free (p);
1241 /* --------------------------------------------------------------------------------------------- */
1243 \brief calculate dirs sizes
1245 calculate dirs sizes and resort panel:
1246 dirs_selected = show size for selected dirs,
1247 otherwise = show size for dir under cursor:
1248 dir under cursor ".." = show size for all dirs,
1249 otherwise = show size for dir under cursor
1252 void
1253 smart_dirsize_cmd (WPanel * panel)
1255 const file_entry_t *entry;
1257 entry = panel_current_entry (panel);
1258 if ((S_ISDIR (entry->st.st_mode) && DIR_IS_DOTDOT (entry->fname->str)) || panel->dirs_marked)
1259 dirsizes_cmd (panel);
1260 else
1261 single_dirsize_cmd (panel);
1264 /* --------------------------------------------------------------------------------------------- */
1266 void
1267 single_dirsize_cmd (WPanel * panel)
1269 file_entry_t *entry;
1271 entry = panel_current_entry (panel);
1273 if (S_ISDIR (entry->st.st_mode) && !DIR_IS_DOTDOT (entry->fname->str))
1275 size_t dir_count = 0;
1276 size_t count = 0;
1277 uintmax_t total = 0;
1278 dirsize_status_msg_t dsm;
1279 vfs_path_t *p;
1281 p = vfs_path_from_str (entry->fname->str);
1283 memset (&dsm, 0, sizeof (dsm));
1284 status_msg_init (STATUS_MSG (&dsm), _("Directory scanning"), 0, dirsize_status_init_cb,
1285 dirsize_status_update_cb, dirsize_status_deinit_cb);
1287 if (compute_dir_size (p, &dsm, &dir_count, &count, &total, FALSE) == FILE_CONT)
1289 entry->st.st_size = (off_t) total;
1290 entry->f.dir_size_computed = 1;
1293 vfs_path_free (p, TRUE);
1295 status_msg_deinit (STATUS_MSG (&dsm));
1298 if (panels_options.mark_moves_down)
1299 send_message (panel, NULL, MSG_ACTION, CK_Down, NULL);
1301 recalculate_panel_summary (panel);
1303 if (panel->sort_field->sort_routine == (GCompareFunc) sort_size)
1304 panel_re_sort (panel);
1306 panel->dirty = TRUE;
1309 /* --------------------------------------------------------------------------------------------- */
1311 void
1312 dirsizes_cmd (WPanel * panel)
1314 int i;
1315 dirsize_status_msg_t dsm;
1317 memset (&dsm, 0, sizeof (dsm));
1318 status_msg_init (STATUS_MSG (&dsm), _("Directory scanning"), 0, dirsize_status_init_cb,
1319 dirsize_status_update_cb, dirsize_status_deinit_cb);
1321 for (i = 0; i < panel->dir.len; i++)
1322 if (S_ISDIR (panel->dir.list[i].st.st_mode)
1323 && ((panel->dirs_marked != 0 && panel->dir.list[i].f.marked != 0)
1324 || panel->dirs_marked == 0) && !DIR_IS_DOTDOT (panel->dir.list[i].fname->str))
1326 vfs_path_t *p;
1327 size_t dir_count = 0;
1328 size_t count = 0;
1329 uintmax_t total = 0;
1330 gboolean ok;
1332 p = vfs_path_from_str (panel->dir.list[i].fname->str);
1333 ok = compute_dir_size (p, &dsm, &dir_count, &count, &total, FALSE) != FILE_CONT;
1334 vfs_path_free (p, TRUE);
1335 if (ok)
1336 break;
1338 panel->dir.list[i].st.st_size = (off_t) total;
1339 panel->dir.list[i].f.dir_size_computed = 1;
1342 status_msg_deinit (STATUS_MSG (&dsm));
1344 recalculate_panel_summary (panel);
1346 if (panel->sort_field->sort_routine == (GCompareFunc) sort_size)
1347 panel_re_sort (panel);
1349 panel->dirty = TRUE;
1352 /* --------------------------------------------------------------------------------------------- */
1354 void
1355 save_setup_cmd (void)
1357 vfs_path_t *vpath;
1358 const char *path;
1360 vpath = vfs_path_from_str_flags (mc_config_get_path (), VPF_STRIP_HOME);
1361 path = vfs_path_as_str (vpath);
1363 if (save_setup (TRUE, TRUE))
1364 message (D_NORMAL, _("Setup"), _("Setup saved to %s"), path);
1365 else
1366 message (D_ERROR, _("Setup"), _("Unable to save setup to %s"), path);
1368 vfs_path_free (vpath, TRUE);
1371 /* --------------------------------------------------------------------------------------------- */
1373 void
1374 info_cmd_no_menu (void)
1376 if (get_panel_type (0) == view_info)
1377 create_panel (0, view_listing);
1378 else if (get_panel_type (1) == view_info)
1379 create_panel (1, view_listing);
1380 else
1381 create_panel (current_panel == left_panel ? 1 : 0, view_info);
1384 /* --------------------------------------------------------------------------------------------- */
1386 void
1387 quick_cmd_no_menu (void)
1389 if (get_panel_type (0) == view_quick)
1390 create_panel (0, view_listing);
1391 else if (get_panel_type (1) == view_quick)
1392 create_panel (1, view_listing);
1393 else
1394 create_panel (current_panel == left_panel ? 1 : 0, view_quick);
1397 /* --------------------------------------------------------------------------------------------- */
1399 void
1400 listing_cmd (void)
1402 WPanel *p;
1404 switch_to_listing (MENU_PANEL_IDX);
1406 p = PANEL (get_panel_widget (MENU_PANEL_IDX));
1408 p->is_panelized = FALSE;
1409 panel_set_filter (p, NULL); /* including panel reload */
1412 /* --------------------------------------------------------------------------------------------- */
1414 void
1415 setup_listing_format_cmd (void)
1417 int list_format;
1418 gboolean use_msformat;
1419 int brief_cols;
1420 char *user, *status;
1421 WPanel *p = NULL;
1423 if (SELECTED_IS_PANEL)
1424 p = MENU_PANEL_IDX == 0 ? left_panel : right_panel;
1426 list_format = panel_listing_box (p, MENU_PANEL_IDX, &user, &status, &use_msformat, &brief_cols);
1427 if (list_format != -1)
1429 switch_to_listing (MENU_PANEL_IDX);
1430 p = MENU_PANEL_IDX == 0 ? left_panel : right_panel;
1431 configure_panel_listing (p, list_format, brief_cols, use_msformat, &user, &status);
1432 g_free (user);
1433 g_free (status);
1437 /* --------------------------------------------------------------------------------------------- */
1439 void
1440 panel_tree_cmd (void)
1442 create_panel (MENU_PANEL_IDX, view_tree);
1445 /* --------------------------------------------------------------------------------------------- */
1447 void
1448 info_cmd (void)
1450 create_panel (MENU_PANEL_IDX, view_info);
1453 /* --------------------------------------------------------------------------------------------- */
1455 void
1456 quick_view_cmd (void)
1458 if (PANEL (get_panel_widget (MENU_PANEL_IDX)) == current_panel)
1459 (void) change_panel ();
1460 create_panel (MENU_PANEL_IDX, view_quick);
1463 /* --------------------------------------------------------------------------------------------- */
1465 #ifdef HAVE_CHARSET
1466 void
1467 encoding_cmd (void)
1469 if (SELECTED_IS_PANEL)
1470 panel_change_encoding (MENU_PANEL);
1472 #endif
1474 /* --------------------------------------------------------------------------------------------- */