Add patch (rev2) from ticket #65
[midnight-commander.git] / src / cmd.c
blob01d110ffb5d26bae2bdf0a189c7da3bfae7e4ef2
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 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 #include <config.h>
23 #include <errno.h>
24 #include <stdio.h>
25 #include <string.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #ifdef HAVE_MMAP
30 # include <sys/mman.h>
31 #endif
32 #ifdef USE_NETCODE
33 #include <netdb.h>
34 #endif
35 #include <unistd.h>
37 #include "global.h"
38 #include "cmd.h" /* Our definitions */
39 #include "fileopctx.h" /* file_op_context_new() */
40 #include "file.h" /* copy_file_file() */
41 #include "find.h" /* do_find() */
42 #include "hotlist.h" /* hotlist_cmd() */
43 #include "tree.h" /* tree_chdir() */
44 #include "subshell.h" /* use_subshell */
45 #include "cons.saver.h" /* console_flag */
46 #include "tty.h" /* LINES */
47 #include "dialog.h" /* Widget */
48 #include "view.h" /* mc_internal_viewer() */
49 #include "wtools.h" /* message() */
50 #include "widget.h" /* push_history() */
51 #include "key.h" /* application_keypad_mode() */
52 #include "win.h" /* do_enter_ca_mode() */
53 #include "main.h" /* change_panel() */
54 #include "panel.h" /* current_panel */
55 #include "help.h" /* interactive_display() */
56 #include "user.h" /* MC_GLOBAL_MENU */
57 #include "command.h" /* cmdline */
58 #include "layout.h" /* get_current_type() */
59 #include "ext.h" /* regex_command() */
60 #include "boxes.h" /* cd_dialog() */
61 #include "setup.h" /* save_setup() */
62 #include "profile.h" /* PROFILE_NAME */
63 #include "execute.h" /* toggle_panels() */
65 #ifndef MAP_FILE
66 # define MAP_FILE 0
67 #endif
69 #ifdef USE_INTERNAL_EDIT
70 # include "../edit/edit.h"
71 #endif
73 /* If set and you don't have subshell support,then C-o will give you a shell */
74 int output_starts_shell = 0;
76 /* If set, use the builtin editor */
77 int use_internal_edit = 1;
80 int
81 view_file_at_line (const char *filename, int plain_view, int internal,
82 int start_line)
84 static const char *viewer = NULL;
85 int move_dir = 0;
88 if (plain_view) {
89 int changed_hex_mode = 0;
90 int changed_nroff_flag = 0;
91 int changed_magic_flag = 0;
93 altered_hex_mode = 0;
94 altered_nroff_flag = 0;
95 altered_magic_flag = 0;
96 if (default_hex_mode)
97 changed_hex_mode = 1;
98 if (default_nroff_flag)
99 changed_nroff_flag = 1;
100 if (default_magic_flag)
101 changed_magic_flag = 1;
102 default_hex_mode = 0;
103 default_nroff_flag = 0;
104 default_magic_flag = 0;
105 mc_internal_viewer (NULL, filename, &move_dir, start_line);
106 if (changed_hex_mode && !altered_hex_mode)
107 default_hex_mode = 1;
108 if (changed_nroff_flag && !altered_nroff_flag)
109 default_nroff_flag = 1;
110 if (changed_magic_flag && !altered_magic_flag)
111 default_magic_flag = 1;
112 repaint_screen ();
113 return move_dir;
115 if (internal) {
116 char view_entry[BUF_TINY];
118 if (start_line != 0)
119 g_snprintf (view_entry, sizeof (view_entry), "View:%d",
120 start_line);
121 else
122 strcpy (view_entry, "View");
124 if (regex_command (filename, view_entry, &move_dir) == 0) {
125 mc_internal_viewer (NULL, filename, &move_dir, start_line);
126 repaint_screen ();
128 } else {
129 if (!viewer) {
130 viewer = getenv ("VIEWER");
131 if (!viewer)
132 viewer = getenv ("PAGER");
133 if (!viewer)
134 viewer = "view";
136 execute_with_vfs_arg (viewer, filename);
138 return move_dir;
141 /* view_file (filename, plain_view, internal)
143 * Inputs:
144 * filename: The file name to view
145 * plain_view: If set does not do any fancy pre-processing (no filtering) and
146 * always invokes the internal viewer.
147 * internal: If set uses the internal viewer, otherwise an external viewer.
150 view_file (const char *filename, int plain_view, int internal)
152 return view_file_at_line (filename, plain_view, internal, 0);
155 /* scan_for_file (panel, idx, direction)
157 * Inputs:
158 * panel: pointer to the panel on which we operate
159 * idx: starting file.
160 * direction: 1, or -1
162 static int scan_for_file (WPanel *panel, int idx, int direction)
164 int i = idx + direction;
166 while (i != idx){
167 if (i < 0)
168 i = panel->count - 1;
169 if (i == panel->count)
170 i = 0;
171 if (!S_ISDIR (panel->dir.list [i].st.st_mode))
172 return i;
173 i += direction;
175 return i;
179 * Run viewer (internal or external) on the currently selected file.
180 * If normal is 1, force internal viewer and raw mode (used for F13).
182 static void
183 do_view_cmd (int normal)
185 int dir, file_idx;
187 /* Directories are viewed by changing to them */
188 if (S_ISDIR (selection (current_panel)->st.st_mode)
189 || link_isdir (selection (current_panel))) {
190 if (confirm_view_dir && (current_panel->marked || current_panel->dirs_marked)) {
191 if (query_dialog
192 (_(" Confirmation "), _("Files tagged, want to cd?"), 0, 2,
193 _("&Yes"), _("&No")) != 0) {
194 return;
197 if (!do_cd (selection (current_panel)->fname, cd_exact))
198 message (1, MSG_ERROR, _("Cannot change directory"));
200 return;
204 file_idx = current_panel->selected;
205 while (1) {
206 char *filename;
208 filename = current_panel->dir.list[file_idx].fname;
210 dir = view_file (filename, normal, use_internal_view);
211 if (dir == 0)
212 break;
213 file_idx = scan_for_file (current_panel, file_idx, dir);
217 /* Run user's preferred viewer on the currently selected file */
218 void
219 view_cmd (void)
221 do_view_cmd (0);
224 /* Ask for file and run user's preferred viewer on it */
225 void
226 view_file_cmd (void)
228 char *filename;
230 filename =
231 input_expand_dialog (_(" View file "), _(" Filename:"),
232 selection (current_panel)->fname);
233 if (!filename)
234 return;
236 view_file (filename, 0, use_internal_view);
237 g_free (filename);
240 /* Run plain internal viewer on the currently selected file */
241 void
242 view_simple_cmd (void)
244 do_view_cmd (1);
247 void
248 filtered_view_cmd (void)
250 char *command;
252 command =
253 input_dialog (_(" Filtered view "),
254 _(" Filter command and arguments:"),
255 selection (current_panel)->fname);
256 if (!command)
257 return;
259 mc_internal_viewer (command, "", NULL, 0);
261 g_free (command);
264 void do_edit_at_line (const char *what, int start_line)
266 static const char *editor = NULL;
268 #ifdef USE_INTERNAL_EDIT
269 if (use_internal_edit){
270 edit_file (what, start_line);
271 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
272 repaint_screen ();
273 return;
275 #endif /* USE_INTERNAL_EDIT */
276 if (!editor){
277 editor = getenv ("EDITOR");
278 if (!editor)
279 editor = get_default_editor ();
281 execute_with_vfs_arg (editor, what);
282 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
283 repaint_screen ();
286 static void
287 do_edit (const char *what)
289 do_edit_at_line (what, 0);
292 void
293 edit_cmd (void)
295 if (regex_command (selection (current_panel)->fname, "Edit", 0) == 0)
296 do_edit (selection (current_panel)->fname);
299 void
300 edit_cmd_new (void)
302 do_edit (NULL);
305 /* Invoked by F5. Copy, default to the other panel. */
306 void
307 copy_cmd (void)
309 save_cwds_stat ();
310 if (panel_operate (current_panel, OP_COPY, 0)) {
311 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
312 repaint_screen ();
316 /* Invoked by F6. Move/rename, default to the other panel, ignore marks. */
317 void ren_cmd (void)
319 save_cwds_stat ();
320 if (panel_operate (current_panel, OP_MOVE, 0)){
321 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
322 repaint_screen ();
326 /* Invoked by F15. Copy, default to the same panel, ignore marks. */
327 void copy_cmd_local (void)
329 save_cwds_stat ();
330 if (panel_operate (current_panel, OP_COPY, 1)){
331 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
332 repaint_screen ();
336 /* Invoked by F16. Move/rename, default to the same panel. */
337 void ren_cmd_local (void)
339 save_cwds_stat ();
340 if (panel_operate (current_panel, OP_MOVE, 1)){
341 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
342 repaint_screen ();
346 void
347 mkdir_cmd (void)
349 char *dir, *absdir;
351 dir =
352 input_expand_dialog (_("Create a new Directory"),
353 _(" Enter directory name:"), "");
354 if (!dir)
355 return;
357 if (dir[0] == '/' || dir[0] == '~')
358 absdir = g_strdup (dir);
359 else
360 absdir = concat_dir_and_file (current_panel->cwd, dir);
362 save_cwds_stat ();
363 if (my_mkdir (absdir, 0777) == 0) {
364 update_panels (UP_OPTIMIZE, dir);
365 repaint_screen ();
366 select_item (current_panel);
367 } else {
368 message (1, MSG_ERROR, " %s ", unix_error_string (errno));
371 g_free (absdir);
372 g_free (dir);
375 void delete_cmd (void)
377 save_cwds_stat ();
379 if (panel_operate (current_panel, OP_DELETE, 0)){
380 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
381 repaint_screen ();
385 /* Invoked by F18. Remove selected file, regardless of marked files. */
386 void delete_cmd_local (void)
388 save_cwds_stat ();
390 if (panel_operate (current_panel, OP_DELETE, 1)){
391 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
392 repaint_screen ();
396 void find_cmd (void)
398 do_find ();
401 static void
402 set_panel_filter_to (WPanel *p, char *allocated_filter_string)
404 g_free (p->filter);
405 p->filter = 0;
407 if (!(allocated_filter_string [0] == '*' && allocated_filter_string [1] == 0))
408 p->filter = allocated_filter_string;
409 else
410 g_free (allocated_filter_string);
411 reread_cmd ();
414 /* Set a given panel filter expression */
415 static void
416 set_panel_filter (WPanel *p)
418 char *reg_exp;
419 const char *x;
421 x = p->filter ? p->filter : easy_patterns ? "*" : ".";
423 reg_exp = input_dialog_help (_(" Filter "),
424 _(" Set expression for filtering filenames"),
425 "[Filter...]", x);
426 if (!reg_exp)
427 return;
428 set_panel_filter_to (p, reg_exp);
431 /* Invoked from the left/right menus */
432 void filter_cmd (void)
434 WPanel *p;
436 if (!SELECTED_IS_PANEL)
437 return;
439 p = MENU_PANEL;
440 set_panel_filter (p);
443 void reread_cmd (void)
445 int flag;
447 if (get_current_type () == view_listing &&
448 get_other_type () == view_listing)
449 flag = strcmp (current_panel->cwd, other_panel->cwd) ? UP_ONLY_CURRENT : 0;
450 else
451 flag = UP_ONLY_CURRENT;
453 update_panels (UP_RELOAD|flag, UP_KEEPSEL);
454 repaint_screen ();
457 void reverse_selection_cmd (void)
459 file_entry *file;
460 int i;
462 for (i = 0; i < current_panel->count; i++){
463 file = &current_panel->dir.list [i];
464 if (S_ISDIR (file->st.st_mode))
465 continue;
466 do_file_mark (current_panel, i, !file->f.marked);
470 static void
471 select_unselect_cmd (const char *title, int cmd)
473 char *reg_exp, *reg_exp_t;
474 int i;
475 int c;
476 int dirflag = 0;
478 reg_exp = input_dialog (title, "", easy_patterns ? "*" : ".");
479 if (!reg_exp)
480 return;
481 if (!*reg_exp) {
482 g_free (reg_exp);
483 return;
486 reg_exp_t = reg_exp;
488 /* Check if they specified a directory */
489 if (*reg_exp_t == PATH_SEP) {
490 dirflag = 1;
491 reg_exp_t++;
493 if (reg_exp_t[strlen (reg_exp_t) - 1] == PATH_SEP) {
494 dirflag = 1;
495 reg_exp_t[strlen (reg_exp_t) - 1] = 0;
498 for (i = 0; i < current_panel->count; i++) {
499 if (!strcmp (current_panel->dir.list[i].fname, ".."))
500 continue;
501 if (S_ISDIR (current_panel->dir.list[i].st.st_mode)) {
502 if (!dirflag)
503 continue;
504 } else {
505 if (dirflag)
506 continue;
508 c = regexp_match (reg_exp_t, current_panel->dir.list[i].fname,
509 match_file);
510 if (c == -1) {
511 message (1, MSG_ERROR, _(" Malformed regular expression "));
512 g_free (reg_exp);
513 return;
515 if (c) {
516 do_file_mark (current_panel, i, cmd);
519 g_free (reg_exp);
522 void select_cmd (void)
524 select_unselect_cmd (_(" Select "), 1);
527 void unselect_cmd (void)
529 select_unselect_cmd (_(" Unselect "), 0);
532 /* Check if the file exists */
533 /* If not copy the default */
534 static int
535 check_for_default(char *default_file, char *file)
537 struct stat s;
538 off_t count = 0;
539 double bytes = 0;
540 FileOpContext *ctx;
542 if (mc_stat (file, &s)){
543 if (mc_stat (default_file, &s)){
544 return -1;
546 ctx = file_op_context_new (OP_COPY);
547 file_op_context_create_ui (ctx, 0);
548 copy_file_file (ctx, default_file, file, 1, &count, &bytes, 1);
549 file_op_context_destroy (ctx);
551 return 0;
554 void ext_cmd (void)
556 char *buffer;
557 char *extdir;
558 int dir;
560 dir = 0;
561 if (geteuid () == 0){
562 dir = query_dialog (_("Extension file edit"),
563 _(" Which extension file you want to edit? "), 0, 2,
564 _("&User"), _("&System Wide"));
566 extdir = concat_dir_and_file (mc_home, MC_LIB_EXT);
568 if (dir == 0){
569 buffer = concat_dir_and_file (home_dir, MC_USER_EXT);
570 check_for_default (extdir, buffer);
571 do_edit (buffer);
572 g_free (buffer);
573 } else if (dir == 1)
574 do_edit (extdir);
576 g_free (extdir);
577 flush_extension_file ();
580 /* where = 0 - do edit file menu for mc */
581 /* where = 1 - do edit file menu for mcedit */
582 static void
583 menu_edit_cmd (int where)
585 char *buffer;
586 char *menufile;
587 int dir = 0;
589 dir = query_dialog (
590 _(" Menu edit "),
591 _(" Which menu file do you want to edit? "),
592 0, geteuid() ? 2 : 3,
593 _("&Local"), _("&User"), _("&System Wide")
596 menufile = concat_dir_and_file (mc_home, where ? CEDIT_GLOBAL_MENU : MC_GLOBAL_MENU);
598 switch (dir) {
599 case 0:
600 buffer = g_strdup (where ? CEDIT_LOCAL_MENU : MC_LOCAL_MENU);
601 check_for_default (menufile, buffer);
602 break;
604 case 1:
605 buffer = concat_dir_and_file (home_dir, where ? CEDIT_HOME_MENU : MC_HOME_MENU);
606 check_for_default (menufile, buffer);
607 break;
609 case 2:
610 buffer = concat_dir_and_file (mc_home, where ? CEDIT_GLOBAL_MENU : MC_GLOBAL_MENU);
611 break;
613 default:
614 g_free (menufile);
615 return;
617 do_edit (buffer);
618 if (dir == 0)
619 chmod(buffer, 0600);
620 g_free (buffer);
621 g_free (menufile);
624 void quick_chdir_cmd (void)
626 char *target;
628 target = hotlist_cmd (LIST_HOTLIST);
629 if (!target)
630 return;
632 if (get_current_type () == view_tree)
633 tree_chdir (the_tree, target);
634 else
635 if (!do_cd (target, cd_exact))
636 message (1, MSG_ERROR, _("Cannot change directory") );
637 g_free (target);
640 /* edit file menu for mc */
641 void
642 edit_mc_menu_cmd (void)
644 menu_edit_cmd (0);
647 #ifdef USE_INTERNAL_EDIT
648 /* edit file menu for mcedit */
649 void
650 edit_user_menu_cmd (void)
652 menu_edit_cmd (1);
655 /* edit syntax file for mcedit */
656 void
657 edit_syntax_cmd (void)
659 char *buffer;
660 char *extdir;
661 int dir = 0;
663 if (geteuid () == 0) {
664 dir =
665 query_dialog (_("Syntax file edit"),
666 _(" Which syntax file you want to edit? "), 0, 2,
667 _("&User"), _("&System Wide"));
669 extdir = concat_dir_and_file (mc_home, "syntax" PATH_SEP_STR "Syntax");
671 if (dir == 0) {
672 buffer = concat_dir_and_file (home_dir, SYNTAX_FILE);
673 check_for_default (extdir, buffer);
674 do_edit (buffer);
675 g_free (buffer);
676 } else if (dir == 1)
677 do_edit (extdir);
679 g_free (extdir);
681 #endif
683 #ifdef USE_VFS
684 void reselect_vfs (void)
686 char *target;
688 target = hotlist_cmd (LIST_VFSLIST);
689 if (!target)
690 return;
692 if (!do_cd (target, cd_exact))
693 message (1, MSG_ERROR, _("Cannot change directory") );
694 g_free (target);
696 #endif /* USE_VFS */
698 static int compare_files (char *name1, char *name2, off_t size)
700 int file1, file2;
701 int result = -1; /* Different by default */
703 file1 = open (name1, O_RDONLY);
704 if (file1 >= 0){
705 file2 = open (name2, O_RDONLY);
706 if (file2 >= 0){
707 #ifdef HAVE_MMAP
708 char *data1, *data2;
709 /* Ugly if jungle */
710 data1 = mmap (0, size, PROT_READ, MAP_FILE | MAP_PRIVATE, file1, 0);
711 if (data1 != (char*) -1){
712 data2 = mmap (0, size, PROT_READ, MAP_FILE | MAP_PRIVATE, file2, 0);
713 if (data2 != (char*) -1){
714 rotate_dash ();
715 result = memcmp (data1, data2, size);
716 munmap (data2, size);
718 munmap (data1, size);
720 #else
721 /* Don't have mmap() :( Even more ugly :) */
722 char buf1[BUFSIZ], buf2[BUFSIZ];
723 int n1, n2;
724 rotate_dash ();
727 while((n1 = read(file1,buf1,BUFSIZ)) == -1 && errno == EINTR);
728 while((n2 = read(file2,buf2,BUFSIZ)) == -1 && errno == EINTR);
729 } while (n1 == n2 && n1 == BUFSIZ && !memcmp(buf1,buf2,BUFSIZ));
730 result = (n1 != n2) || memcmp(buf1,buf2,n1);
731 #endif /* !HAVE_MMAP */
732 close (file2);
734 close (file1);
736 return result;
739 enum CompareMode {
740 compare_quick, compare_size_only, compare_thourough
743 static void
744 compare_dir (WPanel *panel, WPanel *other, enum CompareMode mode)
746 int i, j;
747 char *src_name, *dst_name;
749 /* No marks by default */
750 panel->marked = 0;
751 panel->total = 0;
752 panel->dirs_marked = 0;
754 /* Handle all files in the panel */
755 for (i = 0; i < panel->count; i++){
756 file_entry *source = &panel->dir.list[i];
758 /* Default: unmarked */
759 file_mark (panel, i, 0);
761 /* Skip directories */
762 if (S_ISDIR (source->st.st_mode))
763 continue;
765 /* Search the corresponding entry from the other panel */
766 for (j = 0; j < other->count; j++){
767 if (strcmp (source->fname,
768 other->dir.list[j].fname) == 0)
769 break;
771 if (j >= other->count)
772 /* Not found -> mark */
773 do_file_mark (panel, i, 1);
774 else {
775 /* Found */
776 file_entry *target = &other->dir.list[j];
778 if (mode != compare_size_only){
779 /* Older version is not marked */
780 if (source->st.st_mtime < target->st.st_mtime)
781 continue;
784 /* Newer version with different size is marked */
785 if (source->st.st_size != target->st.st_size){
786 do_file_mark (panel, i, 1);
787 continue;
790 if (mode == compare_size_only)
791 continue;
793 if (mode == compare_quick){
794 /* Thorough compare off, compare only time stamps */
795 /* Mark newer version, don't mark version with the same date */
796 if (source->st.st_mtime > target->st.st_mtime){
797 do_file_mark (panel, i, 1);
799 continue;
802 /* Thorough compare on, do byte-by-byte comparison */
803 src_name = concat_dir_and_file (panel->cwd, source->fname);
804 dst_name = concat_dir_and_file (other->cwd, target->fname);
805 if (compare_files (src_name, dst_name, source->st.st_size))
806 do_file_mark (panel, i, 1);
807 g_free (src_name);
808 g_free (dst_name);
810 } /* for (i ...) */
813 void
814 compare_dirs_cmd (void)
816 int choice;
817 enum CompareMode thorough_flag;
819 choice =
820 query_dialog (_(" Compare directories "),
821 _(" Select compare method: "), 0, 3, _("&Quick"),
822 _("&Size only"), _("&Thorough"), _("&Cancel"));
824 if (choice < 0 || choice > 2)
825 return;
826 else
827 thorough_flag = choice;
829 if (get_current_type () == view_listing
830 && get_other_type () == view_listing) {
831 compare_dir (current_panel, other_panel, thorough_flag);
832 compare_dir (other_panel, current_panel, thorough_flag);
833 } else {
834 message (1, MSG_ERROR,
835 _(" Both panels should be in the "
836 "listing mode to use this command "));
840 void
841 history_cmd (void)
843 Listbox *listbox;
844 GList *current;
846 if (cmdline->need_push) {
847 if (push_history (cmdline, cmdline->buffer) == 2)
848 cmdline->need_push = 0;
850 if (!cmdline->history) {
851 message (1, MSG_ERROR, _(" The command history is empty "));
852 return;
854 current = g_list_first (cmdline->history);
855 listbox = create_listbox_window (60, 10, _(" Command history "),
856 "[Command Menu]");
857 while (current) {
858 LISTBOX_APPEND_TEXT (listbox, 0, (char *) current->data, current);
859 current = g_list_next(current);
861 run_dlg (listbox->dlg);
862 if (listbox->dlg->ret_value == B_CANCEL)
863 current = NULL;
864 else
865 current = listbox->list->current->data;
866 destroy_dlg (listbox->dlg);
867 g_free (listbox);
869 if (!current)
870 return;
871 cmdline->history = current;
872 assign_text (cmdline, (char *) current->data);
873 update_input (cmdline, 1);
876 void swap_cmd (void)
878 swap_panels ();
879 touchwin (stdscr);
880 repaint_screen ();
883 void
884 view_other_cmd (void)
886 static int message_flag = TRUE;
888 if (!xterm_flag && !console_flag && !use_subshell && !output_starts_shell) {
889 if (message_flag)
890 message (1, MSG_ERROR,
891 _(" Not an xterm or Linux console; \n"
892 " the panels cannot be toggled. "));
893 message_flag = FALSE;
894 } else {
895 toggle_panels ();
899 static void
900 do_link (int symbolic_link, const char *fname)
902 char *dest = NULL, *src = NULL;
904 if (!symbolic_link) {
905 src = g_strdup_printf (_("Link %s to:"), name_trunc (fname, 46));
906 dest = input_expand_dialog (_(" Link "), src, "");
907 if (!dest || !*dest)
908 goto cleanup;
909 save_cwds_stat ();
910 if (-1 == mc_link (fname, dest))
911 message (1, MSG_ERROR, _(" link: %s "),
912 unix_error_string (errno));
913 } else {
914 char *s;
915 char *d;
917 /* suggest the full path for symlink */
918 s = concat_dir_and_file (current_panel->cwd, fname);
920 if (get_other_type () == view_listing) {
921 d = concat_dir_and_file (other_panel->cwd, fname);
922 } else {
923 d = g_strdup (fname);
926 symlink_dialog (s, d, &dest, &src);
927 g_free (d);
928 g_free (s);
930 if (!dest || !*dest || !src || !*src)
931 goto cleanup;
932 save_cwds_stat ();
933 if (-1 == mc_symlink (dest, src))
934 message (1, MSG_ERROR, _(" symlink: %s "),
935 unix_error_string (errno));
937 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
938 repaint_screen ();
940 cleanup:
941 g_free (src);
942 g_free (dest);
945 void link_cmd (void)
947 do_link (0, selection (current_panel)->fname);
950 void symlink_cmd (void)
952 char *filename = NULL;
953 filename = selection (current_panel)->fname;
955 if (filename) {
956 do_link (1, filename);
960 void edit_symlink_cmd (void)
962 if (S_ISLNK (selection (current_panel)->st.st_mode)) {
963 char buffer [MC_MAXPATHLEN];
964 char *p = NULL;
965 int i;
966 char *dest, *q;
968 p = selection (current_panel)->fname;
970 q = g_strdup_printf (_(" Symlink `%s\' points to: "), name_trunc (p, 32));
972 i = readlink (p, buffer, MC_MAXPATHLEN - 1);
973 if (i > 0) {
974 buffer [i] = 0;
975 dest = input_expand_dialog (_(" Edit symlink "), q, buffer);
976 if (dest) {
977 if (*dest && strcmp (buffer, dest)) {
978 save_cwds_stat ();
979 if (-1 == mc_unlink (p)){
980 message (1, MSG_ERROR, _(" edit symlink, unable to remove %s: %s "),
981 p, unix_error_string (errno));
982 } else {
983 if (-1 == mc_symlink (dest, p))
984 message (1, MSG_ERROR, _(" edit symlink: %s "),
985 unix_error_string (errno));
987 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
988 repaint_screen ();
990 g_free (dest);
993 g_free (q);
994 } else {
995 message (1, MSG_ERROR, _("`%s' is not a symbolic link"),
996 selection (current_panel)->fname);
1000 void help_cmd (void)
1002 interactive_display (NULL, "[main]");
1005 void
1006 user_file_menu_cmd (void)
1008 user_menu_cmd (NULL);
1011 /* partly taken from dcigettext.c, returns "" for default locale */
1012 /* value should be freed by calling function g_free() */
1013 char *guess_message_value (void)
1015 static const char * const var[] = {
1016 /* The highest priority value is the `LANGUAGE' environment
1017 variable. This is a GNU extension. */
1018 "LANGUAGE",
1019 /* Setting of LC_ALL overwrites all other. */
1020 "LC_ALL",
1021 /* Next comes the name of the desired category. */
1022 "LC_MESSAGES",
1023 /* Last possibility is the LANG environment variable. */
1024 "LANG",
1025 /* NULL exit loops */
1026 NULL
1029 unsigned i = 0;
1030 const char *locale = NULL;
1032 while (var[i] != NULL) {
1033 locale = getenv (var[i]);
1034 if (locale != NULL && locale[0] != '\0')
1035 break;
1036 i++;
1039 if (locale == NULL)
1040 locale = "";
1042 return g_strdup (locale);
1046 * Return a random hint. If force is not 0, ignore the timeout.
1048 char *
1049 get_random_hint (int force)
1051 char *data, *result, *eol;
1052 int len;
1053 int start;
1054 static int last_sec;
1055 static struct timeval tv;
1057 /* Do not change hints more often than one minute */
1058 gettimeofday (&tv, NULL);
1059 if (!force && !(tv.tv_sec > last_sec + 60))
1060 return g_strdup ("");
1061 last_sec = tv.tv_sec;
1063 data = load_mc_home_file (MC_HINT, NULL);
1064 if (!data)
1065 return 0;
1067 /* get a random entry */
1068 srand (tv.tv_sec);
1069 len = strlen (data);
1070 start = rand () % len;
1072 for (; start; start--) {
1073 if (data[start] == '\n') {
1074 start++;
1075 break;
1078 eol = strchr (&data[start], '\n');
1079 if (eol)
1080 *eol = 0;
1081 result = g_strdup (&data[start]);
1082 g_free (data);
1083 return result;
1086 #if defined(USE_NETCODE) || defined(USE_EXT2FSLIB)
1087 static void
1088 nice_cd (const char *text, const char *xtext, const char *help, const char *prefix, int to_home)
1090 char *machine;
1091 char *cd_path;
1093 if (!SELECTED_IS_PANEL)
1094 return;
1096 machine = input_dialog_help (text,
1097 xtext,
1098 help, "");
1099 if (!machine)
1100 return;
1102 to_home = 0; /* FIXME: how to solve going to home nicely? /~/ is
1103 ugly as hell and leads to problems in vfs layer */
1105 if (strncmp (prefix, machine, strlen (prefix)) == 0)
1106 cd_path = g_strconcat (machine, to_home ? "/~/" : (char *) NULL, (char *) NULL);
1107 else
1108 cd_path = g_strconcat (prefix, machine, to_home ? "/~/" : (char *) NULL, (char *) NULL);
1110 if (do_panel_cd (MENU_PANEL, cd_path, 0))
1111 directory_history_add (MENU_PANEL, (MENU_PANEL)->cwd);
1112 else
1113 message (1, MSG_ERROR, _(" Cannot chdir to %s "), cd_path);
1114 g_free (cd_path);
1115 g_free (machine);
1117 #endif /* USE_NETCODE || USE_EXT2FSLIB */
1120 #ifdef USE_NETCODE
1122 static const char *machine_str = N_(" Enter machine name (F1 for details): ");
1124 #ifdef WITH_MCFS
1125 void netlink_cmd (void)
1127 nice_cd (_(" Link to a remote machine "), _(machine_str),
1128 "[Network File System]", "/#mc:", 1);
1130 #endif /* WITH_MCFS */
1132 void ftplink_cmd (void)
1134 nice_cd (_(" FTP to machine "), _(machine_str),
1135 "[FTP File System]", "/#ftp:", 1);
1138 void fishlink_cmd (void)
1140 nice_cd (_(" Shell link to machine "), _(machine_str),
1141 "[FIle transfer over SHell filesystem]", "/#sh:", 1);
1144 #ifdef WITH_SMBFS
1145 void smblink_cmd (void)
1147 nice_cd (_(" SMB link to machine "), _(machine_str),
1148 "[SMB File System]", "/#smb:", 0);
1150 #endif /* WITH_SMBFS */
1151 #endif /* USE_NETCODE */
1153 #ifdef USE_EXT2FSLIB
1154 void undelete_cmd (void)
1156 nice_cd (_(" Undelete files on an ext2 file system "),
1157 _(" Enter device (without /dev/) to undelete\n "
1158 " files on: (F1 for details)"),
1159 "[Undelete File System]", "/#undel:", 0);
1161 #endif /* USE_EXT2FSLIB */
1163 void quick_cd_cmd (void)
1165 char *p = cd_dialog ();
1167 if (p && *p) {
1168 char *q = g_strconcat ("cd ", p, (char *) NULL);
1170 do_cd_command (q);
1171 g_free (q);
1173 g_free (p);
1176 void
1177 single_dirsize_cmd (void)
1179 WPanel *panel = current_panel;
1180 file_entry *entry;
1181 off_t marked;
1182 double total;
1184 entry = &(panel->dir.list[panel->selected]);
1185 if (S_ISDIR (entry->st.st_mode) && strcmp(entry->fname, "..") != 0) {
1186 total = 0.0;
1187 compute_dir_size (entry->fname, &marked, &total);
1188 entry->st.st_size = (off_t) total;
1189 entry->f.dir_size_computed = 1;
1192 if (mark_moves_down)
1193 send_message (&(panel->widget), WIDGET_KEY, KEY_DOWN);
1195 recalculate_panel_summary (panel);
1196 panel->dirty = 1;
1199 void
1200 dirsizes_cmd (void)
1202 WPanel *panel = current_panel;
1203 int i;
1204 off_t marked;
1205 double total;
1207 for (i = 0; i < panel->count; i++)
1208 if (S_ISDIR (panel->dir.list [i].st.st_mode) &&
1209 ((panel->dirs_marked && panel->dir.list [i].f.marked) ||
1210 !panel->dirs_marked) &&
1211 strcmp (panel->dir.list [i].fname, "..") != 0) {
1212 total = 0.0l;
1213 compute_dir_size (panel->dir.list [i].fname, &marked, &total);
1214 panel->dir.list [i].st.st_size = (off_t) total;
1215 panel->dir.list [i].f.dir_size_computed = 1;
1218 recalculate_panel_summary (panel);
1219 panel_re_sort (panel);
1220 panel->dirty = 1;
1223 void
1224 save_setup_cmd (void)
1226 char *str;
1228 save_setup ();
1229 sync_profiles ();
1231 message (0, _(" Setup "), _(" Setup saved to ~/%s"), PROFILE_NAME);
1234 static void
1235 configure_panel_listing (WPanel *p, int view_type, int use_msformat, char *user, char *status)
1237 p->user_mini_status = use_msformat;
1238 p->list_type = view_type;
1240 if (view_type == list_user || use_msformat){
1241 g_free (p->user_format);
1242 p->user_format = user;
1244 g_free (p->user_status_format [view_type]);
1245 p->user_status_format [view_type] = status;
1247 set_panel_formats (p);
1249 else {
1250 g_free (user);
1251 g_free (status);
1254 set_panel_formats (p);
1255 do_refresh ();
1258 void
1259 info_cmd_no_menu (void)
1261 if (get_display_type (0) == view_info)
1262 set_display_type (0, view_listing);
1263 else if (get_display_type (1) == view_info)
1264 set_display_type (1, view_listing);
1265 else
1266 set_display_type (current_panel == left_panel ? 1 : 0, view_info);
1269 void
1270 quick_cmd_no_menu (void)
1272 if (get_display_type (0) == view_quick)
1273 set_display_type (0, view_listing);
1274 else if (get_display_type (1) == view_quick)
1275 set_display_type (1, view_listing);
1276 else
1277 set_display_type (current_panel == left_panel ? 1 : 0, view_quick);
1280 static void
1281 switch_to_listing (int panel_index)
1283 if (get_display_type (panel_index) != view_listing)
1284 set_display_type (panel_index, view_listing);
1287 void
1288 listing_cmd (void)
1290 int view_type, use_msformat;
1291 char *user, *status;
1292 WPanel *p;
1293 int display_type;
1295 display_type = get_display_type (MENU_PANEL_IDX);
1296 if (display_type == view_listing)
1297 p = MENU_PANEL_IDX == 0 ? left_panel : right_panel;
1298 else
1299 p = 0;
1301 view_type = display_box (p, &user, &status, &use_msformat, MENU_PANEL_IDX);
1303 if (view_type == -1)
1304 return;
1306 switch_to_listing (MENU_PANEL_IDX);
1308 p = MENU_PANEL_IDX == 0 ? left_panel : right_panel;
1310 configure_panel_listing (p, view_type, use_msformat, user, status);
1313 void
1314 tree_cmd (void)
1316 set_display_type (MENU_PANEL_IDX, view_tree);
1319 void
1320 info_cmd (void)
1322 set_display_type (MENU_PANEL_IDX, view_info);
1325 void
1326 quick_view_cmd (void)
1328 if ((WPanel *) get_panel_widget (MENU_PANEL_IDX) == current_panel)
1329 change_panel ();
1330 set_display_type (MENU_PANEL_IDX, view_quick);
1333 /* Handle the tree internal listing modes switching */
1334 static int
1335 set_basic_panel_listing_to (int panel_index, int listing_mode)
1337 WPanel *p = (WPanel *) get_panel_widget (panel_index);
1339 switch_to_listing (panel_index);
1340 p->list_type = listing_mode;
1341 if (set_panel_formats (p))
1342 return 0;
1344 do_refresh ();
1345 return 1;
1348 void
1349 toggle_listing_cmd (void)
1351 int current = get_current_index ();
1352 WPanel *p = (WPanel *) get_panel_widget (current);
1354 set_basic_panel_listing_to (current, (p->list_type + 1) % LIST_TYPES);