Don't create and install mcedit man page if mc is built --without-internal-edit.
[midnight-commander.git] / src / execute.c
blobad60ef96b8bfafe804efd58d1e5b432300ebd642
1 /*
2 Execution routines for GNU Midnight Commander
4 Copyright (C) 2003-2024
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/filemanager.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 /*** forward declarations (file scope functions) *************************************************/
68 void do_execute (const char *shell, const char *command, int flags);
69 void do_executev (const char *shell, int flags, char *const argv[]);
70 char *execute_get_external_cmd_opts_from_config (const char *command,
71 const vfs_path_t * filename_vpath,
72 long start_line);
74 /*** file scope variables ************************************************************************/
76 /* --------------------------------------------------------------------------------------------- */
77 /*** file scope functions ************************************************************************/
78 /* --------------------------------------------------------------------------------------------- */
80 static void
81 edition_post_exec (void)
83 tty_enter_ca_mode ();
85 /* FIXME: Missing on slang endwin? */
86 tty_reset_prog_mode ();
87 tty_flush_input ();
89 tty_keypad (TRUE);
90 tty_raw_mode ();
91 channels_up ();
92 enable_mouse ();
93 enable_bracketed_paste ();
94 if (mc_global.tty.alternate_plus_minus)
95 application_keypad_mode ();
98 /* --------------------------------------------------------------------------------------------- */
100 static void
101 edition_pre_exec (void)
103 if (clear_before_exec)
104 tty_clear_screen ();
105 else
107 if (!(mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag))
108 printf ("\n\n");
111 channels_down ();
112 disable_mouse ();
113 disable_bracketed_paste ();
115 tty_reset_shell_mode ();
116 tty_keypad (FALSE);
117 tty_reset_screen ();
119 numeric_keypad_mode ();
121 /* on xterms: maybe endwin did not leave the terminal on the shell
122 * screen page: do it now.
124 * Do not move this before endwin: in some systems rmcup includes
125 * a call to clear screen, so it will end up clearing the shell screen.
127 tty_exit_ca_mode ();
130 /* --------------------------------------------------------------------------------------------- */
132 #ifdef ENABLE_SUBSHELL
133 static void
134 do_possible_cd (const vfs_path_t * new_dir_vpath)
136 if (!panel_cd (current_panel, new_dir_vpath, cd_exact))
137 message (D_ERROR, _("Warning"), "%s",
138 _("The Commander can't change to the directory that\n"
139 "the subshell claims you are in. Perhaps you have\n"
140 "deleted your working directory, or given yourself\n"
141 "extra access permissions with the \"su\" command?"));
143 #endif /* ENABLE_SUBSHELL */
145 /* --------------------------------------------------------------------------------------------- */
147 static void
148 do_suspend_cmd (void)
150 pre_exec ();
152 if (mc_global.tty.console_flag != '\0' && !mc_global.tty.use_subshell)
153 handle_console (CONSOLE_RESTORE);
155 #ifdef SIGTSTP
157 struct sigaction sigtstp_action;
159 memset (&sigtstp_action, 0, sizeof (sigtstp_action));
160 /* Make sure that the SIGTSTP below will suspend us directly,
161 without calling ncurses' SIGTSTP handler; we *don't* want
162 ncurses to redraw the screen immediately after the SIGCONT */
163 sigaction (SIGTSTP, &startup_handler, &sigtstp_action);
165 kill (getpid (), SIGTSTP);
167 /* Restore previous SIGTSTP action */
168 sigaction (SIGTSTP, &sigtstp_action, NULL);
170 #endif /* SIGTSTP */
172 if (mc_global.tty.console_flag != '\0' && !mc_global.tty.use_subshell)
173 handle_console (CONSOLE_SAVE);
175 edition_post_exec ();
178 /* --------------------------------------------------------------------------------------------- */
180 static gboolean
181 execute_prepare_with_vfs_arg (const vfs_path_t * filename_vpath, vfs_path_t ** localcopy_vpath,
182 time_t * mtime)
184 struct stat st;
186 /* Simplest case, this file is local */
187 if ((filename_vpath == NULL && vfs_file_is_local (vfs_get_raw_current_dir ()))
188 || vfs_file_is_local (filename_vpath))
189 return TRUE;
191 /* FIXME: Creation of new files on VFS is not supported */
192 if (filename_vpath == NULL)
193 return FALSE;
195 *localcopy_vpath = mc_getlocalcopy (filename_vpath);
196 if (*localcopy_vpath == NULL)
198 message (D_ERROR, MSG_ERROR, _("Cannot fetch a local copy of %s"),
199 vfs_path_as_str (filename_vpath));
200 return FALSE;
203 mc_stat (*localcopy_vpath, &st);
204 *mtime = st.st_mtime;
205 return TRUE;
208 /* --------------------------------------------------------------------------------------------- */
210 static void
211 execute_cleanup_with_vfs_arg (const vfs_path_t * filename_vpath, vfs_path_t ** localcopy_vpath,
212 time_t * mtime)
214 if (*localcopy_vpath != NULL)
216 struct stat st;
219 * filename can be an entry on panel, it can be changed by executing
220 * the command, so make a copy. Smarter VFS code would make the code
221 * below unnecessary.
223 mc_stat (*localcopy_vpath, &st);
224 mc_ungetlocalcopy (filename_vpath, *localcopy_vpath, *mtime != st.st_mtime);
225 vfs_path_free (*localcopy_vpath, TRUE);
226 *localcopy_vpath = NULL;
230 /* --------------------------------------------------------------------------------------------- */
232 static char *
233 execute_get_opts_from_cfg (const char *command, const char *default_str)
235 char *str_from_config;
237 str_from_config =
238 mc_config_get_string_raw (mc_global.main_config, CONFIG_EXT_EDITOR_VIEWER_SECTION, command,
239 NULL);
241 if (str_from_config == NULL)
243 mc_config_t *cfg;
245 cfg = mc_config_init (mc_global.profile_name, TRUE);
246 if (cfg == NULL)
247 return g_strdup (default_str);
249 str_from_config =
250 mc_config_get_string_raw (cfg, CONFIG_EXT_EDITOR_VIEWER_SECTION, command, default_str);
252 mc_config_deinit (cfg);
255 return str_from_config;
258 /* --------------------------------------------------------------------------------------------- */
259 /*** public functions ****************************************************************************/
260 /* --------------------------------------------------------------------------------------------- */
262 char *
263 execute_get_external_cmd_opts_from_config (const char *command, const vfs_path_t * filename_vpath,
264 long start_line)
266 char *str_from_config, *return_str;
267 char *parameter;
269 if (filename_vpath == NULL)
270 return g_strdup ("");
272 parameter = g_shell_quote (vfs_path_get_last_path_str (filename_vpath));
274 if (start_line <= 0)
275 return parameter;
277 str_from_config = execute_get_opts_from_cfg (command, "%filename");
279 return_str = str_replace_all (str_from_config, "%filename", parameter);
280 g_free (parameter);
281 g_free (str_from_config);
282 str_from_config = return_str;
284 parameter = g_strdup_printf ("%ld", start_line);
285 return_str = str_replace_all (str_from_config, "%lineno", parameter);
286 g_free (parameter);
287 g_free (str_from_config);
289 return return_str;
292 /* --------------------------------------------------------------------------------------------- */
294 void
295 do_executev (const char *shell, int flags, char *const argv[])
297 #ifdef ENABLE_SUBSHELL
298 vfs_path_t *new_dir_vpath = NULL;
299 #endif /* ENABLE_SUBSHELL */
301 vfs_path_t *old_vfs_dir_vpath = NULL;
303 if (!vfs_current_is_local ())
304 old_vfs_dir_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
306 if (mc_global.mc_run_mode == MC_RUN_FULL)
307 save_cwds_stat ();
308 pre_exec ();
309 if (mc_global.tty.console_flag != '\0')
310 handle_console (CONSOLE_RESTORE);
312 if (!mc_global.tty.use_subshell && *argv != NULL && (flags & EXECUTE_INTERNAL) == 0)
314 printf ("%s%s\n", mc_prompt, *argv);
315 fflush (stdout);
317 #ifdef ENABLE_SUBSHELL
318 if (mc_global.tty.use_subshell && (flags & EXECUTE_INTERNAL) == 0)
320 do_update_prompt ();
322 /* We don't care if it died, higher level takes care of this */
323 invoke_subshell (*argv, VISIBLY, old_vfs_dir_vpath != NULL ? NULL : &new_dir_vpath);
325 else
326 #endif /* ENABLE_SUBSHELL */
327 my_systemv_flags (flags, shell, argv);
329 if ((flags & EXECUTE_INTERNAL) == 0)
331 if ((pause_after_run == pause_always
332 || (pause_after_run == pause_on_dumb_terminals && !mc_global.tty.xterm_flag
333 && mc_global.tty.console_flag == '\0')) && quit == 0
334 #ifdef ENABLE_SUBSHELL
335 && subshell_state != RUNNING_COMMAND
336 #endif /* ENABLE_SUBSHELL */
339 printf ("%s", _("Press any key to continue..."));
340 fflush (stdout);
341 tty_raw_mode ();
342 get_key_code (0);
343 printf ("\r\n");
344 fflush (stdout);
346 if (mc_global.tty.console_flag != '\0' && output_lines != 0 && mc_global.keybar_visible)
348 putchar ('\n');
349 fflush (stdout);
353 if (mc_global.tty.console_flag != '\0')
354 handle_console (CONSOLE_SAVE);
355 edition_post_exec ();
357 #ifdef ENABLE_SUBSHELL
358 if (new_dir_vpath != NULL)
360 do_possible_cd (new_dir_vpath);
361 vfs_path_free (new_dir_vpath, TRUE);
364 #endif /* ENABLE_SUBSHELL */
366 if (old_vfs_dir_vpath != NULL)
368 mc_chdir (old_vfs_dir_vpath);
369 vfs_path_free (old_vfs_dir_vpath, TRUE);
372 if (mc_global.mc_run_mode == MC_RUN_FULL)
374 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
375 update_xterm_title_path ();
376 update_terminal_cwd ();
379 do_refresh ();
380 use_dash (TRUE);
383 /* --------------------------------------------------------------------------------------------- */
385 void
386 do_execute (const char *shell, const char *command, int flags)
388 GPtrArray *args_array;
390 args_array = g_ptr_array_new ();
391 g_ptr_array_add (args_array, (char *) command);
392 g_ptr_array_add (args_array, NULL);
394 do_executev (shell, flags, (char *const *) args_array->pdata);
396 g_ptr_array_free (args_array, TRUE);
399 /* --------------------------------------------------------------------------------------------- */
401 /** Set up the terminal before executing a program */
403 void
404 pre_exec (void)
406 use_dash (FALSE);
407 edition_pre_exec ();
410 /* --------------------------------------------------------------------------------------------- */
411 /** Hide the terminal after executing a program */
412 void
413 post_exec (void)
415 edition_post_exec ();
416 use_dash (TRUE);
417 repaint_screen ();
420 /* --------------------------------------------------------------------------------------------- */
421 /* Executes a command */
423 void
424 shell_execute (const char *command, int flags)
426 char *cmd = NULL;
428 if (flags & EXECUTE_HIDE)
430 cmd = g_strconcat (" ", command, (char *) NULL);
431 flags ^= EXECUTE_HIDE;
434 #ifdef ENABLE_SUBSHELL
435 if (mc_global.tty.use_subshell)
437 if (subshell_state == INACTIVE)
438 do_execute (mc_global.shell->path, cmd ? cmd : command, flags | EXECUTE_AS_SHELL);
439 else
440 message (D_ERROR, MSG_ERROR, "%s", _("The shell is already running a command"));
442 else
443 #endif /* ENABLE_SUBSHELL */
444 do_execute (mc_global.shell->path, cmd ? cmd : command, flags | EXECUTE_AS_SHELL);
446 g_free (cmd);
449 /* --------------------------------------------------------------------------------------------- */
451 void
452 toggle_subshell (void)
454 static gboolean message_flag = TRUE;
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 if (!(mc_global.tty.xterm_flag || mc_global.tty.console_flag != '\0'
463 || mc_global.tty.use_subshell || output_starts_shell))
465 if (message_flag)
466 message (D_ERROR, MSG_ERROR,
467 _("Not an xterm or Linux console;\nthe subshell cannot be toggled."));
468 message_flag = FALSE;
469 return;
472 channels_down ();
473 disable_mouse ();
474 disable_bracketed_paste ();
475 if (clear_before_exec)
476 tty_clear_screen ();
477 if (mc_global.tty.alternate_plus_minus)
478 numeric_keypad_mode ();
479 #ifndef HAVE_SLANG
480 /* With slang we don't want any of this, since there
481 * is no raw_mode supported
483 tty_reset_shell_mode ();
484 #endif /* !HAVE_SLANG */
485 tty_noecho ();
486 tty_keypad (FALSE);
487 tty_reset_screen ();
488 tty_exit_ca_mode ();
489 tty_raw_mode ();
490 if (mc_global.tty.console_flag != '\0')
491 handle_console (CONSOLE_RESTORE);
493 #ifdef ENABLE_SUBSHELL
494 if (mc_global.tty.use_subshell)
496 vfs_path_t **new_dir_p;
498 new_dir_p = vfs_current_is_local ()? &new_dir_vpath : NULL;
499 invoke_subshell (NULL, VISIBLY, new_dir_p);
501 else
502 #endif /* ENABLE_SUBSHELL */
504 if (output_starts_shell)
506 fputs (_("Type 'exit' to return to the Midnight Commander"), stderr);
507 fputs ("\n\r\n\r", stderr);
509 my_system (EXECUTE_INTERNAL, mc_global.shell->path, NULL);
511 else
512 get_key_code (0);
515 if (mc_global.tty.console_flag != '\0')
516 handle_console (CONSOLE_SAVE);
518 tty_enter_ca_mode ();
520 tty_reset_prog_mode ();
521 tty_keypad (TRUE);
523 /* Prevent screen flash when user did 'exit' or 'logout' within
524 subshell */
525 if ((quit & SUBSHELL_EXIT) != 0)
527 /* User did 'exit' or 'logout': quit MC */
528 if (quiet_quit_cmd ())
529 return;
531 quit = 0;
532 #ifdef ENABLE_SUBSHELL
533 /* restart subshell */
534 if (mc_global.tty.use_subshell)
535 init_subshell ();
536 #endif /* ENABLE_SUBSHELL */
539 enable_mouse ();
540 enable_bracketed_paste ();
541 channels_up ();
542 if (mc_global.tty.alternate_plus_minus)
543 application_keypad_mode ();
545 /* HACK:
546 * Save sigwinch flag that will be reset in mc_refresh() called via update_panels().
547 * There is some problem with screen redraw in ncurses-based mc in this situation.
549 was_sigwinch = tty_got_winch ();
550 tty_flush_winch ();
552 #ifdef ENABLE_SUBSHELL
553 if (mc_global.tty.use_subshell)
555 if (mc_global.mc_run_mode == MC_RUN_FULL)
557 if (new_dir_vpath != NULL)
558 do_possible_cd (new_dir_vpath);
560 else if (new_dir_vpath != NULL && mc_chdir (new_dir_vpath) != -1)
561 vfs_setup_cwd ();
564 vfs_path_free (new_dir_vpath, TRUE);
565 #endif /* ENABLE_SUBSHELL */
567 if (mc_global.mc_run_mode == MC_RUN_FULL)
569 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
570 update_xterm_title_path ();
571 update_terminal_cwd ();
574 if (was_sigwinch != 0 || tty_got_winch ())
575 dialog_change_screen_size ();
576 else
577 repaint_screen ();
580 /* --------------------------------------------------------------------------------------------- */
582 /* event callback */
583 gboolean
584 execute_suspend (const gchar * event_group_name, const gchar * event_name,
585 gpointer init_data, gpointer data)
587 (void) event_group_name;
588 (void) event_name;
589 (void) init_data;
590 (void) data;
592 if (mc_global.mc_run_mode == MC_RUN_FULL)
593 save_cwds_stat ();
594 do_suspend_cmd ();
595 if (mc_global.mc_run_mode == MC_RUN_FULL)
596 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
597 do_refresh ();
599 return TRUE;
602 /* --------------------------------------------------------------------------------------------- */
605 * Execute command on a filename that can be on VFS.
606 * Errors are reported to the user.
609 void
610 execute_with_vfs_arg (const char *command, const vfs_path_t * filename_vpath)
612 vfs_path_t *localcopy_vpath = NULL;
613 const vfs_path_t *do_execute_vpath;
614 time_t mtime;
616 if (!execute_prepare_with_vfs_arg (filename_vpath, &localcopy_vpath, &mtime))
617 return;
619 do_execute_vpath = (localcopy_vpath == NULL) ? filename_vpath : localcopy_vpath;
621 do_execute (command, vfs_path_get_last_path_str (do_execute_vpath), EXECUTE_INTERNAL);
623 execute_cleanup_with_vfs_arg (filename_vpath, &localcopy_vpath, &mtime);
626 /* --------------------------------------------------------------------------------------------- */
628 * Execute external editor or viewer.
630 * @param command editor/viewer to run
631 * @param filename_vpath path for edit/view
632 * @param start_line cursor will be placed at the 'start_line' position after opening file
633 * if start_line is 0 or negative, no start line will be passed to editor/viewer
636 void
637 execute_external_editor_or_viewer (const char *command, const vfs_path_t * filename_vpath,
638 long start_line)
640 vfs_path_t *localcopy_vpath = NULL;
641 const vfs_path_t *do_execute_vpath;
642 char *extern_cmd_options;
643 time_t mtime = 0;
645 if (!execute_prepare_with_vfs_arg (filename_vpath, &localcopy_vpath, &mtime))
646 return;
648 do_execute_vpath = (localcopy_vpath == NULL) ? filename_vpath : localcopy_vpath;
650 extern_cmd_options =
651 execute_get_external_cmd_opts_from_config (command, do_execute_vpath, start_line);
653 if (extern_cmd_options != NULL)
655 char **argv_cmd_options;
656 int argv_count;
658 if (g_shell_parse_argv (extern_cmd_options, &argv_count, &argv_cmd_options, NULL))
660 do_executev (command, EXECUTE_INTERNAL, argv_cmd_options);
661 g_strfreev (argv_cmd_options);
663 else
664 do_executev (command, EXECUTE_INTERNAL, NULL);
666 g_free (extern_cmd_options);
669 execute_cleanup_with_vfs_arg (filename_vpath, &localcopy_vpath, &mtime);
672 /* --------------------------------------------------------------------------------------------- */