Add support of reading parameters for external editor/viewer from main config.
[midnight-commander.git] / src / execute.c
blob6dff4565ae50e47acd38369d964faa1a99d6dedd
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 <string.h>
31 #include <sys/stat.h>
32 #include <sys/time.h>
34 #include "lib/global.h"
36 #include "lib/tty/tty.h"
37 #include "lib/tty/key.h"
38 #include "lib/tty/win.h"
39 #include "lib/vfs/vfs.h"
40 #include "lib/mcconfig.h"
41 #include "lib/util.h"
42 #include "lib/strutil.h" /* str_replace_all_substrings() */
43 #include "lib/widget.h"
45 #include "filemanager/midnight.h"
46 #include "filemanager/layout.h" /* use_dash() */
47 #include "consaver/cons.saver.h"
48 #ifdef ENABLE_SUBSHELL
49 #include "subshell.h"
50 #endif
51 #include "setup.h" /* clear_before_exec */
53 #include "execute.h"
55 /*** global variables ****************************************************************************/
57 int pause_after_run = pause_on_dumb_terminals;
59 /*** file scope macro definitions ****************************************************************/
61 /*** file scope type declarations ****************************************************************/
63 /*** file scope variables ************************************************************************/
65 /*** file scope functions ************************************************************************/
67 void do_execute (const char *shell, const char *command, int flags);
68 void do_executev (const char *shell, int flags, char *const argv[]);
69 char *execute_get_external_cmd_opts_from_config (const char *command,
70 const vfs_path_t * filename_vpath, int start_line);
72 /* --------------------------------------------------------------------------------------------- */
74 static void
75 edition_post_exec (void)
77 do_enter_ca_mode ();
79 /* FIXME: Missing on slang endwin? */
80 tty_reset_prog_mode ();
81 tty_flush_input ();
83 tty_keypad (TRUE);
84 tty_raw_mode ();
85 channels_up ();
86 enable_mouse ();
87 if (mc_global.tty.alternate_plus_minus)
88 application_keypad_mode ();
91 /* --------------------------------------------------------------------------------------------- */
93 static void
94 edition_pre_exec (void)
96 if (clear_before_exec)
97 clr_scr ();
98 else
100 if (!(mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag))
101 printf ("\n\n");
104 channels_down ();
105 disable_mouse ();
107 tty_reset_shell_mode ();
108 tty_keypad (FALSE);
109 tty_reset_screen ();
111 numeric_keypad_mode ();
113 /* on xterms: maybe endwin did not leave the terminal on the shell
114 * screen page: do it now.
116 * Do not move this before endwin: in some systems rmcup includes
117 * a call to clear screen, so it will end up clearing the shell screen.
119 do_exit_ca_mode ();
122 /* --------------------------------------------------------------------------------------------- */
124 #ifdef ENABLE_SUBSHELL
125 static void
126 do_possible_cd (const vfs_path_t * new_dir_vpath)
128 if (!do_cd (new_dir_vpath, cd_exact))
129 message (D_ERROR, _("Warning"),
130 _("The Commander can't change to the directory that\n"
131 "the subshell claims you are in. Perhaps you have\n"
132 "deleted your working directory, or given yourself\n"
133 "extra access permissions with the \"su\" command?"));
135 #endif /* ENABLE_SUBSHELL */
137 /* --------------------------------------------------------------------------------------------- */
139 static void
140 do_suspend_cmd (void)
142 pre_exec ();
144 if (mc_global.tty.console_flag != '\0' && !mc_global.tty.use_subshell)
145 handle_console (CONSOLE_RESTORE);
147 #ifdef SIGTSTP
149 struct sigaction sigtstp_action;
151 /* Make sure that the SIGTSTP below will suspend us directly,
152 without calling ncurses' SIGTSTP handler; we *don't* want
153 ncurses to redraw the screen immediately after the SIGCONT */
154 sigaction (SIGTSTP, &startup_handler, &sigtstp_action);
156 kill (getpid (), SIGTSTP);
158 /* Restore previous SIGTSTP action */
159 sigaction (SIGTSTP, &sigtstp_action, NULL);
161 #endif /* SIGTSTP */
163 if (mc_global.tty.console_flag != '\0' && !mc_global.tty.use_subshell)
164 handle_console (CONSOLE_SAVE);
166 edition_post_exec ();
169 /* --------------------------------------------------------------------------------------------- */
171 static gboolean
172 execute_prepare_with_vfs_arg (const vfs_path_t * filename_vpath, vfs_path_t ** localcopy_vpath,
173 time_t * mtime)
175 struct stat st;
177 /* Simplest case, this file is local */
178 if ((filename_vpath == NULL && vfs_file_is_local (vfs_get_raw_current_dir ()))
179 || vfs_file_is_local (filename_vpath))
180 return TRUE;
182 /* FIXME: Creation of new files on VFS is not supported */
183 if (filename_vpath == NULL)
184 return FALSE;
186 *localcopy_vpath = mc_getlocalcopy (filename_vpath);
187 if (*localcopy_vpath == NULL)
189 char *filename;
191 filename = vfs_path_to_str (filename_vpath);
192 message (D_ERROR, MSG_ERROR, _("Cannot fetch a local copy of %s"), filename);
193 g_free (filename);
194 return FALSE;
197 mc_stat (*localcopy_vpath, &st);
198 *mtime = st.st_mtime;
199 return TRUE;
202 /* --------------------------------------------------------------------------------------------- */
204 static void
205 execute_cleanup_with_vfs_arg (const vfs_path_t * filename_vpath, vfs_path_t ** localcopy_vpath,
206 time_t * mtime)
208 if (*localcopy_vpath != NULL)
210 struct stat st;
213 * filename can be an entry on panel, it can be changed by executing
214 * the command, so make a copy. Smarter VFS code would make the code
215 * below unnecessary.
217 mc_stat (*localcopy_vpath, &st);
218 mc_ungetlocalcopy (filename_vpath, *localcopy_vpath, *mtime != st.st_mtime);
219 vfs_path_free (*localcopy_vpath);
220 *localcopy_vpath = NULL;
224 /* --------------------------------------------------------------------------------------------- */
226 static char *
227 execute_get_opts_from_cfg (const char *command, const char *default_str)
229 char *str_from_config;
231 str_from_config =
232 mc_config_get_string_raw (mc_main_config, CONFIG_EXT_EDITOR_VIEWER_SECTION, command, NULL);
234 if (str_from_config == NULL)
236 mc_config_t *cfg;
238 cfg = mc_config_init (global_profile_name, TRUE);
239 if (cfg == NULL)
240 return g_strdup (default_str);
242 str_from_config =
243 mc_config_get_string_raw (cfg, CONFIG_EXT_EDITOR_VIEWER_SECTION, command, default_str);
245 mc_config_deinit (cfg);
248 return str_from_config;
251 /* --------------------------------------------------------------------------------------------- */
252 /*** public functions ****************************************************************************/
253 /* --------------------------------------------------------------------------------------------- */
255 char *
256 execute_get_external_cmd_opts_from_config (const char *command, const vfs_path_t * filename_vpath,
257 int start_line)
259 char *str_from_config, *return_str;
260 char *parameter;
262 if (filename_vpath == NULL)
263 return g_strdup ("");
265 str_from_config = execute_get_opts_from_cfg (command, "%filename");
267 parameter = g_shell_quote (vfs_path_get_last_path_str (filename_vpath));
268 return_str = str_replace_all (str_from_config, "%filename", parameter);
269 g_free (parameter);
270 g_free (str_from_config);
271 str_from_config = return_str;
273 parameter = g_strdup_printf ("%d", start_line);
274 return_str = str_replace_all (str_from_config, "%lineno", parameter);
275 g_free (parameter);
276 g_free (str_from_config);
278 return return_str;
281 /* --------------------------------------------------------------------------------------------- */
283 void
284 do_executev (const char *shell, int flags, char *const argv[])
286 #ifdef ENABLE_SUBSHELL
287 vfs_path_t *new_dir_vpath = NULL;
288 #endif /* ENABLE_SUBSHELL */
290 vfs_path_t *old_vfs_dir_vpath = NULL;
292 if (!vfs_current_is_local ())
293 old_vfs_dir_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
295 if (mc_global.mc_run_mode == MC_RUN_FULL)
296 save_cwds_stat ();
297 pre_exec ();
298 if (mc_global.tty.console_flag != '\0')
299 handle_console (CONSOLE_RESTORE);
301 if (!mc_global.tty.use_subshell && *argv != NULL && (flags & EXECUTE_INTERNAL) == 0)
303 printf ("%s%s\n", mc_prompt, *argv);
304 fflush (stdout);
306 #ifdef ENABLE_SUBSHELL
307 if (mc_global.tty.use_subshell && (flags & EXECUTE_INTERNAL) == 0)
309 do_update_prompt ();
311 /* We don't care if it died, higher level takes care of this */
312 invoke_subshell (*argv, VISIBLY, old_vfs_dir_vpath != NULL ? NULL : &new_dir_vpath);
314 else
315 #endif /* ENABLE_SUBSHELL */
316 my_systemv_flags (flags, shell, argv);
318 if ((flags & EXECUTE_INTERNAL) == 0)
320 if ((pause_after_run == pause_always
321 || (pause_after_run == pause_on_dumb_terminals && !mc_global.tty.xterm_flag
322 && mc_global.tty.console_flag == '\0')) && quit == 0
323 #ifdef ENABLE_SUBSHELL
324 && subshell_state != RUNNING_COMMAND
325 #endif /* ENABLE_SUBSHELL */
328 printf (_("Press any key to continue..."));
329 fflush (stdout);
330 tty_raw_mode ();
331 get_key_code (0);
332 printf ("\r\n");
333 fflush (stdout);
335 if (mc_global.tty.console_flag != '\0' && output_lines != 0 && mc_global.keybar_visible)
337 putchar ('\n');
338 fflush (stdout);
342 if (mc_global.tty.console_flag != '\0')
343 handle_console (CONSOLE_SAVE);
344 edition_post_exec ();
346 #ifdef ENABLE_SUBSHELL
347 if (new_dir_vpath != NULL)
349 do_possible_cd (new_dir_vpath);
350 vfs_path_free (new_dir_vpath);
353 #endif /* ENABLE_SUBSHELL */
355 if (old_vfs_dir_vpath != NULL)
357 mc_chdir (old_vfs_dir_vpath);
358 vfs_path_free (old_vfs_dir_vpath);
361 if (mc_global.mc_run_mode == MC_RUN_FULL)
363 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
364 update_xterm_title_path ();
367 do_refresh ();
368 use_dash (TRUE);
371 /* --------------------------------------------------------------------------------------------- */
373 void
374 do_execute (const char *shell, const char *command, int flags)
376 GPtrArray *args_array;
378 args_array = g_ptr_array_new ();
379 g_ptr_array_add (args_array, (char *) command);
380 g_ptr_array_add (args_array, NULL);
382 do_executev (shell, flags, (char *const *) args_array->pdata);
384 g_ptr_array_free (args_array, TRUE);
387 /* --------------------------------------------------------------------------------------------- */
389 /** Set up the terminal before executing a program */
391 void
392 pre_exec (void)
394 use_dash (FALSE);
395 edition_pre_exec ();
398 /* --------------------------------------------------------------------------------------------- */
399 /** Hide the terminal after executing a program */
400 void
401 post_exec (void)
403 edition_post_exec ();
404 use_dash (TRUE);
405 repaint_screen ();
408 /* --------------------------------------------------------------------------------------------- */
409 /* Executes a command */
411 void
412 shell_execute (const char *command, int flags)
414 char *cmd = NULL;
416 if (flags & EXECUTE_HIDE)
418 cmd = g_strconcat (" ", command, (char *) NULL);
419 flags ^= EXECUTE_HIDE;
422 #ifdef ENABLE_SUBSHELL
423 if (mc_global.tty.use_subshell)
424 if (subshell_state == INACTIVE)
425 do_execute (mc_global.tty.shell, cmd ? cmd : command, flags | EXECUTE_AS_SHELL);
426 else
427 message (D_ERROR, MSG_ERROR, _("The shell is already running a command"));
428 else
429 #endif /* ENABLE_SUBSHELL */
430 do_execute (mc_global.tty.shell, cmd ? cmd : command, flags | EXECUTE_AS_SHELL);
432 g_free (cmd);
435 /* --------------------------------------------------------------------------------------------- */
437 void
438 exec_shell (void)
440 do_execute (mc_global.tty.shell, 0, 0);
443 /* --------------------------------------------------------------------------------------------- */
445 void
446 toggle_panels (void)
448 #ifdef ENABLE_SUBSHELL
449 vfs_path_t *new_dir_vpath = NULL;
450 vfs_path_t **new_dir_p;
451 #endif /* ENABLE_SUBSHELL */
453 SIG_ATOMIC_VOLATILE_T was_sigwinch = 0;
455 channels_down ();
456 disable_mouse ();
457 if (clear_before_exec)
458 clr_scr ();
459 if (mc_global.tty.alternate_plus_minus)
460 numeric_keypad_mode ();
461 #ifndef HAVE_SLANG
462 /* With slang we don't want any of this, since there
463 * is no raw_mode supported
465 tty_reset_shell_mode ();
466 #endif /* !HAVE_SLANG */
467 tty_noecho ();
468 tty_keypad (FALSE);
469 tty_reset_screen ();
470 do_exit_ca_mode ();
471 tty_raw_mode ();
472 if (mc_global.tty.console_flag != '\0')
473 handle_console (CONSOLE_RESTORE);
475 #ifdef ENABLE_SUBSHELL
476 if (mc_global.tty.use_subshell)
478 new_dir_p = vfs_current_is_local ()? &new_dir_vpath : NULL;
479 invoke_subshell (NULL, VISIBLY, new_dir_p);
481 else
482 #endif /* ENABLE_SUBSHELL */
484 if (output_starts_shell)
486 fprintf (stderr, _("Type `exit' to return to the Midnight Commander"));
487 fprintf (stderr, "\n\r\n\r");
489 my_system (EXECUTE_INTERNAL, mc_global.tty.shell, NULL);
491 else
492 get_key_code (0);
495 if (mc_global.tty.console_flag != '\0')
496 handle_console (CONSOLE_SAVE);
498 do_enter_ca_mode ();
500 tty_reset_prog_mode ();
501 tty_keypad (TRUE);
503 /* Prevent screen flash when user did 'exit' or 'logout' within
504 subshell */
505 if ((quit & SUBSHELL_EXIT) != 0)
507 /* User did `exit' or `logout': quit MC */
508 if (quiet_quit_cmd ())
509 return;
511 quit = 0;
512 #ifdef ENABLE_SUBSHELL
513 /* restart subshell */
514 if (mc_global.tty.use_subshell)
515 init_subshell ();
516 #endif /* ENABLE_SUBSHELL */
519 enable_mouse ();
520 channels_up ();
521 if (mc_global.tty.alternate_plus_minus)
522 application_keypad_mode ();
524 /* HACK:
525 * Save sigwinch flag that will be reset in mc_refresh() called via update_panels().
526 * There is some problem with screen redraw in ncurses-based mc in this situation.
528 was_sigwinch = mc_global.tty.winch_flag;
529 mc_global.tty.winch_flag = 0;
531 #ifdef ENABLE_SUBSHELL
532 if (mc_global.tty.use_subshell)
534 do_load_prompt ();
535 if (new_dir_vpath != NULL)
536 do_possible_cd (new_dir_vpath);
537 if (mc_global.tty.console_flag != '\0' && output_lines)
538 show_console_contents (output_start_y,
539 LINES - mc_global.keybar_visible - output_lines -
540 1, LINES - mc_global.keybar_visible - 1);
543 vfs_path_free (new_dir_vpath);
544 #endif /* ENABLE_SUBSHELL */
546 if (mc_global.mc_run_mode == MC_RUN_FULL)
548 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
549 update_xterm_title_path ();
552 if (was_sigwinch != 0 || mc_global.tty.winch_flag != 0)
553 dialog_change_screen_size ();
554 else
555 repaint_screen ();
558 /* --------------------------------------------------------------------------------------------- */
560 /* event callback */
561 gboolean
562 execute_suspend (const gchar * event_group_name, const gchar * event_name,
563 gpointer init_data, gpointer data)
565 (void) event_group_name;
566 (void) event_name;
567 (void) init_data;
568 (void) data;
570 if (mc_global.mc_run_mode == MC_RUN_FULL)
571 save_cwds_stat ();
572 do_suspend_cmd ();
573 if (mc_global.mc_run_mode == MC_RUN_FULL)
574 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
575 do_refresh ();
577 return TRUE;
580 /* --------------------------------------------------------------------------------------------- */
583 * Execute command on a filename that can be on VFS.
584 * Errors are reported to the user.
587 void
588 execute_with_vfs_arg (const char *command, const vfs_path_t * filename_vpath)
590 vfs_path_t *localcopy_vpath = NULL;
591 const vfs_path_t *do_execute_vpath;
592 time_t mtime;
594 if (!execute_prepare_with_vfs_arg (filename_vpath, &localcopy_vpath, &mtime))
595 return;
597 do_execute_vpath = (localcopy_vpath == NULL) ? filename_vpath : localcopy_vpath;
599 do_execute (command, vfs_path_get_last_path_str (do_execute_vpath), EXECUTE_INTERNAL);
601 execute_cleanup_with_vfs_arg (filename_vpath, &localcopy_vpath, &mtime);
604 /* --------------------------------------------------------------------------------------------- */
606 * Execute external editor or viewer.
608 * @param command editor/viewer to run
609 * @param filename_vpath path for edit/view
610 * @param start_line cursor will be placed at the 'start_line' position after opening file
613 void
614 execute_external_editor_or_viewer (const char *command, const vfs_path_t * filename_vpath,
615 int start_line)
617 vfs_path_t *localcopy_vpath = NULL;
618 const vfs_path_t *do_execute_vpath;
619 char *extern_cmd_options;
620 time_t mtime;
622 if (!execute_prepare_with_vfs_arg (filename_vpath, &localcopy_vpath, &mtime))
623 return;
625 do_execute_vpath = (localcopy_vpath == NULL) ? filename_vpath : localcopy_vpath;
627 extern_cmd_options =
628 execute_get_external_cmd_opts_from_config (command, do_execute_vpath, start_line);
630 if (extern_cmd_options != NULL)
632 char **argv_cmd_options;
633 int argv_count;
635 g_shell_parse_argv (extern_cmd_options, &argv_count, &argv_cmd_options, NULL);
636 g_free (extern_cmd_options);
638 do_executev (command, EXECUTE_INTERNAL, argv_cmd_options);
640 g_strfreev (argv_cmd_options);
643 execute_cleanup_with_vfs_arg (filename_vpath, &localcopy_vpath, &mtime);
646 /* --------------------------------------------------------------------------------------------- */