Ticket #3472: documentation for vfs/gc.c.
[midnight-commander.git] / src / execute.c
blobdf2f12d80f4547363d45c17718b8c5cf4fc86df2
1 /*
2 Execution routines for GNU Midnight Commander
4 Copyright (C) 2003-2015
5 Free Software Foundation, Inc.
7 Written by:
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/>.
26 /** \file execute.c
27 * \brief Source: execution routines
30 #include <config.h>
32 #include <signal.h>
33 #include <string.h>
34 #include <sys/stat.h>
35 #include <sys/time.h>
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"
44 #include "lib/util.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.h"
53 #endif
54 #include "setup.h" /* clear_before_exec */
56 #include "execute.h"
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,
74 long start_line);
76 /* --------------------------------------------------------------------------------------------- */
78 static void
79 edition_post_exec (void)
81 do_enter_ca_mode ();
83 /* FIXME: Missing on slang endwin? */
84 tty_reset_prog_mode ();
85 tty_flush_input ();
87 tty_keypad (TRUE);
88 tty_raw_mode ();
89 channels_up ();
90 enable_mouse ();
91 enable_bracketed_paste ();
92 if (mc_global.tty.alternate_plus_minus)
93 application_keypad_mode ();
96 /* --------------------------------------------------------------------------------------------- */
98 static void
99 edition_pre_exec (void)
101 if (clear_before_exec)
102 clr_scr ();
103 else
105 if (!(mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag))
106 printf ("\n\n");
109 channels_down ();
110 disable_mouse ();
111 disable_bracketed_paste ();
113 tty_reset_shell_mode ();
114 tty_keypad (FALSE);
115 tty_reset_screen ();
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.
125 do_exit_ca_mode ();
128 /* --------------------------------------------------------------------------------------------- */
130 #ifdef ENABLE_SUBSHELL
131 static void
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 /* --------------------------------------------------------------------------------------------- */
145 static void
146 do_suspend_cmd (void)
148 pre_exec ();
150 if (mc_global.tty.console_flag != '\0' && !mc_global.tty.use_subshell)
151 handle_console (CONSOLE_RESTORE);
153 #ifdef SIGTSTP
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);
168 #endif /* SIGTSTP */
170 if (mc_global.tty.console_flag != '\0' && !mc_global.tty.use_subshell)
171 handle_console (CONSOLE_SAVE);
173 edition_post_exec ();
176 /* --------------------------------------------------------------------------------------------- */
178 static gboolean
179 execute_prepare_with_vfs_arg (const vfs_path_t * filename_vpath, vfs_path_t ** localcopy_vpath,
180 time_t * mtime)
182 struct stat st;
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))
187 return TRUE;
189 /* FIXME: Creation of new files on VFS is not supported */
190 if (filename_vpath == NULL)
191 return FALSE;
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));
198 return FALSE;
201 mc_stat (*localcopy_vpath, &st);
202 *mtime = st.st_mtime;
203 return TRUE;
206 /* --------------------------------------------------------------------------------------------- */
208 static void
209 execute_cleanup_with_vfs_arg (const vfs_path_t * filename_vpath, vfs_path_t ** localcopy_vpath,
210 time_t * mtime)
212 if (*localcopy_vpath != NULL)
214 struct stat st;
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
219 * below unnecessary.
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 /* --------------------------------------------------------------------------------------------- */
230 static char *
231 execute_get_opts_from_cfg (const char *command, const char *default_str)
233 char *str_from_config;
235 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)
240 mc_config_t *cfg;
242 cfg = mc_config_init (global_profile_name, TRUE);
243 if (cfg == NULL)
244 return g_strdup (default_str);
246 str_from_config =
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 /* --------------------------------------------------------------------------------------------- */
259 char *
260 execute_get_external_cmd_opts_from_config (const char *command, const vfs_path_t * filename_vpath,
261 long start_line)
263 char *str_from_config, *return_str;
264 char *parameter;
266 if (filename_vpath == NULL)
267 return g_strdup ("");
269 parameter = g_shell_quote (vfs_path_get_last_path_str (filename_vpath));
271 if (start_line <= 0)
272 return parameter;
274 str_from_config = execute_get_opts_from_cfg (command, "%filename");
276 return_str = str_replace_all (str_from_config, "%filename", parameter);
277 g_free (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);
283 g_free (parameter);
284 g_free (str_from_config);
286 return return_str;
289 /* --------------------------------------------------------------------------------------------- */
291 void
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)
304 save_cwds_stat ();
305 pre_exec ();
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);
312 fflush (stdout);
314 #ifdef ENABLE_SUBSHELL
315 if (mc_global.tty.use_subshell && (flags & EXECUTE_INTERNAL) == 0)
317 do_update_prompt ();
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);
322 else
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..."));
337 fflush (stdout);
338 tty_raw_mode ();
339 get_key_code (0);
340 printf ("\r\n");
341 fflush (stdout);
343 if (mc_global.tty.console_flag != '\0' && output_lines != 0 && mc_global.keybar_visible)
345 putchar ('\n');
346 fflush (stdout);
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 ();
375 do_refresh ();
376 use_dash (TRUE);
379 /* --------------------------------------------------------------------------------------------- */
381 void
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 */
399 void
400 pre_exec (void)
402 use_dash (FALSE);
403 edition_pre_exec ();
406 /* --------------------------------------------------------------------------------------------- */
407 /** Hide the terminal after executing a program */
408 void
409 post_exec (void)
411 edition_post_exec ();
412 use_dash (TRUE);
413 repaint_screen ();
416 /* --------------------------------------------------------------------------------------------- */
417 /* Executes a command */
419 void
420 shell_execute (const char *command, int flags)
422 char *cmd = NULL;
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.tty.shell, cmd ? cmd : command, flags | EXECUTE_AS_SHELL);
434 else
435 message (D_ERROR, MSG_ERROR, _("The shell is already running a command"));
436 else
437 #endif /* ENABLE_SUBSHELL */
438 do_execute (mc_global.tty.shell, cmd ? cmd : command, flags | EXECUTE_AS_SHELL);
440 g_free (cmd);
443 /* --------------------------------------------------------------------------------------------- */
445 void
446 exec_shell (void)
448 do_execute (mc_global.tty.shell, 0, 0);
451 /* --------------------------------------------------------------------------------------------- */
453 void
454 toggle_panels (void)
456 #ifdef ENABLE_SUBSHELL
457 vfs_path_t *new_dir_vpath = NULL;
458 #endif /* ENABLE_SUBSHELL */
460 SIG_ATOMIC_VOLATILE_T was_sigwinch = 0;
462 channels_down ();
463 disable_mouse ();
464 disable_bracketed_paste ();
465 if (clear_before_exec)
466 clr_scr ();
467 if (mc_global.tty.alternate_plus_minus)
468 numeric_keypad_mode ();
469 #ifndef HAVE_SLANG
470 /* With slang we don't want any of this, since there
471 * is no raw_mode supported
473 tty_reset_shell_mode ();
474 #endif /* !HAVE_SLANG */
475 tty_noecho ();
476 tty_keypad (FALSE);
477 tty_reset_screen ();
478 do_exit_ca_mode ();
479 tty_raw_mode ();
480 if (mc_global.tty.console_flag != '\0')
481 handle_console (CONSOLE_RESTORE);
483 #ifdef ENABLE_SUBSHELL
484 if (mc_global.tty.use_subshell)
486 vfs_path_t **new_dir_p;
488 new_dir_p = vfs_current_is_local ()? &new_dir_vpath : NULL;
489 invoke_subshell (NULL, VISIBLY, new_dir_p);
491 else
492 #endif /* ENABLE_SUBSHELL */
494 if (output_starts_shell)
496 fprintf (stderr, _("Type 'exit' to return to the Midnight Commander"));
497 fprintf (stderr, "\n\r\n\r");
499 my_system (EXECUTE_INTERNAL, mc_global.tty.shell, NULL);
501 else
502 get_key_code (0);
505 if (mc_global.tty.console_flag != '\0')
506 handle_console (CONSOLE_SAVE);
508 do_enter_ca_mode ();
510 tty_reset_prog_mode ();
511 tty_keypad (TRUE);
513 /* Prevent screen flash when user did 'exit' or 'logout' within
514 subshell */
515 if ((quit & SUBSHELL_EXIT) != 0)
517 /* User did 'exit' or 'logout': quit MC */
518 if (quiet_quit_cmd ())
519 return;
521 quit = 0;
522 #ifdef ENABLE_SUBSHELL
523 /* restart subshell */
524 if (mc_global.tty.use_subshell)
525 init_subshell ();
526 #endif /* ENABLE_SUBSHELL */
529 enable_mouse ();
530 enable_bracketed_paste ();
531 channels_up ();
532 if (mc_global.tty.alternate_plus_minus)
533 application_keypad_mode ();
535 /* HACK:
536 * Save sigwinch flag that will be reset in mc_refresh() called via update_panels().
537 * There is some problem with screen redraw in ncurses-based mc in this situation.
539 was_sigwinch = mc_global.tty.winch_flag;
540 mc_global.tty.winch_flag = 0;
542 #ifdef ENABLE_SUBSHELL
543 if (mc_global.tty.use_subshell)
545 do_load_prompt ();
546 if (new_dir_vpath != NULL)
547 do_possible_cd (new_dir_vpath);
548 if (mc_global.tty.console_flag != '\0' && output_lines)
549 show_console_contents (output_start_y,
550 LINES - mc_global.keybar_visible - output_lines -
551 1, LINES - mc_global.keybar_visible - 1);
554 vfs_path_free (new_dir_vpath);
555 #endif /* ENABLE_SUBSHELL */
557 if (mc_global.mc_run_mode == MC_RUN_FULL)
559 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
560 update_xterm_title_path ();
563 if (was_sigwinch != 0 || mc_global.tty.winch_flag != 0)
564 dialog_change_screen_size ();
565 else
566 repaint_screen ();
569 /* --------------------------------------------------------------------------------------------- */
571 /* event callback */
572 gboolean
573 execute_suspend (const gchar * event_group_name, const gchar * event_name,
574 gpointer init_data, gpointer data)
576 (void) event_group_name;
577 (void) event_name;
578 (void) init_data;
579 (void) data;
581 if (mc_global.mc_run_mode == MC_RUN_FULL)
582 save_cwds_stat ();
583 do_suspend_cmd ();
584 if (mc_global.mc_run_mode == MC_RUN_FULL)
585 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
586 do_refresh ();
588 return TRUE;
591 /* --------------------------------------------------------------------------------------------- */
594 * Execute command on a filename that can be on VFS.
595 * Errors are reported to the user.
598 void
599 execute_with_vfs_arg (const char *command, const vfs_path_t * filename_vpath)
601 vfs_path_t *localcopy_vpath = NULL;
602 const vfs_path_t *do_execute_vpath;
603 time_t mtime;
605 if (!execute_prepare_with_vfs_arg (filename_vpath, &localcopy_vpath, &mtime))
606 return;
608 do_execute_vpath = (localcopy_vpath == NULL) ? filename_vpath : localcopy_vpath;
610 do_execute (command, vfs_path_get_last_path_str (do_execute_vpath), EXECUTE_INTERNAL);
612 execute_cleanup_with_vfs_arg (filename_vpath, &localcopy_vpath, &mtime);
615 /* --------------------------------------------------------------------------------------------- */
617 * Execute external editor or viewer.
619 * @param command editor/viewer to run
620 * @param filename_vpath path for edit/view
621 * @param start_line cursor will be placed at the 'start_line' position after opening file
622 * if start_line is 0 or negative, no start line will be passed to editor/viewer
625 void
626 execute_external_editor_or_viewer (const char *command, const vfs_path_t * filename_vpath,
627 long start_line)
629 vfs_path_t *localcopy_vpath = NULL;
630 const vfs_path_t *do_execute_vpath;
631 char *extern_cmd_options;
632 time_t mtime;
634 if (!execute_prepare_with_vfs_arg (filename_vpath, &localcopy_vpath, &mtime))
635 return;
637 do_execute_vpath = (localcopy_vpath == NULL) ? filename_vpath : localcopy_vpath;
639 extern_cmd_options =
640 execute_get_external_cmd_opts_from_config (command, do_execute_vpath, start_line);
642 if (extern_cmd_options != NULL)
644 char **argv_cmd_options;
645 int argv_count;
647 if (g_shell_parse_argv (extern_cmd_options, &argv_count, &argv_cmd_options, NULL))
649 do_executev (command, EXECUTE_INTERNAL, argv_cmd_options);
650 g_strfreev (argv_cmd_options);
652 else
653 do_executev (command, EXECUTE_INTERNAL, NULL);
655 g_free (extern_cmd_options);
659 execute_cleanup_with_vfs_arg (filename_vpath, &localcopy_vpath, &mtime);
662 /* --------------------------------------------------------------------------------------------- */