2 Execution routines for GNU Midnight Commander
4 Copyright (C) 2003, 2004, 2005, 2007, 2011, 2013
5 The 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
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 if (mc_global
.tty
.alternate_plus_minus
)
92 application_keypad_mode ();
95 /* --------------------------------------------------------------------------------------------- */
98 edition_pre_exec (void)
100 if (clear_before_exec
)
104 if (!(mc_global
.tty
.console_flag
!= '\0' || mc_global
.tty
.xterm_flag
))
111 tty_reset_shell_mode ();
115 numeric_keypad_mode ();
117 /* on xterms: maybe endwin did not leave the terminal on the shell
118 * screen page: do it now.
120 * Do not move this before endwin: in some systems rmcup includes
121 * a call to clear screen, so it will end up clearing the shell screen.
126 /* --------------------------------------------------------------------------------------------- */
128 #ifdef ENABLE_SUBSHELL
130 do_possible_cd (const vfs_path_t
* new_dir_vpath
)
132 if (!do_cd (new_dir_vpath
, cd_exact
))
133 message (D_ERROR
, _("Warning"),
134 _("The Commander can't change to the directory that\n"
135 "the subshell claims you are in. Perhaps you have\n"
136 "deleted your working directory, or given yourself\n"
137 "extra access permissions with the \"su\" command?"));
139 #endif /* ENABLE_SUBSHELL */
141 /* --------------------------------------------------------------------------------------------- */
144 do_suspend_cmd (void)
148 if (mc_global
.tty
.console_flag
!= '\0' && !mc_global
.tty
.use_subshell
)
149 handle_console (CONSOLE_RESTORE
);
153 struct sigaction sigtstp_action
;
155 /* Make sure that the SIGTSTP below will suspend us directly,
156 without calling ncurses' SIGTSTP handler; we *don't* want
157 ncurses to redraw the screen immediately after the SIGCONT */
158 sigaction (SIGTSTP
, &startup_handler
, &sigtstp_action
);
160 kill (getpid (), SIGTSTP
);
162 /* Restore previous SIGTSTP action */
163 sigaction (SIGTSTP
, &sigtstp_action
, NULL
);
167 if (mc_global
.tty
.console_flag
!= '\0' && !mc_global
.tty
.use_subshell
)
168 handle_console (CONSOLE_SAVE
);
170 edition_post_exec ();
173 /* --------------------------------------------------------------------------------------------- */
176 execute_prepare_with_vfs_arg (const vfs_path_t
* filename_vpath
, vfs_path_t
** localcopy_vpath
,
181 /* Simplest case, this file is local */
182 if ((filename_vpath
== NULL
&& vfs_file_is_local (vfs_get_raw_current_dir ()))
183 || vfs_file_is_local (filename_vpath
))
186 /* FIXME: Creation of new files on VFS is not supported */
187 if (filename_vpath
== NULL
)
190 *localcopy_vpath
= mc_getlocalcopy (filename_vpath
);
191 if (*localcopy_vpath
== NULL
)
193 message (D_ERROR
, MSG_ERROR
, _("Cannot fetch a local copy of %s"),
194 vfs_path_as_str (filename_vpath
));
198 mc_stat (*localcopy_vpath
, &st
);
199 *mtime
= st
.st_mtime
;
203 /* --------------------------------------------------------------------------------------------- */
206 execute_cleanup_with_vfs_arg (const vfs_path_t
* filename_vpath
, vfs_path_t
** localcopy_vpath
,
209 if (*localcopy_vpath
!= NULL
)
214 * filename can be an entry on panel, it can be changed by executing
215 * the command, so make a copy. Smarter VFS code would make the code
218 mc_stat (*localcopy_vpath
, &st
);
219 mc_ungetlocalcopy (filename_vpath
, *localcopy_vpath
, *mtime
!= st
.st_mtime
);
220 vfs_path_free (*localcopy_vpath
);
221 *localcopy_vpath
= NULL
;
225 /* --------------------------------------------------------------------------------------------- */
228 execute_get_opts_from_cfg (const char *command
, const char *default_str
)
230 char *str_from_config
;
233 mc_config_get_string_raw (mc_main_config
, CONFIG_EXT_EDITOR_VIEWER_SECTION
, command
, NULL
);
235 if (str_from_config
== NULL
)
239 cfg
= mc_config_init (global_profile_name
, TRUE
);
241 return g_strdup (default_str
);
244 mc_config_get_string_raw (cfg
, CONFIG_EXT_EDITOR_VIEWER_SECTION
, command
, default_str
);
246 mc_config_deinit (cfg
);
249 return str_from_config
;
252 /* --------------------------------------------------------------------------------------------- */
253 /*** public functions ****************************************************************************/
254 /* --------------------------------------------------------------------------------------------- */
257 execute_get_external_cmd_opts_from_config (const char *command
, const vfs_path_t
* filename_vpath
,
260 char *str_from_config
, *return_str
;
263 if (filename_vpath
== NULL
)
264 return g_strdup ("");
266 str_from_config
= execute_get_opts_from_cfg (command
, "%filename");
268 parameter
= g_shell_quote (vfs_path_get_last_path_str (filename_vpath
));
269 return_str
= str_replace_all (str_from_config
, "%filename", parameter
);
271 g_free (str_from_config
);
272 str_from_config
= return_str
;
274 parameter
= g_strdup_printf ("%ld", start_line
);
275 return_str
= str_replace_all (str_from_config
, "%lineno", parameter
);
277 g_free (str_from_config
);
282 /* --------------------------------------------------------------------------------------------- */
285 do_executev (const char *shell
, int flags
, char *const argv
[])
287 #ifdef ENABLE_SUBSHELL
288 vfs_path_t
*new_dir_vpath
= NULL
;
289 #endif /* ENABLE_SUBSHELL */
291 vfs_path_t
*old_vfs_dir_vpath
= NULL
;
293 if (!vfs_current_is_local ())
294 old_vfs_dir_vpath
= vfs_path_clone (vfs_get_raw_current_dir ());
296 if (mc_global
.mc_run_mode
== MC_RUN_FULL
)
299 if (mc_global
.tty
.console_flag
!= '\0')
300 handle_console (CONSOLE_RESTORE
);
302 if (!mc_global
.tty
.use_subshell
&& *argv
!= NULL
&& (flags
& EXECUTE_INTERNAL
) == 0)
304 printf ("%s%s\n", mc_prompt
, *argv
);
307 #ifdef ENABLE_SUBSHELL
308 if (mc_global
.tty
.use_subshell
&& (flags
& EXECUTE_INTERNAL
) == 0)
312 /* We don't care if it died, higher level takes care of this */
313 invoke_subshell (*argv
, VISIBLY
, old_vfs_dir_vpath
!= NULL
? NULL
: &new_dir_vpath
);
316 #endif /* ENABLE_SUBSHELL */
317 my_systemv_flags (flags
, shell
, argv
);
319 if ((flags
& EXECUTE_INTERNAL
) == 0)
321 if ((pause_after_run
== pause_always
322 || (pause_after_run
== pause_on_dumb_terminals
&& !mc_global
.tty
.xterm_flag
323 && mc_global
.tty
.console_flag
== '\0')) && quit
== 0
324 #ifdef ENABLE_SUBSHELL
325 && subshell_state
!= RUNNING_COMMAND
326 #endif /* ENABLE_SUBSHELL */
329 printf (_("Press any key to continue..."));
336 if (mc_global
.tty
.console_flag
!= '\0' && output_lines
!= 0 && mc_global
.keybar_visible
)
343 if (mc_global
.tty
.console_flag
!= '\0')
344 handle_console (CONSOLE_SAVE
);
345 edition_post_exec ();
347 #ifdef ENABLE_SUBSHELL
348 if (new_dir_vpath
!= NULL
)
350 do_possible_cd (new_dir_vpath
);
351 vfs_path_free (new_dir_vpath
);
354 #endif /* ENABLE_SUBSHELL */
356 if (old_vfs_dir_vpath
!= NULL
)
358 mc_chdir (old_vfs_dir_vpath
);
359 vfs_path_free (old_vfs_dir_vpath
);
362 if (mc_global
.mc_run_mode
== MC_RUN_FULL
)
364 update_panels (UP_OPTIMIZE
, UP_KEEPSEL
);
365 update_xterm_title_path ();
372 /* --------------------------------------------------------------------------------------------- */
375 do_execute (const char *shell
, const char *command
, int flags
)
377 GPtrArray
*args_array
;
379 args_array
= g_ptr_array_new ();
380 g_ptr_array_add (args_array
, (char *) command
);
381 g_ptr_array_add (args_array
, NULL
);
383 do_executev (shell
, flags
, (char *const *) args_array
->pdata
);
385 g_ptr_array_free (args_array
, TRUE
);
388 /* --------------------------------------------------------------------------------------------- */
390 /** Set up the terminal before executing a program */
399 /* --------------------------------------------------------------------------------------------- */
400 /** Hide the terminal after executing a program */
404 edition_post_exec ();
409 /* --------------------------------------------------------------------------------------------- */
410 /* Executes a command */
413 shell_execute (const char *command
, int flags
)
417 if (flags
& EXECUTE_HIDE
)
419 cmd
= g_strconcat (" ", command
, (char *) NULL
);
420 flags
^= EXECUTE_HIDE
;
423 #ifdef ENABLE_SUBSHELL
424 if (mc_global
.tty
.use_subshell
)
425 if (subshell_state
== INACTIVE
)
426 do_execute (mc_global
.tty
.shell
, cmd
? cmd
: command
, flags
| EXECUTE_AS_SHELL
);
428 message (D_ERROR
, MSG_ERROR
, _("The shell is already running a command"));
430 #endif /* ENABLE_SUBSHELL */
431 do_execute (mc_global
.tty
.shell
, cmd
? cmd
: command
, flags
| EXECUTE_AS_SHELL
);
436 /* --------------------------------------------------------------------------------------------- */
441 do_execute (mc_global
.tty
.shell
, 0, 0);
444 /* --------------------------------------------------------------------------------------------- */
449 #ifdef ENABLE_SUBSHELL
450 vfs_path_t
*new_dir_vpath
= NULL
;
451 vfs_path_t
**new_dir_p
;
452 #endif /* ENABLE_SUBSHELL */
454 SIG_ATOMIC_VOLATILE_T was_sigwinch
= 0;
458 if (clear_before_exec
)
460 if (mc_global
.tty
.alternate_plus_minus
)
461 numeric_keypad_mode ();
463 /* With slang we don't want any of this, since there
464 * is no raw_mode supported
466 tty_reset_shell_mode ();
467 #endif /* !HAVE_SLANG */
473 if (mc_global
.tty
.console_flag
!= '\0')
474 handle_console (CONSOLE_RESTORE
);
476 #ifdef ENABLE_SUBSHELL
477 if (mc_global
.tty
.use_subshell
)
479 new_dir_p
= vfs_current_is_local ()? &new_dir_vpath
: NULL
;
480 invoke_subshell (NULL
, VISIBLY
, new_dir_p
);
483 #endif /* ENABLE_SUBSHELL */
485 if (output_starts_shell
)
487 fprintf (stderr
, _("Type 'exit' to return to the Midnight Commander"));
488 fprintf (stderr
, "\n\r\n\r");
490 my_system (EXECUTE_INTERNAL
, mc_global
.tty
.shell
, NULL
);
496 if (mc_global
.tty
.console_flag
!= '\0')
497 handle_console (CONSOLE_SAVE
);
501 tty_reset_prog_mode ();
504 /* Prevent screen flash when user did 'exit' or 'logout' within
506 if ((quit
& SUBSHELL_EXIT
) != 0)
508 /* User did 'exit' or 'logout': quit MC */
509 if (quiet_quit_cmd ())
513 #ifdef ENABLE_SUBSHELL
514 /* restart subshell */
515 if (mc_global
.tty
.use_subshell
)
517 #endif /* ENABLE_SUBSHELL */
522 if (mc_global
.tty
.alternate_plus_minus
)
523 application_keypad_mode ();
526 * Save sigwinch flag that will be reset in mc_refresh() called via update_panels().
527 * There is some problem with screen redraw in ncurses-based mc in this situation.
529 was_sigwinch
= mc_global
.tty
.winch_flag
;
530 mc_global
.tty
.winch_flag
= 0;
532 #ifdef ENABLE_SUBSHELL
533 if (mc_global
.tty
.use_subshell
)
536 if (new_dir_vpath
!= NULL
)
537 do_possible_cd (new_dir_vpath
);
538 if (mc_global
.tty
.console_flag
!= '\0' && output_lines
)
539 show_console_contents (output_start_y
,
540 LINES
- mc_global
.keybar_visible
- output_lines
-
541 1, LINES
- mc_global
.keybar_visible
- 1);
544 vfs_path_free (new_dir_vpath
);
545 #endif /* ENABLE_SUBSHELL */
547 if (mc_global
.mc_run_mode
== MC_RUN_FULL
)
549 update_panels (UP_OPTIMIZE
, UP_KEEPSEL
);
550 update_xterm_title_path ();
553 if (was_sigwinch
!= 0 || mc_global
.tty
.winch_flag
!= 0)
554 dialog_change_screen_size ();
559 /* --------------------------------------------------------------------------------------------- */
563 execute_suspend (const gchar
* event_group_name
, const gchar
* event_name
,
564 gpointer init_data
, gpointer data
)
566 (void) event_group_name
;
571 if (mc_global
.mc_run_mode
== MC_RUN_FULL
)
574 if (mc_global
.mc_run_mode
== MC_RUN_FULL
)
575 update_panels (UP_OPTIMIZE
, UP_KEEPSEL
);
581 /* --------------------------------------------------------------------------------------------- */
584 * Execute command on a filename that can be on VFS.
585 * Errors are reported to the user.
589 execute_with_vfs_arg (const char *command
, const vfs_path_t
* filename_vpath
)
591 vfs_path_t
*localcopy_vpath
= NULL
;
592 const vfs_path_t
*do_execute_vpath
;
595 if (!execute_prepare_with_vfs_arg (filename_vpath
, &localcopy_vpath
, &mtime
))
598 do_execute_vpath
= (localcopy_vpath
== NULL
) ? filename_vpath
: localcopy_vpath
;
600 do_execute (command
, vfs_path_get_last_path_str (do_execute_vpath
), EXECUTE_INTERNAL
);
602 execute_cleanup_with_vfs_arg (filename_vpath
, &localcopy_vpath
, &mtime
);
605 /* --------------------------------------------------------------------------------------------- */
607 * Execute external editor or viewer.
609 * @param command editor/viewer to run
610 * @param filename_vpath path for edit/view
611 * @param start_line cursor will be placed at the 'start_line' position after opening file
615 execute_external_editor_or_viewer (const char *command
, const vfs_path_t
* filename_vpath
,
618 vfs_path_t
*localcopy_vpath
= NULL
;
619 const vfs_path_t
*do_execute_vpath
;
620 char *extern_cmd_options
;
623 if (!execute_prepare_with_vfs_arg (filename_vpath
, &localcopy_vpath
, &mtime
))
626 do_execute_vpath
= (localcopy_vpath
== NULL
) ? filename_vpath
: localcopy_vpath
;
629 execute_get_external_cmd_opts_from_config (command
, do_execute_vpath
, start_line
);
631 if (extern_cmd_options
!= NULL
)
633 char **argv_cmd_options
;
636 g_shell_parse_argv (extern_cmd_options
, &argv_count
, &argv_cmd_options
, NULL
);
637 g_free (extern_cmd_options
);
639 do_executev (command
, EXECUTE_INTERNAL
, argv_cmd_options
);
641 g_strfreev (argv_cmd_options
);
644 execute_cleanup_with_vfs_arg (filename_vpath
, &localcopy_vpath
, &mtime
);
647 /* --------------------------------------------------------------------------------------------- */