Merge branch '1617_history_fix'
[midnight-commander.git] / src / cmd.c
blobde413554e063df308929a3489c143444c1bdc650
1 /* Routines invoked by a function key
2 They normally operate on the current panel.
4 Copyright (C) 1994, 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
5 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
21 /** \file cmd.c
22 * \brief Source: routines invoked by a function key
24 * They normally operate on the current panel.
27 #include <config.h>
29 #include <errno.h>
30 #include <stdio.h>
31 #include <string.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #ifdef HAVE_MMAP
36 # include <sys/mman.h>
37 #endif
38 #ifdef USE_NETCODE
39 #include <netdb.h>
40 #endif
41 #include <unistd.h>
42 #include <stdlib.h>
43 #include <fcntl.h>
44 #include <pwd.h>
45 #include <grp.h>
46 #include <sys/time.h>
48 #include "global.h"
50 #include "../src/tty/tty.h" /* LINES, tty_touch_screen() */
51 #include "../src/tty/key.h" /* ALT() macro */
52 #include "../src/tty/win.h" /* do_enter_ca_mode() */
54 #include "../src/mcconfig/mcconfig.h"
55 #include "../src/search/search.h"
56 #include "../src/viewer/mcviewer.h"
57 #include "../src/filehighlight/fhl.h" /* MC_FHL_INI_FILE */
59 #include "cmd.h" /* Our definitions */
60 #include "fileopctx.h"
61 #include "file.h" /* file operation routines */
62 #include "find.h" /* do_find() */
63 #include "hotlist.h" /* hotlist_cmd() */
64 #include "tree.h" /* tree_chdir() */
65 #include "subshell.h" /* use_subshell */
66 #include "cons.saver.h" /* console_flag */
67 #include "dialog.h" /* Widget */
68 #include "wtools.h" /* message() */
69 #include "widget.h" /* push_history() */
70 #include "main.h" /* change_panel() */
71 #include "panel.h" /* current_panel */
72 #include "help.h" /* interactive_display() */
73 #include "user.h" /* MC_GLOBAL_MENU */
74 #include "command.h" /* cmdline */
75 #include "layout.h" /* get_current_type() */
76 #include "ext.h" /* regex_command() */
77 #include "boxes.h" /* cd_dialog() */
78 #include "setup.h"
79 #include "execute.h" /* toggle_panels() */
80 #include "history.h"
81 #include "strutil.h"
82 #include "dir.h"
83 #include "cmddef.h" /* CK_InputHistoryShow */
84 #include "fileloc.h"
86 #ifndef MAP_FILE
87 # define MAP_FILE 0
88 #endif
90 #ifdef USE_INTERNAL_EDIT
91 # include "../edit/edit.h"
92 #endif
94 /* If set and you don't have subshell support,then C-o will give you a shell */
95 int output_starts_shell = 0;
97 /* If set, use the builtin editor */
98 int use_internal_edit = 1;
100 /* Automatically fills name with current selected item name on mkdir */
101 int auto_fill_mkdir_name = 1;
103 /* if set, only selection of files is inverted */
104 int reverse_files_only = 1;
106 /* selection flags */
107 typedef enum {
108 SELECT_FILES_ONLY = 1 << 0,
109 SELECT_MATCH_CASE = 1 << 1,
110 SELECT_SHELL_PATTERNS = 1 << 2
111 } select_flags_t;
113 static select_flags_t select_flags = SELECT_MATCH_CASE | SELECT_SHELL_PATTERNS;
116 view_file_at_line (const char *filename, int plain_view, int internal,
117 int start_line)
119 static const char *viewer = NULL;
120 int move_dir = 0;
123 if (plain_view) {
124 int changed_hex_mode = 0;
125 int changed_nroff_flag = 0;
126 int changed_magic_flag = 0;
128 mcview_altered_hex_mode = 0;
129 mcview_altered_nroff_flag = 0;
130 mcview_altered_magic_flag = 0;
131 if (mcview_default_hex_mode)
132 changed_hex_mode = 1;
133 if (mcview_default_nroff_flag)
134 changed_nroff_flag = 1;
135 if (mcview_default_magic_flag)
136 changed_magic_flag = 1;
137 mcview_default_hex_mode = 0;
138 mcview_default_nroff_flag = 0;
139 mcview_default_magic_flag = 0;
140 mcview_viewer (NULL, filename, &move_dir, start_line);
141 if (changed_hex_mode && !mcview_altered_hex_mode)
142 mcview_default_hex_mode = 1;
143 if (changed_nroff_flag && !mcview_altered_nroff_flag)
144 mcview_default_nroff_flag = 1;
145 if (changed_magic_flag && !mcview_altered_magic_flag)
146 mcview_default_magic_flag = 1;
147 repaint_screen ();
148 return move_dir;
150 if (internal) {
151 char view_entry[BUF_TINY];
153 if (start_line != 0)
154 g_snprintf (view_entry, sizeof (view_entry), "View:%d",
155 start_line);
156 else
157 strcpy (view_entry, "View");
159 if (regex_command (filename, view_entry, &move_dir) == 0) {
160 mcview_viewer (NULL, filename, &move_dir, start_line);
161 repaint_screen ();
163 } else {
164 if (!viewer) {
165 viewer = getenv ("VIEWER");
166 if (!viewer)
167 viewer = getenv ("PAGER");
168 if (!viewer)
169 viewer = "view";
171 execute_with_vfs_arg (viewer, filename);
173 return move_dir;
176 /* view_file (filename, plain_view, internal)
178 * Inputs:
179 * filename: The file name to view
180 * plain_view: If set does not do any fancy pre-processing (no filtering) and
181 * always invokes the internal viewer.
182 * internal: If set uses the internal viewer, otherwise an external viewer.
185 view_file (const char *filename, int plain_view, int internal)
187 return view_file_at_line (filename, plain_view, internal, 0);
190 /* scan_for_file (panel, idx, direction)
192 * Inputs:
193 * panel: pointer to the panel on which we operate
194 * idx: starting file.
195 * direction: 1, or -1
197 static int scan_for_file (WPanel *panel, int idx, int direction)
199 int i = idx + direction;
201 while (i != idx){
202 if (i < 0)
203 i = panel->count - 1;
204 if (i == panel->count)
205 i = 0;
206 if (!S_ISDIR (panel->dir.list [i].st.st_mode))
207 return i;
208 i += direction;
210 return i;
214 * Run viewer (internal or external) on the currently selected file.
215 * If normal is 1, force internal viewer and raw mode (used for F13).
217 static void
218 do_view_cmd (int normal)
220 /* Directories are viewed by changing to them */
221 if (S_ISDIR (selection (current_panel)->st.st_mode)
222 || link_isdir (selection (current_panel))) {
223 if (confirm_view_dir && (current_panel->marked || current_panel->dirs_marked)) {
224 if (query_dialog
225 (_(" Confirmation "), _("Files tagged, want to cd?"), D_NORMAL, 2,
226 _("&Yes"), _("&No")) != 0) {
227 return;
230 if (!do_cd (selection (current_panel)->fname, cd_exact))
231 message (D_ERROR, MSG_ERROR, _("Cannot change directory"));
232 } else {
233 int dir, file_idx;
234 char *filename;
236 file_idx = current_panel->selected;
237 while (1) {
238 filename = current_panel->dir.list[file_idx].fname;
240 dir = view_file (filename, normal, use_internal_view);
241 if (dir == 0)
242 break;
243 file_idx = scan_for_file (current_panel, file_idx, dir);
247 repaint_screen();
250 /* Run user's preferred viewer on the currently selected file */
251 void
252 view_cmd (void)
254 do_view_cmd (0);
257 /* Ask for file and run user's preferred viewer on it */
258 void
259 view_file_cmd (void)
261 char *filename;
263 filename =
264 input_expand_dialog (_(" View file "), _(" Filename:"),
265 MC_HISTORY_FM_VIEW_FILE, selection (current_panel)->fname);
266 if (!filename)
267 return;
269 view_file (filename, 0, use_internal_view);
270 g_free (filename);
273 /* Run plain internal viewer on the currently selected file */
274 void
275 view_simple_cmd (void)
277 do_view_cmd (1);
280 void
281 filtered_view_cmd (void)
283 char *command;
285 command =
286 input_dialog (_(" Filtered view "),
287 _(" Filter command and arguments:"),
288 MC_HISTORY_FM_FILTERED_VIEW,
289 selection (current_panel)->fname);
290 if (!command)
291 return;
293 mcview_viewer (command, "", NULL, 0);
295 g_free (command);
298 void
299 do_edit_at_line (const char *what, int start_line)
301 static const char *editor = NULL;
303 #ifdef USE_INTERNAL_EDIT
304 if (use_internal_edit)
305 edit_file (what, start_line);
306 else
307 #endif /* USE_INTERNAL_EDIT */
309 if (editor == NULL) {
310 editor = getenv ("EDITOR");
311 if (editor == NULL)
312 editor = get_default_editor ();
314 execute_with_vfs_arg (editor, what);
316 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
317 repaint_screen ();
320 static void
321 do_edit (const char *what)
323 do_edit_at_line (what, 0);
326 void
327 edit_cmd (void)
329 if (regex_command (selection (current_panel)->fname, "Edit", 0) == 0)
330 do_edit (selection (current_panel)->fname);
333 void
334 edit_cmd_new (void)
336 do_edit (NULL);
339 /* Invoked by F5. Copy, default to the other panel. */
340 void
341 copy_cmd (void)
343 save_cwds_stat ();
344 if (panel_operate (current_panel, OP_COPY, 0)) {
345 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
346 repaint_screen ();
350 /* Invoked by F6. Move/rename, default to the other panel, ignore marks. */
351 void ren_cmd (void)
353 save_cwds_stat ();
354 if (panel_operate (current_panel, OP_MOVE, 0)){
355 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
356 repaint_screen ();
360 /* Invoked by F15. Copy, default to the same panel, ignore marks. */
361 void copy_cmd_local (void)
363 save_cwds_stat ();
364 if (panel_operate (current_panel, OP_COPY, 1)){
365 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
366 repaint_screen ();
370 /* Invoked by F16. Move/rename, default to the same panel. */
371 void ren_cmd_local (void)
373 save_cwds_stat ();
374 if (panel_operate (current_panel, OP_MOVE, 1)){
375 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
376 repaint_screen ();
380 void
381 mkdir_cmd (void)
383 char *dir, *absdir;
384 const char *name = "";
386 /* If 'on' then automatically fills name with current selected item name */
387 if (auto_fill_mkdir_name)
388 name = selection (current_panel)->fname;
390 dir =
391 input_expand_dialog (_("Create a new Directory"),
392 _(" Enter directory name:"),
393 MC_HISTORY_FM_MKDIR,
394 name);
396 if (!dir)
397 return;
399 if (dir[0] == '/' || dir[0] == '~')
400 absdir = g_strdup (dir);
401 else
402 absdir = concat_dir_and_file (current_panel->cwd, dir);
404 save_cwds_stat ();
405 if (my_mkdir (absdir, 0777) == 0) {
406 update_panels (UP_OPTIMIZE, dir);
407 repaint_screen ();
408 select_item (current_panel);
409 } else {
410 message (D_ERROR, MSG_ERROR, " %s ", unix_error_string (errno));
413 g_free (absdir);
414 g_free (dir);
417 void delete_cmd (void)
419 save_cwds_stat ();
421 if (panel_operate (current_panel, OP_DELETE, 0)){
422 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
423 repaint_screen ();
427 /* Invoked by F18. Remove selected file, regardless of marked files. */
428 void delete_cmd_local (void)
430 save_cwds_stat ();
432 if (panel_operate (current_panel, OP_DELETE, 1)){
433 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
434 repaint_screen ();
438 void find_cmd (void)
440 do_find ();
443 static void
444 set_panel_filter_to (WPanel *p, char *allocated_filter_string)
446 g_free (p->filter);
447 p->filter = 0;
449 if (!(allocated_filter_string [0] == '*' && allocated_filter_string [1] == 0))
450 p->filter = allocated_filter_string;
451 else
452 g_free (allocated_filter_string);
453 reread_cmd ();
456 /* Set a given panel filter expression */
457 static void
458 set_panel_filter (WPanel *p)
460 char *reg_exp;
461 const char *x;
463 x = p->filter ? p->filter : easy_patterns ? "*" : ".";
465 reg_exp = input_dialog_help (_(" Filter "),
466 _(" Set expression for filtering filenames"),
467 "[Filter...]", MC_HISTORY_FM_PANEL_FILTER, x);
468 if (!reg_exp)
469 return;
470 set_panel_filter_to (p, reg_exp);
473 /* Invoked from the left/right menus */
474 void filter_cmd (void)
476 WPanel *p;
478 if (!SELECTED_IS_PANEL)
479 return;
481 p = MENU_PANEL;
482 set_panel_filter (p);
485 void reread_cmd (void)
487 int flag;
489 if (get_current_type () == view_listing &&
490 get_other_type () == view_listing)
491 flag = strcmp (current_panel->cwd, other_panel->cwd) ? UP_ONLY_CURRENT : 0;
492 else
493 flag = UP_ONLY_CURRENT;
495 update_panels (UP_RELOAD|flag, UP_KEEPSEL);
496 repaint_screen ();
499 void
500 reverse_selection_cmd (void)
502 int i;
503 file_entry *file;
505 for (i = 0; i < current_panel->count; i++) {
506 file = &current_panel->dir.list [i];
507 if ((reverse_files_only == 0) || !S_ISDIR (file->st.st_mode))
508 do_file_mark (current_panel, i, !file->f.marked);
512 static void
513 select_unselect_cmd (const char *title, const char *history_name, gboolean do_select)
515 /* dialog sizes */
516 const int DX = 50;
517 const int DY = 7;
519 int files_only = (select_flags & SELECT_FILES_ONLY) != 0;
520 int case_sens = (select_flags & SELECT_MATCH_CASE) != 0;
521 int shell_patterns = (select_flags & SELECT_SHELL_PATTERNS) != 0;
523 char *reg_exp;
524 mc_search_t *search;
525 int i;
527 QuickWidget quick_widgets[] =
529 QUICK_CHECKBOX (3, DX, DY - 3, DY, N_("&Using shell patterns"), &shell_patterns),
530 QUICK_CHECKBOX (DX/2 + 1, DX, DY - 4, DY, N_("&Case sensitive"), &case_sens),
531 QUICK_CHECKBOX (3, DX, DY - 4, DY, N_("&Files only"), &files_only),
532 QUICK_INPUT (3, DX, DY - 5, DY, INPUT_LAST_TEXT, DX - 6, 0, history_name, &reg_exp),
533 QUICK_END
536 QuickDialog quick_dlg =
538 DX, DY, -1, -1, title,
539 "[Select/Unselect Files]", quick_widgets, FALSE
542 if (quick_dialog (&quick_dlg) == B_CANCEL)
543 return;
545 if (!reg_exp)
546 return;
547 if (!*reg_exp) {
548 g_free (reg_exp);
549 return;
551 search = mc_search_new (reg_exp, -1);
552 search->search_type = (shell_patterns != 0) ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
553 search->is_entire_line = TRUE;
554 search->is_case_sentitive = case_sens != 0;
556 for (i = 0; i < current_panel->count; i++) {
557 if (strcmp (current_panel->dir.list[i].fname, "..") == 0)
558 continue;
559 if (S_ISDIR (current_panel->dir.list[i].st.st_mode) && files_only != 0)
560 continue;
562 if (mc_search_run (search, current_panel->dir.list[i].fname,
563 0, current_panel->dir.list[i].fnamelen, NULL))
564 do_file_mark (current_panel, i, do_select);
567 mc_search_free (search);
568 g_free (reg_exp);
570 /* result flags */
571 select_flags = 0;
572 if (case_sens != 0)
573 select_flags |= SELECT_MATCH_CASE;
574 if (files_only != 0)
575 select_flags |= SELECT_FILES_ONLY;
576 if (shell_patterns != 0)
577 select_flags |= SELECT_SHELL_PATTERNS;
580 void select_cmd (void)
582 select_unselect_cmd (_(" Select "), ":select_cmd: Select ", TRUE);
585 void unselect_cmd (void)
587 select_unselect_cmd (_(" Unselect "), ":unselect_cmd: Unselect ", FALSE);
590 void ext_cmd (void)
592 char *buffer;
593 char *extdir;
594 int dir;
596 dir = 0;
597 if (geteuid () == 0){
598 dir = query_dialog (_("Extension file edit"),
599 _(" Which extension file you want to edit? "), D_NORMAL, 2,
600 _("&User"), _("&System Wide"));
602 extdir = concat_dir_and_file (mc_home, MC_LIB_EXT);
604 if (dir == 0){
605 buffer = g_build_filename (home_dir, MC_USERCONF_DIR, MC_FILEBIND_FILE, NULL);
606 check_for_default (extdir, buffer);
607 do_edit (buffer);
608 g_free (buffer);
609 } else if (dir == 1) {
610 if (!exist_file(extdir)) {
611 g_free (extdir);
612 extdir = concat_dir_and_file (mc_home_alt, MC_LIB_EXT);
614 do_edit (extdir);
616 g_free (extdir);
617 flush_extension_file ();
620 /* edit file menu for mc */
621 void
622 edit_mc_menu_cmd (void)
624 char *buffer;
625 char *menufile;
626 int dir = 0;
628 dir = query_dialog (
629 _(" Menu edit "),
630 _(" Which menu file do you want to edit? "),
631 D_NORMAL, geteuid() ? 2 : 3,
632 _("&Local"), _("&User"), _("&System Wide")
635 menufile = concat_dir_and_file (mc_home, MC_GLOBAL_MENU);
637 if (!exist_file(menufile)) {
638 g_free (menufile);
639 menufile = concat_dir_and_file (mc_home_alt, MC_GLOBAL_MENU);
642 switch (dir) {
643 case 0:
644 buffer = g_strdup (MC_LOCAL_MENU);
645 check_for_default (menufile, buffer);
646 chmod (buffer, 0600);
647 break;
649 case 1:
650 buffer = g_build_filename (home_dir, MC_USERCONF_DIR, MC_USERMENU_FILE, NULL);
651 check_for_default (menufile, buffer);
652 break;
654 case 2:
655 buffer = concat_dir_and_file (mc_home, MC_GLOBAL_MENU);
656 if (!exist_file(buffer)) {
657 g_free (buffer);
658 buffer = concat_dir_and_file (mc_home_alt, MC_GLOBAL_MENU);
660 break;
662 default:
663 g_free (menufile);
664 return;
667 do_edit (buffer);
669 g_free (buffer);
670 g_free (menufile);
673 void edit_fhl_cmd (void)
675 char *buffer = NULL;
676 char *fhlfile = NULL;
678 int dir;
680 dir = 0;
681 if (geteuid () == 0){
682 dir = query_dialog (_("Highlighting groups file edit"),
683 _(" Which highlighting file you want to edit? "), D_NORMAL, 2,
684 _("&User"), _("&System Wide"));
686 fhlfile = concat_dir_and_file (mc_home, MC_FHL_INI_FILE);
688 if (dir == 0){
689 buffer = g_build_filename (home_dir, MC_USERCONF_DIR, MC_FHL_INI_FILE, NULL);
690 check_for_default (fhlfile, buffer);
691 do_edit (buffer);
692 g_free (buffer);
693 } else if (dir == 1) {
694 if (!exist_file(fhlfile)) {
695 g_free (fhlfile);
696 fhlfile = concat_dir_and_file (mc_home, MC_FHL_INI_FILE);
698 do_edit (fhlfile);
700 g_free (fhlfile);
702 /* refresh highlighting rules */
703 mc_fhl_free (&mc_filehighlight);
704 mc_filehighlight = mc_fhl_new (TRUE);
707 void quick_chdir_cmd (void)
709 char *target;
711 target = hotlist_cmd (LIST_HOTLIST);
712 if (!target)
713 return;
715 if (get_current_type () == view_tree)
716 tree_chdir (the_tree, target);
717 else {
718 char *cmd = g_strconcat ("cd ", target, (char *) NULL);
719 do_cd_command (cmd);
720 g_free (cmd);
722 g_free (target);
725 #ifdef USE_VFS
726 void reselect_vfs (void)
728 char *target;
730 target = hotlist_cmd (LIST_VFSLIST);
731 if (!target)
732 return;
734 if (!do_cd (target, cd_exact))
735 message (D_ERROR, MSG_ERROR, _("Cannot change directory") );
736 g_free (target);
738 #endif /* USE_VFS */
740 static int compare_files (char *name1, char *name2, off_t size)
742 int file1, file2;
743 int result = -1; /* Different by default */
745 if (size == 0)
746 return 0;
748 file1 = open (name1, O_RDONLY);
749 if (file1 >= 0){
750 file2 = open (name2, O_RDONLY);
751 if (file2 >= 0){
752 #ifdef HAVE_MMAP
753 char *data1, *data2;
754 /* Ugly if jungle */
755 data1 = mmap (0, size, PROT_READ, MAP_FILE | MAP_PRIVATE, file1, 0);
756 if (data1 != (char*) -1){
757 data2 = mmap (0, size, PROT_READ, MAP_FILE | MAP_PRIVATE, file2, 0);
758 if (data2 != (char*) -1){
759 rotate_dash ();
760 result = memcmp (data1, data2, size);
761 munmap (data2, size);
763 munmap (data1, size);
765 #else
766 /* Don't have mmap() :( Even more ugly :) */
767 char buf1[BUFSIZ], buf2[BUFSIZ];
768 int n1, n2;
769 rotate_dash ();
772 while((n1 = read(file1,buf1,BUFSIZ)) == -1 && errno == EINTR);
773 while((n2 = read(file2,buf2,BUFSIZ)) == -1 && errno == EINTR);
774 } while (n1 == n2 && n1 == BUFSIZ && !memcmp(buf1,buf2,BUFSIZ));
775 result = (n1 != n2) || memcmp(buf1,buf2,n1);
776 #endif /* !HAVE_MMAP */
777 close (file2);
779 close (file1);
781 return result;
784 enum CompareMode {
785 compare_quick, compare_size_only, compare_thourough
788 static void
789 compare_dir (WPanel *panel, WPanel *other, enum CompareMode mode)
791 int i, j;
792 char *src_name, *dst_name;
794 /* No marks by default */
795 panel->marked = 0;
796 panel->total = 0;
797 panel->dirs_marked = 0;
799 /* Handle all files in the panel */
800 for (i = 0; i < panel->count; i++){
801 file_entry *source = &panel->dir.list[i];
803 /* Default: unmarked */
804 file_mark (panel, i, 0);
806 /* Skip directories */
807 if (S_ISDIR (source->st.st_mode))
808 continue;
810 /* Search the corresponding entry from the other panel */
811 for (j = 0; j < other->count; j++){
812 if (strcmp (source->fname,
813 other->dir.list[j].fname) == 0)
814 break;
816 if (j >= other->count)
817 /* Not found -> mark */
818 do_file_mark (panel, i, 1);
819 else {
820 /* Found */
821 file_entry *target = &other->dir.list[j];
823 if (mode != compare_size_only){
824 /* Older version is not marked */
825 if (source->st.st_mtime < target->st.st_mtime)
826 continue;
829 /* Newer version with different size is marked */
830 if (source->st.st_size != target->st.st_size){
831 do_file_mark (panel, i, 1);
832 continue;
835 if (mode == compare_size_only)
836 continue;
838 if (mode == compare_quick){
839 /* Thorough compare off, compare only time stamps */
840 /* Mark newer version, don't mark version with the same date */
841 if (source->st.st_mtime > target->st.st_mtime){
842 do_file_mark (panel, i, 1);
844 continue;
847 /* Thorough compare on, do byte-by-byte comparison */
848 src_name = concat_dir_and_file (panel->cwd, source->fname);
849 dst_name = concat_dir_and_file (other->cwd, target->fname);
850 if (compare_files (src_name, dst_name, source->st.st_size))
851 do_file_mark (panel, i, 1);
852 g_free (src_name);
853 g_free (dst_name);
855 } /* for (i ...) */
858 void
859 compare_dirs_cmd (void)
861 int choice;
862 enum CompareMode thorough_flag;
864 choice =
865 query_dialog (_(" Compare directories "),
866 _(" Select compare method: "), D_NORMAL, 4,
867 _("&Quick"), _("&Size only"), _("&Thorough"), _("&Cancel"));
869 if (choice < 0 || choice > 2)
870 return;
871 else
872 thorough_flag = choice;
874 if (get_current_type () == view_listing
875 && get_other_type () == view_listing) {
876 compare_dir (current_panel, other_panel, thorough_flag);
877 compare_dir (other_panel, current_panel, thorough_flag);
878 } else {
879 message (D_ERROR, MSG_ERROR,
880 _(" Both panels should be in the "
881 "listing mode to use this command "));
885 void
886 history_cmd (void)
888 /* show the history of command line widget */
889 send_message (&cmdline->widget, WIDGET_COMMAND, CK_InputHistoryShow);
892 void swap_cmd (void)
894 swap_panels ();
895 tty_touch_screen ();
896 repaint_screen ();
899 void
900 view_other_cmd (void)
902 static int message_flag = TRUE;
904 if (!xterm_flag && !console_flag && !use_subshell && !output_starts_shell) {
905 if (message_flag)
906 message (D_ERROR, MSG_ERROR,
907 _(" Not an xterm or Linux console; \n"
908 " the panels cannot be toggled. "));
909 message_flag = FALSE;
910 } else {
911 toggle_panels ();
915 static void
916 do_link (int symbolic_link, const char *fname)
918 char *dest = NULL, *src = NULL;
920 if (!symbolic_link) {
921 src = g_strdup_printf (_("Link %s to:"), str_trunc (fname, 46));
922 dest = input_expand_dialog (_(" Link "), src, MC_HISTORY_FM_LINK, "");
923 if (!dest || !*dest)
924 goto cleanup;
925 save_cwds_stat ();
926 if (-1 == mc_link (fname, dest))
927 message (D_ERROR, MSG_ERROR, _(" link: %s "),
928 unix_error_string (errno));
929 } else {
930 char *s;
931 char *d;
933 /* suggest the full path for symlink */
934 s = concat_dir_and_file (current_panel->cwd, fname);
936 if (get_other_type () == view_listing) {
937 d = concat_dir_and_file (other_panel->cwd, fname);
938 } else {
939 d = g_strdup (fname);
942 symlink_dialog (s, d, &dest, &src);
943 g_free (d);
944 g_free (s);
946 if (!dest || !*dest || !src || !*src)
947 goto cleanup;
948 save_cwds_stat ();
949 if (-1 == mc_symlink (dest, src))
950 message (D_ERROR, MSG_ERROR, _(" symlink: %s "),
951 unix_error_string (errno));
953 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
954 repaint_screen ();
956 cleanup:
957 g_free (src);
958 g_free (dest);
961 void link_cmd (void)
963 do_link (0, selection (current_panel)->fname);
966 void symlink_cmd (void)
968 char *filename = NULL;
969 filename = selection (current_panel)->fname;
971 if (filename) {
972 do_link (1, filename);
976 void edit_symlink_cmd (void)
978 if (S_ISLNK (selection (current_panel)->st.st_mode)) {
979 char buffer [MC_MAXPATHLEN];
980 char *p = NULL;
981 int i;
982 char *dest, *q;
984 p = selection (current_panel)->fname;
986 q = g_strdup_printf (_(" Symlink `%s\' points to: "), str_trunc (p, 32));
988 i = readlink (p, buffer, MC_MAXPATHLEN - 1);
989 if (i > 0) {
990 buffer [i] = 0;
991 dest = input_expand_dialog (_(" Edit symlink "), q, MC_HISTORY_FM_EDIT_LINK, buffer);
992 if (dest) {
993 if (*dest && strcmp (buffer, dest)) {
994 save_cwds_stat ();
995 if (-1 == mc_unlink (p)){
996 message (D_ERROR, MSG_ERROR, _(" edit symlink, unable to remove %s: %s "),
997 p, unix_error_string (errno));
998 } else {
999 if (-1 == mc_symlink (dest, p))
1000 message (D_ERROR, MSG_ERROR, _(" edit symlink: %s "),
1001 unix_error_string (errno));
1003 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
1004 repaint_screen ();
1006 g_free (dest);
1009 g_free (q);
1010 } else {
1011 message (D_ERROR, MSG_ERROR, _("`%s' is not a symbolic link"),
1012 selection (current_panel)->fname);
1016 void help_cmd (void)
1018 interactive_display (NULL, "[main]");
1021 void
1022 user_file_menu_cmd (void)
1024 user_menu_cmd (NULL);
1027 /* partly taken from dcigettext.c, returns "" for default locale */
1028 /* value should be freed by calling function g_free() */
1029 char *guess_message_value (void)
1031 static const char * const var[] = {
1032 /* The highest priority value is the `LANGUAGE' environment
1033 variable. This is a GNU extension. */
1034 "LANGUAGE",
1035 /* Setting of LC_ALL overwrites all other. */
1036 "LC_ALL",
1037 /* Next comes the name of the desired category. */
1038 "LC_MESSAGES",
1039 /* Last possibility is the LANG environment variable. */
1040 "LANG",
1041 /* NULL exit loops */
1042 NULL
1045 unsigned i = 0;
1046 const char *locale = NULL;
1048 while (var[i] != NULL) {
1049 locale = getenv (var[i]);
1050 if (locale != NULL && locale[0] != '\0')
1051 break;
1052 i++;
1055 if (locale == NULL)
1056 locale = "";
1058 return g_strdup (locale);
1062 * Return a random hint. If force is not 0, ignore the timeout.
1064 char *
1065 get_random_hint (int force)
1067 char *data, *result = NULL, *eol;
1068 int len;
1069 int start;
1070 static int last_sec;
1071 static struct timeval tv;
1072 GIConv conv;
1073 GString *buffer;
1075 /* Do not change hints more often than one minute */
1076 gettimeofday (&tv, NULL);
1077 if (!force && !(tv.tv_sec > last_sec + 60))
1078 return g_strdup ("");
1079 last_sec = tv.tv_sec;
1081 data = load_mc_home_file (MC_HINT, NULL);
1082 if (!data)
1083 return 0;
1085 /* get a random entry */
1086 srand (tv.tv_sec);
1087 len = strlen (data);
1088 start = rand () % len;
1090 for (; start; start--) {
1091 if (data[start] == '\n') {
1092 start++;
1093 break;
1096 eol = strchr (&data[start], '\n');
1097 if (eol)
1098 *eol = 0;
1100 /* hint files are stored in utf-8 */
1101 /* try convert hint file from utf-8 to terminal encoding */
1102 conv = str_crt_conv_from ("UTF-8");
1103 if (conv != INVALID_CONV) {
1104 buffer = g_string_new ("");
1105 if (str_convert (conv, &data[start], buffer) != ESTR_FAILURE) {
1106 result = g_strdup (buffer->str);
1108 g_string_free (buffer, TRUE);
1109 str_close_conv (conv);
1112 g_free (data);
1113 return result;
1116 #if defined(USE_NETCODE) || defined(USE_EXT2FSLIB)
1117 static void
1118 nice_cd (const char *text, const char *xtext, const char *help,
1119 const char *history_name, const char *prefix, int to_home)
1121 char *machine;
1122 char *cd_path;
1124 if (!SELECTED_IS_PANEL)
1125 return;
1127 machine = input_dialog_help (text, xtext, help, history_name, "");
1128 if (!machine)
1129 return;
1131 to_home = 0; /* FIXME: how to solve going to home nicely? /~/ is
1132 ugly as hell and leads to problems in vfs layer */
1134 if (strncmp (prefix, machine, strlen (prefix)) == 0)
1135 cd_path = g_strconcat (machine, to_home ? "/~/" : (char *) NULL, (char *) NULL);
1136 else
1137 cd_path = g_strconcat (prefix, machine, to_home ? "/~/" : (char *) NULL, (char *) NULL);
1139 if (do_panel_cd (MENU_PANEL, cd_path, 0))
1140 directory_history_add (MENU_PANEL, (MENU_PANEL)->cwd);
1141 else
1142 message (D_ERROR, MSG_ERROR, _(" Cannot chdir to %s "), cd_path);
1143 g_free (cd_path);
1144 g_free (machine);
1146 #endif /* USE_NETCODE || USE_EXT2FSLIB */
1149 #ifdef USE_NETCODE
1151 static const char *machine_str = N_(" Enter machine name (F1 for details): ");
1153 #ifdef ENABLE_VFS_MCFS
1154 void netlink_cmd (void)
1156 nice_cd (_(" Link to a remote machine "), _(machine_str),
1157 "[Network File System]", ":netlink_cmd: Link to a remote ",
1158 "/#mc:", 1);
1160 #endif /* ENABLE_VFS_MCFS */
1162 void ftplink_cmd (void)
1164 nice_cd (_(" FTP to machine "), _(machine_str),
1165 "[FTP File System]", ":ftplink_cmd: FTP to machine ", "/#ftp:", 1);
1168 void fishlink_cmd (void)
1170 nice_cd (_(" Shell link to machine "), _(machine_str),
1171 "[FIle transfer over SHell filesystem]", ":fishlink_cmd: Shell link to machine ",
1172 "/#sh:", 1);
1175 #ifdef WITH_SMBFS
1176 void smblink_cmd (void)
1178 nice_cd (_(" SMB link to machine "), _(machine_str),
1179 "[SMB File System]", ":smblink_cmd: SMB link to machine ",
1180 "/#smb:", 0);
1182 #endif /* WITH_SMBFS */
1183 #endif /* USE_NETCODE */
1185 #ifdef USE_EXT2FSLIB
1186 void undelete_cmd (void)
1188 nice_cd (_(" Undelete files on an ext2 file system "),
1189 _(" Enter device (without /dev/) to undelete\n "
1190 " files on: (F1 for details)"),
1191 "[Undelete File System]", ":undelete_cmd: Undel on ext2 fs ",
1192 "/#undel:", 0);
1194 #endif /* USE_EXT2FSLIB */
1196 void quick_cd_cmd (void)
1198 char *p = cd_dialog ();
1200 if (p && *p) {
1201 char *q = g_strconcat ("cd ", p, (char *) NULL);
1203 do_cd_command (q);
1204 g_free (q);
1206 g_free (p);
1211 \brief calculate dirs sizes
1213 calculate dirs sizes and resort panel:
1214 dirs_selected = show size for selected dirs,
1215 otherwise = show size for dir under cursor:
1216 dir under cursor ".." = show size for all dirs,
1217 otherwise = show size for dir under cursor
1219 void
1220 smart_dirsize_cmd (void)
1222 WPanel *panel = current_panel;
1223 file_entry *entry;
1225 entry = &(panel->dir.list[panel->selected]);
1226 if ( ( S_ISDIR (entry->st.st_mode) && ( strcmp(entry->fname, "..") == 0 ) )
1227 || panel->dirs_marked )
1228 dirsizes_cmd ();
1229 else
1230 single_dirsize_cmd ();
1233 void
1234 single_dirsize_cmd (void)
1236 WPanel *panel = current_panel;
1237 file_entry *entry;
1238 off_t marked;
1239 double total;
1240 ComputeDirSizeUI *ui;
1242 entry = &(panel->dir.list[panel->selected]);
1243 if (S_ISDIR (entry->st.st_mode) && strcmp(entry->fname, "..") != 0) {
1244 ui = compute_dir_size_create_ui ();
1246 total = 0.0;
1248 if (compute_dir_size (entry->fname, ui, compute_dir_size_update_ui,
1249 &marked, &total) == FILE_CONT) {
1250 entry->st.st_size = (off_t) total;
1251 entry->f.dir_size_computed = 1;
1254 compute_dir_size_destroy_ui (ui);
1257 if (mark_moves_down)
1258 send_message (&(panel->widget), WIDGET_KEY, KEY_DOWN);
1260 recalculate_panel_summary (panel);
1262 if ( current_panel->current_sort_field->sort_routine == (sortfn *) sort_size )
1263 panel_re_sort (panel);
1265 panel->dirty = 1;
1268 void
1269 dirsizes_cmd (void)
1271 WPanel *panel = current_panel;
1272 int i;
1273 off_t marked;
1274 double total;
1275 ComputeDirSizeUI *ui;
1277 ui = compute_dir_size_create_ui ();
1279 for (i = 0; i < panel->count; i++)
1280 if (S_ISDIR (panel->dir.list [i].st.st_mode)
1281 && ((panel->dirs_marked && panel->dir.list [i].f.marked)
1282 || !panel->dirs_marked)
1283 && strcmp (panel->dir.list [i].fname, "..") != 0) {
1284 total = 0.0l;
1286 if (compute_dir_size (panel->dir.list [i].fname,
1287 ui, compute_dir_size_update_ui,
1288 &marked, &total) != FILE_CONT)
1289 break;
1291 panel->dir.list [i].st.st_size = (off_t) total;
1292 panel->dir.list [i].f.dir_size_computed = 1;
1295 compute_dir_size_destroy_ui (ui);
1297 recalculate_panel_summary (panel);
1299 if ( current_panel->current_sort_field->sort_routine == (sortfn *) sort_size )
1300 panel_re_sort (panel);
1302 panel->dirty = 1;
1305 void
1306 save_setup_cmd (void)
1308 save_setup ();
1309 message (D_NORMAL, _(" Setup "), _(" Setup saved to ~/%s"),
1310 MC_USERCONF_DIR PATH_SEP_STR MC_CONFIG_FILE);
1313 static void
1314 configure_panel_listing (WPanel *p, int view_type, int use_msformat, char *user, char *status)
1316 p->user_mini_status = use_msformat;
1317 p->list_type = view_type;
1319 if (view_type == list_user || use_msformat){
1320 g_free (p->user_format);
1321 p->user_format = user;
1323 g_free (p->user_status_format [view_type]);
1324 p->user_status_format [view_type] = status;
1326 set_panel_formats (p);
1328 else {
1329 g_free (user);
1330 g_free (status);
1333 set_panel_formats (p);
1334 do_refresh ();
1337 void
1338 info_cmd_no_menu (void)
1340 if (get_display_type (0) == view_info)
1341 set_display_type (0, view_listing);
1342 else if (get_display_type (1) == view_info)
1343 set_display_type (1, view_listing);
1344 else
1345 set_display_type (current_panel == left_panel ? 1 : 0, view_info);
1348 void
1349 quick_cmd_no_menu (void)
1351 if (get_display_type (0) == view_quick)
1352 set_display_type (0, view_listing);
1353 else if (get_display_type (1) == view_quick)
1354 set_display_type (1, view_listing);
1355 else
1356 set_display_type (current_panel == left_panel ? 1 : 0, view_quick);
1359 static void
1360 switch_to_listing (int panel_index)
1362 if (get_display_type (panel_index) != view_listing)
1363 set_display_type (panel_index, view_listing);
1366 void
1367 listing_cmd (void)
1369 int view_type, use_msformat;
1370 char *user, *status;
1371 WPanel *p;
1372 int display_type;
1374 display_type = get_display_type (MENU_PANEL_IDX);
1375 if (display_type == view_listing)
1376 p = MENU_PANEL_IDX == 0 ? left_panel : right_panel;
1377 else
1378 p = 0;
1380 view_type = display_box (p, &user, &status, &use_msformat, MENU_PANEL_IDX);
1382 if (view_type == -1)
1383 return;
1385 switch_to_listing (MENU_PANEL_IDX);
1387 p = MENU_PANEL_IDX == 0 ? left_panel : right_panel;
1389 configure_panel_listing (p, view_type, use_msformat, user, status);
1392 void
1393 tree_cmd (void)
1395 set_display_type (MENU_PANEL_IDX, view_tree);
1398 void
1399 info_cmd (void)
1401 set_display_type (MENU_PANEL_IDX, view_info);
1404 void
1405 quick_view_cmd (void)
1407 if ((WPanel *) get_panel_widget (MENU_PANEL_IDX) == current_panel)
1408 change_panel ();
1409 set_display_type (MENU_PANEL_IDX, view_quick);
1412 /* Handle the tree internal listing modes switching */
1413 static int
1414 set_basic_panel_listing_to (int panel_index, int listing_mode)
1416 WPanel *p = (WPanel *) get_panel_widget (panel_index);
1418 switch_to_listing (panel_index);
1419 p->list_type = listing_mode;
1420 if (set_panel_formats (p))
1421 return 0;
1423 do_refresh ();
1424 return 1;
1427 void
1428 toggle_listing_cmd (void)
1430 int current = get_current_index ();
1431 WPanel *p = (WPanel *) get_panel_widget (current);
1433 set_basic_panel_listing_to (current, (p->list_type + 1) % LIST_TYPES);
1436 void
1437 encoding_cmd (void)
1439 if (SELECTED_IS_PANEL)
1440 set_panel_encoding (MENU_PANEL);