execute.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. /*
  2. Execution routines for GNU Midnight Commander
  3. Copyright (C) 2003-2017
  4. Free Software Foundation, Inc.
  5. Written by:
  6. Slava Zanko <slavazanko@gmail.com>, 2013
  7. This file is part of the Midnight Commander.
  8. The Midnight Commander is free software: you can redistribute it
  9. and/or modify it under the terms of the GNU General Public License as
  10. published by the Free Software Foundation, either version 3 of the License,
  11. or (at your option) any later version.
  12. The Midnight Commander is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. GNU General Public License for more details.
  16. You should have received a copy of the GNU General Public License
  17. along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. /** \file execute.c
  20. * \brief Source: execution routines
  21. */
  22. #include <config.h>
  23. #include <signal.h>
  24. #include <string.h>
  25. #include <sys/stat.h>
  26. #include <sys/time.h>
  27. #include "lib/global.h"
  28. #include "lib/tty/tty.h"
  29. #include "lib/tty/key.h"
  30. #include "lib/tty/win.h"
  31. #include "lib/vfs/vfs.h"
  32. #include "lib/mcconfig.h"
  33. #include "lib/util.h"
  34. #include "lib/strutil.h" /* str_replace_all_substrings() */
  35. #include "lib/widget.h"
  36. #include "filemanager/midnight.h"
  37. #include "filemanager/layout.h" /* use_dash() */
  38. #include "consaver/cons.saver.h"
  39. #ifdef ENABLE_SUBSHELL
  40. #include "subshell/subshell.h"
  41. #endif
  42. #include "setup.h" /* clear_before_exec */
  43. #include "execute.h"
  44. /*** global variables ****************************************************************************/
  45. int pause_after_run = pause_on_dumb_terminals;
  46. /*** file scope macro definitions ****************************************************************/
  47. /*** file scope type declarations ****************************************************************/
  48. /*** file scope variables ************************************************************************/
  49. /*** file scope functions ************************************************************************/
  50. void do_execute (const char *shell, const char *command, int flags);
  51. void do_executev (const char *shell, int flags, char *const argv[]);
  52. char *execute_get_external_cmd_opts_from_config (const char *command,
  53. const vfs_path_t * filename_vpath,
  54. long start_line);
  55. /* --------------------------------------------------------------------------------------------- */
  56. static void
  57. edition_post_exec (void)
  58. {
  59. tty_enter_ca_mode ();
  60. /* FIXME: Missing on slang endwin? */
  61. tty_reset_prog_mode ();
  62. tty_flush_input ();
  63. tty_keypad (TRUE);
  64. tty_raw_mode ();
  65. channels_up ();
  66. enable_mouse ();
  67. enable_bracketed_paste ();
  68. if (mc_global.tty.alternate_plus_minus)
  69. application_keypad_mode ();
  70. }
  71. /* --------------------------------------------------------------------------------------------- */
  72. static void
  73. edition_pre_exec (void)
  74. {
  75. if (clear_before_exec)
  76. clr_scr ();
  77. else
  78. {
  79. if (!(mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag))
  80. printf ("\n\n");
  81. }
  82. channels_down ();
  83. disable_mouse ();
  84. disable_bracketed_paste ();
  85. tty_reset_shell_mode ();
  86. tty_keypad (FALSE);
  87. tty_reset_screen ();
  88. numeric_keypad_mode ();
  89. /* on xterms: maybe endwin did not leave the terminal on the shell
  90. * screen page: do it now.
  91. *
  92. * Do not move this before endwin: in some systems rmcup includes
  93. * a call to clear screen, so it will end up clearing the shell screen.
  94. */
  95. tty_exit_ca_mode ();
  96. }
  97. /* --------------------------------------------------------------------------------------------- */
  98. #ifdef ENABLE_SUBSHELL
  99. static void
  100. do_possible_cd (const vfs_path_t * new_dir_vpath)
  101. {
  102. if (!do_cd (new_dir_vpath, cd_exact))
  103. message (D_ERROR, _("Warning"), "%s",
  104. _("The Commander can't change to the directory that\n"
  105. "the subshell claims you are in. Perhaps you have\n"
  106. "deleted your working directory, or given yourself\n"
  107. "extra access permissions with the \"su\" command?"));
  108. }
  109. #endif /* ENABLE_SUBSHELL */
  110. /* --------------------------------------------------------------------------------------------- */
  111. static void
  112. do_suspend_cmd (void)
  113. {
  114. pre_exec ();
  115. if (mc_global.tty.console_flag != '\0' && !mc_global.tty.use_subshell)
  116. handle_console (CONSOLE_RESTORE);
  117. #ifdef SIGTSTP
  118. {
  119. struct sigaction sigtstp_action;
  120. memset (&sigtstp_action, 0, sizeof (sigtstp_action));
  121. /* Make sure that the SIGTSTP below will suspend us directly,
  122. without calling ncurses' SIGTSTP handler; we *don't* want
  123. ncurses to redraw the screen immediately after the SIGCONT */
  124. sigaction (SIGTSTP, &startup_handler, &sigtstp_action);
  125. kill (getpid (), SIGTSTP);
  126. /* Restore previous SIGTSTP action */
  127. sigaction (SIGTSTP, &sigtstp_action, NULL);
  128. }
  129. #endif /* SIGTSTP */
  130. if (mc_global.tty.console_flag != '\0' && !mc_global.tty.use_subshell)
  131. handle_console (CONSOLE_SAVE);
  132. edition_post_exec ();
  133. }
  134. /* --------------------------------------------------------------------------------------------- */
  135. static gboolean
  136. execute_prepare_with_vfs_arg (const vfs_path_t * filename_vpath, vfs_path_t ** localcopy_vpath,
  137. time_t * mtime)
  138. {
  139. struct stat st;
  140. /* Simplest case, this file is local */
  141. if ((filename_vpath == NULL && vfs_file_is_local (vfs_get_raw_current_dir ()))
  142. || vfs_file_is_local (filename_vpath))
  143. return TRUE;
  144. /* FIXME: Creation of new files on VFS is not supported */
  145. if (filename_vpath == NULL)
  146. return FALSE;
  147. *localcopy_vpath = mc_getlocalcopy (filename_vpath);
  148. if (*localcopy_vpath == NULL)
  149. {
  150. message (D_ERROR, MSG_ERROR, _("Cannot fetch a local copy of %s"),
  151. vfs_path_as_str (filename_vpath));
  152. return FALSE;
  153. }
  154. mc_stat (*localcopy_vpath, &st);
  155. *mtime = st.st_mtime;
  156. return TRUE;
  157. }
  158. /* --------------------------------------------------------------------------------------------- */
  159. static void
  160. execute_cleanup_with_vfs_arg (const vfs_path_t * filename_vpath, vfs_path_t ** localcopy_vpath,
  161. time_t * mtime)
  162. {
  163. if (*localcopy_vpath != NULL)
  164. {
  165. struct stat st;
  166. /*
  167. * filename can be an entry on panel, it can be changed by executing
  168. * the command, so make a copy. Smarter VFS code would make the code
  169. * below unnecessary.
  170. */
  171. mc_stat (*localcopy_vpath, &st);
  172. mc_ungetlocalcopy (filename_vpath, *localcopy_vpath, *mtime != st.st_mtime);
  173. vfs_path_free (*localcopy_vpath);
  174. *localcopy_vpath = NULL;
  175. }
  176. }
  177. /* --------------------------------------------------------------------------------------------- */
  178. static char *
  179. execute_get_opts_from_cfg (const char *command, const char *default_str)
  180. {
  181. char *str_from_config;
  182. str_from_config =
  183. mc_config_get_string_raw (mc_global.main_config, CONFIG_EXT_EDITOR_VIEWER_SECTION, command,
  184. NULL);
  185. if (str_from_config == NULL)
  186. {
  187. mc_config_t *cfg;
  188. cfg = mc_config_init (global_profile_name, TRUE);
  189. if (cfg == NULL)
  190. return g_strdup (default_str);
  191. str_from_config =
  192. mc_config_get_string_raw (cfg, CONFIG_EXT_EDITOR_VIEWER_SECTION, command, default_str);
  193. mc_config_deinit (cfg);
  194. }
  195. return str_from_config;
  196. }
  197. /* --------------------------------------------------------------------------------------------- */
  198. /*** public functions ****************************************************************************/
  199. /* --------------------------------------------------------------------------------------------- */
  200. char *
  201. execute_get_external_cmd_opts_from_config (const char *command, const vfs_path_t * filename_vpath,
  202. long start_line)
  203. {
  204. char *str_from_config, *return_str;
  205. char *parameter;
  206. if (filename_vpath == NULL)
  207. return g_strdup ("");
  208. parameter = g_shell_quote (vfs_path_get_last_path_str (filename_vpath));
  209. if (start_line <= 0)
  210. return parameter;
  211. str_from_config = execute_get_opts_from_cfg (command, "%filename");
  212. return_str = str_replace_all (str_from_config, "%filename", parameter);
  213. g_free (parameter);
  214. g_free (str_from_config);
  215. str_from_config = return_str;
  216. parameter = g_strdup_printf ("%ld", start_line);
  217. return_str = str_replace_all (str_from_config, "%lineno", parameter);
  218. g_free (parameter);
  219. g_free (str_from_config);
  220. return return_str;
  221. }
  222. /* --------------------------------------------------------------------------------------------- */
  223. void
  224. do_executev (const char *shell, int flags, char *const argv[])
  225. {
  226. #ifdef ENABLE_SUBSHELL
  227. vfs_path_t *new_dir_vpath = NULL;
  228. #endif /* ENABLE_SUBSHELL */
  229. vfs_path_t *old_vfs_dir_vpath = NULL;
  230. if (!vfs_current_is_local ())
  231. old_vfs_dir_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
  232. if (mc_global.mc_run_mode == MC_RUN_FULL)
  233. save_cwds_stat ();
  234. pre_exec ();
  235. if (mc_global.tty.console_flag != '\0')
  236. handle_console (CONSOLE_RESTORE);
  237. if (!mc_global.tty.use_subshell && *argv != NULL && (flags & EXECUTE_INTERNAL) == 0)
  238. {
  239. printf ("%s%s\n", mc_prompt, *argv);
  240. fflush (stdout);
  241. }
  242. #ifdef ENABLE_SUBSHELL
  243. if (mc_global.tty.use_subshell && (flags & EXECUTE_INTERNAL) == 0)
  244. {
  245. do_update_prompt ();
  246. /* We don't care if it died, higher level takes care of this */
  247. invoke_subshell (*argv, VISIBLY, old_vfs_dir_vpath != NULL ? NULL : &new_dir_vpath);
  248. }
  249. else
  250. #endif /* ENABLE_SUBSHELL */
  251. my_systemv_flags (flags, shell, argv);
  252. if ((flags & EXECUTE_INTERNAL) == 0)
  253. {
  254. if ((pause_after_run == pause_always
  255. || (pause_after_run == pause_on_dumb_terminals && !mc_global.tty.xterm_flag
  256. && mc_global.tty.console_flag == '\0')) && quit == 0
  257. #ifdef ENABLE_SUBSHELL
  258. && subshell_state != RUNNING_COMMAND
  259. #endif /* ENABLE_SUBSHELL */
  260. )
  261. {
  262. printf ("%s", _("Press any key to continue..."));
  263. fflush (stdout);
  264. tty_raw_mode ();
  265. get_key_code (0);
  266. printf ("\r\n");
  267. fflush (stdout);
  268. }
  269. if (mc_global.tty.console_flag != '\0' && output_lines != 0 && mc_global.keybar_visible)
  270. {
  271. putchar ('\n');
  272. fflush (stdout);
  273. }
  274. }
  275. if (mc_global.tty.console_flag != '\0')
  276. handle_console (CONSOLE_SAVE);
  277. edition_post_exec ();
  278. #ifdef ENABLE_SUBSHELL
  279. if (new_dir_vpath != NULL)
  280. {
  281. do_possible_cd (new_dir_vpath);
  282. vfs_path_free (new_dir_vpath);
  283. }
  284. #endif /* ENABLE_SUBSHELL */
  285. if (old_vfs_dir_vpath != NULL)
  286. {
  287. mc_chdir (old_vfs_dir_vpath);
  288. vfs_path_free (old_vfs_dir_vpath);
  289. }
  290. if (mc_global.mc_run_mode == MC_RUN_FULL)
  291. {
  292. update_panels (UP_OPTIMIZE, UP_KEEPSEL);
  293. update_xterm_title_path ();
  294. }
  295. do_refresh ();
  296. use_dash (TRUE);
  297. }
  298. /* --------------------------------------------------------------------------------------------- */
  299. void
  300. do_execute (const char *shell, const char *command, int flags)
  301. {
  302. GPtrArray *args_array;
  303. args_array = g_ptr_array_new ();
  304. g_ptr_array_add (args_array, (char *) command);
  305. g_ptr_array_add (args_array, NULL);
  306. do_executev (shell, flags, (char *const *) args_array->pdata);
  307. g_ptr_array_free (args_array, TRUE);
  308. }
  309. /* --------------------------------------------------------------------------------------------- */
  310. /** Set up the terminal before executing a program */
  311. void
  312. pre_exec (void)
  313. {
  314. use_dash (FALSE);
  315. edition_pre_exec ();
  316. }
  317. /* --------------------------------------------------------------------------------------------- */
  318. /** Hide the terminal after executing a program */
  319. void
  320. post_exec (void)
  321. {
  322. edition_post_exec ();
  323. use_dash (TRUE);
  324. repaint_screen ();
  325. }
  326. /* --------------------------------------------------------------------------------------------- */
  327. /* Executes a command */
  328. void
  329. shell_execute (const char *command, int flags)
  330. {
  331. char *cmd = NULL;
  332. if (flags & EXECUTE_HIDE)
  333. {
  334. cmd = g_strconcat (" ", command, (char *) NULL);
  335. flags ^= EXECUTE_HIDE;
  336. }
  337. #ifdef ENABLE_SUBSHELL
  338. if (mc_global.tty.use_subshell)
  339. {
  340. if (subshell_state == INACTIVE)
  341. do_execute (mc_global.shell->path, cmd ? cmd : command, flags | EXECUTE_AS_SHELL);
  342. else
  343. message (D_ERROR, MSG_ERROR, "%s", _("The shell is already running a command"));
  344. }
  345. else
  346. #endif /* ENABLE_SUBSHELL */
  347. do_execute (mc_global.shell->path, cmd ? cmd : command, flags | EXECUTE_AS_SHELL);
  348. g_free (cmd);
  349. }
  350. /* --------------------------------------------------------------------------------------------- */
  351. void
  352. toggle_panels (void)
  353. {
  354. #ifdef ENABLE_SUBSHELL
  355. vfs_path_t *new_dir_vpath = NULL;
  356. #endif /* ENABLE_SUBSHELL */
  357. SIG_ATOMIC_VOLATILE_T was_sigwinch = 0;
  358. channels_down ();
  359. disable_mouse ();
  360. disable_bracketed_paste ();
  361. if (clear_before_exec)
  362. clr_scr ();
  363. if (mc_global.tty.alternate_plus_minus)
  364. numeric_keypad_mode ();
  365. #ifndef HAVE_SLANG
  366. /* With slang we don't want any of this, since there
  367. * is no raw_mode supported
  368. */
  369. tty_reset_shell_mode ();
  370. #endif /* !HAVE_SLANG */
  371. tty_noecho ();
  372. tty_keypad (FALSE);
  373. tty_reset_screen ();
  374. tty_exit_ca_mode ();
  375. tty_raw_mode ();
  376. if (mc_global.tty.console_flag != '\0')
  377. handle_console (CONSOLE_RESTORE);
  378. #ifdef ENABLE_SUBSHELL
  379. if (mc_global.tty.use_subshell)
  380. {
  381. vfs_path_t **new_dir_p;
  382. new_dir_p = vfs_current_is_local ()? &new_dir_vpath : NULL;
  383. invoke_subshell (NULL, VISIBLY, new_dir_p);
  384. }
  385. else
  386. #endif /* ENABLE_SUBSHELL */
  387. {
  388. if (output_starts_shell)
  389. {
  390. fputs (_("Type 'exit' to return to the Midnight Commander"), stderr);
  391. fputs ("\n\r\n\r", stderr);
  392. my_system (EXECUTE_INTERNAL, mc_global.shell->path, NULL);
  393. }
  394. else
  395. get_key_code (0);
  396. }
  397. if (mc_global.tty.console_flag != '\0')
  398. handle_console (CONSOLE_SAVE);
  399. tty_enter_ca_mode ();
  400. tty_reset_prog_mode ();
  401. tty_keypad (TRUE);
  402. /* Prevent screen flash when user did 'exit' or 'logout' within
  403. subshell */
  404. if ((quit & SUBSHELL_EXIT) != 0)
  405. {
  406. /* User did 'exit' or 'logout': quit MC */
  407. if (quiet_quit_cmd ())
  408. return;
  409. quit = 0;
  410. #ifdef ENABLE_SUBSHELL
  411. /* restart subshell */
  412. if (mc_global.tty.use_subshell)
  413. init_subshell ();
  414. #endif /* ENABLE_SUBSHELL */
  415. }
  416. enable_mouse ();
  417. enable_bracketed_paste ();
  418. channels_up ();
  419. if (mc_global.tty.alternate_plus_minus)
  420. application_keypad_mode ();
  421. /* HACK:
  422. * Save sigwinch flag that will be reset in mc_refresh() called via update_panels().
  423. * There is some problem with screen redraw in ncurses-based mc in this situation.
  424. */
  425. was_sigwinch = mc_global.tty.winch_flag;
  426. mc_global.tty.winch_flag = 0;
  427. #ifdef ENABLE_SUBSHELL
  428. if (mc_global.tty.use_subshell)
  429. {
  430. do_load_prompt ();
  431. if (new_dir_vpath != NULL)
  432. do_possible_cd (new_dir_vpath);
  433. if (mc_global.tty.console_flag != '\0' && output_lines)
  434. show_console_contents (output_start_y,
  435. LINES - mc_global.keybar_visible - output_lines -
  436. 1, LINES - mc_global.keybar_visible - 1);
  437. }
  438. vfs_path_free (new_dir_vpath);
  439. #endif /* ENABLE_SUBSHELL */
  440. if (mc_global.mc_run_mode == MC_RUN_FULL)
  441. {
  442. update_panels (UP_OPTIMIZE, UP_KEEPSEL);
  443. update_xterm_title_path ();
  444. }
  445. if (was_sigwinch != 0 || mc_global.tty.winch_flag != 0)
  446. dialog_change_screen_size ();
  447. else
  448. repaint_screen ();
  449. }
  450. /* --------------------------------------------------------------------------------------------- */
  451. /* event callback */
  452. gboolean
  453. execute_suspend (const gchar * event_group_name, const gchar * event_name,
  454. gpointer init_data, gpointer data)
  455. {
  456. (void) event_group_name;
  457. (void) event_name;
  458. (void) init_data;
  459. (void) data;
  460. if (mc_global.mc_run_mode == MC_RUN_FULL)
  461. save_cwds_stat ();
  462. do_suspend_cmd ();
  463. if (mc_global.mc_run_mode == MC_RUN_FULL)
  464. update_panels (UP_OPTIMIZE, UP_KEEPSEL);
  465. do_refresh ();
  466. return TRUE;
  467. }
  468. /* --------------------------------------------------------------------------------------------- */
  469. /**
  470. * Execute command on a filename that can be on VFS.
  471. * Errors are reported to the user.
  472. */
  473. void
  474. execute_with_vfs_arg (const char *command, const vfs_path_t * filename_vpath)
  475. {
  476. vfs_path_t *localcopy_vpath = NULL;
  477. const vfs_path_t *do_execute_vpath;
  478. time_t mtime;
  479. if (!execute_prepare_with_vfs_arg (filename_vpath, &localcopy_vpath, &mtime))
  480. return;
  481. do_execute_vpath = (localcopy_vpath == NULL) ? filename_vpath : localcopy_vpath;
  482. do_execute (command, vfs_path_get_last_path_str (do_execute_vpath), EXECUTE_INTERNAL);
  483. execute_cleanup_with_vfs_arg (filename_vpath, &localcopy_vpath, &mtime);
  484. }
  485. /* --------------------------------------------------------------------------------------------- */
  486. /**
  487. * Execute external editor or viewer.
  488. *
  489. * @param command editor/viewer to run
  490. * @param filename_vpath path for edit/view
  491. * @param start_line cursor will be placed at the 'start_line' position after opening file
  492. * if start_line is 0 or negative, no start line will be passed to editor/viewer
  493. */
  494. void
  495. execute_external_editor_or_viewer (const char *command, const vfs_path_t * filename_vpath,
  496. long start_line)
  497. {
  498. vfs_path_t *localcopy_vpath = NULL;
  499. const vfs_path_t *do_execute_vpath;
  500. char *extern_cmd_options;
  501. time_t mtime = 0;
  502. if (!execute_prepare_with_vfs_arg (filename_vpath, &localcopy_vpath, &mtime))
  503. return;
  504. do_execute_vpath = (localcopy_vpath == NULL) ? filename_vpath : localcopy_vpath;
  505. extern_cmd_options =
  506. execute_get_external_cmd_opts_from_config (command, do_execute_vpath, start_line);
  507. if (extern_cmd_options != NULL)
  508. {
  509. char **argv_cmd_options;
  510. int argv_count;
  511. if (g_shell_parse_argv (extern_cmd_options, &argv_count, &argv_cmd_options, NULL))
  512. {
  513. do_executev (command, EXECUTE_INTERNAL, argv_cmd_options);
  514. g_strfreev (argv_cmd_options);
  515. }
  516. else
  517. do_executev (command, EXECUTE_INTERNAL, NULL);
  518. g_free (extern_cmd_options);
  519. }
  520. execute_cleanup_with_vfs_arg (filename_vpath, &localcopy_vpath, &mtime);
  521. }
  522. /* --------------------------------------------------------------------------------------------- */