2 Execution routines for GNU Midnight Commander
4 Copyright (C) 2003-2018
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"), "%s",
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_global
.main_config
, CONFIG_EXT_EDITOR_VIEWER_SECTION
, command
,
239 if (str_from_config
== NULL
)
243 cfg
= mc_config_init (global_profile_name
, TRUE
);
245 return g_strdup (default_str
);
248 mc_config_get_string_raw (cfg
, CONFIG_EXT_EDITOR_VIEWER_SECTION
, command
, default_str
);
250 mc_config_deinit (cfg
);
253 return str_from_config
;
256 /* --------------------------------------------------------------------------------------------- */
257 /*** public functions ****************************************************************************/
258 /* --------------------------------------------------------------------------------------------- */
261 execute_get_external_cmd_opts_from_config (const char *command
, const vfs_path_t
* filename_vpath
,
264 char *str_from_config
, *return_str
;
267 if (filename_vpath
== NULL
)
268 return g_strdup ("");
270 parameter
= g_shell_quote (vfs_path_get_last_path_str (filename_vpath
));
275 str_from_config
= execute_get_opts_from_cfg (command
, "%filename");
277 return_str
= str_replace_all (str_from_config
, "%filename", parameter
);
279 g_free (str_from_config
);
280 str_from_config
= return_str
;
282 parameter
= g_strdup_printf ("%ld", start_line
);
283 return_str
= str_replace_all (str_from_config
, "%lineno", parameter
);
285 g_free (str_from_config
);
290 /* --------------------------------------------------------------------------------------------- */
293 do_executev (const char *shell
, int flags
, char *const argv
[])
295 #ifdef ENABLE_SUBSHELL
296 vfs_path_t
*new_dir_vpath
= NULL
;
297 #endif /* ENABLE_SUBSHELL */
299 vfs_path_t
*old_vfs_dir_vpath
= NULL
;
301 if (!vfs_current_is_local ())
302 old_vfs_dir_vpath
= vfs_path_clone (vfs_get_raw_current_dir ());
304 if (mc_global
.mc_run_mode
== MC_RUN_FULL
)
307 if (mc_global
.tty
.console_flag
!= '\0')
308 handle_console (CONSOLE_RESTORE
);
310 if (!mc_global
.tty
.use_subshell
&& *argv
!= NULL
&& (flags
& EXECUTE_INTERNAL
) == 0)
312 printf ("%s%s\n", mc_prompt
, *argv
);
315 #ifdef ENABLE_SUBSHELL
316 if (mc_global
.tty
.use_subshell
&& (flags
& EXECUTE_INTERNAL
) == 0)
320 /* We don't care if it died, higher level takes care of this */
321 invoke_subshell (*argv
, VISIBLY
, old_vfs_dir_vpath
!= NULL
? NULL
: &new_dir_vpath
);
324 #endif /* ENABLE_SUBSHELL */
325 my_systemv_flags (flags
, shell
, argv
);
327 if ((flags
& EXECUTE_INTERNAL
) == 0)
329 if ((pause_after_run
== pause_always
330 || (pause_after_run
== pause_on_dumb_terminals
&& !mc_global
.tty
.xterm_flag
331 && mc_global
.tty
.console_flag
== '\0')) && quit
== 0
332 #ifdef ENABLE_SUBSHELL
333 && subshell_state
!= RUNNING_COMMAND
334 #endif /* ENABLE_SUBSHELL */
337 printf ("%s", _("Press any key to continue..."));
344 if (mc_global
.tty
.console_flag
!= '\0' && output_lines
!= 0 && mc_global
.keybar_visible
)
351 if (mc_global
.tty
.console_flag
!= '\0')
352 handle_console (CONSOLE_SAVE
);
353 edition_post_exec ();
355 #ifdef ENABLE_SUBSHELL
356 if (new_dir_vpath
!= NULL
)
358 do_possible_cd (new_dir_vpath
);
359 vfs_path_free (new_dir_vpath
);
362 #endif /* ENABLE_SUBSHELL */
364 if (old_vfs_dir_vpath
!= NULL
)
366 mc_chdir (old_vfs_dir_vpath
);
367 vfs_path_free (old_vfs_dir_vpath
);
370 if (mc_global
.mc_run_mode
== MC_RUN_FULL
)
372 update_panels (UP_OPTIMIZE
, UP_KEEPSEL
);
373 update_xterm_title_path ();
380 /* --------------------------------------------------------------------------------------------- */
383 do_execute (const char *shell
, const char *command
, int flags
)
385 GPtrArray
*args_array
;
387 args_array
= g_ptr_array_new ();
388 g_ptr_array_add (args_array
, (char *) command
);
389 g_ptr_array_add (args_array
, NULL
);
391 do_executev (shell
, flags
, (char *const *) args_array
->pdata
);
393 g_ptr_array_free (args_array
, TRUE
);
396 /* --------------------------------------------------------------------------------------------- */
398 /** Set up the terminal before executing a program */
407 /* --------------------------------------------------------------------------------------------- */
408 /** Hide the terminal after executing a program */
412 edition_post_exec ();
417 /* --------------------------------------------------------------------------------------------- */
418 /* Executes a command */
421 shell_execute (const char *command
, int flags
)
425 if (flags
& EXECUTE_HIDE
)
427 cmd
= g_strconcat (" ", command
, (char *) NULL
);
428 flags
^= EXECUTE_HIDE
;
431 #ifdef ENABLE_SUBSHELL
432 if (mc_global
.tty
.use_subshell
)
434 if (subshell_state
== INACTIVE
)
435 do_execute (mc_global
.shell
->path
, cmd
? cmd
: command
, flags
| EXECUTE_AS_SHELL
);
437 message (D_ERROR
, MSG_ERROR
, "%s", _("The shell is already running a command"));
440 #endif /* ENABLE_SUBSHELL */
441 do_execute (mc_global
.shell
->path
, cmd
? cmd
: command
, flags
| EXECUTE_AS_SHELL
);
446 /* --------------------------------------------------------------------------------------------- */
451 #ifdef ENABLE_SUBSHELL
452 vfs_path_t
*new_dir_vpath
= NULL
;
453 #endif /* ENABLE_SUBSHELL */
455 SIG_ATOMIC_VOLATILE_T was_sigwinch
= 0;
459 disable_bracketed_paste ();
460 if (clear_before_exec
)
462 if (mc_global
.tty
.alternate_plus_minus
)
463 numeric_keypad_mode ();
465 /* With slang we don't want any of this, since there
466 * is no raw_mode supported
468 tty_reset_shell_mode ();
469 #endif /* !HAVE_SLANG */
475 if (mc_global
.tty
.console_flag
!= '\0')
476 handle_console (CONSOLE_RESTORE
);
478 #ifdef ENABLE_SUBSHELL
479 if (mc_global
.tty
.use_subshell
)
481 vfs_path_t
**new_dir_p
;
483 new_dir_p
= vfs_current_is_local ()? &new_dir_vpath
: NULL
;
484 invoke_subshell (NULL
, VISIBLY
, new_dir_p
);
487 #endif /* ENABLE_SUBSHELL */
489 if (output_starts_shell
)
491 fputs (_("Type 'exit' to return to the Midnight Commander"), stderr
);
492 fputs ("\n\r\n\r", stderr
);
494 my_system (EXECUTE_INTERNAL
, mc_global
.shell
->path
, NULL
);
500 if (mc_global
.tty
.console_flag
!= '\0')
501 handle_console (CONSOLE_SAVE
);
503 tty_enter_ca_mode ();
505 tty_reset_prog_mode ();
508 /* Prevent screen flash when user did 'exit' or 'logout' within
510 if ((quit
& SUBSHELL_EXIT
) != 0)
512 /* User did 'exit' or 'logout': quit MC */
513 if (quiet_quit_cmd ())
517 #ifdef ENABLE_SUBSHELL
518 /* restart subshell */
519 if (mc_global
.tty
.use_subshell
)
521 #endif /* ENABLE_SUBSHELL */
525 enable_bracketed_paste ();
527 if (mc_global
.tty
.alternate_plus_minus
)
528 application_keypad_mode ();
531 * Save sigwinch flag that will be reset in mc_refresh() called via update_panels().
532 * There is some problem with screen redraw in ncurses-based mc in this situation.
534 was_sigwinch
= mc_global
.tty
.winch_flag
;
535 mc_global
.tty
.winch_flag
= 0;
537 #ifdef ENABLE_SUBSHELL
538 if (mc_global
.tty
.use_subshell
)
541 if (new_dir_vpath
!= NULL
)
542 do_possible_cd (new_dir_vpath
);
543 if (mc_global
.tty
.console_flag
!= '\0' && output_lines
)
544 show_console_contents (output_start_y
,
545 LINES
- mc_global
.keybar_visible
- output_lines
-
546 1, LINES
- mc_global
.keybar_visible
- 1);
549 vfs_path_free (new_dir_vpath
);
550 #endif /* ENABLE_SUBSHELL */
552 if (mc_global
.mc_run_mode
== MC_RUN_FULL
)
554 update_panels (UP_OPTIMIZE
, UP_KEEPSEL
);
555 update_xterm_title_path ();
558 if (was_sigwinch
!= 0 || mc_global
.tty
.winch_flag
!= 0)
559 dialog_change_screen_size ();
564 /* --------------------------------------------------------------------------------------------- */
568 execute_suspend (const gchar
* event_group_name
, const gchar
* event_name
,
569 gpointer init_data
, gpointer data
)
571 (void) event_group_name
;
576 if (mc_global
.mc_run_mode
== MC_RUN_FULL
)
579 if (mc_global
.mc_run_mode
== MC_RUN_FULL
)
580 update_panels (UP_OPTIMIZE
, UP_KEEPSEL
);
586 /* --------------------------------------------------------------------------------------------- */
589 * Execute command on a filename that can be on VFS.
590 * Errors are reported to the user.
594 execute_with_vfs_arg (const char *command
, const vfs_path_t
* filename_vpath
)
596 vfs_path_t
*localcopy_vpath
= NULL
;
597 const vfs_path_t
*do_execute_vpath
;
600 if (!execute_prepare_with_vfs_arg (filename_vpath
, &localcopy_vpath
, &mtime
))
603 do_execute_vpath
= (localcopy_vpath
== NULL
) ? filename_vpath
: localcopy_vpath
;
605 do_execute (command
, vfs_path_get_last_path_str (do_execute_vpath
), EXECUTE_INTERNAL
);
607 execute_cleanup_with_vfs_arg (filename_vpath
, &localcopy_vpath
, &mtime
);
610 /* --------------------------------------------------------------------------------------------- */
612 * Execute external editor or viewer.
614 * @param command editor/viewer to run
615 * @param filename_vpath path for edit/view
616 * @param start_line cursor will be placed at the 'start_line' position after opening file
617 * if start_line is 0 or negative, no start line will be passed to editor/viewer
621 execute_external_editor_or_viewer (const char *command
, const vfs_path_t
* filename_vpath
,
624 vfs_path_t
*localcopy_vpath
= NULL
;
625 const vfs_path_t
*do_execute_vpath
;
626 char *extern_cmd_options
;
629 if (!execute_prepare_with_vfs_arg (filename_vpath
, &localcopy_vpath
, &mtime
))
632 do_execute_vpath
= (localcopy_vpath
== NULL
) ? filename_vpath
: localcopy_vpath
;
635 execute_get_external_cmd_opts_from_config (command
, do_execute_vpath
, start_line
);
637 if (extern_cmd_options
!= NULL
)
639 char **argv_cmd_options
;
642 if (g_shell_parse_argv (extern_cmd_options
, &argv_count
, &argv_cmd_options
, NULL
))
644 do_executev (command
, EXECUTE_INTERNAL
, argv_cmd_options
);
645 g_strfreev (argv_cmd_options
);
648 do_executev (command
, EXECUTE_INTERNAL
, NULL
);
650 g_free (extern_cmd_options
);
653 execute_cleanup_with_vfs_arg (filename_vpath
, &localcopy_vpath
, &mtime
);
656 /* --------------------------------------------------------------------------------------------- */