1 /*
2    Main dialog (file panels) of the Midnight Commander
3 
4    Copyright (C) 1994-2021
5    Free Software Foundation, Inc.
6 
7    Written by:
8    Miguel de Icaza, 1994, 1995, 1996, 1997
9    Janne Kukonlehto, 1994, 1995
10    Norbert Warmuth, 1997
11    Andrew Borodin <aborodin@vmail.ru>, 2009, 2010, 2012, 2013, 2020
12    Slava Zanko <slavazanko@gmail.com>, 2013
13 
14    This file is part of the Midnight Commander.
15 
16    The Midnight Commander is free software: you can redistribute it
17    and/or modify it under the terms of the GNU General Public License as
18    published by the Free Software Foundation, either version 3 of the License,
19    or (at your option) any later version.
20 
21    The Midnight Commander is distributed in the hope that it will be useful,
22    but WITHOUT ANY WARRANTY; without even the implied warranty of
23    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24    GNU General Public License for more details.
25 
26    You should have received a copy of the GNU General Public License
27    along with this program.  If not, see <http://www.gnu.org/licenses/>.
28  */
29 
30 /** \file filemanager.c
31  *  \brief Source: main dialog (file panels) of the Midnight Commander
32  */
33 
34 #include <config.h>
35 
36 #include <ctype.h>
37 #include <errno.h>
38 #include <locale.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <sys/wait.h>
45 #include <pwd.h>                /* for username in xterm title */
46 
47 #include "lib/global.h"
48 #include "lib/fileloc.h"        /* MC_HINT */
49 
50 #include "lib/tty/tty.h"
51 #include "lib/tty/key.h"        /* KEY_M_* masks */
52 #include "lib/skin.h"
53 #include "lib/util.h"
54 
55 #include "lib/vfs/vfs.h"
56 
57 #include "src/args.h"
58 #ifdef ENABLE_SUBSHELL
59 #include "src/subshell/subshell.h"
60 #endif
61 #include "src/execute.h"        /* toggle_subshell */
62 #include "src/setup.h"          /* variables */
63 #include "src/learn.h"          /* learn_keys() */
64 #include "src/keymap.h"
65 #include "lib/fileloc.h"        /* MC_FILEPOS_FILE */
66 #include "lib/keybind.h"
67 #include "lib/event.h"
68 
69 #include "tree.h"
70 #include "boxes.h"              /* sort_box(), tree_box() */
71 #include "layout.h"
72 #include "cmd.h"                /* commands */
73 #include "hotlist.h"
74 #include "panelize.h"
75 #include "command.h"            /* cmdline */
76 #include "dir.h"                /* dir_list_clean() */
77 
78 #ifdef USE_INTERNAL_EDIT
79 #include "src/editor/edit.h"
80 #endif
81 
82 #ifdef USE_DIFF_VIEW
83 #include "src/diffviewer/ydiff.h"
84 #endif
85 
86 #include "src/consaver/cons.saver.h"    /* show_console_contents */
87 #include "src/file_history.h"   /* show_file_history() */
88 
89 #include "filemanager.h"
90 
91 /*** global variables ****************************************************************************/
92 
93 /* When the modes are active, left_panel, right_panel and tree_panel */
94 /* point to a proper data structure.  You should check with the functions */
95 /* get_current_type and get_other_type the types of the panels before using */
96 /* this pointer variables */
97 
98 /* The structures for the panels */
99 WPanel *left_panel = NULL;
100 WPanel *right_panel = NULL;
101 /* Pointer to the selected and unselected panel */
102 WPanel *current_panel = NULL;
103 
104 /* The Menubar */
105 WMenuBar *the_menubar = NULL;
106 /* The widget where we draw the prompt */
107 WLabel *the_prompt;
108 /* The hint bar */
109 WLabel *the_hint;
110 /* The button bar */
111 WButtonBar *the_bar;
112 
113 /* The prompt */
114 const char *mc_prompt = NULL;
115 
116 /*** file scope macro definitions ****************************************************************/
117 
118 #ifdef HAVE_CHARSET
119 /*
120  * Don't restrict the output on the screen manager level,
121  * the translation tables take care of it.
122  */
123 #endif /* !HAVE_CHARSET */
124 
125 /*** file scope type declarations ****************************************************************/
126 
127 /*** file scope variables ************************************************************************/
128 
129 static menu_t *left_menu, *right_menu;
130 
131 /*** file scope functions ************************************************************************/
132 
133 /** Stop MC main dialog and the current dialog if it exists.
134   * Needed to provide fast exit from MC viewer or editor on shell exit */
135 static void
stop_dialogs(void)136 stop_dialogs (void)
137 {
138     dlg_stop (filemanager);
139 
140     if (top_dlg != NULL)
141         dlg_stop (DIALOG (top_dlg->data));
142 }
143 
144 /* --------------------------------------------------------------------------------------------- */
145 
146 static void
treebox_cmd(void)147 treebox_cmd (void)
148 {
149     char *sel_dir;
150 
151     sel_dir = tree_box (selection (current_panel)->fname->str);
152     if (sel_dir != NULL)
153     {
154         vfs_path_t *sel_vdir;
155 
156         sel_vdir = vfs_path_from_str (sel_dir);
157         panel_cd (current_panel, sel_vdir, cd_exact);
158         vfs_path_free (sel_vdir, TRUE);
159         g_free (sel_dir);
160     }
161 }
162 
163 /* --------------------------------------------------------------------------------------------- */
164 
165 #ifdef LISTMODE_EDITOR
166 static void
listmode_cmd(void)167 listmode_cmd (void)
168 {
169     char *newmode;
170 
171     if (get_current_type () != view_listing)
172         return;
173 
174     newmode = listmode_edit (current_panel->user_format);
175     if (!newmode)
176         return;
177 
178     g_free (current_panel->user_format);
179     current_panel->list_format = list_user;
180     current_panel->user_format = newmode;
181     set_panel_formats (current_panel);
182 
183     do_refresh ();
184 }
185 #endif /* LISTMODE_EDITOR */
186 
187 /* --------------------------------------------------------------------------------------------- */
188 
189 static GList *
create_panel_menu(void)190 create_panel_menu (void)
191 {
192     GList *entries = NULL;
193 
194     entries = g_list_prepend (entries, menu_entry_create (_("File listin&g"), CK_PanelListing));
195     entries = g_list_prepend (entries, menu_entry_create (_("&Quick view"), CK_PanelQuickView));
196     entries = g_list_prepend (entries, menu_entry_create (_("&Info"), CK_PanelInfo));
197     entries = g_list_prepend (entries, menu_entry_create (_("&Tree"), CK_PanelTree));
198     entries = g_list_prepend (entries, menu_separator_create ());
199     entries =
200         g_list_prepend (entries,
201                         menu_entry_create (_("&Listing format..."), CK_SetupListingFormat));
202     entries = g_list_prepend (entries, menu_entry_create (_("&Sort order..."), CK_Sort));
203     entries = g_list_prepend (entries, menu_entry_create (_("&Filter..."), CK_Filter));
204 #ifdef HAVE_CHARSET
205     entries = g_list_prepend (entries, menu_entry_create (_("&Encoding..."), CK_SelectCodepage));
206 #endif
207     entries = g_list_prepend (entries, menu_separator_create ());
208 #ifdef ENABLE_VFS_FTP
209     entries = g_list_prepend (entries, menu_entry_create (_("FT&P link..."), CK_ConnectFtp));
210 #endif
211 #ifdef ENABLE_VFS_FISH
212     entries = g_list_prepend (entries, menu_entry_create (_("S&hell link..."), CK_ConnectFish));
213 #endif
214 #ifdef ENABLE_VFS_SFTP
215     entries = g_list_prepend (entries, menu_entry_create (_("S&FTP link..."), CK_ConnectSftp));
216 #endif
217 #ifdef ENABLE_VFS_SMB
218     entries = g_list_prepend (entries, menu_entry_create (_("SM&B link..."), CK_ConnectSmb));
219 #endif
220     entries = g_list_prepend (entries, menu_entry_create (_("Paneli&ze"), CK_Panelize));
221     entries = g_list_prepend (entries, menu_separator_create ());
222     entries = g_list_prepend (entries, menu_entry_create (_("&Rescan"), CK_Reread));
223 
224     return g_list_reverse (entries);
225 }
226 
227 /* --------------------------------------------------------------------------------------------- */
228 
229 static GList *
create_file_menu(void)230 create_file_menu (void)
231 {
232     GList *entries = NULL;
233 
234     entries = g_list_prepend (entries, menu_entry_create (_("&View"), CK_View));
235     entries = g_list_prepend (entries, menu_entry_create (_("Vie&w file..."), CK_ViewFile));
236     entries = g_list_prepend (entries, menu_entry_create (_("&Filtered view"), CK_ViewFiltered));
237     entries = g_list_prepend (entries, menu_entry_create (_("&Edit"), CK_Edit));
238     entries = g_list_prepend (entries, menu_entry_create (_("&Copy"), CK_Copy));
239     entries = g_list_prepend (entries, menu_entry_create (_("C&hmod"), CK_ChangeMode));
240     entries = g_list_prepend (entries, menu_entry_create (_("&Link"), CK_Link));
241     entries = g_list_prepend (entries, menu_entry_create (_("&Symlink"), CK_LinkSymbolic));
242     entries =
243         g_list_prepend (entries,
244                         menu_entry_create (_("Relative symlin&k"), CK_LinkSymbolicRelative));
245     entries = g_list_prepend (entries, menu_entry_create (_("Edit s&ymlink"), CK_LinkSymbolicEdit));
246     entries = g_list_prepend (entries, menu_entry_create (_("Ch&own"), CK_ChangeOwn));
247     entries =
248         g_list_prepend (entries, menu_entry_create (_("&Advanced chown"), CK_ChangeOwnAdvanced));
249 #ifdef ENABLE_EXT2FS_ATTR
250     entries = g_list_prepend (entries, menu_entry_create (_("Cha&ttr"), CK_ChangeAttributes));
251 #endif
252     entries = g_list_prepend (entries, menu_entry_create (_("&Rename/Move"), CK_Move));
253     entries = g_list_prepend (entries, menu_entry_create (_("&Mkdir"), CK_MakeDir));
254     entries = g_list_prepend (entries, menu_entry_create (_("&Delete"), CK_Delete));
255     entries = g_list_prepend (entries, menu_entry_create (_("&Quick cd"), CK_CdQuick));
256     entries = g_list_prepend (entries, menu_separator_create ());
257     entries = g_list_prepend (entries, menu_entry_create (_("Select &group"), CK_Select));
258     entries = g_list_prepend (entries, menu_entry_create (_("U&nselect group"), CK_Unselect));
259     entries = g_list_prepend (entries, menu_entry_create (_("&Invert selection"), CK_SelectInvert));
260     entries = g_list_prepend (entries, menu_separator_create ());
261     entries = g_list_prepend (entries, menu_entry_create (_("E&xit"), CK_Quit));
262 
263     return g_list_reverse (entries);
264 }
265 
266 /* --------------------------------------------------------------------------------------------- */
267 
268 static GList *
create_command_menu(void)269 create_command_menu (void)
270 {
271     /* I know, I'm lazy, but the tree widget when it's not running
272      * as a panel still has some problems, I have not yet finished
273      * the WTree widget port, sorry.
274      */
275     GList *entries = NULL;
276 
277     entries = g_list_prepend (entries, menu_entry_create (_("&User menu"), CK_UserMenu));
278     entries = g_list_prepend (entries, menu_entry_create (_("&Directory tree"), CK_Tree));
279     entries = g_list_prepend (entries, menu_entry_create (_("&Find file"), CK_Find));
280     entries = g_list_prepend (entries, menu_entry_create (_("S&wap panels"), CK_Swap));
281     entries = g_list_prepend (entries, menu_entry_create (_("Switch &panels on/off"), CK_Shell));
282     entries =
283         g_list_prepend (entries, menu_entry_create (_("&Compare directories"), CK_CompareDirs));
284 #ifdef USE_DIFF_VIEW
285     entries = g_list_prepend (entries, menu_entry_create (_("C&ompare files"), CK_CompareFiles));
286 #endif
287     entries =
288         g_list_prepend (entries, menu_entry_create (_("E&xternal panelize"), CK_ExternalPanelize));
289     entries = g_list_prepend (entries, menu_entry_create (_("Show directory s&izes"), CK_DirSize));
290     entries = g_list_prepend (entries, menu_separator_create ());
291     entries = g_list_prepend (entries, menu_entry_create (_("Command &history"), CK_History));
292     entries =
293         g_list_prepend (entries,
294                         menu_entry_create (_("Viewed/edited files hi&story"),
295                                            CK_EditorViewerHistory));
296     entries = g_list_prepend (entries, menu_entry_create (_("Di&rectory hotlist"), CK_HotList));
297 #ifdef ENABLE_VFS
298     entries = g_list_prepend (entries, menu_entry_create (_("&Active VFS list"), CK_VfsList));
299 #endif
300 #ifdef ENABLE_BACKGROUND
301     entries = g_list_prepend (entries, menu_entry_create (_("&Background jobs"), CK_Jobs));
302 #endif
303     entries = g_list_prepend (entries, menu_entry_create (_("Screen lis&t"), CK_ScreenList));
304     entries = g_list_prepend (entries, menu_separator_create ());
305 #ifdef ENABLE_VFS_UNDELFS
306     entries =
307         g_list_prepend (entries,
308                         menu_entry_create (_("&Undelete files (ext2fs only)"), CK_Undelete));
309 #endif
310 #ifdef LISTMODE_EDITOR
311     entries = g_list_prepend (entries, menu_entry_create (_("&Listing format edit"), CK_ListMode));
312 #endif
313 #if defined (ENABLE_VFS_UNDELFS) || defined (LISTMODE_EDITOR)
314     entries = g_list_prepend (entries, menu_separator_create ());
315 #endif
316     entries =
317         g_list_prepend (entries,
318                         menu_entry_create (_("Edit &extension file"), CK_EditExtensionsFile));
319     entries = g_list_prepend (entries, menu_entry_create (_("Edit &menu file"), CK_EditUserMenu));
320     entries =
321         g_list_prepend (entries,
322                         menu_entry_create (_("Edit hi&ghlighting group file"),
323                                            CK_EditFileHighlightFile));
324 
325     return g_list_reverse (entries);
326 }
327 
328 /* --------------------------------------------------------------------------------------------- */
329 
330 static GList *
create_options_menu(void)331 create_options_menu (void)
332 {
333     GList *entries = NULL;
334 
335     entries = g_list_prepend (entries, menu_entry_create (_("&Configuration..."), CK_Options));
336     entries = g_list_prepend (entries, menu_entry_create (_("&Layout..."), CK_OptionsLayout));
337     entries = g_list_prepend (entries, menu_entry_create (_("&Panel options..."), CK_OptionsPanel));
338     entries =
339         g_list_prepend (entries, menu_entry_create (_("C&onfirmation..."), CK_OptionsConfirm));
340     entries =
341         g_list_prepend (entries, menu_entry_create (_("&Appearance..."), CK_OptionsAppearance));
342     entries =
343         g_list_prepend (entries, menu_entry_create (_("&Display bits..."), CK_OptionsDisplayBits));
344     entries = g_list_prepend (entries, menu_entry_create (_("Learn &keys..."), CK_LearnKeys));
345 #ifdef ENABLE_VFS
346     entries = g_list_prepend (entries, menu_entry_create (_("&Virtual FS..."), CK_OptionsVfs));
347 #endif
348     entries = g_list_prepend (entries, menu_separator_create ());
349     entries = g_list_prepend (entries, menu_entry_create (_("&Save setup"), CK_SaveSetup));
350 
351     return g_list_reverse (entries);
352 }
353 
354 /* --------------------------------------------------------------------------------------------- */
355 
356 static void
init_menu(void)357 init_menu (void)
358 {
359     left_menu = create_menu ("", create_panel_menu (), "[Left and Right Menus]");
360     menubar_add_menu (the_menubar, left_menu);
361     menubar_add_menu (the_menubar, create_menu (_("&File"), create_file_menu (), "[File Menu]"));
362     menubar_add_menu (the_menubar,
363                       create_menu (_("&Command"), create_command_menu (), "[Command Menu]"));
364     menubar_add_menu (the_menubar,
365                       create_menu (_("&Options"), create_options_menu (), "[Options Menu]"));
366     right_menu = create_menu ("", create_panel_menu (), "[Left and Right Menus]");
367     menubar_add_menu (the_menubar, right_menu);
368     update_menu ();
369 }
370 
371 /* --------------------------------------------------------------------------------------------- */
372 
373 static void
menu_last_selected_cmd(void)374 menu_last_selected_cmd (void)
375 {
376     menubar_activate (the_menubar, drop_menus, -1);
377 }
378 
379 /* --------------------------------------------------------------------------------------------- */
380 
381 static void
menu_cmd(void)382 menu_cmd (void)
383 {
384     int selected;
385 
386     if ((get_current_index () == 0) == current_panel->active)
387         selected = 0;
388     else
389         selected = g_list_length (the_menubar->menu) - 1;
390 
391     menubar_activate (the_menubar, drop_menus, selected);
392 }
393 
394 /* --------------------------------------------------------------------------------------------- */
395 
396 static void
sort_cmd(void)397 sort_cmd (void)
398 {
399     WPanel *p;
400     const panel_field_t *sort_order;
401 
402     if (!SELECTED_IS_PANEL)
403         return;
404 
405     p = MENU_PANEL;
406     sort_order = sort_box (&p->sort_info, p->sort_field);
407     panel_set_sort_order (p, sort_order);
408 }
409 
410 /* --------------------------------------------------------------------------------------------- */
411 
412 static char *
midnight_get_shortcut(long command)413 midnight_get_shortcut (long command)
414 {
415     const char *ext_map;
416     const char *shortcut = NULL;
417 
418     shortcut = keybind_lookup_keymap_shortcut (filemanager_map, command);
419     if (shortcut != NULL)
420         return g_strdup (shortcut);
421 
422     shortcut = keybind_lookup_keymap_shortcut (panel_map, command);
423     if (shortcut != NULL)
424         return g_strdup (shortcut);
425 
426     ext_map = keybind_lookup_keymap_shortcut (filemanager_map, CK_ExtendedKeyMap);
427     if (ext_map != NULL)
428         shortcut = keybind_lookup_keymap_shortcut (filemanager_x_map, command);
429     if (shortcut != NULL)
430         return g_strdup_printf ("%s %s", ext_map, shortcut);
431 
432     return NULL;
433 }
434 
435 /* --------------------------------------------------------------------------------------------- */
436 
437 static char *
midnight_get_title(const WDialog * h,size_t len)438 midnight_get_title (const WDialog * h, size_t len)
439 {
440     char *path;
441     char *login;
442     char *p;
443 
444     (void) h;
445 
446     title_path_prepare (&path, &login);
447 
448     p = g_strdup_printf ("%s [%s]:%s", _("Panels:"), login, path);
449     g_free (path);
450     g_free (login);
451     path = g_strdup (str_trunc (p, len - 4));
452     g_free (p);
453 
454     return path;
455 }
456 
457 /* --------------------------------------------------------------------------------------------- */
458 
459 static void
toggle_panels_split(void)460 toggle_panels_split (void)
461 {
462     panels_layout.horizontal_split = !panels_layout.horizontal_split;
463     layout_change ();
464     do_refresh ();
465 }
466 
467 /* --------------------------------------------------------------------------------------------- */
468 
469 #ifdef ENABLE_VFS
470 /* event helper */
471 static gboolean
check_panel_timestamp(const WPanel * panel,panel_view_mode_t mode,struct vfs_class * vclass,vfsid id)472 check_panel_timestamp (const WPanel * panel, panel_view_mode_t mode, struct vfs_class *vclass,
473                        vfsid id)
474 {
475     if (mode == view_listing)
476     {
477         const vfs_path_element_t *path_element;
478 
479         path_element = vfs_path_get_by_index (panel->cwd_vpath, -1);
480 
481         if (path_element->class != vclass)
482             return FALSE;
483 
484         if (vfs_getid (panel->cwd_vpath) != id)
485             return FALSE;
486     }
487     return TRUE;
488 }
489 
490 /* --------------------------------------------------------------------------------------------- */
491 
492 /* event callback */
493 static gboolean
check_current_panel_timestamp(const gchar * event_group_name,const gchar * event_name,gpointer init_data,gpointer data)494 check_current_panel_timestamp (const gchar * event_group_name, const gchar * event_name,
495                                gpointer init_data, gpointer data)
496 {
497     ev_vfs_stamp_create_t *event_data = (ev_vfs_stamp_create_t *) data;
498 
499     (void) event_group_name;
500     (void) event_name;
501     (void) init_data;
502 
503     event_data->ret =
504         check_panel_timestamp (current_panel, get_current_type (), event_data->vclass,
505                                event_data->id);
506     return !event_data->ret;
507 }
508 
509 /* --------------------------------------------------------------------------------------------- */
510 
511 /* event callback */
512 static gboolean
check_other_panel_timestamp(const gchar * event_group_name,const gchar * event_name,gpointer init_data,gpointer data)513 check_other_panel_timestamp (const gchar * event_group_name, const gchar * event_name,
514                              gpointer init_data, gpointer data)
515 {
516     ev_vfs_stamp_create_t *event_data = (ev_vfs_stamp_create_t *) data;
517 
518     (void) event_group_name;
519     (void) event_name;
520     (void) init_data;
521 
522     event_data->ret =
523         check_panel_timestamp (other_panel, get_other_type (), event_data->vclass, event_data->id);
524     return !event_data->ret;
525 }
526 #endif /* ENABLE_VFS */
527 
528 /* --------------------------------------------------------------------------------------------- */
529 
530 /* event callback */
531 static gboolean
print_vfs_message(const gchar * event_group_name,const gchar * event_name,gpointer init_data,gpointer data)532 print_vfs_message (const gchar * event_group_name, const gchar * event_name,
533                    gpointer init_data, gpointer data)
534 {
535     ev_vfs_print_message_t *event_data = (ev_vfs_print_message_t *) data;
536 
537     (void) event_group_name;
538     (void) event_name;
539     (void) init_data;
540 
541     if (mc_global.midnight_shutdown)
542         goto ret;
543 
544     if (!mc_global.message_visible || the_hint == NULL || WIDGET (the_hint)->owner == NULL)
545     {
546         int col, row;
547 
548         if (!nice_rotating_dash || (ok_to_refresh <= 0))
549             goto ret;
550 
551         /* Preserve current cursor position */
552         tty_getyx (&row, &col);
553 
554         tty_gotoyx (0, 0);
555         tty_setcolor (NORMAL_COLOR);
556         tty_print_string (str_fit_to_term (event_data->msg, COLS - 1, J_LEFT));
557 
558         /* Restore cursor position */
559         tty_gotoyx (row, col);
560         mc_refresh ();
561         goto ret;
562     }
563 
564     if (mc_global.message_visible)
565         set_hintbar (event_data->msg);
566 
567   ret:
568     MC_PTR_FREE (event_data->msg);
569     return TRUE;
570 }
571 
572 /* --------------------------------------------------------------------------------------------- */
573 
574 static void
create_panels(void)575 create_panels (void)
576 {
577     int current_index, other_index;
578     panel_view_mode_t current_mode, other_mode;
579     char *current_dir, *other_dir;
580     vfs_path_t *original_dir;
581 
582     /*
583      * Following cases from command line are possible:
584      * 'mc' (no arguments):            mc_run_param0 == NULL, mc_run_param1 == NULL
585      *                                 active panel uses current directory
586      *                                 passive panel uses "other_dir" from panels.ini
587      *
588      * 'mc dir1 dir2' (two arguments): mc_run_param0 != NULL, mc_run_param1 != NULL
589      *                                 active panel uses mc_run_param0
590      *                                 passive panel uses mc_run_param1
591      *
592      * 'mc dir1' (single argument):    mc_run_param0 != NULL, mc_run_param1 == NULL
593      *                                 active panel uses mc_run_param0
594      *                                 passive panel uses "other_dir" from panels.ini
595      */
596 
597     /* Set up panel directories */
598     if (boot_current_is_left)
599     {
600         /* left panel is active */
601         current_index = 0;
602         other_index = 1;
603         current_mode = startup_left_mode;
604         other_mode = startup_right_mode;
605 
606         if (mc_run_param0 == NULL && mc_run_param1 == NULL)
607         {
608             /* no arguments */
609             current_dir = NULL; /* assume current dir */
610             other_dir = saved_other_dir;        /* from ini */
611         }
612         else if (mc_run_param0 != NULL && mc_run_param1 != NULL)
613         {
614             /* two arguments */
615             current_dir = (char *) mc_run_param0;
616             other_dir = mc_run_param1;
617         }
618         else                    /* mc_run_param0 != NULL && mc_run_param1 == NULL */
619         {
620             /* one argument */
621             current_dir = (char *) mc_run_param0;
622             other_dir = saved_other_dir;        /* from ini */
623         }
624     }
625     else
626     {
627         /* right panel is active */
628         current_index = 1;
629         other_index = 0;
630         current_mode = startup_right_mode;
631         other_mode = startup_left_mode;
632 
633         if (mc_run_param0 == NULL && mc_run_param1 == NULL)
634         {
635             /* no arguments */
636             current_dir = NULL; /* assume current dir */
637             other_dir = saved_other_dir;        /* from ini */
638         }
639         else if (mc_run_param0 != NULL && mc_run_param1 != NULL)
640         {
641             /* two arguments */
642             current_dir = (char *) mc_run_param0;
643             other_dir = mc_run_param1;
644         }
645         else                    /* mc_run_param0 != NULL && mc_run_param1 == NULL */
646         {
647             /* one argument */
648             current_dir = (char *) mc_run_param0;
649             other_dir = saved_other_dir;        /* from ini */
650         }
651     }
652 
653     /* 1. Get current dir */
654     original_dir = vfs_path_clone (vfs_get_raw_current_dir ());
655 
656     /* 2. Create passive panel */
657     if (other_dir != NULL)
658     {
659         vfs_path_t *vpath;
660 
661         if (g_path_is_absolute (other_dir))
662             vpath = vfs_path_from_str (other_dir);
663         else
664             vpath = vfs_path_append_new (original_dir, other_dir, (char *) NULL);
665         mc_chdir (vpath);
666         vfs_path_free (vpath, TRUE);
667     }
668     create_panel (other_index, other_mode);
669 
670     /* 3. Create active panel */
671     if (current_dir == NULL)
672         mc_chdir (original_dir);
673     else
674     {
675         vfs_path_t *vpath;
676 
677         if (g_path_is_absolute (current_dir))
678             vpath = vfs_path_from_str (current_dir);
679         else
680             vpath = vfs_path_append_new (original_dir, current_dir, (char *) NULL);
681         mc_chdir (vpath);
682         vfs_path_free (vpath, TRUE);
683     }
684     create_panel (current_index, current_mode);
685 
686     if (startup_left_mode == view_listing)
687         current_panel = left_panel;
688     else if (right_panel != NULL)
689         current_panel = right_panel;
690     else
691         current_panel = left_panel;
692 
693     vfs_path_free (original_dir, TRUE);
694 
695 #ifdef ENABLE_VFS
696     mc_event_add (MCEVENT_GROUP_CORE, "vfs_timestamp", check_other_panel_timestamp, NULL, NULL);
697     mc_event_add (MCEVENT_GROUP_CORE, "vfs_timestamp", check_current_panel_timestamp, NULL, NULL);
698 #endif /* ENABLE_VFS */
699 
700     mc_event_add (MCEVENT_GROUP_CORE, "vfs_print_message", print_vfs_message, NULL, NULL);
701 }
702 
703 /* --------------------------------------------------------------------------------------------- */
704 
705 static void
midnight_put_panel_path(WPanel * panel)706 midnight_put_panel_path (WPanel * panel)
707 {
708     vfs_path_t *cwd_vpath;
709     const char *cwd_vpath_str;
710 
711     if (!command_prompt)
712         return;
713 
714 #ifdef HAVE_CHARSET
715     cwd_vpath = remove_encoding_from_path (panel->cwd_vpath);
716 #else
717     cwd_vpath = vfs_path_clone (panel->cwd_vpath);
718 #endif
719 
720     cwd_vpath_str = vfs_path_as_str (cwd_vpath);
721 
722     command_insert (cmdline, cwd_vpath_str, FALSE);
723 
724     if (!IS_PATH_SEP (cwd_vpath_str[strlen (cwd_vpath_str) - 1]))
725         command_insert (cmdline, PATH_SEP_STR, FALSE);
726 
727     vfs_path_free (cwd_vpath, TRUE);
728 }
729 
730 /* --------------------------------------------------------------------------------------------- */
731 
732 static void
put_link(WPanel * panel)733 put_link (WPanel * panel)
734 {
735     if (!command_prompt)
736         return;
737     if (S_ISLNK (selection (panel)->st.st_mode))
738     {
739         char buffer[MC_MAXPATHLEN];
740         vfs_path_t *vpath;
741         int i;
742 
743         vpath =
744             vfs_path_append_new (panel->cwd_vpath, selection (panel)->fname->str, (char *) NULL);
745         i = mc_readlink (vpath, buffer, sizeof (buffer) - 1);
746         vfs_path_free (vpath, TRUE);
747 
748         if (i > 0)
749         {
750             buffer[i] = '\0';
751             command_insert (cmdline, buffer, TRUE);
752         }
753     }
754 }
755 
756 /* --------------------------------------------------------------------------------------------- */
757 
758 static void
put_current_link(void)759 put_current_link (void)
760 {
761     put_link (current_panel);
762 }
763 
764 /* --------------------------------------------------------------------------------------------- */
765 
766 static void
put_other_link(void)767 put_other_link (void)
768 {
769     if (get_other_type () == view_listing)
770         put_link (other_panel);
771 }
772 
773 /* --------------------------------------------------------------------------------------------- */
774 
775 /** Insert the selected file name into the input line */
776 static void
put_current_selected(void)777 put_current_selected (void)
778 {
779     const char *tmp;
780 
781     if (!command_prompt)
782         return;
783 
784     if (get_current_type () == view_tree)
785     {
786         WTree *tree;
787         const vfs_path_t *selected_name;
788 
789         tree = (WTree *) get_panel_widget (get_current_index ());
790         selected_name = tree_selected_name (tree);
791         tmp = vfs_path_as_str (selected_name);
792     }
793     else
794         tmp = selection (current_panel)->fname->str;
795 
796     command_insert (cmdline, tmp, TRUE);
797 }
798 
799 /* --------------------------------------------------------------------------------------------- */
800 
801 static void
put_tagged(WPanel * panel)802 put_tagged (WPanel * panel)
803 {
804     if (!command_prompt)
805         return;
806     input_disable_update (cmdline);
807     if (panel->marked)
808     {
809         int i;
810 
811         for (i = 0; i < panel->dir.len; i++)
812         {
813             if (panel->dir.list[i].f.marked)
814                 command_insert (cmdline, panel->dir.list[i].fname->str, TRUE);
815         }
816     }
817     else
818     {
819         command_insert (cmdline, panel->dir.list[panel->selected].fname->str, TRUE);
820     }
821     input_enable_update (cmdline);
822 }
823 
824 /* --------------------------------------------------------------------------------------------- */
825 
826 static void
put_current_tagged(void)827 put_current_tagged (void)
828 {
829     put_tagged (current_panel);
830 }
831 
832 /* --------------------------------------------------------------------------------------------- */
833 
834 static void
put_other_tagged(void)835 put_other_tagged (void)
836 {
837     if (get_other_type () == view_listing)
838         put_tagged (other_panel);
839 }
840 
841 /* --------------------------------------------------------------------------------------------- */
842 
843 static void
setup_mc(void)844 setup_mc (void)
845 {
846 #ifdef HAVE_SLANG
847 #ifdef HAVE_CHARSET
848     tty_display_8bit (TRUE);
849 #else
850     tty_display_8bit (mc_global.full_eight_bits);
851 #endif /* HAVE_CHARSET */
852 
853 #else /* HAVE_SLANG */
854 
855 #ifdef HAVE_CHARSET
856     tty_display_8bit (TRUE);
857 #else
858     tty_display_8bit (mc_global.eight_bit_clean);
859 #endif /* HAVE_CHARSET */
860 #endif /* HAVE_SLANG */
861 
862     if ((tty_baudrate () > 0 && tty_baudrate () < 9600) || mc_global.tty.slow_terminal)
863         verbose = FALSE;
864 }
865 
866 /* --------------------------------------------------------------------------------------------- */
867 
868 static void
setup_dummy_mc(void)869 setup_dummy_mc (void)
870 {
871     vfs_path_t *vpath;
872     char *d;
873     int ret;
874 
875     d = _vfs_get_cwd ();
876     setup_mc ();
877     vpath = vfs_path_from_str (d);
878     ret = mc_chdir (vpath);
879     (void) ret;
880     vfs_path_free (vpath, TRUE);
881     g_free (d);
882 }
883 
884 /* --------------------------------------------------------------------------------------------- */
885 
886 static void
done_mc(void)887 done_mc (void)
888 {
889     /* Setup shutdown
890      *
891      * We sync the profiles since the hotlist may have changed, while
892      * we only change the setup data if we have the auto save feature set
893      */
894 
895     save_setup (auto_save_setup, panels_options.auto_save_setup);
896 
897     vfs_stamp_path (vfs_get_raw_current_dir ());
898 }
899 
900 /* --------------------------------------------------------------------------------------------- */
901 
902 static void
create_file_manager(void)903 create_file_manager (void)
904 {
905     Widget *w = WIDGET (filemanager);
906     WGroup *g = GROUP (filemanager);
907 
908     w->keymap = filemanager_map;
909     w->ext_keymap = filemanager_x_map;
910 
911     filemanager->get_shortcut = midnight_get_shortcut;
912     filemanager->get_title = midnight_get_title;
913     /* allow rebind tab */
914     widget_want_tab (w, TRUE);
915 
916     the_menubar = menubar_new (NULL);
917     group_add_widget (g, the_menubar);
918     init_menu ();
919 
920     create_panels ();
921     group_add_widget (g, get_panel_widget (0));
922     group_add_widget (g, get_panel_widget (1));
923 
924     the_hint = label_new (0, 0, 0);
925     the_hint->transparent = TRUE;
926     the_hint->auto_adjust_cols = 0;
927     WIDGET (the_hint)->cols = COLS;
928     group_add_widget (g, the_hint);
929 
930     cmdline = command_new (0, 0, 0);
931     group_add_widget (g, cmdline);
932 
933     the_prompt = label_new (0, 0, mc_prompt);
934     the_prompt->transparent = TRUE;
935     group_add_widget (g, the_prompt);
936 
937     the_bar = buttonbar_new ();
938     group_add_widget (g, the_bar);
939     midnight_set_buttonbar (the_bar);
940 
941 #ifdef ENABLE_SUBSHELL
942     /* Must be done after creation of cmdline and promt widgets to avoid potential
943        NULL dereference in load_prompt() -> ... -> setup_cmdline() -> label_set_text(). */
944     if (mc_global.tty.use_subshell)
945         add_select_channel (mc_global.tty.subshell_pty, load_prompt, NULL);
946 #endif /* !ENABLE_SUBSHELL */
947 }
948 
949 /* --------------------------------------------------------------------------------------------- */
950 
951 /** result must be free'd (I think this should go in util.c) */
952 static vfs_path_t *
prepend_cwd_on_local(const char * filename)953 prepend_cwd_on_local (const char *filename)
954 {
955     vfs_path_t *vpath;
956 
957     vpath = vfs_path_from_str (filename);
958     if (!vfs_file_is_local (vpath) || g_path_is_absolute (filename))
959         return vpath;
960 
961     vfs_path_free (vpath, TRUE);
962 
963     return vfs_path_append_new (vfs_get_raw_current_dir (), filename, (char *) NULL);
964 }
965 
966 /* --------------------------------------------------------------------------------------------- */
967 
968 /** Invoke the internal view/edit routine with:
969  * the default processing and forcing the internal viewer/editor
970  */
971 static gboolean
mc_maybe_editor_or_viewer(void)972 mc_maybe_editor_or_viewer (void)
973 {
974     gboolean ret;
975 
976     switch (mc_global.mc_run_mode)
977     {
978 #ifdef USE_INTERNAL_EDIT
979     case MC_RUN_EDITOR:
980         ret = edit_files ((GList *) mc_run_param0);
981         break;
982 #endif /* USE_INTERNAL_EDIT */
983     case MC_RUN_VIEWER:
984         {
985             vfs_path_t *vpath = NULL;
986 
987             if (mc_run_param0 != NULL && *(char *) mc_run_param0 != '\0')
988                 vpath = prepend_cwd_on_local ((char *) mc_run_param0);
989 
990             ret = view_file (vpath, FALSE, TRUE);
991             vfs_path_free (vpath, TRUE);
992             break;
993         }
994 #ifdef USE_DIFF_VIEW
995     case MC_RUN_DIFFVIEWER:
996         ret = dview_diff_cmd (mc_run_param0, mc_run_param1);
997         break;
998 #endif /* USE_DIFF_VIEW */
999     default:
1000         ret = FALSE;
1001     }
1002 
1003     return ret;
1004 }
1005 
1006 /* --------------------------------------------------------------------------------------------- */
1007 
1008 static void
show_editor_viewer_history(void)1009 show_editor_viewer_history (void)
1010 {
1011     char *s;
1012     int act;
1013 
1014     s = show_file_history (WIDGET (filemanager), &act);
1015     if (s != NULL)
1016     {
1017         vfs_path_t *s_vpath;
1018 
1019         switch (act)
1020         {
1021         case CK_Edit:
1022             s_vpath = vfs_path_from_str (s);
1023             edit_file_at_line (s_vpath, use_internal_edit, 0);
1024             break;
1025 
1026         case CK_View:
1027             s_vpath = vfs_path_from_str (s);
1028             view_file (s_vpath, use_internal_view, FALSE);
1029             break;
1030 
1031         default:
1032             {
1033                 char *d;
1034 
1035                 d = g_path_get_dirname (s);
1036                 s_vpath = vfs_path_from_str (d);
1037                 panel_cd (current_panel, s_vpath, cd_exact);
1038                 try_to_select (current_panel, s);
1039                 g_free (d);
1040             }
1041         }
1042 
1043         g_free (s);
1044         vfs_path_free (s_vpath, TRUE);
1045     }
1046 }
1047 
1048 /* --------------------------------------------------------------------------------------------- */
1049 
1050 static gboolean
quit_cmd_internal(int quiet)1051 quit_cmd_internal (int quiet)
1052 {
1053     int q = quit;
1054     size_t n;
1055 
1056     n = dialog_switch_num () - 1;
1057     if (n != 0)
1058     {
1059         char msg[BUF_MEDIUM];
1060 
1061         g_snprintf (msg, sizeof (msg),
1062                     ngettext ("You have %zu opened screen. Quit anyway?",
1063                               "You have %zu opened screens. Quit anyway?", n), n);
1064 
1065         if (query_dialog (_("The Midnight Commander"), msg, D_NORMAL, 2, _("&Yes"), _("&No")) != 0)
1066             return FALSE;
1067         q = 1;
1068     }
1069     else if (quiet || !confirm_exit)
1070         q = 1;
1071     else if (query_dialog (_("The Midnight Commander"),
1072                            _("Do you really want to quit the Midnight Commander?"),
1073                            D_NORMAL, 2, _("&Yes"), _("&No")) == 0)
1074         q = 1;
1075 
1076     if (q != 0)
1077     {
1078 #ifdef ENABLE_SUBSHELL
1079         if (!mc_global.tty.use_subshell)
1080             stop_dialogs ();
1081         else if ((q = exit_subshell ()? 1 : 0) != 0)
1082 #endif
1083             stop_dialogs ();
1084     }
1085 
1086     if (q != 0)
1087         quit |= 1;
1088     return (quit != 0);
1089 }
1090 
1091 /* --------------------------------------------------------------------------------------------- */
1092 
1093 static gboolean
quit_cmd(void)1094 quit_cmd (void)
1095 {
1096     return quit_cmd_internal (0);
1097 }
1098 
1099 /* --------------------------------------------------------------------------------------------- */
1100 
1101 /**
1102  * Repaint the contents of the panels without frames.  To schedule panel
1103  * for repainting, set panel->dirty to TRUE.  There are many reasons why
1104  * the panels need to be repainted, and this is a costly operation, so
1105  * it's done once per event.
1106  */
1107 
1108 static void
update_dirty_panels(void)1109 update_dirty_panels (void)
1110 {
1111     if (get_current_type () == view_listing && current_panel->dirty)
1112         widget_draw (WIDGET (current_panel));
1113 
1114     if (get_other_type () == view_listing && other_panel->dirty)
1115         widget_draw (WIDGET (other_panel));
1116 }
1117 
1118 /* --------------------------------------------------------------------------------------------- */
1119 
1120 static void
toggle_show_hidden(void)1121 toggle_show_hidden (void)
1122 {
1123     panels_options.show_dot_files = !panels_options.show_dot_files;
1124     update_panels (UP_RELOAD, UP_KEEPSEL);
1125     /* redraw panels forced */
1126     update_dirty_panels ();
1127 }
1128 
1129 /* --------------------------------------------------------------------------------------------- */
1130 
1131 static cb_ret_t
midnight_execute_cmd(Widget * sender,long command)1132 midnight_execute_cmd (Widget * sender, long command)
1133 {
1134     cb_ret_t res = MSG_HANDLED;
1135 
1136     (void) sender;
1137 
1138     /* stop quick search before executing any command */
1139     send_message (current_panel, NULL, MSG_ACTION, CK_SearchStop, NULL);
1140 
1141     switch (command)
1142     {
1143     case CK_ChangePanel:
1144         (void) change_panel ();
1145         break;
1146     case CK_HotListAdd:
1147         add2hotlist_cmd (current_panel);
1148         break;
1149     case CK_SetupListingFormat:
1150         setup_listing_format_cmd ();
1151         break;
1152     case CK_ChangeMode:
1153         chmod_cmd (current_panel);
1154         break;
1155     case CK_ChangeOwn:
1156         chown_cmd (current_panel);
1157         break;
1158     case CK_ChangeOwnAdvanced:
1159         advanced_chown_cmd (current_panel);
1160         break;
1161 #ifdef ENABLE_EXT2FS_ATTR
1162     case CK_ChangeAttributes:
1163         chattr_cmd (current_panel);
1164         break;
1165 #endif
1166     case CK_CompareDirs:
1167         compare_dirs_cmd ();
1168         break;
1169     case CK_Options:
1170         configure_box ();
1171         break;
1172 #ifdef ENABLE_VFS
1173     case CK_OptionsVfs:
1174         configure_vfs_box ();
1175         break;
1176 #endif
1177     case CK_OptionsConfirm:
1178         confirm_box ();
1179         break;
1180     case CK_Copy:
1181         copy_cmd (current_panel);
1182         break;
1183     case CK_PutCurrentPath:
1184         midnight_put_panel_path (current_panel);
1185         break;
1186     case CK_PutCurrentSelected:
1187         put_current_selected ();
1188         break;
1189     case CK_PutCurrentFullSelected:
1190         midnight_put_panel_path (current_panel);
1191         put_current_selected ();
1192         break;
1193     case CK_PutCurrentLink:
1194         put_current_link ();
1195         break;
1196     case CK_PutCurrentTagged:
1197         put_current_tagged ();
1198         break;
1199     case CK_PutOtherPath:
1200         if (get_other_type () == view_listing)
1201             midnight_put_panel_path (other_panel);
1202         break;
1203     case CK_PutOtherLink:
1204         put_other_link ();
1205         break;
1206     case CK_PutOtherTagged:
1207         put_other_tagged ();
1208         break;
1209     case CK_Delete:
1210         delete_cmd (current_panel);
1211         break;
1212     case CK_ScreenList:
1213         dialog_switch_list ();
1214         break;
1215 #ifdef USE_DIFF_VIEW
1216     case CK_CompareFiles:
1217         diff_view_cmd ();
1218         break;
1219 #endif
1220     case CK_OptionsDisplayBits:
1221         display_bits_box ();
1222         break;
1223     case CK_Edit:
1224         edit_cmd (current_panel);
1225         break;
1226 #ifdef USE_INTERNAL_EDIT
1227     case CK_EditForceInternal:
1228         edit_cmd_force_internal (current_panel);
1229         break;
1230 #endif
1231     case CK_EditExtensionsFile:
1232         ext_cmd ();
1233         break;
1234     case CK_EditFileHighlightFile:
1235         edit_fhl_cmd ();
1236         break;
1237     case CK_EditUserMenu:
1238         edit_mc_menu_cmd ();
1239         break;
1240     case CK_LinkSymbolicEdit:
1241         edit_symlink_cmd ();
1242         break;
1243     case CK_ExternalPanelize:
1244         external_panelize ();
1245         break;
1246     case CK_Filter:
1247         filter_cmd ();
1248         break;
1249     case CK_ViewFiltered:
1250         view_filtered_cmd (current_panel);
1251         break;
1252     case CK_Find:
1253         find_cmd (current_panel);
1254         break;
1255 #ifdef ENABLE_VFS_FISH
1256     case CK_ConnectFish:
1257         fishlink_cmd ();
1258         break;
1259 #endif
1260 #ifdef ENABLE_VFS_FTP
1261     case CK_ConnectFtp:
1262         ftplink_cmd ();
1263         break;
1264 #endif
1265 #ifdef ENABLE_VFS_SFTP
1266     case CK_ConnectSftp:
1267         sftplink_cmd ();
1268         break;
1269 #endif
1270 #ifdef ENABLE_VFS_SMB
1271     case CK_ConnectSmb:
1272         smblink_cmd ();
1273         break;
1274 #endif /* ENABLE_VFS_SMB */
1275     case CK_Panelize:
1276         cd_panelize_cmd ();
1277         break;
1278     case CK_Help:
1279         help_cmd ();
1280         break;
1281     case CK_History:
1282         /* show the history of command line widget */
1283         send_message (cmdline, NULL, MSG_ACTION, CK_History, NULL);
1284         break;
1285     case CK_PanelInfo:
1286         if (sender == WIDGET (the_menubar))
1287             info_cmd ();        /* menu */
1288         else
1289             info_cmd_no_menu ();        /* shortcut or buttonbar */
1290         break;
1291 #ifdef ENABLE_BACKGROUND
1292     case CK_Jobs:
1293         jobs_box ();
1294         break;
1295 #endif
1296     case CK_OptionsLayout:
1297         layout_box ();
1298         break;
1299     case CK_OptionsAppearance:
1300         appearance_box ();
1301         break;
1302     case CK_LearnKeys:
1303         learn_keys ();
1304         break;
1305     case CK_Link:
1306         link_cmd (LINK_HARDLINK);
1307         break;
1308     case CK_PanelListing:
1309         listing_cmd ();
1310         break;
1311 #ifdef LISTMODE_EDITOR
1312     case CK_ListMode:
1313         listmode_cmd ();
1314         break;
1315 #endif
1316     case CK_Menu:
1317         menu_cmd ();
1318         break;
1319     case CK_MenuLastSelected:
1320         menu_last_selected_cmd ();
1321         break;
1322     case CK_MakeDir:
1323         mkdir_cmd (current_panel);
1324         break;
1325     case CK_OptionsPanel:
1326         panel_options_box ();
1327         break;
1328 #ifdef HAVE_CHARSET
1329     case CK_SelectCodepage:
1330         encoding_cmd ();
1331         break;
1332 #endif
1333     case CK_CdQuick:
1334         quick_cd_cmd (current_panel);
1335         break;
1336     case CK_HotList:
1337         hotlist_cmd (current_panel);
1338         break;
1339     case CK_PanelQuickView:
1340         if (sender == WIDGET (the_menubar))
1341             quick_view_cmd ();  /* menu */
1342         else
1343             quick_cmd_no_menu ();       /* shortcut or buttonabr */
1344         break;
1345     case CK_QuitQuiet:
1346         quiet_quit_cmd ();
1347         break;
1348     case CK_Quit:
1349         quit_cmd ();
1350         break;
1351     case CK_LinkSymbolicRelative:
1352         link_cmd (LINK_SYMLINK_RELATIVE);
1353         break;
1354     case CK_Move:
1355         rename_cmd (current_panel);
1356         break;
1357     case CK_Reread:
1358         reread_cmd ();
1359         break;
1360 #ifdef ENABLE_VFS
1361     case CK_VfsList:
1362         vfs_list (current_panel);
1363         break;
1364 #endif
1365     case CK_SaveSetup:
1366         save_setup_cmd ();
1367         break;
1368     case CK_Select:
1369     case CK_Unselect:
1370     case CK_SelectInvert:
1371         res = send_message (current_panel, filemanager, MSG_ACTION, command, NULL);
1372         break;
1373     case CK_Shell:
1374         toggle_subshell ();
1375         break;
1376     case CK_DirSize:
1377         smart_dirsize_cmd (current_panel);
1378         break;
1379     case CK_Sort:
1380         sort_cmd ();
1381         break;
1382     case CK_ExtendedKeyMap:
1383         WIDGET (filemanager)->ext_mode = TRUE;
1384         break;
1385     case CK_Suspend:
1386         mc_event_raise (MCEVENT_GROUP_CORE, "suspend", NULL);
1387         break;
1388     case CK_Swap:
1389         swap_cmd ();
1390         break;
1391     case CK_LinkSymbolic:
1392         link_cmd (LINK_SYMLINK_ABSOLUTE);
1393         break;
1394     case CK_ShowHidden:
1395         toggle_show_hidden ();
1396         break;
1397     case CK_SplitVertHoriz:
1398         toggle_panels_split ();
1399         break;
1400     case CK_SplitEqual:
1401         panels_split_equal ();
1402         break;
1403     case CK_SplitMore:
1404         panels_split_more ();
1405         break;
1406     case CK_SplitLess:
1407         panels_split_less ();
1408         break;
1409     case CK_PanelTree:
1410         panel_tree_cmd ();
1411         break;
1412     case CK_Tree:
1413         treebox_cmd ();
1414         break;
1415 #ifdef ENABLE_VFS_UNDELFS
1416     case CK_Undelete:
1417         undelete_cmd ();
1418         break;
1419 #endif
1420     case CK_UserMenu:
1421         user_file_menu_cmd ();
1422         break;
1423     case CK_View:
1424         view_cmd (current_panel);
1425         break;
1426     case CK_ViewFile:
1427         view_file_cmd (current_panel);
1428         break;
1429     case CK_EditorViewerHistory:
1430         show_editor_viewer_history ();
1431         break;
1432     case CK_Cancel:
1433         /* don't close panels due to SIGINT */
1434         break;
1435     default:
1436         res = MSG_NOT_HANDLED;
1437     }
1438 
1439     return res;
1440 }
1441 
1442 /* --------------------------------------------------------------------------------------------- */
1443 
1444 /**
1445  * Whether the command-line should not respond to key events.
1446  *
1447  * This is TRUE if a QuickView or TreeView have the focus, as they're going
1448  * to consume some keys and there's no sense in passing to the command-line
1449  * just the leftovers.
1450  */
1451 static gboolean
is_cmdline_mute(void)1452 is_cmdline_mute (void)
1453 {
1454     /* When one of panels is other than view_listing,
1455        current_panel points to view_listing panel all time independently of
1456        it's activity. Thus, we can't use get_current_type() here.
1457        current_panel should point to actualy current active panel
1458        independently of it's type. */
1459     return (!current_panel->active
1460             && (get_other_type () == view_quick || get_other_type () == view_tree));
1461 }
1462 
1463 /* --------------------------------------------------------------------------------------------- */
1464 
1465 /**
1466  * Handles the Enter key on the command-line.
1467  *
1468  * Returns TRUE if non-whitespace was indeed processed.
1469  */
1470 static gboolean
handle_cmdline_enter(void)1471 handle_cmdline_enter (void)
1472 {
1473     size_t i;
1474 
1475     for (i = 0; cmdline->buffer[i] != '\0' && whitespace (cmdline->buffer[i]); i++)
1476         ;
1477 
1478     if (cmdline->buffer[i] != '\0')
1479     {
1480         send_message (cmdline, NULL, MSG_KEY, '\n', NULL);
1481         return TRUE;
1482     }
1483 
1484     input_insert (cmdline, "", FALSE);
1485     cmdline->point = 0;
1486 
1487     return FALSE;
1488 }
1489 
1490 /* --------------------------------------------------------------------------------------------- */
1491 
1492 static cb_ret_t
midnight_callback(Widget * w,Widget * sender,widget_msg_t msg,int parm,void * data)1493 midnight_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
1494 {
1495     long command;
1496 
1497     switch (msg)
1498     {
1499     case MSG_INIT:
1500         panel_init ();
1501         setup_panels ();
1502         return MSG_HANDLED;
1503 
1504     case MSG_DRAW:
1505         load_hint (TRUE);
1506         group_default_callback (w, NULL, MSG_DRAW, 0, NULL);
1507         /* We handle the special case of the output lines */
1508         if (mc_global.tty.console_flag != '\0' && output_lines != 0)
1509         {
1510             unsigned char end_line;
1511 
1512             end_line = LINES - (mc_global.keybar_visible ? 1 : 0) - 1;
1513             show_console_contents (output_start_y, end_line - output_lines, end_line);
1514         }
1515         return MSG_HANDLED;
1516 
1517     case MSG_RESIZE:
1518         widget_adjust_position (w->pos_flags, &w->y, &w->x, &w->lines, &w->cols);
1519         setup_panels ();
1520         menubar_arrange (the_menubar);
1521         return MSG_HANDLED;
1522 
1523     case MSG_IDLE:
1524         /* We only need the first idle event to show user menu after start */
1525         widget_idle (w, FALSE);
1526 
1527         if (boot_current_is_left)
1528             widget_select (get_panel_widget (0));
1529         else
1530             widget_select (get_panel_widget (1));
1531 
1532         if (auto_menu)
1533             midnight_execute_cmd (NULL, CK_UserMenu);
1534         return MSG_HANDLED;
1535 
1536     case MSG_KEY:
1537         if (w->ext_mode)
1538         {
1539             command = widget_lookup_key (w, parm);
1540             if (command != CK_IgnoreKey)
1541                 return midnight_execute_cmd (NULL, command);
1542         }
1543 
1544         /* FIXME: should handle all menu shortcuts before this point */
1545         if (widget_get_state (WIDGET (the_menubar), WST_FOCUSED))
1546             return MSG_NOT_HANDLED;
1547 
1548         if (parm == '\n' && !is_cmdline_mute ())
1549         {
1550             if (handle_cmdline_enter ())
1551                 return MSG_HANDLED;
1552             /* Else: the panel will handle it. */
1553         }
1554 
1555         if ((!mc_global.tty.alternate_plus_minus
1556              || !(mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag)) && !quote
1557             && !current_panel->quick_search.active)
1558         {
1559             if (!only_leading_plus_minus)
1560             {
1561                 /* Special treatement, since the input line will eat them */
1562                 if (parm == '+')
1563                     return send_message (current_panel, filemanager, MSG_ACTION, CK_Select, NULL);
1564 
1565                 if (parm == '\\' || parm == '-')
1566                     return send_message (current_panel, filemanager, MSG_ACTION, CK_Unselect, NULL);
1567 
1568                 if (parm == '*')
1569                     return send_message (current_panel, filemanager, MSG_ACTION, CK_SelectInvert,
1570                                          NULL);
1571             }
1572             else if (!command_prompt || input_is_empty (cmdline))
1573             {
1574                 /* Special treatement '+', '-', '\', '*' only when this is
1575                  * first char on input line
1576                  */
1577                 if (parm == '+')
1578                     return send_message (current_panel, filemanager, MSG_ACTION, CK_Select, NULL);
1579 
1580                 if (parm == '\\' || parm == '-')
1581                     return send_message (current_panel, filemanager, MSG_ACTION, CK_Unselect, NULL);
1582 
1583                 if (parm == '*')
1584                     return send_message (current_panel, filemanager, MSG_ACTION, CK_SelectInvert,
1585                                          NULL);
1586             }
1587         }
1588         return MSG_NOT_HANDLED;
1589 
1590     case MSG_HOTKEY_HANDLED:
1591         if ((get_current_type () == view_listing) && current_panel->quick_search.active)
1592         {
1593             current_panel->dirty = TRUE;        /* FIXME: unneeded? */
1594             send_message (current_panel, NULL, MSG_ACTION, CK_SearchStop, NULL);
1595         }
1596         return MSG_HANDLED;
1597 
1598     case MSG_UNHANDLED_KEY:
1599         {
1600             cb_ret_t v = MSG_NOT_HANDLED;
1601 
1602             command = widget_lookup_key (w, parm);
1603             if (command != CK_IgnoreKey)
1604                 v = midnight_execute_cmd (NULL, command);
1605 
1606             if (v == MSG_NOT_HANDLED && command_prompt && !is_cmdline_mute ())
1607                 v = send_message (cmdline, NULL, MSG_KEY, parm, NULL);
1608 
1609             return v;
1610         }
1611 
1612     case MSG_POST_KEY:
1613         if (!widget_get_state (WIDGET (the_menubar), WST_FOCUSED))
1614             update_dirty_panels ();
1615         return MSG_HANDLED;
1616 
1617     case MSG_ACTION:
1618         /* Handle shortcuts, menu, and buttonbar. */
1619         return midnight_execute_cmd (sender, parm);
1620 
1621     case MSG_DESTROY:
1622         panel_deinit ();
1623         return MSG_HANDLED;
1624 
1625     default:
1626         return dlg_default_callback (w, sender, msg, parm, data);
1627     }
1628 }
1629 
1630 /* --------------------------------------------------------------------------------------------- */
1631 /*** public functions ****************************************************************************/
1632 /* --------------------------------------------------------------------------------------------- */
1633 
1634 void
update_menu(void)1635 update_menu (void)
1636 {
1637     menu_set_name (left_menu, panels_layout.horizontal_split ? _("&Above") : _("&Left"));
1638     menu_set_name (right_menu, panels_layout.horizontal_split ? _("&Below") : _("&Right"));
1639     menubar_arrange (the_menubar);
1640     widget_set_visibility (WIDGET (the_menubar), menubar_visible);
1641 }
1642 
1643 /* --------------------------------------------------------------------------------------------- */
1644 
1645 void
midnight_set_buttonbar(WButtonBar * b)1646 midnight_set_buttonbar (WButtonBar * b)
1647 {
1648     Widget *w = WIDGET (filemanager);
1649 
1650     buttonbar_set_label (b, 1, Q_ ("ButtonBar|Help"), w->keymap, NULL);
1651     buttonbar_set_label (b, 2, Q_ ("ButtonBar|Menu"), w->keymap, NULL);
1652     buttonbar_set_label (b, 3, Q_ ("ButtonBar|View"), w->keymap, NULL);
1653     buttonbar_set_label (b, 4, Q_ ("ButtonBar|Edit"), w->keymap, NULL);
1654     buttonbar_set_label (b, 5, Q_ ("ButtonBar|Copy"), w->keymap, NULL);
1655     buttonbar_set_label (b, 6, Q_ ("ButtonBar|RenMov"), w->keymap, NULL);
1656     buttonbar_set_label (b, 7, Q_ ("ButtonBar|Mkdir"), w->keymap, NULL);
1657     buttonbar_set_label (b, 8, Q_ ("ButtonBar|Delete"), w->keymap, NULL);
1658     buttonbar_set_label (b, 9, Q_ ("ButtonBar|PullDn"), w->keymap, NULL);
1659     buttonbar_set_label (b, 10, Q_ ("ButtonBar|Quit"), w->keymap, NULL);
1660 }
1661 
1662 /* --------------------------------------------------------------------------------------------- */
1663 /**
1664  * Return a random hint.  If force is TRUE, ignore the timeout.
1665  */
1666 
1667 char *
get_random_hint(gboolean force)1668 get_random_hint (gboolean force)
1669 {
1670     static const gint64 update_period = 60 * G_USEC_PER_SEC;
1671     static gint64 tv = 0;
1672 
1673     char *data, *result = NULL, *eop;
1674     size_t len, start;
1675     GIConv conv;
1676 
1677     /* Do not change hints more often than one minute */
1678     if (!force && !mc_time_elapsed (&tv, update_period))
1679         return g_strdup ("");
1680 
1681     data = load_mc_home_file (mc_global.share_data_dir, MC_HINT, NULL, &len);
1682     if (data == NULL)
1683         return NULL;
1684 
1685     /* get a random entry */
1686     srand ((unsigned int) (tv / G_USEC_PER_SEC));
1687     start = ((size_t) rand ()) % (len - 1);
1688 
1689     /* Search the start of paragraph */
1690     for (; start != 0; start--)
1691         if (data[start] == '\n' && data[start + 1] == '\n')
1692         {
1693             start += 2;
1694             break;
1695         }
1696 
1697     /* Search the end of paragraph */
1698     for (eop = data + start; *eop != '\0'; eop++)
1699     {
1700         if (*eop == '\n' && *(eop + 1) == '\n')
1701         {
1702             *eop = '\0';
1703             break;
1704         }
1705         if (*eop == '\n')
1706             *eop = ' ';
1707     }
1708 
1709     /* hint files are stored in utf-8 */
1710     /* try convert hint file from utf-8 to terminal encoding */
1711     conv = str_crt_conv_from ("UTF-8");
1712     if (conv != INVALID_CONV)
1713     {
1714         GString *buffer;
1715 
1716         buffer = g_string_sized_new (len - start);
1717         if (str_convert (conv, &data[start], buffer) != ESTR_FAILURE)
1718             result = g_string_free (buffer, FALSE);
1719         else
1720             g_string_free (buffer, TRUE);
1721         str_close_conv (conv);
1722     }
1723     else
1724         result = g_strndup (data + start, len - start);
1725 
1726     g_free (data);
1727     return result;
1728 }
1729 
1730 
1731 /* --------------------------------------------------------------------------------------------- */
1732 /**
1733  * Load new hint and display it.
1734  * IF force is not 0, ignore the timeout.
1735  */
1736 
1737 void
load_hint(gboolean force)1738 load_hint (gboolean force)
1739 {
1740     char *hint;
1741 
1742     if (WIDGET (the_hint)->owner == NULL)
1743         return;
1744 
1745     if (!mc_global.message_visible)
1746     {
1747         label_set_text (the_hint, NULL);
1748         return;
1749     }
1750 
1751     hint = get_random_hint (force);
1752 
1753     if (hint != NULL)
1754     {
1755         if (*hint != '\0')
1756             set_hintbar (hint);
1757         g_free (hint);
1758     }
1759     else
1760     {
1761         char text[BUF_SMALL];
1762 
1763         g_snprintf (text, sizeof (text), _("GNU Midnight Commander %s\n"), mc_global.mc_version);
1764         set_hintbar (text);
1765     }
1766 }
1767 
1768 /* --------------------------------------------------------------------------------------------- */
1769 /**
1770   * Change current panel in the file manager.
1771   *
1772   * @return current_panel
1773   */
1774 
1775 WPanel *
change_panel(void)1776 change_panel (void)
1777 {
1778     input_complete_free (cmdline);
1779     group_select_next_widget (GROUP (filemanager));
1780     return current_panel;
1781 }
1782 
1783 /* --------------------------------------------------------------------------------------------- */
1784 
1785 /** Save current stat of directories to avoid reloading the panels
1786  * when no modifications have taken place
1787  */
1788 void
save_cwds_stat(void)1789 save_cwds_stat (void)
1790 {
1791     if (panels_options.fast_reload)
1792     {
1793         mc_stat (current_panel->cwd_vpath, &(current_panel->dir_stat));
1794         if (get_other_type () == view_listing)
1795             mc_stat (other_panel->cwd_vpath, &(other_panel->dir_stat));
1796     }
1797 }
1798 
1799 /* --------------------------------------------------------------------------------------------- */
1800 
1801 gboolean
quiet_quit_cmd(void)1802 quiet_quit_cmd (void)
1803 {
1804     print_last_revert = TRUE;
1805     return quit_cmd_internal (1);
1806 }
1807 
1808 /* --------------------------------------------------------------------------------------------- */
1809 
1810 /** Run the main dialog that occupies the whole screen */
1811 gboolean
do_nc(void)1812 do_nc (void)
1813 {
1814     gboolean ret;
1815 
1816 #ifdef USE_INTERNAL_EDIT
1817     edit_stack_init ();
1818 #endif
1819 
1820     filemanager = dlg_create (FALSE, 0, 0, 1, 1, WPOS_FULLSCREEN, FALSE, dialog_colors,
1821                               midnight_callback, NULL, "[main]", NULL);
1822 
1823     /* Check if we were invoked as an editor or file viewer */
1824     if (mc_global.mc_run_mode != MC_RUN_FULL)
1825     {
1826         setup_dummy_mc ();
1827         ret = mc_maybe_editor_or_viewer ();
1828     }
1829     else
1830     {
1831         /* We only need the first idle event to show user menu after start */
1832         widget_idle (WIDGET (filemanager), TRUE);
1833 
1834         setup_mc ();
1835         mc_filehighlight = mc_fhl_new (TRUE);
1836 
1837         create_file_manager ();
1838         (void) dlg_run (filemanager);
1839 
1840         mc_fhl_free (&mc_filehighlight);
1841 
1842         ret = TRUE;
1843 
1844         /* widget_destroy destroys even current_panel->cwd_vpath, so we have to save a copy :) */
1845         if (mc_args__last_wd_file != NULL && vfs_current_is_local ())
1846             last_wd_string = g_strdup (vfs_path_as_str (current_panel->cwd_vpath));
1847 
1848         /* don't handle VFS timestamps for dirs opened in panels */
1849         mc_event_destroy (MCEVENT_GROUP_CORE, "vfs_timestamp");
1850 
1851         dir_list_free_list (&panelized_panel.list);
1852     }
1853 
1854     /* Program end */
1855     mc_global.midnight_shutdown = TRUE;
1856     dialog_switch_shutdown ();
1857     done_mc ();
1858     widget_destroy (WIDGET (filemanager));
1859     current_panel = NULL;
1860 
1861 #ifdef USE_INTERNAL_EDIT
1862     edit_stack_free ();
1863 #endif
1864 
1865     if ((quit & SUBSHELL_EXIT) == 0)
1866         tty_clear_screen ();
1867 
1868     return ret;
1869 }
1870 
1871 /* --------------------------------------------------------------------------------------------- */
1872