More use link_isdir().
[midnight-commander.git] / src / execute.c
blob1cf0075704ccc30ef31d9d1a3be4398117574fd2
1 /*
2 Execution routines for GNU Midnight Commander
4 Copyright (C) 2003-2016
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/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 tty_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 tty_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_global.main_config, CONFIG_EXT_EDITOR_VIEWER_SECTION, command,
237 NULL);
239 if (str_from_config == NULL)
241 mc_config_t *cfg;
243 cfg = mc_config_init (global_profile_name, TRUE);
244 if (cfg == NULL)
245 return g_strdup (default_str);
247 str_from_config =
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 /* --------------------------------------------------------------------------------------------- */
260 char *
261 execute_get_external_cmd_opts_from_config (const char *command, const vfs_path_t * filename_vpath,
262 long start_line)
264 char *str_from_config, *return_str;
265 char *parameter;
267 if (filename_vpath == NULL)
268 return g_strdup ("");
270 parameter = g_shell_quote (vfs_path_get_last_path_str (filename_vpath));
272 if (start_line <= 0)
273 return parameter;
275 str_from_config = execute_get_opts_from_cfg (command, "%filename");
277 return_str = str_replace_all (str_from_config, "%filename", parameter);
278 g_free (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);
284 g_free (parameter);
285 g_free (str_from_config);
287 return return_str;
290 /* --------------------------------------------------------------------------------------------- */
292 void
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)
305 save_cwds_stat ();
306 pre_exec ();
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);
313 fflush (stdout);
315 #ifdef ENABLE_SUBSHELL
316 if (mc_global.tty.use_subshell && (flags & EXECUTE_INTERNAL) == 0)
318 do_update_prompt ();
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);
323 else
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 (_("Press any key to continue..."));
338 fflush (stdout);
339 tty_raw_mode ();
340 get_key_code (0);
341 printf ("\r\n");
342 fflush (stdout);
344 if (mc_global.tty.console_flag != '\0' && output_lines != 0 && mc_global.keybar_visible)
346 putchar ('\n');
347 fflush (stdout);
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 ();
376 do_refresh ();
377 use_dash (TRUE);
380 /* --------------------------------------------------------------------------------------------- */
382 void
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 */
400 void
401 pre_exec (void)
403 use_dash (FALSE);
404 edition_pre_exec ();
407 /* --------------------------------------------------------------------------------------------- */
408 /** Hide the terminal after executing a program */
409 void
410 post_exec (void)
412 edition_post_exec ();
413 use_dash (TRUE);
414 repaint_screen ();
417 /* --------------------------------------------------------------------------------------------- */
418 /* Executes a command */
420 void
421 shell_execute (const char *command, int flags)
423 char *cmd = NULL;
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)
433 if (subshell_state == INACTIVE)
434 do_execute (mc_global.shell->path, cmd ? cmd : command, flags | EXECUTE_AS_SHELL);
435 else
436 message (D_ERROR, MSG_ERROR, _("The shell is already running a command"));
437 else
438 #endif /* ENABLE_SUBSHELL */
439 do_execute (mc_global.shell->path, cmd ? cmd : command, flags | EXECUTE_AS_SHELL);
441 g_free (cmd);
444 /* --------------------------------------------------------------------------------------------- */
446 void
447 toggle_panels (void)
449 #ifdef ENABLE_SUBSHELL
450 vfs_path_t *new_dir_vpath = NULL;
451 #endif /* ENABLE_SUBSHELL */
453 SIG_ATOMIC_VOLATILE_T was_sigwinch = 0;
455 channels_down ();
456 disable_mouse ();
457 disable_bracketed_paste ();
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 tty_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 vfs_path_t **new_dir_p;
481 new_dir_p = vfs_current_is_local ()? &new_dir_vpath : NULL;
482 invoke_subshell (NULL, VISIBLY, new_dir_p);
484 else
485 #endif /* ENABLE_SUBSHELL */
487 if (output_starts_shell)
489 fprintf (stderr, _("Type 'exit' to return to the Midnight Commander"));
490 fprintf (stderr, "\n\r\n\r");
492 my_system (EXECUTE_INTERNAL, mc_global.shell->path, NULL);
494 else
495 get_key_code (0);
498 if (mc_global.tty.console_flag != '\0')
499 handle_console (CONSOLE_SAVE);
501 tty_enter_ca_mode ();
503 tty_reset_prog_mode ();
504 tty_keypad (TRUE);
506 /* Prevent screen flash when user did 'exit' or 'logout' within
507 subshell */
508 if ((quit & SUBSHELL_EXIT) != 0)
510 /* User did 'exit' or 'logout': quit MC */
511 if (quiet_quit_cmd ())
512 return;
514 quit = 0;
515 #ifdef ENABLE_SUBSHELL
516 /* restart subshell */
517 if (mc_global.tty.use_subshell)
518 init_subshell ();
519 #endif /* ENABLE_SUBSHELL */
522 enable_mouse ();
523 enable_bracketed_paste ();
524 channels_up ();
525 if (mc_global.tty.alternate_plus_minus)
526 application_keypad_mode ();
528 /* HACK:
529 * Save sigwinch flag that will be reset in mc_refresh() called via update_panels().
530 * There is some problem with screen redraw in ncurses-based mc in this situation.
532 was_sigwinch = mc_global.tty.winch_flag;
533 mc_global.tty.winch_flag = 0;
535 #ifdef ENABLE_SUBSHELL
536 if (mc_global.tty.use_subshell)
538 do_load_prompt ();
539 if (new_dir_vpath != NULL)
540 do_possible_cd (new_dir_vpath);
541 if (mc_global.tty.console_flag != '\0' && output_lines)
542 show_console_contents (output_start_y,
543 LINES - mc_global.keybar_visible - output_lines -
544 1, LINES - mc_global.keybar_visible - 1);
547 vfs_path_free (new_dir_vpath);
548 #endif /* ENABLE_SUBSHELL */
550 if (mc_global.mc_run_mode == MC_RUN_FULL)
552 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
553 update_xterm_title_path ();
556 if (was_sigwinch != 0 || mc_global.tty.winch_flag != 0)
557 dialog_change_screen_size ();
558 else
559 repaint_screen ();
562 /* --------------------------------------------------------------------------------------------- */
564 /* event callback */
565 gboolean
566 execute_suspend (const gchar * event_group_name, const gchar * event_name,
567 gpointer init_data, gpointer data)
569 (void) event_group_name;
570 (void) event_name;
571 (void) init_data;
572 (void) data;
574 if (mc_global.mc_run_mode == MC_RUN_FULL)
575 save_cwds_stat ();
576 do_suspend_cmd ();
577 if (mc_global.mc_run_mode == MC_RUN_FULL)
578 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
579 do_refresh ();
581 return TRUE;
584 /* --------------------------------------------------------------------------------------------- */
587 * Execute command on a filename that can be on VFS.
588 * Errors are reported to the user.
591 void
592 execute_with_vfs_arg (const char *command, const vfs_path_t * filename_vpath)
594 vfs_path_t *localcopy_vpath = NULL;
595 const vfs_path_t *do_execute_vpath;
596 time_t mtime;
598 if (!execute_prepare_with_vfs_arg (filename_vpath, &localcopy_vpath, &mtime))
599 return;
601 do_execute_vpath = (localcopy_vpath == NULL) ? filename_vpath : localcopy_vpath;
603 do_execute (command, vfs_path_get_last_path_str (do_execute_vpath), EXECUTE_INTERNAL);
605 execute_cleanup_with_vfs_arg (filename_vpath, &localcopy_vpath, &mtime);
608 /* --------------------------------------------------------------------------------------------- */
610 * Execute external editor or viewer.
612 * @param command editor/viewer to run
613 * @param filename_vpath path for edit/view
614 * @param start_line cursor will be placed at the 'start_line' position after opening file
615 * if start_line is 0 or negative, no start line will be passed to editor/viewer
618 void
619 execute_external_editor_or_viewer (const char *command, const vfs_path_t * filename_vpath,
620 long start_line)
622 vfs_path_t *localcopy_vpath = NULL;
623 const vfs_path_t *do_execute_vpath;
624 char *extern_cmd_options;
625 time_t mtime;
627 if (!execute_prepare_with_vfs_arg (filename_vpath, &localcopy_vpath, &mtime))
628 return;
630 do_execute_vpath = (localcopy_vpath == NULL) ? filename_vpath : localcopy_vpath;
632 extern_cmd_options =
633 execute_get_external_cmd_opts_from_config (command, do_execute_vpath, start_line);
635 if (extern_cmd_options != NULL)
637 char **argv_cmd_options;
638 int argv_count;
640 if (g_shell_parse_argv (extern_cmd_options, &argv_count, &argv_cmd_options, NULL))
642 do_executev (command, EXECUTE_INTERNAL, argv_cmd_options);
643 g_strfreev (argv_cmd_options);
645 else
646 do_executev (command, EXECUTE_INTERNAL, NULL);
648 g_free (extern_cmd_options);
652 execute_cleanup_with_vfs_arg (filename_vpath, &localcopy_vpath, &mtime);
655 /* --------------------------------------------------------------------------------------------- */