remove trailing whitespace at end of lines
[reactos.git] / rosapps / mc / src / cmd.c
1 /* Routines invoked by a function key
2 They normally operate on the current panel.
3
4 Copyright (C) 1994, 1995 Miguel de Icaza
5 Copyright (C) 1994, 1995 Janne Kukonlehto
6
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.
11
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.
16
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., 675 Mass Ave, Cambridge, MA 02139, USA. */
20
21 #include <config.h>
22 #ifdef __os2__
23 # define INCL_DOSFILEMGR
24 # define INCL_DOSMISC
25 # define INCL_DOSERROR
26 #endif
27 #ifdef HAVE_UNISTD_H
28 # include <unistd.h>
29 #endif
30 #include "tty.h"
31 #include <stdio.h>
32 #include <stdlib.h> /* getenv (), rand */
33 #include <sys/types.h>
34 #include <sys/param.h>
35 #include <sys/stat.h>
36 #if defined(_MSC_VER)
37 #include <sys/time.h___>
38 #else
39 #include <time.h>
40 #endif
41 #include <malloc.h>
42 #include <string.h>
43 #include <fcntl.h> /* open, O_RDWR */
44 #include <errno.h>
45
46 #ifdef OS2_NT
47 # include <io.h>
48 #endif
49
50 #ifdef USE_NETCODE
51 #include <netdb.h>
52 #endif
53
54 #ifdef HAVE_MMAP
55 # include <sys/mman.h>
56 #endif
57 #include "mad.h"
58 #include "dir.h"
59 #include "util.h"
60 #include "panel.h"
61 #include "cmd.h" /* Our definitions */
62 #include "view.h" /* view() */
63 #include "dialog.h" /* query_dialog, message */
64 #include "file.h" /* the file operations */
65 #include "find.h" /* do_find */
66 #include "hotlist.h"
67 #include "tree.h"
68 #include "subshell.h" /* use_subshell */
69 #include "cons.saver.h"
70 #include "global.h"
71 #include "dlg.h" /* required by wtools.h */
72 #include "widget.h" /* required by wtools.h */
73 #include "wtools.h" /* listbox */
74 #include "command.h" /* for input_w */
75 #include "win.h" /* do_exit_ca_mode */
76 #include "layout.h" /* get_current/other_type */
77 #include "ext.h" /* regex_command */
78 #include "view.h" /* view */
79 #include "key.h" /* get_key_code */
80 #include "help.h" /* interactive_display */
81 #include "fs.h"
82 #include "boxes.h" /* cd_dialog */
83 #include "color.h"
84 #include "user.h"
85 #include "setup.h"
86 #include "x.h"
87 #include "profile.h"
88
89 #define MIDNIGHT
90 #ifdef USE_INTERNAL_EDIT
91 extern int edit (const char *file, int line);
92 #endif
93 #include "../vfs/vfs.h"
94 #define WANT_WIDGETS
95 #include "main.h" /* global variables, global functions */
96 #ifndef MAP_FILE
97 # define MAP_FILE 0
98 #endif
99
100 #ifdef HAVE_TK
101 # include "tkscreen.h"
102 #endif
103
104 /* If set and you don't have subshell support,then C-o will give you a shell */
105 int output_starts_shell = 0;
106
107 /* Source routing destination */
108 int source_route = 0;
109
110 /* If set, use the builtin editor */
111 int use_internal_edit = 1;
112
113 /* Ugly hack in order to distinguish between left and right panel in menubar */
114 int is_right;
115 #define MENU_PANEL_IDX (is_right ? 1 : 0)
116
117
118 #ifndef PORT_HAS_FILTER_CHANGED
119 # define x_filter_changed(p)
120 #endif
121
122 /* This is used since the parameter panel on some of the commands */
123 /* defined in this file may receive a 0 parameter if they are invoked */
124 /* The drop down menu */
125
126 WPanel *get_a_panel (WPanel *panel)
127 {
128 if (panel)
129 return panel;
130 if (get_current_type () == view_listing){
131 return cpanel;
132 } else
133 return other_panel;
134 }
135
136 /* view_file (filename, normal, internal)
137 *
138 * Inputs:
139 * filename: The file name to view
140 * plain_view: If set does not do any fancy pre-processing (no filtering) and
141 * always invokes the internal viewer.
142 * internal: If set uses the internal viewer, otherwise an external viewer.
143 */
144 int view_file_at_line (char *filename, int plain_view, int internal, int start_line)
145 {
146 static char *viewer = 0;
147 int move_dir = 0;
148
149
150 if (plain_view) {
151 int changed_hex_mode = 0;
152 int changed_nroff_flag = 0;
153 int changed_magic_flag = 0;
154
155 altered_hex_mode = 0;
156 altered_nroff_flag = 0;
157 altered_magic_flag = 0;
158 if (default_hex_mode)
159 changed_hex_mode = 1;
160 if (default_nroff_flag)
161 changed_nroff_flag = 1;
162 if (default_magic_flag)
163 changed_magic_flag = 1;
164 default_hex_mode = 0;
165 default_nroff_flag = 0;
166 default_magic_flag = 0;
167 view (0, filename, &move_dir, start_line);
168 if (changed_hex_mode && !altered_hex_mode)
169 default_hex_mode = 1;
170 if (changed_nroff_flag && !altered_nroff_flag)
171 default_nroff_flag = 1;
172 if (changed_magic_flag && !altered_magic_flag)
173 default_magic_flag = 1;
174 repaint_screen ();
175 return move_dir;
176 }
177 if (internal){
178 char view_entry [32];
179
180 if (start_line != 0)
181 sprintf (view_entry, "View:%d", start_line);
182 else
183 strcpy (view_entry, "View");
184
185 if (!regex_command (filename, view_entry, NULL, &move_dir)){
186 view (0, filename, &move_dir, start_line);
187 repaint_screen ();
188 }
189 } else {
190 char *localcopy;
191
192 if (!viewer){
193 viewer = getenv ("PAGER");
194 if (!viewer)
195 viewer = "view";
196 }
197 /* The file may be a non local file, get a copy */
198 if (!vfs_file_is_local (filename)){
199 localcopy = mc_getlocalcopy (filename);
200 if (localcopy == NULL){
201 message (1, MSG_ERROR, _(" Can not fetch a local copy of %s "), filename);
202 return 0;
203 }
204 execute_internal (viewer, localcopy);
205 mc_ungetlocalcopy (filename, localcopy, 0);
206 } else
207 execute_internal (viewer, filename);
208 }
209 return move_dir;
210 }
211
212 int
213 view_file (char *filename, int plain_view, int internal)
214 {
215 return view_file_at_line (filename, plain_view, internal, 0);
216 }
217
218 /* scan_for_file (panel, idx, direction)
219 *
220 * Inputs:
221 * panel: pointer to the panel on which we operate
222 * idx: starting file.
223 * direction: 1, or -1
224 */
225 static int scan_for_file (WPanel *panel, int idx, int direction)
226 {
227 int i = idx + direction;
228
229 while (i != idx){
230 if (i < 0)
231 i = panel->count - 1;
232 if (i == panel->count)
233 i = 0;
234 if (!S_ISDIR (panel->dir.list [i].buf.st_mode))
235 return i;
236 i += direction;
237 }
238 return i;
239 }
240
241 /* do_view: Invoked as the F3/F13 key. */
242 static void do_view_cmd (WPanel *panel, int normal)
243 {
244 int dir, file_idx;
245 panel = get_a_panel (panel);
246
247 /* Directories are viewed by changing to them */
248 if (S_ISDIR (selection (panel)->buf.st_mode) ||
249 link_isdir (selection (panel))){
250 if (confirm_view_dir && (panel->marked || panel->dirs_marked)){
251 if (query_dialog (_(" CD "), _("Files tagged, want to cd?"),
252 0, 2, _("&Yes"), _("&No")) == 1){
253 return;
254 }
255 }
256 do_cd (selection (panel)->fname, cd_exact);
257 return;
258
259 }
260
261 file_idx = panel->selected;
262 while (1) {
263 char *filename;
264
265 filename = panel->dir.list [file_idx].fname;
266
267 dir = view_file (filename, normal, use_internal_view);
268 if (dir == 0)
269 break;
270 file_idx = scan_for_file (panel, file_idx, dir);
271 }
272 }
273
274 void view_cmd (WPanel *panel)
275 {
276 do_view_cmd (panel, 0);
277 }
278
279 void view_simple_cmd (WPanel *panel)
280 {
281 do_view_cmd (panel, 1);
282 }
283
284 void filtered_view_cmd (WPanel *panel)
285 {
286 char *command;
287
288 panel = get_a_panel (panel);
289 command = input_dialog (_(" Filtered view "), _(" Filter command and arguments:"),
290 selection (panel)->fname);
291 if (!command)
292 return;
293
294 view (command, "", 0, 0);
295
296 free (command);
297 }
298
299 void filtered_view_cmd_cpanel (void)
300 {
301 filtered_view_cmd (cpanel);
302 }
303
304 void do_edit_at_line (const char *what, int start_line)
305 {
306 static char *editor = 0;
307
308 #ifdef USE_INTERNAL_EDIT
309 if (use_internal_edit){
310 edit (what, start_line);
311 reread_cmd ();
312 return;
313 }
314 #endif
315 if (!editor){
316 editor = getenv ("EDITOR");
317 if (!editor)
318 editor = get_default_editor ();
319 }
320 execute_internal (editor, what);
321 reread_cmd ();
322 }
323
324 void
325 do_edit (const char *what)
326 {
327 do_edit_at_line (what, 1);
328 }
329
330 void edit_cmd (WPanel *panel)
331 {
332 panel = get_a_panel(panel);
333 if (!regex_command (selection (panel)->fname, "Edit", NULL, 0))
334 do_edit (selection (panel)->fname);
335 }
336
337 void edit_cmd_new (WPanel *panel)
338 {
339 do_edit ("");
340 }
341
342 void copy_cmd (void)
343 {
344 save_cwds_stat ();
345 if (panel_operate (cpanel, OP_COPY, NULL)){
346 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
347 repaint_screen ();
348 }
349 }
350
351 void ren_cmd (void)
352 {
353 save_cwds_stat ();
354 if (panel_operate (cpanel, OP_MOVE, NULL)){
355 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
356 repaint_screen ();
357 }
358 }
359
360 void copymove_cmd_with_default (int copy, char *thedefault)
361 {
362 save_cwds_stat ();
363 if (panel_operate (cpanel, copy ? OP_COPY : OP_MOVE, thedefault)){
364 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
365 repaint_screen ();
366 }
367 }
368
369 void mkdir_cmd (WPanel *panel)
370 {
371 char *dir;
372
373 panel = get_a_panel (panel);
374 dir = input_expand_dialog (_(" Mkdir "), _(" Enter directory name:") , "");
375
376 if (!dir)
377 return;
378
379 save_cwds_stat ();
380 if (my_mkdir (dir, 0777) == 0){
381 update_panels (UP_OPTIMIZE, dir);
382 repaint_screen ();
383 select_item (cpanel);
384 free (dir);
385 return;
386 }
387 free (dir);
388 message (1, MSG_ERROR, " %s ", unix_error_string (errno));
389 }
390
391 void delete_cmd (void)
392 {
393 save_cwds_stat ();
394
395 if (panel_operate (cpanel, OP_DELETE, NULL)){
396 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
397 repaint_screen ();
398 }
399 }
400
401 void find_cmd (void)
402 {
403 do_find ();
404 }
405
406 void
407 set_panel_filter_to (WPanel *p, char *allocated_filter_string)
408 {
409 if (p->filter){
410 free (p->filter);
411 p->filter = 0;
412 }
413 if (!(allocated_filter_string [0] == '*' && allocated_filter_string [1] == 0))
414 p->filter = allocated_filter_string;
415 else
416 free (allocated_filter_string);
417 reread_cmd ();
418 x_filter_changed (p);
419 }
420
421 /* Set a given panel filter expression */
422 void set_panel_filter (WPanel *p)
423 {
424 char *reg_exp;
425 char *x;
426
427 x = p->filter ? p->filter : easy_patterns ? "*" : ".";
428
429 reg_exp = input_dialog (_(" Filter "), _(" Set expression for filtering filenames"), x);
430 if (!reg_exp)
431 return;
432 set_panel_filter_to (p, reg_exp);
433 }
434
435 /* Invoked from the left/right menus */
436 void filter_cmd (void)
437 {
438 WPanel *p;
439
440 if (!SELECTED_IS_PANEL)
441 return;
442
443 p = MENU_PANEL;
444 set_panel_filter (p);
445 }
446
447 void reread_cmd (void)
448 {
449 int flag;
450
451 mad_check (__FILE__, __LINE__);
452 if (get_current_type () == view_listing &&
453 get_other_type () == view_listing)
454 flag = strcmp (cpanel->cwd, opanel->cwd) ? UP_ONLY_CURRENT : 0;
455 else
456 flag = UP_ONLY_CURRENT;
457
458 update_panels (UP_RELOAD|flag, UP_KEEPSEL);
459 repaint_screen ();
460 }
461
462 /* Panel sorting related routines */
463 void do_re_sort (WPanel *panel)
464 {
465 char *filename;
466 int i;
467
468 panel = get_a_panel (panel);
469 filename = strdup (selection (cpanel)->fname);
470 unselect_item (panel);
471 do_sort (&panel->dir, panel->sort_type, panel->count-1, panel->reverse, panel->case_sensitive);
472 panel->selected = -1;
473 for (i = panel->count; i; i--){
474 if (!strcmp (panel->dir.list [i-1].fname, filename)){
475 panel->selected = i-1;
476 break;
477 }
478 }
479 free (filename);
480 cpanel->top_file = cpanel->selected - ITEMS (cpanel)/2;
481 if (cpanel->top_file < 0)
482 cpanel->top_file = 0;
483 select_item (panel);
484 panel_update_contents (panel);
485 }
486
487 void reverse_selection_cmd_panel (WPanel *panel)
488 {
489 file_entry *file;
490 int i;
491
492 for (i = 0; i < panel->count; i++){
493 file = &panel->dir.list [i];
494 if (S_ISDIR (file->buf.st_mode))
495 continue;
496 do_file_mark (panel, i, !file->f.marked);
497 }
498 paint_panel (panel);
499 }
500
501 void reverse_selection_cmd (void)
502 {
503 reverse_selection_cmd_panel (cpanel);
504 }
505
506 void select_cmd_panel (WPanel *panel)
507 {
508 char *reg_exp, *reg_exp_t;
509 int i;
510 int c;
511 int dirflag = 0;
512
513 reg_exp = input_dialog (_(" Select "), "", easy_patterns ? "*" : ".");
514 if (!reg_exp)
515 return;
516
517 reg_exp_t = reg_exp;
518
519 /* Check if they specified a directory */
520 if (*reg_exp_t == PATH_SEP){
521 dirflag = 1;
522 reg_exp_t++;
523 }
524 if (reg_exp_t [strlen(reg_exp_t) - 1] == PATH_SEP){
525 dirflag = 1;
526 reg_exp_t [strlen(reg_exp_t) - 1] = 0;
527 }
528
529 for (i = 0; i < panel->count; i++){
530 if (!strcmp (panel->dir.list [i].fname, ".."))
531 continue;
532 if (S_ISDIR (panel->dir.list [i].buf.st_mode)){
533 if (!dirflag)
534 continue;
535 } else {
536 if (dirflag)
537 continue;
538 }
539 c = regexp_match (reg_exp_t, panel->dir.list [i].fname, match_file);
540 if (c == -1){
541 message (1, MSG_ERROR, _(" Malformed regular expression "));
542 free (reg_exp);
543 return;
544 }
545 if (c){
546 do_file_mark (panel, i, 1);
547 }
548 }
549 paint_panel (panel);
550 free (reg_exp);
551 }
552
553 void select_cmd (void)
554 {
555 select_cmd_panel (cpanel);
556 }
557
558 void unselect_cmd_panel (WPanel *panel)
559 {
560 char *reg_exp, *reg_exp_t;
561 int i;
562 int c;
563 int dirflag = 0;
564
565 reg_exp = input_dialog (_(" Unselect "),"", easy_patterns ? "*" : ".");
566 if (!reg_exp)
567 return;
568
569 reg_exp_t = reg_exp;
570
571 /* Check if they specified directory matching */
572 if (*reg_exp_t == PATH_SEP){
573 dirflag = 1;
574 reg_exp_t ++;
575 }
576 if (reg_exp_t [strlen(reg_exp_t) - 1] == PATH_SEP){
577 dirflag = 1;
578 reg_exp_t [strlen(reg_exp_t) - 1] = 0;
579 }
580 for (i = 0; i < panel->count; i++){
581 if (!strcmp (panel->dir.list [i].fname, ".."))
582 continue;
583 if (S_ISDIR (panel->dir.list [i].buf.st_mode)){
584 if (!dirflag)
585 continue;
586 } else {
587 if (dirflag)
588 continue;
589 }
590 c = regexp_match (reg_exp_t, panel->dir.list [i].fname, match_file);
591 if (c == -1){
592 message (1, MSG_ERROR, _(" Malformed regular expression "));
593 free (reg_exp);
594 return;
595 }
596 if (c){
597 do_file_mark (panel, i, 0);
598 }
599 }
600 paint_panel (panel);
601 free (reg_exp);
602 }
603
604 void unselect_cmd (void)
605 {
606 unselect_cmd_panel (cpanel);
607 }
608
609 /* Check if the file exists */
610 /* If not copy the default */
611 static int check_for_default(char *default_file, char *file)
612 {
613 struct stat s;
614 if (mc_stat (file, &s)){
615 if (mc_stat (default_file, &s)){
616 return -1;
617 }
618 create_op_win (OP_COPY, 0);
619 file_mask_defaults ();
620 copy_file_file (default_file, file, 1);
621 destroy_op_win ();
622 }
623 return 0;
624 }
625
626 void ext_cmd (void)
627 {
628 char *buffer;
629 char *extdir;
630 int dir;
631
632 dir = 0;
633 if (geteuid () == 0){
634 dir = query_dialog (_("Extension file edit"),
635 _(" Which extension file you want to edit? "), 0, 2,
636 _("&User"), _("&System Wide"));
637 }
638 extdir = concat_dir_and_file (mc_home, MC_LIB_EXT);
639
640 if (dir == 0){
641 buffer = concat_dir_and_file (home_dir, MC_USER_EXT);
642 check_for_default (extdir, buffer);
643 do_edit (buffer);
644 free (buffer);
645 } else if (dir == 1)
646 do_edit (extdir);
647
648 free (extdir);
649 flush_extension_file ();
650 }
651
652 void menu_edit_cmd (void)
653 {
654 char *buffer;
655 char *menufile;
656 int dir = 0;
657
658 dir = query_dialog (
659 _("Menu file edit"),
660 _(" Which menu file will you edit? "),
661 0, geteuid() ? 2 : 3,
662 _("&Local"), _("&Home"), _("&System Wide")
663 );
664
665 menufile = concat_dir_and_file(mc_home, MC_GLOBAL_MENU);
666
667 switch (dir){
668 case 0:
669 buffer = strdup (MC_LOCAL_MENU);
670 check_for_default (menufile, buffer);
671 break;
672
673 case 1:
674 buffer = concat_dir_and_file (home_dir, MC_HOME_MENU);
675 check_for_default (menufile, buffer);
676 break;
677
678 case 2:
679 buffer = concat_dir_and_file (mc_home, MC_GLOBAL_MENU);
680 break;
681
682 default:
683 free (menufile);
684 return;
685 }
686 do_edit (buffer);
687 if (dir == 0)
688 chmod(buffer, 0600);
689 free (buffer);
690 free (menufile);
691 }
692
693 void quick_chdir_cmd (void)
694 {
695 char *target;
696
697 target = hotlist_cmd (LIST_HOTLIST);
698 if (!target)
699 return;
700
701 if (get_current_type () == view_tree)
702 tree_chdir (the_tree, target);
703 else
704 do_cd (target, cd_exact);
705 free (target);
706 }
707
708 #ifdef USE_VFS
709 void reselect_vfs (void)
710 {
711 char *target;
712
713 target = hotlist_cmd (LIST_VFSLIST);
714 if (!target)
715 return;
716
717 do_cd (target, cd_exact);
718 free (target);
719 }
720 #endif
721
722 static int compare_files (char *name1, char *name2, long size)
723 {
724 int file1, file2;
725 char *data1, *data2;
726 int result = -1; /* Different by default */
727
728 file1 = open (name1, O_RDONLY);
729 if (file1 >= 0){
730 file2 = open (name2, O_RDONLY);
731 if (file2 >= 0){
732 #ifdef HAVE_MMAP
733 /* Ugly if jungle */
734 data1 = mmap (0, size, PROT_READ, MAP_FILE | MAP_PRIVATE, file1, 0);
735 if (data1 != (char*) -1){
736 data2 = mmap (0, size, PROT_READ, MAP_FILE | MAP_PRIVATE, file2, 0);
737 if (data2 != (char*) -1){
738 rotate_dash ();
739 result = memcmp (data1, data2, size);
740 munmap (data2, size);
741 }
742 munmap (data1, size);
743 }
744 #else
745 /* Don't have mmap() :( Even more ugly :) */
746 char buf1[BUFSIZ], buf2[BUFSIZ];
747 int n1, n2;
748 rotate_dash ();
749 do
750 {
751 while((n1 = read(file1,buf1,BUFSIZ)) == -1 && errno == EINTR);
752 while((n2 = read(file2,buf2,BUFSIZ)) == -1 && errno == EINTR);
753 } while (n1 == n2 && n1 == BUFSIZ && !memcmp(buf1,buf2,BUFSIZ));
754 result = (n1 != n2) || memcmp(buf1,buf2,n1);
755 #endif
756 close (file2);
757 }
758 close (file1);
759 }
760 return result;
761 }
762
763 enum CompareMode {
764 compare_quick, compare_size_only, compare_thourough
765 };
766
767 static void
768 compare_dir (WPanel *panel, WPanel *other, enum CompareMode mode)
769 {
770 int i, j;
771 char *src_name, *dst_name;
772
773 panel = get_a_panel (panel);
774
775 /* No marks by default */
776 panel->marked = 0;
777 panel->total = 0;
778 panel->dirs_marked = 0;
779
780 /* Handle all files in the panel */
781 for (i = 0; i < panel->count; i++){
782 file_entry *source = &panel->dir.list[i];
783
784 /* Default: unmarked */
785 file_mark (panel, i, 0);
786
787 /* Skip directories */
788 if (S_ISDIR (source->buf.st_mode))
789 continue;
790
791 /* Search the corresponding entry from the other panel */
792 for (j = 0; j < other->count; j++){
793 if (strcmp (source->fname,
794 other->dir.list[j].fname) == 0)
795 break;
796 }
797 if (j >= other->count)
798 /* Not found -> mark */
799 do_file_mark (panel, i, 1);
800 else {
801 /* Found */
802 file_entry *target = &other->dir.list[j];
803
804 if (mode != compare_size_only){
805 /* Older version is not marked */
806 if (source->buf.st_mtime < target->buf.st_mtime)
807 continue;
808 }
809
810 /* Newer version with different size is marked */
811 if (source->buf.st_size != target->buf.st_size){
812 do_file_mark (panel, i, 1);
813 continue;
814
815 }
816 if (mode == compare_size_only)
817 continue;
818
819 if (mode == compare_quick){
820 /* Thorough compare off, compare only time stamps */
821 /* Mark newer version, don't mark version with the same date */
822 if (source->buf.st_mtime > target->buf.st_mtime){
823 do_file_mark (panel, i, 1);
824 }
825 continue;
826 }
827
828 /* Thorough compare on, do byte-by-byte comparison */
829 src_name = get_full_name (panel->cwd, source->fname);
830 dst_name = get_full_name (other->cwd, target->fname);
831 if (compare_files (src_name, dst_name, source->buf.st_size))
832 do_file_mark (panel, i, 1);
833 free (src_name);
834 free (dst_name);
835 }
836 } /* for (i ...) */
837 }
838
839 void compare_dirs_cmd (void)
840 {
841 enum CompareMode thorough_flag = compare_quick;
842
843 thorough_flag = query_dialog (_(" Compare directories "), _(" Select compare method: "),
844 0, 3, _("&Quick"), _("&Size only"), _("&Thorough"), _("&Cancel"));
845 if (thorough_flag < 0 || thorough_flag > 2)
846 return;
847 if (get_current_type () == view_listing &&
848 get_other_type () == view_listing){
849 compare_dir (cpanel, opanel, thorough_flag);
850 compare_dir (opanel, cpanel, thorough_flag);
851 paint_panel (cpanel);
852 paint_panel (opanel);
853 } else {
854 message (1, MSG_ERROR, _(" Both panels should be on the listing view mode to use this command "));
855 }
856 }
857
858 void history_cmd (void)
859 {
860 Listbox *listbox;
861 Hist *current;
862
863 if (input_w (cmdline)->need_push){
864 if (push_history (input_w (cmdline), input_w (cmdline)->buffer) == 2)
865 input_w (cmdline)->need_push = 0;
866 }
867 if (!input_w (cmdline)->history){
868 message (1, MSG_ERROR, _(" The command history is empty "));
869 return;
870 }
871 current = input_w (cmdline)->history;
872 while (current->prev)
873 current = current->prev;
874 listbox = create_listbox_window (60, 10, _(" Command history "),
875 "[Command Menu]");
876 while (current){
877 LISTBOX_APPEND_TEXT (listbox, 0, current->text,
878 current);
879 current = current->next;
880 }
881 run_dlg (listbox->dlg);
882 if (listbox->dlg->ret_value == B_CANCEL)
883 current = NULL;
884 else
885 current = listbox->list->current->data;
886 destroy_dlg (listbox->dlg);
887 free (listbox);
888
889 if (!current)
890 return;
891 input_w (cmdline)->history = current;
892 assign_text (input_w (cmdline), input_w (cmdline)->history->text);
893 update_input (input_w (cmdline), 1);
894 }
895
896 #if !defined(HAVE_XVIEW) && !defined(HAVE_GNOME)
897 void swap_cmd (void)
898 {
899 swap_panels ();
900 touchwin (stdscr);
901 repaint_screen ();
902 }
903 #endif
904
905 void
906 view_other_cmd (void)
907 {
908 static int message_flag = TRUE;
909 #ifdef HAVE_SUBSHELL_SUPPORT
910 char *new_dir = NULL;
911 char **new_dir_p;
912 #endif
913
914 if (!xterm_flag && !console_flag && !use_subshell){
915 if (message_flag)
916 message (1, MSG_ERROR, _(" Not an xterm or Linux console; \n"
917 " the panels cannot be toggled. "));
918 message_flag = FALSE;
919 } else {
920 #ifndef HAVE_X
921 if (use_mouse_p)
922 shut_mouse ();
923 if (clear_before_exec)
924 clr_scr ();
925 if (alternate_plus_minus)
926 numeric_keypad_mode ();
927 #endif
928 #ifndef HAVE_SLANG
929 /* With slang we don't want any of this, since there
930 * is no mc_raw_mode supported
931 */
932 reset_shell_mode ();
933 noecho ();
934 #endif
935 keypad(stdscr, FALSE);
936 endwin ();
937 if (!status_using_ncurses)
938 do_exit_ca_mode ();
939 mc_raw_mode ();
940 if (console_flag)
941 restore_console ();
942
943 #ifdef HAVE_SUBSHELL_SUPPORT
944 if (use_subshell){
945 new_dir_p = vfs_current_is_local () ? &new_dir : NULL;
946 if (invoke_subshell (NULL, VISIBLY, new_dir_p))
947 quiet_quit_cmd(); /* User did `exit' or `logout': quit MC quietly */
948 } else
949 #endif
950 {
951 if (output_starts_shell){
952 fprintf (stderr,
953 _("Type `exit' to return to the Midnight Commander\n\r\n\r"));
954 my_system (EXECUTE_AS_SHELL, shell, NULL);
955 } else
956 get_key_code (0);
957 }
958 if (console_flag)
959 handle_console (CONSOLE_SAVE);
960
961 if (!status_using_ncurses)
962 do_enter_ca_mode ();
963
964 reset_prog_mode ();
965 keypad(stdscr, TRUE);
966 #ifndef HAVE_X
967 if (use_mouse_p)
968 init_mouse ();
969 if (alternate_plus_minus)
970 application_keypad_mode ();
971 #endif
972
973 #ifdef HAVE_SUBSHELL_SUPPORT
974 if (use_subshell){
975 load_prompt (0, 0);
976 if (new_dir)
977 do_possible_cd (new_dir);
978 if (console_flag && output_lines)
979 show_console_contents (output_start_y,
980 LINES-keybar_visible-output_lines-1,
981 LINES-keybar_visible-1);
982 }
983 #endif
984 touchwin (stdscr);
985
986 /* prevent screen flash when user did 'exit' or 'logout' within
987 subshell */
988 if (!quit)
989 repaint_screen ();
990 }
991 }
992
993 #ifndef OS2_NT
994 static void
995 do_link (int symbolic_link, char *fname)
996 {
997 struct stat s;
998 char *dest, *src;
999 int stat_r;
1000
1001 if (!symbolic_link){
1002 stat_r = mc_stat (fname, &s);
1003 if (stat_r != 0){
1004 message (1, MSG_ERROR, _(" Couldn't stat %s \n %s "),
1005 fname, unix_error_string (errno));
1006 return;
1007 }
1008 if (!S_ISREG (s.st_mode))
1009 return;
1010 }
1011
1012 if (!symbolic_link){
1013 src = copy_strings (_(" Link "), name_trunc (fname, 46),
1014 _(" to:"), NULL);
1015 dest = input_expand_dialog (_(" Link "), src, "");
1016 free (src);
1017 if (!dest)
1018 return;
1019 if (!*dest) {
1020 free (dest);
1021 return;
1022 }
1023 save_cwds_stat ();
1024 if (-1 == mc_link (fname, dest))
1025 message (1, MSG_ERROR, _(" link: %s "), unix_error_string (errno));
1026 } else {
1027 #ifdef OLD_SYMLINK_VERSION
1028 symlink_dialog (fname, "", &dest, &src);
1029 #else
1030 /* suggest the full path for symlink */
1031 char s[MC_MAXPATHLEN];
1032 char d[MC_MAXPATHLEN];
1033
1034 strcpy(s, cpanel->cwd);
1035 if ( ! ((s[0] == '/') && (s[1] == 0)))
1036 strcat(s, "/");
1037 strcat(s, fname);
1038 if (get_other_type () == view_listing)
1039 strcpy(d, opanel->cwd);
1040 else
1041 strcpy (d,"");
1042
1043 if ( ! ((d[0] == '/') && (d[1] == 0)))
1044 strcat(d, "/");
1045 symlink_dialog (s, d, &dest, &src);
1046 #endif
1047 if (!dest || !*dest) {
1048 if (src)
1049 free (src);
1050 if (dest)
1051 free (dest);
1052 return;
1053 }
1054 if (src){
1055 if (*src) {
1056 save_cwds_stat ();
1057 if (-1 == mc_symlink (dest, src))
1058 message (1, MSG_ERROR, _(" symlink: %s "),
1059 unix_error_string (errno));
1060 }
1061 free (src);
1062 }
1063 }
1064 free (dest);
1065 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
1066 repaint_screen ();
1067 }
1068
1069 void link_cmd (void)
1070 {
1071 do_link (0, selection (cpanel)->fname);
1072 }
1073
1074 void symlink_cmd (void)
1075 {
1076 do_link (1, selection (cpanel)->fname);
1077 }
1078
1079 void edit_symlink_cmd (void)
1080 {
1081 if (S_ISLNK (selection (cpanel)->buf.st_mode)) {
1082 char buffer [MC_MAXPATHLEN], *p = selection (cpanel)->fname;
1083 int i;
1084 char *dest, *q = copy_strings (_(" Symlink "), name_trunc (p, 32), _(" points to:"), NULL);
1085
1086 i = readlink (p, buffer, MC_MAXPATHLEN);
1087 if (i > 0) {
1088 buffer [i] = 0;
1089 dest = input_expand_dialog (_(" Edit symlink "), q, buffer);
1090 if (dest) {
1091 if (*dest && strcmp (buffer, dest)) {
1092 save_cwds_stat ();
1093 mc_unlink (p);
1094 if (-1 == mc_symlink (dest, p))
1095 message (1, MSG_ERROR, _(" edit symlink: %s "),
1096 unix_error_string (errno));
1097 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
1098 repaint_screen ();
1099 }
1100 free (dest);
1101 }
1102 }
1103 free (q);
1104 }
1105 }
1106
1107 void other_symlink_cmd (void)
1108 {
1109 char *dest, *q, *p, *r, *s, *t;
1110
1111 if (get_other_type () != view_listing)
1112 return;
1113
1114 if (!strcmp (selection (opanel)->fname, ".."))
1115 return;
1116 p = concat_dir_and_file (cpanel->cwd, selection (cpanel)->fname);
1117 r = concat_dir_and_file (opanel->cwd, selection (cpanel)->fname);
1118
1119 q = copy_strings (_(" Link symbolically "), name_trunc (p, 32), _(" to:"), NULL);
1120 dest = input_expand_dialog (_(" Relative symlink "), q, r);
1121 if (dest) {
1122 if (*dest) {
1123 t = strrchr (dest, PATH_SEP);
1124 if (t) {
1125 t[1] = 0;
1126 s = diff_two_paths (dest, p);
1127 t[1] = PATH_SEP;
1128 if (s) {
1129 save_cwds_stat ();
1130 if (-1 == mc_symlink (dest, s))
1131 message (1, MSG_ERROR, _(" relative symlink: %s "),
1132 unix_error_string (errno));
1133 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
1134 repaint_screen ();
1135 free (s);
1136 }
1137 }
1138 }
1139 free (dest);
1140 }
1141 free (q);
1142 free (p);
1143 free (r);
1144 }
1145 #endif
1146
1147 void help_cmd (void)
1148 {
1149 char *hlpfile = concat_dir_and_file (mc_home, "mc.hlp");
1150 interactive_display (hlpfile, "[main]");
1151 free (hlpfile);
1152 }
1153
1154 void view_panel_cmd (void)
1155 {
1156 view_cmd (cpanel);
1157 }
1158
1159 void edit_panel_cmd (void)
1160 {
1161 edit_cmd (cpanel);
1162 }
1163
1164 void mkdir_panel_cmd (void)
1165 {
1166 mkdir_cmd (cpanel);
1167 }
1168
1169 /* Returns a random hint */
1170 char *get_random_hint (void)
1171 {
1172 char *data, *result, *eol;
1173 char *hintfile;
1174 int len;
1175 int start;
1176
1177 /* Do not change hints more often than one minute */
1178
1179 #ifdef SCO_FLAVOR
1180 static time_t last;
1181 time_t now;
1182
1183 time (&now);
1184 if ((now - last) < 60)
1185 return strdup ("");
1186 last = now;
1187 #else
1188 static int last_sec;
1189 static struct timeval tv;
1190
1191 gettimeofday (&tv, NULL);
1192 if (!(tv.tv_sec> last_sec+60))
1193 return strdup ("");
1194 last_sec = tv.tv_sec;
1195 #endif
1196
1197 hintfile = concat_dir_and_file (mc_home, MC_HINT);
1198 data = load_file (hintfile);
1199 free (hintfile);
1200 if (!data)
1201 return 0;
1202
1203 #ifdef SCO_FLAVOR
1204 srand ((short) now);
1205 #else
1206 srand (tv.tv_sec);
1207 #endif
1208 /* get a random entry */
1209 len = strlen (data);
1210 start = rand () % len;
1211
1212 for (;start; start--){
1213 if (data [start] == '\n'){
1214 start++;
1215 break;
1216 }
1217 }
1218 eol = strchr (&data [start], '\n');
1219 if (eol)
1220 *eol = 0;
1221 result = strdup (&data [start]);
1222 free (data);
1223 return result;
1224 }
1225
1226 #ifndef USE_VFS
1227 #ifdef USE_NETCODE
1228 #undef USE_NETCODE
1229 #endif
1230 #endif
1231
1232 #ifdef USE_NETCODE
1233
1234 static char *machine_str = N_(" Enter machine name (F1 for details): ");
1235
1236 static void nice_cd (char *text, char *xtext, char *help, char *prefix, int to_home)
1237 {
1238 char *machine;
1239 char *cd_path;
1240
1241 if (!SELECTED_IS_PANEL)
1242 return;
1243
1244 machine = input_dialog_help (text,
1245 xtext,
1246 help, "");
1247 if (!machine)
1248 return;
1249
1250 if (strncmp (prefix, machine, strlen (prefix)) == 0)
1251 cd_path = copy_strings (machine, to_home ? "/~/" : NULL, NULL);
1252 else
1253 cd_path = copy_strings (prefix, machine, to_home ? "/~/" : NULL, NULL);
1254
1255 if (do_panel_cd (MENU_PANEL, cd_path, 0))
1256 directory_history_add (MENU_PANEL, (MENU_PANEL)->cwd);
1257 else
1258 message (1, MSG_ERROR, N_(" Could not chdir to %s "), cd_path);
1259 free (cd_path);
1260 free (machine);
1261 }
1262
1263 void netlink_cmd (void)
1264 {
1265 nice_cd (_(" Link to a remote machine "), _(machine_str),
1266 "[Network File System]", "mc:", 1);
1267 }
1268
1269 void ftplink_cmd (void)
1270 {
1271 nice_cd (_(" FTP to machine "), _(machine_str),
1272 "[FTP File System]", "ftp://", 1);
1273 }
1274
1275 #ifdef HAVE_SETSOCKOPT
1276 void source_routing (void)
1277 {
1278 char *source;
1279 struct hostent *hp;
1280
1281 source = input_dialog (_(" Socket source routing setup "),
1282 _(" Enter host name to use as a source routing hop: ")n,
1283 "");
1284 if (!source)
1285 return;
1286
1287 hp = gethostbyname (source);
1288 if (!hp){
1289 message (1, _(" Host name "), _(" Error while looking up IP address "));
1290 return;
1291 }
1292 source_route = *((int *)hp->h_addr);
1293 }
1294 #endif /* HAVE_SETSOCKOPT */
1295 #endif /* USE_NETCODE */
1296
1297 #ifdef USE_EXT2FSLIB
1298 void undelete_cmd (void)
1299 {
1300 nice_cd (_(" Undelete files on an ext2 file system "),
1301 _(" Enter the file system name where you want to run the\n "
1302 " undelete file system on: (F1 for details)"),
1303 "[Undelete File System]", "undel:", 0);
1304 }
1305 #endif
1306
1307 void quick_cd_cmd (void)
1308 {
1309 char *p = cd_dialog ();
1310
1311 if (p && *p) {
1312 char *q = copy_strings ("cd ", p, NULL);
1313
1314 do_cd_command (q);
1315 free (q);
1316 }
1317 if (p)
1318 free (p);
1319 }
1320
1321 #ifdef SCO_FLAVOR
1322 #undef DUSUM_USEB
1323 #undef DUSUM_FACTOR
1324 #endif /* SCO_FLAVOR */
1325
1326 #ifdef HAVE_DUSUM
1327 void dirsizes_cmd (void)
1328 {
1329 WPanel *panel = cpanel;
1330 int i, j = 0;
1331 char *cmd, *p, *q, *r;
1332 FILE *f;
1333 #ifdef DUSUM_USEB
1334 # define dirsizes_command "du -s -b "
1335 #else
1336 # define dirsizes_command "du -s "
1337 #endif
1338 #ifndef DUSUM_FACTOR
1339 # define DUSUM_FACTOR 512
1340 #endif
1341
1342 if (!vfs_current_is_local ())
1343 return;
1344 for (i = 0; i < panel->count; i++)
1345 if (S_ISDIR (panel->dir.list [i].buf.st_mode))
1346 j += strlen (panel->dir.list [i].fname) + 1;
1347 if (!j)
1348 return;
1349 cmd = xmalloc (strlen (dirsizes_command) + j + 1, "dirsizes_cmd");
1350 strcpy (cmd, dirsizes_command);
1351 p = strchr (cmd, 0);
1352 for (i = 0; i < panel->count; i++)
1353 if (S_ISDIR (panel->dir.list [i].buf.st_mode) &&
1354 strcmp (panel->dir.list [i].fname, "..")) {
1355 strcpy (p, panel->dir.list [i].fname);
1356 p = strchr (p, 0);
1357 *(p++) = ' ';
1358 }
1359 *(--p) = 0;
1360 open_error_pipe ();
1361 f = popen (cmd, "r");
1362 free (cmd);
1363 if (f != NULL) {
1364 /* Assume that du will display the directories in the order
1365 * I've passed to it :(
1366 */
1367 i = 0;
1368 p = xmalloc (1024, "dirsizes_cmd");
1369 while (fgets (p, 1024, f)) {
1370 j = atoi (p) * DUSUM_FACTOR;
1371 for (q = p; *q && *q != ' ' && *q != '\t'; q++);
1372 while (*q == ' ' || *q == '\t')
1373 q++;
1374 r = strchr (q, '\n');
1375 if (r == NULL)
1376 r = strchr (q, 0);
1377 for (; i < panel->count; i++)
1378 if (S_ISDIR (panel->dir.list [i].buf.st_mode))
1379 if (!strncmp (q, panel->dir.list [i].fname,
1380 r - q)) {
1381 if (panel->dir.list [i].f.marked)
1382 panel->total += j -
1383 ((panel->has_dir_sizes) ? panel->dir.list [i].buf.st_size : 0);
1384 panel->dir.list [i].buf.st_size = j;
1385 break;
1386 }
1387 if (i == panel->count)
1388 break;
1389 }
1390 free (p);
1391 if (pclose (f) < 0)
1392 #ifndef SCO_FLAVOR
1393 message (0, _("Show directory sizes"), _("Pipe close failed"));
1394 else
1395 #else /* SCO_FLAVOR */
1396 /*
1397 ** SCO reports about error while all seems to be ok. Just ignore it...
1398 ** (alex@bcs.zaporizhzhe.ua)
1399 */
1400 ;
1401 #endif /* SCO_FLAVOR */
1402 panel->has_dir_sizes = 1;
1403 close_error_pipe (0, 0);
1404 paint_panel (panel);
1405 } else
1406 close_error_pipe (1, _("Cannot invoke du command."));
1407 }
1408 #endif
1409
1410 void
1411 save_setup_cmd (void)
1412 {
1413 char *str;
1414
1415 save_setup ();
1416 sync_profiles ();
1417 str = copy_strings ( _(" Setup saved to ~/"), PROFILE_NAME, NULL);
1418
1419 #ifdef HAVE_GNOME
1420 set_hintbar (str);
1421 #else
1422 message (0, _(" Setup "), str);
1423 #endif
1424 free (str);
1425 }
1426
1427 void
1428 configure_panel_listing (WPanel *p, int view_type, int use_msformat, char *user, char *status)
1429 {
1430 int err;
1431
1432 p->user_mini_status = use_msformat;
1433 p->list_type = view_type;
1434
1435 if (view_type == list_user || use_msformat){
1436 free (p->user_format);
1437 p->user_format = user;
1438
1439 free (p->user_status_format [view_type]);
1440 p->user_status_format [view_type] = status;
1441
1442 err = set_panel_formats (p);
1443
1444 if (err){
1445 if (err & 0x01){
1446 free (p->user_format);
1447 p->user_format = strdup (DEFAULT_USER_FORMAT);
1448 }
1449
1450 if (err & 0x02){
1451 free (p->user_status_format [view_type]);
1452 p->user_status_format [view_type] = strdup (DEFAULT_USER_FORMAT);
1453 }
1454 }
1455 }
1456 else {
1457 free (user);
1458 free (status);
1459 }
1460
1461 set_panel_formats (p);
1462 paint_panel (p);
1463
1464 do_refresh ();
1465 }
1466
1467 #ifndef HAVE_GNOME
1468 void
1469 info_cmd_no_menu (void)
1470 {
1471 set_display_type (cpanel == left_panel ? 1 : 0, view_info);
1472 }
1473
1474 void
1475 quick_cmd_no_menu (void)
1476 {
1477 set_display_type (cpanel == left_panel ? 1 : 0, view_quick);
1478 }
1479
1480 void
1481 switch_to_listing (int panel_index)
1482 {
1483 if (get_display_type (panel_index) != view_listing)
1484 set_display_type (panel_index, view_listing);
1485 }
1486
1487 void
1488 listing_cmd (void)
1489 {
1490 int view_type, use_msformat;
1491 char *user, *status;
1492 WPanel *p;
1493 int display_type;
1494
1495 display_type = get_display_type (MENU_PANEL_IDX);
1496 if (display_type == view_listing)
1497 p = MENU_PANEL_IDX == 0 ? left_panel : right_panel;
1498 else
1499 p = 0;
1500
1501 view_type = display_box (p, &user, &status, &use_msformat, MENU_PANEL_IDX);
1502
1503 if (view_type == -1)
1504 return;
1505
1506 switch_to_listing (MENU_PANEL_IDX);
1507
1508 p = MENU_PANEL_IDX == 0 ? left_panel : right_panel;
1509
1510 configure_panel_listing (p, view_type, use_msformat, user, status);
1511 }
1512
1513 void
1514 tree_cmd (void)
1515 {
1516 set_display_type (MENU_PANEL_IDX, view_tree);
1517 }
1518
1519 void
1520 info_cmd (void)
1521 {
1522 set_display_type (MENU_PANEL_IDX, view_info);
1523 }
1524
1525 void
1526 quick_view_cmd (void)
1527 {
1528 set_display_type (MENU_PANEL_IDX, view_quick);
1529 }
1530 #endif
1531
1532 /* Handle the tree internal listing modes switching */
1533 static int
1534 set_basic_panel_listing_to (int panel_index, int listing_mode)
1535 {
1536 WPanel *p = (WPanel *) get_panel_widget (panel_index);
1537
1538 #ifndef HAVE_GNOME
1539 switch_to_listing (panel_index);
1540 #endif
1541 p->list_type = listing_mode;
1542 if (set_panel_formats (p))
1543 return 0;
1544
1545 paint_panel (p);
1546 do_refresh ();
1547 return 1;
1548 }
1549
1550 void
1551 toggle_listing_cmd (void)
1552 {
1553 int current = get_current_index ();
1554 WPanel *p = (WPanel *) get_panel_widget (current);
1555 int list_mode = p->list_type;
1556 int m;
1557
1558 switch (list_mode){
1559 case list_full:
1560 case list_brief:
1561 m = list_long;
1562 break;
1563 case list_long:
1564 m = list_user;
1565 break;
1566 default:
1567 m = list_full;
1568 }
1569 if (set_basic_panel_listing_to (current, m))
1570 return;
1571 set_basic_panel_listing_to (current, list_full);
1572 }
1573