Merge branch '3016_extfs_segfault'
[midnight-commander.git] / src / execute.c
blob79bbeadaef10b8bc27b9a8416b444e772ef97e57
1 /*
2 Execution routines for GNU Midnight Commander
4 Copyright (C) 2003, 2004, 2005, 2007, 2011, 2013
5 The 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 if (mc_global.tty.alternate_plus_minus)
92 application_keypad_mode ();
95 /* --------------------------------------------------------------------------------------------- */
97 static void
98 edition_pre_exec (void)
100 if (clear_before_exec)
101 clr_scr ();
102 else
104 if (!(mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag))
105 printf ("\n\n");
108 channels_down ();
109 disable_mouse ();
111 tty_reset_shell_mode ();
112 tty_keypad (FALSE);
113 tty_reset_screen ();
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.
123 do_exit_ca_mode ();
126 /* --------------------------------------------------------------------------------------------- */
128 #ifdef ENABLE_SUBSHELL
129 static void
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 /* --------------------------------------------------------------------------------------------- */
143 static void
144 do_suspend_cmd (void)
146 pre_exec ();
148 if (mc_global.tty.console_flag != '\0' && !mc_global.tty.use_subshell)
149 handle_console (CONSOLE_RESTORE);
151 #ifdef SIGTSTP
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);
165 #endif /* SIGTSTP */
167 if (mc_global.tty.console_flag != '\0' && !mc_global.tty.use_subshell)
168 handle_console (CONSOLE_SAVE);
170 edition_post_exec ();
173 /* --------------------------------------------------------------------------------------------- */
175 static gboolean
176 execute_prepare_with_vfs_arg (const vfs_path_t * filename_vpath, vfs_path_t ** localcopy_vpath,
177 time_t * mtime)
179 struct stat st;
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))
184 return TRUE;
186 /* FIXME: Creation of new files on VFS is not supported */
187 if (filename_vpath == NULL)
188 return FALSE;
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));
195 return FALSE;
198 mc_stat (*localcopy_vpath, &st);
199 *mtime = st.st_mtime;
200 return TRUE;
203 /* --------------------------------------------------------------------------------------------- */
205 static void
206 execute_cleanup_with_vfs_arg (const vfs_path_t * filename_vpath, vfs_path_t ** localcopy_vpath,
207 time_t * mtime)
209 if (*localcopy_vpath != NULL)
211 struct stat st;
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
216 * below unnecessary.
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 /* --------------------------------------------------------------------------------------------- */
227 static char *
228 execute_get_opts_from_cfg (const char *command, const char *default_str)
230 char *str_from_config;
232 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)
237 mc_config_t *cfg;
239 cfg = mc_config_init (global_profile_name, TRUE);
240 if (cfg == NULL)
241 return g_strdup (default_str);
243 str_from_config =
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 /* --------------------------------------------------------------------------------------------- */
256 char *
257 execute_get_external_cmd_opts_from_config (const char *command, const vfs_path_t * filename_vpath,
258 long start_line)
260 char *str_from_config, *return_str;
261 char *parameter;
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);
270 g_free (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);
276 g_free (parameter);
277 g_free (str_from_config);
279 return return_str;
282 /* --------------------------------------------------------------------------------------------- */
284 void
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)
297 save_cwds_stat ();
298 pre_exec ();
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);
305 fflush (stdout);
307 #ifdef ENABLE_SUBSHELL
308 if (mc_global.tty.use_subshell && (flags & EXECUTE_INTERNAL) == 0)
310 do_update_prompt ();
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);
315 else
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..."));
330 fflush (stdout);
331 tty_raw_mode ();
332 get_key_code (0);
333 printf ("\r\n");
334 fflush (stdout);
336 if (mc_global.tty.console_flag != '\0' && output_lines != 0 && mc_global.keybar_visible)
338 putchar ('\n');
339 fflush (stdout);
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 ();
368 do_refresh ();
369 use_dash (TRUE);
372 /* --------------------------------------------------------------------------------------------- */
374 void
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 */
392 void
393 pre_exec (void)
395 use_dash (FALSE);
396 edition_pre_exec ();
399 /* --------------------------------------------------------------------------------------------- */
400 /** Hide the terminal after executing a program */
401 void
402 post_exec (void)
404 edition_post_exec ();
405 use_dash (TRUE);
406 repaint_screen ();
409 /* --------------------------------------------------------------------------------------------- */
410 /* Executes a command */
412 void
413 shell_execute (const char *command, int flags)
415 char *cmd = NULL;
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);
427 else
428 message (D_ERROR, MSG_ERROR, _("The shell is already running a command"));
429 else
430 #endif /* ENABLE_SUBSHELL */
431 do_execute (mc_global.tty.shell, cmd ? cmd : command, flags | EXECUTE_AS_SHELL);
433 g_free (cmd);
436 /* --------------------------------------------------------------------------------------------- */
438 void
439 exec_shell (void)
441 do_execute (mc_global.tty.shell, 0, 0);
444 /* --------------------------------------------------------------------------------------------- */
446 void
447 toggle_panels (void)
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;
456 channels_down ();
457 disable_mouse ();
458 if (clear_before_exec)
459 clr_scr ();
460 if (mc_global.tty.alternate_plus_minus)
461 numeric_keypad_mode ();
462 #ifndef HAVE_SLANG
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 */
468 tty_noecho ();
469 tty_keypad (FALSE);
470 tty_reset_screen ();
471 do_exit_ca_mode ();
472 tty_raw_mode ();
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);
482 else
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);
492 else
493 get_key_code (0);
496 if (mc_global.tty.console_flag != '\0')
497 handle_console (CONSOLE_SAVE);
499 do_enter_ca_mode ();
501 tty_reset_prog_mode ();
502 tty_keypad (TRUE);
504 /* Prevent screen flash when user did 'exit' or 'logout' within
505 subshell */
506 if ((quit & SUBSHELL_EXIT) != 0)
508 /* User did 'exit' or 'logout': quit MC */
509 if (quiet_quit_cmd ())
510 return;
512 quit = 0;
513 #ifdef ENABLE_SUBSHELL
514 /* restart subshell */
515 if (mc_global.tty.use_subshell)
516 init_subshell ();
517 #endif /* ENABLE_SUBSHELL */
520 enable_mouse ();
521 channels_up ();
522 if (mc_global.tty.alternate_plus_minus)
523 application_keypad_mode ();
525 /* HACK:
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)
535 do_load_prompt ();
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 ();
555 else
556 repaint_screen ();
559 /* --------------------------------------------------------------------------------------------- */
561 /* event callback */
562 gboolean
563 execute_suspend (const gchar * event_group_name, const gchar * event_name,
564 gpointer init_data, gpointer data)
566 (void) event_group_name;
567 (void) event_name;
568 (void) init_data;
569 (void) data;
571 if (mc_global.mc_run_mode == MC_RUN_FULL)
572 save_cwds_stat ();
573 do_suspend_cmd ();
574 if (mc_global.mc_run_mode == MC_RUN_FULL)
575 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
576 do_refresh ();
578 return TRUE;
581 /* --------------------------------------------------------------------------------------------- */
584 * Execute command on a filename that can be on VFS.
585 * Errors are reported to the user.
588 void
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;
593 time_t mtime;
595 if (!execute_prepare_with_vfs_arg (filename_vpath, &localcopy_vpath, &mtime))
596 return;
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
614 void
615 execute_external_editor_or_viewer (const char *command, const vfs_path_t * filename_vpath,
616 long start_line)
618 vfs_path_t *localcopy_vpath = NULL;
619 const vfs_path_t *do_execute_vpath;
620 char *extern_cmd_options;
621 time_t mtime;
623 if (!execute_prepare_with_vfs_arg (filename_vpath, &localcopy_vpath, &mtime))
624 return;
626 do_execute_vpath = (localcopy_vpath == NULL) ? filename_vpath : localcopy_vpath;
628 extern_cmd_options =
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;
634 int argv_count;
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 /* --------------------------------------------------------------------------------------------- */