Fix segfault on switch to subshell in mcedit/mcview/mcdiffview.
[midnight-commander.git] / src / main.c
blobd691bcb2a72df3f394513bdf7872ea22787bd47a
1 /*
2 Main program for the Midnight Commander
4 Copyright (C) 1994-2020
5 Free Software Foundation, Inc.
7 Written by:
8 Miguel de Icaza, 1994, 1995, 1996, 1997
9 Janne Kukonlehto, 1994, 1995
10 Norbert Warmuth, 1997
12 This file is part of the Midnight Commander.
14 The Midnight Commander is free software: you can redistribute it
15 and/or modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation, either version 3 of the License,
17 or (at your option) any later version.
19 The Midnight Commander is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
24 You should have received a copy of the GNU General Public License
25 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 /** \file main.c
29 * \brief Source: this is a main module
32 #include <config.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <locale.h>
37 #include <pwd.h> /* for username in xterm title */
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/types.h>
42 #include <sys/wait.h>
43 #include <signal.h>
44 #include <unistd.h> /* getsid() */
46 #include "lib/global.h"
48 #include "lib/event.h"
49 #include "lib/tty/tty.h"
50 #include "lib/tty/key.h" /* For init_key() */
51 #include "lib/tty/mouse.h" /* init_mouse() */
52 #include "lib/skin.h"
53 #include "lib/filehighlight.h"
54 #include "lib/fileloc.h"
55 #include "lib/strutil.h"
56 #include "lib/util.h"
57 #include "lib/vfs/vfs.h" /* vfs_init(), vfs_shut() */
59 #include "filemanager/filemanager.h"
60 #include "filemanager/treestore.h" /* tree_store_save */
61 #include "filemanager/layout.h"
62 #include "filemanager/ext.h" /* flush_extension_file() */
63 #include "filemanager/command.h" /* cmdline */
64 #include "filemanager/panel.h" /* panalized_panel */
66 #include "vfs/plugins_init.h"
68 #include "events_init.h"
69 #include "args.h"
70 #ifdef ENABLE_SUBSHELL
71 #include "subshell/subshell.h"
72 #endif
73 #include "setup.h" /* load_setup() */
75 #ifdef HAVE_CHARSET
76 #include "lib/charsets.h"
77 #include "selcodepage.h"
78 #endif /* HAVE_CHARSET */
80 #include "consaver/cons.saver.h" /* cons_saver_pid */
82 /*** global variables ****************************************************************************/
84 /*** file scope macro definitions ****************************************************************/
86 /*** file scope type declarations ****************************************************************/
88 /*** file scope variables ************************************************************************/
90 /*** file scope functions ************************************************************************/
91 /* --------------------------------------------------------------------------------------------- */
93 static void
94 check_codeset (void)
96 const char *current_system_codepage = NULL;
98 current_system_codepage = str_detect_termencoding ();
100 #ifdef HAVE_CHARSET
102 const char *_display_codepage;
104 _display_codepage = get_codepage_id (mc_global.display_codepage);
106 if (strcmp (_display_codepage, current_system_codepage) != 0)
108 mc_global.display_codepage = get_codepage_index (current_system_codepage);
109 if (mc_global.display_codepage == -1)
110 mc_global.display_codepage = 0;
112 mc_config_set_string (mc_global.main_config, CONFIG_MISC_SECTION, "display_codepage",
113 cp_display);
116 #endif
118 mc_global.utf8_display = str_isutf8 (current_system_codepage);
121 /* --------------------------------------------------------------------------------------------- */
122 /** POSIX version. The only version we support. */
124 static void
125 OS_Setup (void)
127 const char *datadir_env;
129 mc_shell_init ();
131 /* This is the directory, where MC was installed, on Unix this is DATADIR */
132 /* and can be overriden by the MC_DATADIR environment variable */
133 datadir_env = g_getenv ("MC_DATADIR");
134 if (datadir_env != NULL)
135 mc_global.sysconfig_dir = g_strdup (datadir_env);
136 else
137 mc_global.sysconfig_dir = g_strdup (SYSCONFDIR);
139 mc_global.share_data_dir = g_strdup (DATADIR);
142 /* --------------------------------------------------------------------------------------------- */
144 static void
145 sigchld_handler_no_subshell (int sig)
147 #ifdef __linux__
148 int pid, status;
150 if (mc_global.tty.console_flag == '\0')
151 return;
153 /* COMMENT: if it were true that after the call to handle_console(..INIT)
154 the value of mc_global.tty.console_flag never changed, we could simply not install
155 this handler at all if (!mc_global.tty.console_flag && !mc_global.tty.use_subshell). */
157 /* That comment is no longer true. We need to wait() on a sigchld
158 handler (that's at least what the tarfs code expects currently). */
160 pid = waitpid (cons_saver_pid, &status, WUNTRACED | WNOHANG);
162 if (pid == cons_saver_pid)
164 if (WIFSTOPPED (status))
166 /* Someone has stopped cons.saver - restart it */
167 kill (pid, SIGCONT);
169 else
171 /* cons.saver has died - disable console saving */
172 handle_console (CONSOLE_DONE);
173 mc_global.tty.console_flag = '\0';
176 /* If we got here, some other child exited; ignore it */
177 #endif /* __linux__ */
179 (void) sig;
182 /* --------------------------------------------------------------------------------------------- */
184 static void
185 init_sigchld (void)
187 struct sigaction sigchld_action;
189 memset (&sigchld_action, 0, sizeof (sigchld_action));
190 sigchld_action.sa_handler =
191 #ifdef ENABLE_SUBSHELL
192 mc_global.tty.use_subshell ? sigchld_handler :
193 #endif /* ENABLE_SUBSHELL */
194 sigchld_handler_no_subshell;
196 sigemptyset (&sigchld_action.sa_mask);
198 #ifdef SA_RESTART
199 sigchld_action.sa_flags = SA_RESTART;
200 #endif /* !SA_RESTART */
202 if (sigaction (SIGCHLD, &sigchld_action, NULL) == -1)
204 #ifdef ENABLE_SUBSHELL
206 * This may happen on QNX Neutrino 6, where SA_RESTART
207 * is defined but not implemented. Fallback to no subshell.
209 mc_global.tty.use_subshell = FALSE;
210 #endif /* ENABLE_SUBSHELL */
214 /* --------------------------------------------------------------------------------------------- */
216 * Check MC_SID to prevent running one mc from another.
218 * @return TRUE if no parent mc in our session was found, FALSE otherwise.
221 static gboolean
222 check_sid (void)
224 pid_t my_sid, old_sid;
225 const char *sid_str;
227 sid_str = getenv ("MC_SID");
228 if (sid_str == NULL)
229 return TRUE;
231 old_sid = (pid_t) strtol (sid_str, NULL, 0);
232 if (old_sid == 0)
233 return TRUE;
235 my_sid = getsid (0);
236 if (my_sid == -1)
237 return TRUE;
239 /* The parent mc is in a different session, it's OK */
240 return (old_sid != my_sid);
243 /* --------------------------------------------------------------------------------------------- */
244 /*** public functions ****************************************************************************/
245 /* --------------------------------------------------------------------------------------------- */
248 main (int argc, char *argv[])
250 GError *mcerror = NULL;
251 gboolean config_migrated = FALSE;
252 char *config_migrate_msg = NULL;
253 int exit_code = EXIT_FAILURE;
255 mc_global.run_from_parent_mc = !check_sid ();
257 /* We had LC_CTYPE before, LC_ALL includs LC_TYPE as well */
258 #ifdef HAVE_SETLOCALE
259 (void) setlocale (LC_ALL, "");
260 #endif
261 (void) bindtextdomain (PACKAGE, LOCALEDIR);
262 (void) textdomain (PACKAGE);
264 /* do this before args parsing */
265 str_init_strings (NULL);
267 mc_setup_run_mode (argv); /* are we mc? editor? viewer? etc... */
269 if (!mc_args_parse (&argc, &argv, "mc", &mcerror))
271 startup_exit_falure:
272 fprintf (stderr, _("Failed to run:\n%s\n"), mcerror->message);
273 g_error_free (mcerror);
274 startup_exit_ok:
275 mc_shell_deinit ();
276 str_uninit_strings ();
277 return exit_code;
280 /* do this before mc_args_show_info () to view paths in the --datadir-info output */
281 OS_Setup ();
283 if (!g_path_is_absolute (mc_config_get_home_dir ()))
285 mc_propagate_error (&mcerror, 0, "%s: %s", _("Home directory path is not absolute"),
286 mc_config_get_home_dir ());
287 mc_event_deinit (NULL);
288 goto startup_exit_falure;
291 if (!mc_args_show_info ())
293 exit_code = EXIT_SUCCESS;
294 goto startup_exit_ok;
297 if (!events_init (&mcerror))
298 goto startup_exit_falure;
300 mc_config_init_config_paths (&mcerror);
301 config_migrated = mc_config_migrate_from_old_place (&mcerror, &config_migrate_msg);
302 if (mcerror != NULL)
304 mc_event_deinit (NULL);
305 goto startup_exit_falure;
308 vfs_init ();
309 vfs_plugins_init ();
311 load_setup ();
313 /* Must be done after load_setup because depends on mc_global.vfs.cd_symlinks */
314 vfs_setup_work_dir ();
316 /* Set up temporary directory after VFS initialization */
317 mc_tmpdir ();
319 /* do this after vfs initialization and vfs working directory setup
320 due to mc_setctl() and mcedit_arg_vpath_new() calls in mc_setup_by_args() */
321 if (!mc_setup_by_args (argc, argv, &mcerror))
323 vfs_shut ();
324 done_setup ();
325 g_free (saved_other_dir);
326 mc_event_deinit (NULL);
327 goto startup_exit_falure;
330 /* Resolve the other_dir panel option.
331 * 1. Must be done after vfs_setup_work_dir().
332 * 2. Must be done after mc_setup_by_args() because of mc_run_mode.
334 if (mc_global.mc_run_mode == MC_RUN_FULL)
336 char *buffer;
337 vfs_path_t *vpath;
339 buffer = mc_config_get_string (mc_global.panels_config, "Dirs", "other_dir", ".");
340 vpath = vfs_path_from_str (buffer);
341 if (vfs_file_is_local (vpath))
342 saved_other_dir = buffer;
343 else
344 g_free (buffer);
345 vfs_path_free (vpath);
348 /* check terminal type
349 * $TERM must be set and not empty
350 * mc_global.tty.xterm_flag is used in init_key() and tty_init()
351 * Do this after mc_args_handle() where mc_args__force_xterm is set up.
353 mc_global.tty.xterm_flag = tty_check_term (mc_args__force_xterm);
355 /* NOTE: This has to be called before tty_init or whatever routine
356 calls any define_sequence */
357 init_key ();
359 /* Must be done before installing the SIGCHLD handler [[FIXME]] */
360 handle_console (CONSOLE_INIT);
362 #ifdef ENABLE_SUBSHELL
363 /* Disallow subshell when invoked as standalone viewer or editor from running mc */
364 if (mc_global.mc_run_mode != MC_RUN_FULL && mc_global.run_from_parent_mc)
365 mc_global.tty.use_subshell = FALSE;
367 if (mc_global.tty.use_subshell)
368 subshell_get_console_attributes ();
369 #endif /* ENABLE_SUBSHELL */
371 /* Install the SIGCHLD handler; must be done before init_subshell() */
372 init_sigchld ();
374 /* We need this, since ncurses endwin () doesn't restore the signals */
375 save_stop_handler ();
377 /* Must be done before init_subshell, to set up the terminal size: */
378 /* FIXME: Should be removed and LINES and COLS computed on subshell */
379 tty_init (!mc_args__nomouse, mc_global.tty.xterm_flag);
381 /* start check mc_global.display_codepage and mc_global.source_codepage */
382 check_codeset ();
384 /* Removing this from the X code let's us type C-c */
385 load_key_defs ();
387 load_keymap_defs (!mc_args__nokeymap);
389 #ifdef USE_INTERNAL_EDIT
390 macros_list = g_array_new (TRUE, FALSE, sizeof (macros_t));
391 #endif /* USE_INTERNAL_EDIT */
393 tty_init_colors (mc_global.tty.disable_colors, mc_args__force_colors);
395 mc_skin_init (NULL, &mcerror);
396 dlg_set_default_colors ();
397 input_set_default_colors ();
398 if (mc_global.mc_run_mode == MC_RUN_FULL)
399 command_set_default_colors ();
401 mc_error_message (&mcerror, NULL);
403 #ifdef ENABLE_SUBSHELL
404 /* Done here to ensure that the subshell doesn't */
405 /* inherit the file descriptors opened below, etc */
406 if (mc_global.tty.use_subshell && mc_global.run_from_parent_mc)
408 int r;
410 r = query_dialog (_("Warning"),
411 _("GNU Midnight Commander\nis already running on this terminal.\n"
412 "Subshell support will be disabled."),
413 D_ERROR, 2, _("&OK"), _("&Quit"));
414 if (r == 0)
416 /* parent mc was found and the user wants to continue */
419 else
421 /* parent mc was found and the user wants to quit mc */
422 mc_global.midnight_shutdown = TRUE;
425 mc_global.tty.use_subshell = FALSE;
428 if (mc_global.tty.use_subshell)
429 init_subshell ();
430 #endif /* ENABLE_SUBSHELL */
432 if (!mc_global.midnight_shutdown)
434 /* Also done after init_subshell, to save any shell init file messages */
435 if (mc_global.tty.console_flag != '\0')
436 handle_console (CONSOLE_SAVE);
438 if (mc_global.tty.alternate_plus_minus)
439 application_keypad_mode ();
441 /* Done after subshell initialization to allow select and paste text by mouse
442 w/o Shift button in subshell in the native console */
443 init_mouse ();
445 /* Done after tty_enter_ca_mode (tty_init) because in VTE bracketed mode is
446 separate for the normal and alternate screens */
447 enable_bracketed_paste ();
449 /* subshell_prompt is NULL here */
450 mc_prompt = (geteuid () == 0) ? "# " : "$ ";
452 if (config_migrated)
454 message (D_ERROR, _("Warning"), "%s", config_migrate_msg);
455 g_free (config_migrate_msg);
459 /* Program main loop */
460 if (mc_global.midnight_shutdown)
461 exit_code = EXIT_SUCCESS;
462 else
463 exit_code = do_nc ()? EXIT_SUCCESS : EXIT_FAILURE;
465 disable_bracketed_paste ();
467 disable_mouse ();
469 /* Save the tree store */
470 (void) tree_store_save ();
472 free_keymap_defs ();
474 /* Virtual File System shutdown */
475 vfs_shut ();
477 flush_extension_file (); /* does only free memory */
479 mc_skin_deinit ();
480 tty_colors_done ();
482 tty_shutdown ();
484 done_setup ();
486 if (mc_global.tty.console_flag != '\0' && (quit & SUBSHELL_EXIT) == 0)
487 handle_console (CONSOLE_RESTORE);
488 if (mc_global.tty.alternate_plus_minus)
489 numeric_keypad_mode ();
491 (void) signal (SIGCHLD, SIG_DFL); /* Disable the SIGCHLD handler */
493 if (mc_global.tty.console_flag != '\0')
494 handle_console (CONSOLE_DONE);
496 if (mc_global.mc_run_mode == MC_RUN_FULL && mc_args__last_wd_file != NULL
497 && last_wd_string != NULL && !print_last_revert)
499 int last_wd_fd;
501 last_wd_fd = open (mc_args__last_wd_file, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,
502 S_IRUSR | S_IWUSR);
503 if (last_wd_fd != -1)
505 ssize_t ret1;
506 int ret2;
507 ret1 = write (last_wd_fd, last_wd_string, strlen (last_wd_string));
508 ret2 = close (last_wd_fd);
509 (void) ret1;
510 (void) ret2;
513 g_free (last_wd_string);
515 mc_shell_deinit ();
517 done_key ();
519 #ifdef USE_INTERNAL_EDIT
520 if (macros_list != NULL)
522 guint i;
524 for (i = 0; i < macros_list->len; i++)
526 macros_t *macros;
528 macros = &g_array_index (macros_list, struct macros_t, i);
529 if (macros != NULL && macros->macro != NULL)
530 (void) g_array_free (macros->macro, TRUE);
532 (void) g_array_free (macros_list, TRUE);
534 #endif /* USE_INTERNAL_EDIT */
536 str_uninit_strings ();
538 if (mc_global.mc_run_mode != MC_RUN_EDITOR)
539 g_free (mc_run_param0);
540 else
541 g_list_free_full ((GList *) mc_run_param0, (GDestroyNotify) mcedit_arg_free);
543 g_free (mc_run_param1);
544 g_free (saved_other_dir);
546 mc_config_deinit_config_paths ();
548 (void) mc_event_deinit (&mcerror);
549 if (mcerror != NULL)
551 fprintf (stderr, _("\nFailed while close:\n%s\n"), mcerror->message);
552 g_error_free (mcerror);
553 exit_code = EXIT_FAILURE;
556 (void) putchar ('\n'); /* Hack to make shell's prompt start at left of screen */
558 return exit_code;
561 /* --------------------------------------------------------------------------------------------- */