2 Execution routines for GNU Midnight Commander
4 Copyright (C) 2003-2016
5 Free Software Foundation, Inc.
8 Slava Zanko <slavazanko@gmail.com>, 2013
10 This file is part of the Midnight Commander.
12 The Midnight Commander is free software: you can redistribute it
13 and/or modify it under the terms of the GNU General Public License as
14 published by the Free Software Foundation, either version 3 of the License,
15 or (at your option) any later version.
17 The Midnight Commander is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 * \brief Source: execution routines
37 #include "lib/global.h"
39 #include "lib/tty/tty.h"
40 #include "lib/tty/key.h"
41 #include "lib/tty/win.h"
42 #include "lib/vfs/vfs.h"
43 #include "lib/mcconfig.h"
45 #include "lib/strutil.h" /* str_replace_all_substrings() */
46 #include "lib/widget.h"
48 #include "filemanager/midnight.h"
49 #include "filemanager/layout.h" /* use_dash() */
50 #include "consaver/cons.saver.h"
51 #ifdef ENABLE_SUBSHELL
52 #include "subshell/subshell.h"
54 #include "setup.h" /* clear_before_exec */
58 /*** global variables ****************************************************************************/
60 int pause_after_run
= pause_on_dumb_terminals
;
62 /*** file scope macro definitions ****************************************************************/
64 /*** file scope type declarations ****************************************************************/
66 /*** file scope variables ************************************************************************/
68 /*** file scope functions ************************************************************************/
70 void do_execute (const char *shell
, const char *command
, int flags
);
71 void do_executev (const char *shell
, int flags
, char *const argv
[]);
72 char *execute_get_external_cmd_opts_from_config (const char *command
,
73 const vfs_path_t
* filename_vpath
,
76 /* --------------------------------------------------------------------------------------------- */
79 edition_post_exec (void)
83 /* FIXME: Missing on slang endwin? */
84 tty_reset_prog_mode ();
91 enable_bracketed_paste ();
92 if (mc_global
.tty
.alternate_plus_minus
)
93 application_keypad_mode ();
96 /* --------------------------------------------------------------------------------------------- */
99 edition_pre_exec (void)
101 if (clear_before_exec
)
105 if (!(mc_global
.tty
.console_flag
!= '\0' || mc_global
.tty
.xterm_flag
))
111 disable_bracketed_paste ();
113 tty_reset_shell_mode ();
117 numeric_keypad_mode ();
119 /* on xterms: maybe endwin did not leave the terminal on the shell
120 * screen page: do it now.
122 * Do not move this before endwin: in some systems rmcup includes
123 * a call to clear screen, so it will end up clearing the shell screen.
128 /* --------------------------------------------------------------------------------------------- */
130 #ifdef ENABLE_SUBSHELL
132 do_possible_cd (const vfs_path_t
* new_dir_vpath
)
134 if (!do_cd (new_dir_vpath
, cd_exact
))
135 message (D_ERROR
, _("Warning"),
136 _("The Commander can't change to the directory that\n"
137 "the subshell claims you are in. Perhaps you have\n"
138 "deleted your working directory, or given yourself\n"
139 "extra access permissions with the \"su\" command?"));
141 #endif /* ENABLE_SUBSHELL */
143 /* --------------------------------------------------------------------------------------------- */
146 do_suspend_cmd (void)
150 if (mc_global
.tty
.console_flag
!= '\0' && !mc_global
.tty
.use_subshell
)
151 handle_console (CONSOLE_RESTORE
);
155 struct sigaction sigtstp_action
;
157 memset (&sigtstp_action
, 0, sizeof (sigtstp_action
));
158 /* Make sure that the SIGTSTP below will suspend us directly,
159 without calling ncurses' SIGTSTP handler; we *don't* want
160 ncurses to redraw the screen immediately after the SIGCONT */
161 sigaction (SIGTSTP
, &startup_handler
, &sigtstp_action
);
163 kill (getpid (), SIGTSTP
);
165 /* Restore previous SIGTSTP action */
166 sigaction (SIGTSTP
, &sigtstp_action
, NULL
);
170 if (mc_global
.tty
.console_flag
!= '\0' && !mc_global
.tty
.use_subshell
)
171 handle_console (CONSOLE_SAVE
);
173 edition_post_exec ();
176 /* --------------------------------------------------------------------------------------------- */
179 execute_prepare_with_vfs_arg (const vfs_path_t
* filename_vpath
, vfs_path_t
** localcopy_vpath
,
184 /* Simplest case, this file is local */
185 if ((filename_vpath
== NULL
&& vfs_file_is_local (vfs_get_raw_current_dir ()))
186 || vfs_file_is_local (filename_vpath
))
189 /* FIXME: Creation of new files on VFS is not supported */
190 if (filename_vpath
== NULL
)
193 *localcopy_vpath
= mc_getlocalcopy (filename_vpath
);
194 if (*localcopy_vpath
== NULL
)
196 message (D_ERROR
, MSG_ERROR
, _("Cannot fetch a local copy of %s"),
197 vfs_path_as_str (filename_vpath
));
201 mc_stat (*localcopy_vpath
, &st
);
202 *mtime
= st
.st_mtime
;
206 /* --------------------------------------------------------------------------------------------- */
209 execute_cleanup_with_vfs_arg (const vfs_path_t
* filename_vpath
, vfs_path_t
** localcopy_vpath
,
212 if (*localcopy_vpath
!= NULL
)
217 * filename can be an entry on panel, it can be changed by executing
218 * the command, so make a copy. Smarter VFS code would make the code
221 mc_stat (*localcopy_vpath
, &st
);
222 mc_ungetlocalcopy (filename_vpath
, *localcopy_vpath
, *mtime
!= st
.st_mtime
);
223 vfs_path_free (*localcopy_vpath
);
224 *localcopy_vpath
= NULL
;
228 /* --------------------------------------------------------------------------------------------- */
231 execute_get_opts_from_cfg (const char *command
, const char *default_str
)
233 char *str_from_config
;
236 mc_config_get_string_raw (mc_main_config
, CONFIG_EXT_EDITOR_VIEWER_SECTION
, command
, NULL
);
238 if (str_from_config
== NULL
)
242 cfg
= mc_config_init (global_profile_name
, TRUE
);
244 return g_strdup (default_str
);
247 mc_config_get_string_raw (cfg
, CONFIG_EXT_EDITOR_VIEWER_SECTION
, command
, default_str
);
249 mc_config_deinit (cfg
);
252 return str_from_config
;
255 /* --------------------------------------------------------------------------------------------- */
256 /*** public functions ****************************************************************************/
257 /* --------------------------------------------------------------------------------------------- */
260 execute_get_external_cmd_opts_from_config (const char *command
, const vfs_path_t
* filename_vpath
,
263 char *str_from_config
, *return_str
;
266 if (filename_vpath
== NULL
)
267 return g_strdup ("");
269 parameter
= g_shell_quote (vfs_path_get_last_path_str (filename_vpath
));
274 str_from_config
= execute_get_opts_from_cfg (command
, "%filename");
276 return_str
= str_replace_all (str_from_config
, "%filename", parameter
);
278 g_free (str_from_config
);
279 str_from_config
= return_str
;
281 parameter
= g_strdup_printf ("%ld", start_line
);
282 return_str
= str_replace_all (str_from_config
, "%lineno", parameter
);
284 g_free (str_from_config
);
289 /* --------------------------------------------------------------------------------------------- */
292 do_executev (const char *shell
, int flags
, char *const argv
[])
294 #ifdef ENABLE_SUBSHELL
295 vfs_path_t
*new_dir_vpath
= NULL
;
296 #endif /* ENABLE_SUBSHELL */
298 vfs_path_t
*old_vfs_dir_vpath
= NULL
;
300 if (!vfs_current_is_local ())
301 old_vfs_dir_vpath
= vfs_path_clone (vfs_get_raw_current_dir ());
303 if (mc_global
.mc_run_mode
== MC_RUN_FULL
)
306 if (mc_global
.tty
.console_flag
!= '\0')
307 handle_console (CONSOLE_RESTORE
);
309 if (!mc_global
.tty
.use_subshell
&& *argv
!= NULL
&& (flags
& EXECUTE_INTERNAL
) == 0)
311 printf ("%s%s\n", mc_prompt
, *argv
);
314 #ifdef ENABLE_SUBSHELL
315 if (mc_global
.tty
.use_subshell
&& (flags
& EXECUTE_INTERNAL
) == 0)
319 /* We don't care if it died, higher level takes care of this */
320 invoke_subshell (*argv
, VISIBLY
, old_vfs_dir_vpath
!= NULL
? NULL
: &new_dir_vpath
);
323 #endif /* ENABLE_SUBSHELL */
324 my_systemv_flags (flags
, shell
, argv
);
326 if ((flags
& EXECUTE_INTERNAL
) == 0)
328 if ((pause_after_run
== pause_always
329 || (pause_after_run
== pause_on_dumb_terminals
&& !mc_global
.tty
.xterm_flag
330 && mc_global
.tty
.console_flag
== '\0')) && quit
== 0
331 #ifdef ENABLE_SUBSHELL
332 && subshell_state
!= RUNNING_COMMAND
333 #endif /* ENABLE_SUBSHELL */
336 printf (_("Press any key to continue..."));
343 if (mc_global
.tty
.console_flag
!= '\0' && output_lines
!= 0 && mc_global
.keybar_visible
)
350 if (mc_global
.tty
.console_flag
!= '\0')
351 handle_console (CONSOLE_SAVE
);
352 edition_post_exec ();
354 #ifdef ENABLE_SUBSHELL
355 if (new_dir_vpath
!= NULL
)
357 do_possible_cd (new_dir_vpath
);
358 vfs_path_free (new_dir_vpath
);
361 #endif /* ENABLE_SUBSHELL */
363 if (old_vfs_dir_vpath
!= NULL
)
365 mc_chdir (old_vfs_dir_vpath
);
366 vfs_path_free (old_vfs_dir_vpath
);
369 if (mc_global
.mc_run_mode
== MC_RUN_FULL
)
371 update_panels (UP_OPTIMIZE
, UP_KEEPSEL
);
372 update_xterm_title_path ();
379 /* --------------------------------------------------------------------------------------------- */
382 do_execute (const char *shell
, const char *command
, int flags
)
384 GPtrArray
*args_array
;
386 args_array
= g_ptr_array_new ();
387 g_ptr_array_add (args_array
, (char *) command
);
388 g_ptr_array_add (args_array
, NULL
);
390 do_executev (shell
, flags
, (char *const *) args_array
->pdata
);
392 g_ptr_array_free (args_array
, TRUE
);
395 /* --------------------------------------------------------------------------------------------- */
397 /** Set up the terminal before executing a program */
406 /* --------------------------------------------------------------------------------------------- */
407 /** Hide the terminal after executing a program */
411 edition_post_exec ();
416 /* --------------------------------------------------------------------------------------------- */
417 /* Executes a command */
420 shell_execute (const char *command
, int flags
)
424 if (flags
& EXECUTE_HIDE
)
426 cmd
= g_strconcat (" ", command
, (char *) NULL
);
427 flags
^= EXECUTE_HIDE
;
430 #ifdef ENABLE_SUBSHELL
431 if (mc_global
.tty
.use_subshell
)
432 if (subshell_state
== INACTIVE
)
433 do_execute (mc_global
.shell
->path
, cmd
? cmd
: command
, flags
| EXECUTE_AS_SHELL
);
435 message (D_ERROR
, MSG_ERROR
, _("The shell is already running a command"));
437 #endif /* ENABLE_SUBSHELL */
438 do_execute (mc_global
.shell
->path
, cmd
? cmd
: command
, flags
| EXECUTE_AS_SHELL
);
443 /* --------------------------------------------------------------------------------------------- */
448 #ifdef ENABLE_SUBSHELL
449 vfs_path_t
*new_dir_vpath
= NULL
;
450 #endif /* ENABLE_SUBSHELL */
452 SIG_ATOMIC_VOLATILE_T was_sigwinch
= 0;
456 disable_bracketed_paste ();
457 if (clear_before_exec
)
459 if (mc_global
.tty
.alternate_plus_minus
)
460 numeric_keypad_mode ();
462 /* With slang we don't want any of this, since there
463 * is no raw_mode supported
465 tty_reset_shell_mode ();
466 #endif /* !HAVE_SLANG */
472 if (mc_global
.tty
.console_flag
!= '\0')
473 handle_console (CONSOLE_RESTORE
);
475 #ifdef ENABLE_SUBSHELL
476 if (mc_global
.tty
.use_subshell
)
478 vfs_path_t
**new_dir_p
;
480 new_dir_p
= vfs_current_is_local ()? &new_dir_vpath
: NULL
;
481 invoke_subshell (NULL
, VISIBLY
, new_dir_p
);
484 #endif /* ENABLE_SUBSHELL */
486 if (output_starts_shell
)
488 fprintf (stderr
, _("Type 'exit' to return to the Midnight Commander"));
489 fprintf (stderr
, "\n\r\n\r");
491 my_system (EXECUTE_INTERNAL
, mc_global
.shell
->path
, NULL
);
497 if (mc_global
.tty
.console_flag
!= '\0')
498 handle_console (CONSOLE_SAVE
);
502 tty_reset_prog_mode ();
505 /* Prevent screen flash when user did 'exit' or 'logout' within
507 if ((quit
& SUBSHELL_EXIT
) != 0)
509 /* User did 'exit' or 'logout': quit MC */
510 if (quiet_quit_cmd ())
514 #ifdef ENABLE_SUBSHELL
515 /* restart subshell */
516 if (mc_global
.tty
.use_subshell
)
518 #endif /* ENABLE_SUBSHELL */
522 enable_bracketed_paste ();
524 if (mc_global
.tty
.alternate_plus_minus
)
525 application_keypad_mode ();
528 * Save sigwinch flag that will be reset in mc_refresh() called via update_panels().
529 * There is some problem with screen redraw in ncurses-based mc in this situation.
531 was_sigwinch
= mc_global
.tty
.winch_flag
;
532 mc_global
.tty
.winch_flag
= 0;
534 #ifdef ENABLE_SUBSHELL
535 if (mc_global
.tty
.use_subshell
)
538 if (new_dir_vpath
!= NULL
)
539 do_possible_cd (new_dir_vpath
);
540 if (mc_global
.tty
.console_flag
!= '\0' && output_lines
)
541 show_console_contents (output_start_y
,
542 LINES
- mc_global
.keybar_visible
- output_lines
-
543 1, LINES
- mc_global
.keybar_visible
- 1);
546 vfs_path_free (new_dir_vpath
);
547 #endif /* ENABLE_SUBSHELL */
549 if (mc_global
.mc_run_mode
== MC_RUN_FULL
)
551 update_panels (UP_OPTIMIZE
, UP_KEEPSEL
);
552 update_xterm_title_path ();
555 if (was_sigwinch
!= 0 || mc_global
.tty
.winch_flag
!= 0)
556 dialog_change_screen_size ();
561 /* --------------------------------------------------------------------------------------------- */
565 execute_suspend (const gchar
* event_group_name
, const gchar
* event_name
,
566 gpointer init_data
, gpointer data
)
568 (void) event_group_name
;
573 if (mc_global
.mc_run_mode
== MC_RUN_FULL
)
576 if (mc_global
.mc_run_mode
== MC_RUN_FULL
)
577 update_panels (UP_OPTIMIZE
, UP_KEEPSEL
);
583 /* --------------------------------------------------------------------------------------------- */
586 * Execute command on a filename that can be on VFS.
587 * Errors are reported to the user.
591 execute_with_vfs_arg (const char *command
, const vfs_path_t
* filename_vpath
)
593 vfs_path_t
*localcopy_vpath
= NULL
;
594 const vfs_path_t
*do_execute_vpath
;
597 if (!execute_prepare_with_vfs_arg (filename_vpath
, &localcopy_vpath
, &mtime
))
600 do_execute_vpath
= (localcopy_vpath
== NULL
) ? filename_vpath
: localcopy_vpath
;
602 do_execute (command
, vfs_path_get_last_path_str (do_execute_vpath
), EXECUTE_INTERNAL
);
604 execute_cleanup_with_vfs_arg (filename_vpath
, &localcopy_vpath
, &mtime
);
607 /* --------------------------------------------------------------------------------------------- */
609 * Execute external editor or viewer.
611 * @param command editor/viewer to run
612 * @param filename_vpath path for edit/view
613 * @param start_line cursor will be placed at the 'start_line' position after opening file
614 * if start_line is 0 or negative, no start line will be passed to editor/viewer
618 execute_external_editor_or_viewer (const char *command
, const vfs_path_t
* filename_vpath
,
621 vfs_path_t
*localcopy_vpath
= NULL
;
622 const vfs_path_t
*do_execute_vpath
;
623 char *extern_cmd_options
;
626 if (!execute_prepare_with_vfs_arg (filename_vpath
, &localcopy_vpath
, &mtime
))
629 do_execute_vpath
= (localcopy_vpath
== NULL
) ? filename_vpath
: localcopy_vpath
;
632 execute_get_external_cmd_opts_from_config (command
, do_execute_vpath
, start_line
);
634 if (extern_cmd_options
!= NULL
)
636 char **argv_cmd_options
;
639 if (g_shell_parse_argv (extern_cmd_options
, &argv_count
, &argv_cmd_options
, NULL
))
641 do_executev (command
, EXECUTE_INTERNAL
, argv_cmd_options
);
642 g_strfreev (argv_cmd_options
);
645 do_executev (command
, EXECUTE_INTERNAL
, NULL
);
647 g_free (extern_cmd_options
);
651 execute_cleanup_with_vfs_arg (filename_vpath
, &localcopy_vpath
, &mtime
);
654 /* --------------------------------------------------------------------------------------------- */