Add tests for src/execute.c:execute_with_vfs_arg() function.
[midnight-commander.git] / src / execute.c
blob3b2fd36d05ced592061a1976e7635ed530e0e86f
1 /*
2 Execution routines for GNU Midnight Commander
4 Copyright (C) 2003, 2004, 2005, 2007, 2011
5 The Free Software Foundation, Inc.
7 This file is part of the Midnight Commander.
9 The Midnight Commander is free software: you can redistribute it
10 and/or modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation, either version 3 of the License,
12 or (at your option) any later version.
14 The Midnight Commander is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 /** \file execute.c
24 * \brief Source: execution routines
27 #include <config.h>
29 #include <signal.h>
30 #include <sys/stat.h>
31 #include <sys/time.h>
33 #include "lib/global.h"
35 #include "lib/tty/tty.h"
36 #include "lib/tty/key.h"
37 #include "lib/tty/win.h"
38 #include "lib/vfs/vfs.h"
39 #include "lib/mcconfig.h"
40 #include "lib/util.h"
41 #include "lib/widget.h"
43 #include "filemanager/midnight.h"
44 #include "filemanager/layout.h" /* use_dash() */
45 #include "consaver/cons.saver.h"
46 #ifdef ENABLE_SUBSHELL
47 #include "subshell.h"
48 #endif
49 #include "setup.h" /* clear_before_exec */
51 #include "execute.h"
53 /*** global variables ****************************************************************************/
55 int pause_after_run = pause_on_dumb_terminals;
57 /*** file scope macro definitions ****************************************************************/
59 /*** file scope type declarations ****************************************************************/
61 /*** file scope variables ************************************************************************/
63 /*** file scope functions ************************************************************************/
65 void do_execute (const char *shell, const char *command, int flags);
67 /* --------------------------------------------------------------------------------------------- */
69 static void
70 edition_post_exec (void)
72 do_enter_ca_mode ();
74 /* FIXME: Missing on slang endwin? */
75 tty_reset_prog_mode ();
76 tty_flush_input ();
78 tty_keypad (TRUE);
79 tty_raw_mode ();
80 channels_up ();
81 enable_mouse ();
82 if (mc_global.tty.alternate_plus_minus)
83 application_keypad_mode ();
86 /* --------------------------------------------------------------------------------------------- */
88 static void
89 edition_pre_exec (void)
91 if (clear_before_exec)
92 clr_scr ();
93 else
95 if (!(mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag))
96 printf ("\n\n");
99 channels_down ();
100 disable_mouse ();
102 tty_reset_shell_mode ();
103 tty_keypad (FALSE);
104 tty_reset_screen ();
106 numeric_keypad_mode ();
108 /* on xterms: maybe endwin did not leave the terminal on the shell
109 * screen page: do it now.
111 * Do not move this before endwin: in some systems rmcup includes
112 * a call to clear screen, so it will end up clearing the shell screen.
114 do_exit_ca_mode ();
117 /* --------------------------------------------------------------------------------------------- */
119 #ifdef ENABLE_SUBSHELL
120 static void
121 do_possible_cd (const vfs_path_t * new_dir_vpath)
123 if (!do_cd (new_dir_vpath, cd_exact))
124 message (D_ERROR, _("Warning"),
125 _("The Commander can't change to the directory that\n"
126 "the subshell claims you are in. Perhaps you have\n"
127 "deleted your working directory, or given yourself\n"
128 "extra access permissions with the \"su\" command?"));
130 #endif /* ENABLE_SUBSHELL */
132 /* --------------------------------------------------------------------------------------------- */
134 static void
135 do_suspend_cmd (void)
137 pre_exec ();
139 if (mc_global.tty.console_flag != '\0' && !mc_global.tty.use_subshell)
140 handle_console (CONSOLE_RESTORE);
142 #ifdef SIGTSTP
144 struct sigaction sigtstp_action;
146 /* Make sure that the SIGTSTP below will suspend us directly,
147 without calling ncurses' SIGTSTP handler; we *don't* want
148 ncurses to redraw the screen immediately after the SIGCONT */
149 sigaction (SIGTSTP, &startup_handler, &sigtstp_action);
151 kill (getpid (), SIGTSTP);
153 /* Restore previous SIGTSTP action */
154 sigaction (SIGTSTP, &sigtstp_action, NULL);
156 #endif /* SIGTSTP */
158 if (mc_global.tty.console_flag != '\0' && !mc_global.tty.use_subshell)
159 handle_console (CONSOLE_SAVE);
161 edition_post_exec ();
164 /* --------------------------------------------------------------------------------------------- */
165 /*** public functions ****************************************************************************/
166 /* --------------------------------------------------------------------------------------------- */
168 void
169 do_execute (const char *shell, const char *command, int flags)
171 #ifdef ENABLE_SUBSHELL
172 vfs_path_t *new_dir_vpath = NULL;
173 #endif /* ENABLE_SUBSHELL */
175 vfs_path_t *old_vfs_dir_vpath = NULL;
177 if (!vfs_current_is_local ())
178 old_vfs_dir_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
180 if (mc_global.mc_run_mode == MC_RUN_FULL)
181 save_cwds_stat ();
182 pre_exec ();
183 if (mc_global.tty.console_flag != '\0')
184 handle_console (CONSOLE_RESTORE);
186 if (!mc_global.tty.use_subshell && command && !(flags & EXECUTE_INTERNAL))
188 printf ("%s%s\n", mc_prompt, command);
189 fflush (stdout);
191 #ifdef ENABLE_SUBSHELL
192 if (mc_global.tty.use_subshell && !(flags & EXECUTE_INTERNAL))
194 do_update_prompt ();
196 /* We don't care if it died, higher level takes care of this */
197 invoke_subshell (command, VISIBLY, old_vfs_dir_vpath != NULL ? NULL : &new_dir_vpath);
199 else
200 #endif /* ENABLE_SUBSHELL */
201 my_system (flags, shell, command);
203 if (!(flags & EXECUTE_INTERNAL))
205 if ((pause_after_run == pause_always
206 || (pause_after_run == pause_on_dumb_terminals && !mc_global.tty.xterm_flag
207 && mc_global.tty.console_flag == '\0')) && quit == 0
208 #ifdef ENABLE_SUBSHELL
209 && subshell_state != RUNNING_COMMAND
210 #endif /* ENABLE_SUBSHELL */
213 printf (_("Press any key to continue..."));
214 fflush (stdout);
215 tty_raw_mode ();
216 get_key_code (0);
217 printf ("\r\n");
218 fflush (stdout);
220 if (mc_global.tty.console_flag != '\0')
222 if (output_lines && mc_global.keybar_visible)
224 putchar ('\n');
225 fflush (stdout);
230 if (mc_global.tty.console_flag != '\0')
231 handle_console (CONSOLE_SAVE);
232 edition_post_exec ();
234 #ifdef ENABLE_SUBSHELL
235 if (new_dir_vpath != NULL)
237 do_possible_cd (new_dir_vpath);
238 vfs_path_free (new_dir_vpath);
241 #endif /* ENABLE_SUBSHELL */
243 if (old_vfs_dir_vpath != NULL)
245 mc_chdir (old_vfs_dir_vpath);
246 vfs_path_free (old_vfs_dir_vpath);
249 if (mc_global.mc_run_mode == MC_RUN_FULL)
251 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
252 update_xterm_title_path ();
255 do_refresh ();
256 use_dash (TRUE);
259 /* --------------------------------------------------------------------------------------------- */
261 /** Set up the terminal before executing a program */
263 void
264 pre_exec (void)
266 use_dash (FALSE);
267 edition_pre_exec ();
270 /* --------------------------------------------------------------------------------------------- */
271 /** Hide the terminal after executing a program */
272 void
273 post_exec (void)
275 edition_post_exec ();
276 use_dash (TRUE);
277 repaint_screen ();
280 /* --------------------------------------------------------------------------------------------- */
281 /* Executes a command */
283 void
284 shell_execute (const char *command, int flags)
286 char *cmd = NULL;
288 if (flags & EXECUTE_HIDE)
290 cmd = g_strconcat (" ", command, (char *) NULL);
291 flags ^= EXECUTE_HIDE;
294 #ifdef ENABLE_SUBSHELL
295 if (mc_global.tty.use_subshell)
296 if (subshell_state == INACTIVE)
297 do_execute (mc_global.tty.shell, cmd ? cmd : command, flags | EXECUTE_AS_SHELL);
298 else
299 message (D_ERROR, MSG_ERROR, _("The shell is already running a command"));
300 else
301 #endif /* ENABLE_SUBSHELL */
302 do_execute (mc_global.tty.shell, cmd ? cmd : command, flags | EXECUTE_AS_SHELL);
304 g_free (cmd);
307 /* --------------------------------------------------------------------------------------------- */
309 void
310 exec_shell (void)
312 do_execute (mc_global.tty.shell, 0, 0);
315 /* --------------------------------------------------------------------------------------------- */
317 void
318 toggle_panels (void)
320 #ifdef ENABLE_SUBSHELL
321 vfs_path_t *new_dir_vpath = NULL;
322 vfs_path_t **new_dir_p;
323 #endif /* ENABLE_SUBSHELL */
325 SIG_ATOMIC_VOLATILE_T was_sigwinch = 0;
327 channels_down ();
328 disable_mouse ();
329 if (clear_before_exec)
330 clr_scr ();
331 if (mc_global.tty.alternate_plus_minus)
332 numeric_keypad_mode ();
333 #ifndef HAVE_SLANG
334 /* With slang we don't want any of this, since there
335 * is no raw_mode supported
337 tty_reset_shell_mode ();
338 #endif /* !HAVE_SLANG */
339 tty_noecho ();
340 tty_keypad (FALSE);
341 tty_reset_screen ();
342 do_exit_ca_mode ();
343 tty_raw_mode ();
344 if (mc_global.tty.console_flag != '\0')
345 handle_console (CONSOLE_RESTORE);
347 #ifdef ENABLE_SUBSHELL
348 if (mc_global.tty.use_subshell)
350 new_dir_p = vfs_current_is_local ()? &new_dir_vpath : NULL;
351 invoke_subshell (NULL, VISIBLY, new_dir_p);
353 else
354 #endif /* ENABLE_SUBSHELL */
356 if (output_starts_shell)
358 fprintf (stderr, _("Type `exit' to return to the Midnight Commander"));
359 fprintf (stderr, "\n\r\n\r");
361 my_system (EXECUTE_INTERNAL, mc_global.tty.shell, NULL);
363 else
364 get_key_code (0);
367 if (mc_global.tty.console_flag != '\0')
368 handle_console (CONSOLE_SAVE);
370 do_enter_ca_mode ();
372 tty_reset_prog_mode ();
373 tty_keypad (TRUE);
375 /* Prevent screen flash when user did 'exit' or 'logout' within
376 subshell */
377 if ((quit & SUBSHELL_EXIT) != 0)
379 /* User did `exit' or `logout': quit MC */
380 if (quiet_quit_cmd ())
381 return;
383 quit = 0;
384 #ifdef ENABLE_SUBSHELL
385 /* restart subshell */
386 if (mc_global.tty.use_subshell)
387 init_subshell ();
388 #endif /* ENABLE_SUBSHELL */
391 enable_mouse ();
392 channels_up ();
393 if (mc_global.tty.alternate_plus_minus)
394 application_keypad_mode ();
396 /* HACK:
397 * Save sigwinch flag that will be reset in mc_refresh() called via update_panels().
398 * There is some problem with screen redraw in ncurses-based mc in this situation.
400 was_sigwinch = mc_global.tty.winch_flag;
401 mc_global.tty.winch_flag = 0;
403 #ifdef ENABLE_SUBSHELL
404 if (mc_global.tty.use_subshell)
406 do_load_prompt ();
407 if (new_dir_vpath != NULL)
408 do_possible_cd (new_dir_vpath);
409 if (mc_global.tty.console_flag != '\0' && output_lines)
410 show_console_contents (output_start_y,
411 LINES - mc_global.keybar_visible - output_lines -
412 1, LINES - mc_global.keybar_visible - 1);
415 vfs_path_free (new_dir_vpath);
416 #endif /* ENABLE_SUBSHELL */
418 if (mc_global.mc_run_mode == MC_RUN_FULL)
420 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
421 update_xterm_title_path ();
424 if (was_sigwinch != 0 || mc_global.tty.winch_flag != 0)
425 dialog_change_screen_size ();
426 else
427 repaint_screen ();
430 /* --------------------------------------------------------------------------------------------- */
432 /* event callback */
433 gboolean
434 execute_suspend (const gchar * event_group_name, const gchar * event_name,
435 gpointer init_data, gpointer data)
437 (void) event_group_name;
438 (void) event_name;
439 (void) init_data;
440 (void) data;
442 if (mc_global.mc_run_mode == MC_RUN_FULL)
443 save_cwds_stat ();
444 do_suspend_cmd ();
445 if (mc_global.mc_run_mode == MC_RUN_FULL)
446 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
447 do_refresh ();
449 return TRUE;
452 /* --------------------------------------------------------------------------------------------- */
454 * Execute command on a filename that can be on VFS.
455 * Errors are reported to the user.
458 void
459 execute_with_vfs_arg (const char *command, const vfs_path_t * filename_vpath)
461 struct stat st;
462 time_t mtime;
463 vfs_path_t *localcopy_vpath;
465 /* Simplest case, this file is local */
466 if ((filename_vpath == NULL && vfs_file_is_local (vfs_get_raw_current_dir ()))
467 || vfs_file_is_local (filename_vpath))
469 do_execute (command, vfs_path_get_last_path_str (filename_vpath), EXECUTE_INTERNAL);
470 return;
473 /* FIXME: Creation of new files on VFS is not supported */
474 if (filename_vpath == NULL)
475 return;
477 localcopy_vpath = mc_getlocalcopy (filename_vpath);
478 if (localcopy_vpath == NULL)
480 char *filename;
482 filename = vfs_path_to_str (filename_vpath);
483 message (D_ERROR, MSG_ERROR, _("Cannot fetch a local copy of %s"), filename);
484 g_free (filename);
485 return;
489 * filename can be an entry on panel, it can be changed by executing
490 * the command, so make a copy. Smarter VFS code would make the code
491 * below unnecessary.
493 mc_stat (localcopy_vpath, &st);
494 mtime = st.st_mtime;
495 do_execute (command, vfs_path_get_last_path_str (localcopy_vpath), EXECUTE_INTERNAL);
496 mc_stat (localcopy_vpath, &st);
497 mc_ungetlocalcopy (filename_vpath, localcopy_vpath, mtime != st.st_mtime);
498 vfs_path_free (localcopy_vpath);
501 /* --------------------------------------------------------------------------------------------- */