summaryrefslogtreecommitdiffstats
path: root/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c556
1 files changed, 556 insertions, 0 deletions
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..c18e069
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,556 @@
+/*
+ Main program for the Midnight Commander
+
+ Copyright (C) 1994-2023
+ Free Software Foundation, Inc.
+
+ Written by:
+ Miguel de Icaza, 1994, 1995, 1996, 1997
+ Janne Kukonlehto, 1994, 1995
+ Norbert Warmuth, 1997
+
+ This file is part of the Midnight Commander.
+
+ The Midnight Commander is free software: you can redistribute it
+ and/or modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the License,
+ or (at your option) any later version.
+
+ The Midnight Commander is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file main.c
+ * \brief Source: this is a main module
+ */
+
+#include <config.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <locale.h>
+#include <pwd.h> /* for username in xterm title */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <unistd.h> /* getsid() */
+
+#include "lib/global.h"
+
+#include "lib/event.h"
+#include "lib/tty/tty.h"
+#include "lib/tty/key.h" /* For init_key() */
+#include "lib/tty/mouse.h" /* init_mouse() */
+#include "lib/skin.h"
+#include "lib/filehighlight.h"
+#include "lib/fileloc.h"
+#include "lib/strutil.h"
+#include "lib/util.h"
+#include "lib/vfs/vfs.h" /* vfs_init(), vfs_shut() */
+
+#include "filemanager/filemanager.h"
+#include "filemanager/treestore.h" /* tree_store_save */
+#include "filemanager/layout.h"
+#include "filemanager/ext.h" /* flush_extension_file() */
+#include "filemanager/command.h" /* cmdline */
+#include "filemanager/panel.h" /* panalized_panel */
+
+#include "vfs/plugins_init.h"
+
+#include "events_init.h"
+#include "args.h"
+#ifdef ENABLE_SUBSHELL
+#include "subshell/subshell.h"
+#endif
+#include "keymap.h"
+#include "setup.h" /* load_setup() */
+
+#ifdef HAVE_CHARSET
+#include "lib/charsets.h"
+#include "selcodepage.h"
+#endif /* HAVE_CHARSET */
+
+#include "consaver/cons.saver.h" /* cons_saver_pid */
+
+/*** global variables ****************************************************************************/
+
+/*** file scope macro definitions ****************************************************************/
+
+/*** file scope type declarations ****************************************************************/
+
+/*** forward declarations (file scope functions) *************************************************/
+
+/*** file scope variables ************************************************************************/
+
+/* --------------------------------------------------------------------------------------------- */
+/*** file scope functions ************************************************************************/
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+check_codeset (void)
+{
+ const char *current_system_codepage = NULL;
+
+ current_system_codepage = str_detect_termencoding ();
+
+#ifdef HAVE_CHARSET
+ {
+ const char *_display_codepage;
+
+ _display_codepage = get_codepage_id (mc_global.display_codepage);
+
+ if (strcmp (_display_codepage, current_system_codepage) != 0)
+ {
+ mc_global.display_codepage = get_codepage_index (current_system_codepage);
+ if (mc_global.display_codepage == -1)
+ mc_global.display_codepage = 0;
+
+ mc_config_set_string (mc_global.main_config, CONFIG_MISC_SECTION, "display_codepage",
+ cp_display);
+ }
+ }
+#endif
+
+ mc_global.utf8_display = str_isutf8 (current_system_codepage);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/** POSIX version. The only version we support. */
+
+static void
+OS_Setup (void)
+{
+ const char *datadir_env;
+
+ mc_shell_init ();
+
+ /* This is the directory, where MC was installed, on Unix this is DATADIR */
+ /* and can be overridden by the MC_DATADIR environment variable */
+ datadir_env = g_getenv ("MC_DATADIR");
+ if (datadir_env != NULL)
+ mc_global.sysconfig_dir = g_strdup (datadir_env);
+ else
+ mc_global.sysconfig_dir = g_strdup (SYSCONFDIR);
+
+ mc_global.share_data_dir = g_strdup (DATADIR);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+sigchld_handler_no_subshell (int sig)
+{
+#ifdef __linux__
+ int pid, status;
+
+ if (mc_global.tty.console_flag == '\0')
+ return;
+
+ /* COMMENT: if it were true that after the call to handle_console(..INIT)
+ the value of mc_global.tty.console_flag never changed, we could simply not install
+ this handler at all if (!mc_global.tty.console_flag && !mc_global.tty.use_subshell). */
+
+ /* That comment is no longer true. We need to wait() on a sigchld
+ handler (that's at least what the tarfs code expects currently). */
+
+ pid = waitpid (cons_saver_pid, &status, WUNTRACED | WNOHANG);
+
+ if (pid == cons_saver_pid)
+ {
+ if (WIFSTOPPED (status))
+ {
+ /* Someone has stopped cons.saver - restart it */
+ kill (pid, SIGCONT);
+ }
+ else
+ {
+ /* cons.saver has died - disable console saving */
+ handle_console (CONSOLE_DONE);
+ mc_global.tty.console_flag = '\0';
+ }
+ }
+ /* If we got here, some other child exited; ignore it */
+#endif /* __linux__ */
+
+ (void) sig;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+init_sigchld (void)
+{
+ struct sigaction sigchld_action;
+
+ memset (&sigchld_action, 0, sizeof (sigchld_action));
+ sigchld_action.sa_handler =
+#ifdef ENABLE_SUBSHELL
+ mc_global.tty.use_subshell ? sigchld_handler :
+#endif /* ENABLE_SUBSHELL */
+ sigchld_handler_no_subshell;
+
+ sigemptyset (&sigchld_action.sa_mask);
+
+#ifdef SA_RESTART
+ sigchld_action.sa_flags = SA_RESTART;
+#endif /* !SA_RESTART */
+
+ if (sigaction (SIGCHLD, &sigchld_action, NULL) == -1)
+ {
+#ifdef ENABLE_SUBSHELL
+ /*
+ * This may happen on QNX Neutrino 6, where SA_RESTART
+ * is defined but not implemented. Fallback to no subshell.
+ */
+ mc_global.tty.use_subshell = FALSE;
+#endif /* ENABLE_SUBSHELL */
+ }
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Check MC_SID to prevent running one mc from another.
+ *
+ * @return TRUE if no parent mc in our session was found, FALSE otherwise.
+ */
+
+static gboolean
+check_sid (void)
+{
+ pid_t my_sid, old_sid;
+ const char *sid_str;
+
+ sid_str = getenv ("MC_SID");
+ if (sid_str == NULL)
+ return TRUE;
+
+ old_sid = (pid_t) strtol (sid_str, NULL, 0);
+ if (old_sid == 0)
+ return TRUE;
+
+ my_sid = getsid (0);
+ if (my_sid == -1)
+ return TRUE;
+
+ /* The parent mc is in a different session, it's OK */
+ return (old_sid != my_sid);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/*** public functions ****************************************************************************/
+/* --------------------------------------------------------------------------------------------- */
+
+int
+main (int argc, char *argv[])
+{
+ GError *mcerror = NULL;
+ int exit_code = EXIT_FAILURE;
+
+ mc_global.run_from_parent_mc = !check_sid ();
+
+ /* We had LC_CTYPE before, LC_ALL includs LC_TYPE as well */
+#ifdef HAVE_SETLOCALE
+ (void) setlocale (LC_ALL, "");
+#endif
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
+ (void) textdomain (PACKAGE);
+
+ /* do this before args parsing */
+ str_init_strings (NULL);
+
+ mc_setup_run_mode (argv); /* are we mc? editor? viewer? etc... */
+
+ if (!mc_args_parse (&argc, &argv, "mc", &mcerror))
+ {
+ startup_exit_falure:
+ fprintf (stderr, _("Failed to run:\n%s\n"), mcerror->message);
+ g_error_free (mcerror);
+ startup_exit_ok:
+ mc_shell_deinit ();
+ str_uninit_strings ();
+ return exit_code;
+ }
+
+ /* do this before mc_args_show_info () to view paths in the --datadir-info output */
+ OS_Setup ();
+
+ if (!g_path_is_absolute (mc_config_get_home_dir ()))
+ {
+ mc_propagate_error (&mcerror, 0, "%s: %s", _("Home directory path is not absolute"),
+ mc_config_get_home_dir ());
+ mc_event_deinit (NULL);
+ goto startup_exit_falure;
+ }
+
+ if (!mc_args_show_info ())
+ {
+ exit_code = EXIT_SUCCESS;
+ goto startup_exit_ok;
+ }
+
+ if (!events_init (&mcerror))
+ goto startup_exit_falure;
+
+ mc_config_init_config_paths (&mcerror);
+ if (mcerror != NULL)
+ {
+ mc_event_deinit (NULL);
+ goto startup_exit_falure;
+ }
+
+ vfs_init ();
+ vfs_plugins_init ();
+
+ load_setup ();
+
+ /* Must be done after load_setup because depends on mc_global.vfs.cd_symlinks */
+ vfs_setup_work_dir ();
+
+ /* Set up temporary directory after VFS initialization */
+ mc_tmpdir ();
+
+ /* do this after vfs initialization and vfs working directory setup
+ due to mc_setctl() and mcedit_arg_vpath_new() calls in mc_setup_by_args() */
+ if (!mc_setup_by_args (argc, argv, &mcerror))
+ {
+ vfs_shut ();
+ done_setup ();
+ g_free (saved_other_dir);
+ mc_event_deinit (NULL);
+ goto startup_exit_falure;
+ }
+
+ /* Resolve the other_dir panel option.
+ * 1. Must be done after vfs_setup_work_dir().
+ * 2. Must be done after mc_setup_by_args() because of mc_run_mode.
+ */
+ if (mc_global.mc_run_mode == MC_RUN_FULL)
+ {
+ char *buffer;
+ vfs_path_t *vpath;
+
+ buffer = mc_config_get_string (mc_global.panels_config, "Dirs", "other_dir", ".");
+ vpath = vfs_path_from_str (buffer);
+ if (vfs_file_is_local (vpath))
+ saved_other_dir = buffer;
+ else
+ g_free (buffer);
+ vfs_path_free (vpath, TRUE);
+ }
+
+ /* check terminal type
+ * $TERM must be set and not empty
+ * mc_global.tty.xterm_flag is used in init_key() and tty_init()
+ * Do this after mc_args_handle() where mc_args__force_xterm is set up.
+ */
+ mc_global.tty.xterm_flag = tty_check_term (mc_args__force_xterm);
+
+ /* NOTE: This has to be called before tty_init or whatever routine
+ calls any define_sequence */
+ init_key ();
+
+ /* Must be done before installing the SIGCHLD handler [[FIXME]] */
+ handle_console (CONSOLE_INIT);
+
+#ifdef ENABLE_SUBSHELL
+ /* Disallow subshell when invoked as standalone viewer or editor from running mc */
+ if (mc_global.mc_run_mode != MC_RUN_FULL && mc_global.run_from_parent_mc)
+ mc_global.tty.use_subshell = FALSE;
+
+ if (mc_global.tty.use_subshell)
+ subshell_get_console_attributes ();
+#endif /* ENABLE_SUBSHELL */
+
+ /* Install the SIGCHLD handler; must be done before init_subshell() */
+ init_sigchld ();
+
+ /* We need this, since ncurses endwin () doesn't restore the signals */
+ save_stop_handler ();
+
+ /* Must be done before init_subshell, to set up the terminal size: */
+ /* FIXME: Should be removed and LINES and COLS computed on subshell */
+ tty_init (!mc_args__nomouse, mc_global.tty.xterm_flag);
+
+ /* start check mc_global.display_codepage and mc_global.source_codepage */
+ check_codeset ();
+
+ /* Removing this from the X code let's us type C-c */
+ load_key_defs ();
+
+ keymap_load (!mc_args__nokeymap);
+
+#ifdef USE_INTERNAL_EDIT
+ macros_list = g_array_new (TRUE, FALSE, sizeof (macros_t));
+#endif /* USE_INTERNAL_EDIT */
+
+ tty_init_colors (mc_global.tty.disable_colors, mc_args__force_colors);
+
+ mc_skin_init (NULL, &mcerror);
+ dlg_set_default_colors ();
+ input_set_default_colors ();
+ if (mc_global.mc_run_mode == MC_RUN_FULL)
+ command_set_default_colors ();
+
+ mc_error_message (&mcerror, NULL);
+
+#ifdef ENABLE_SUBSHELL
+ /* Done here to ensure that the subshell doesn't */
+ /* inherit the file descriptors opened below, etc */
+ if (mc_global.tty.use_subshell && mc_global.run_from_parent_mc)
+ {
+ int r;
+
+ r = query_dialog (_("Warning"),
+ _("GNU Midnight Commander\nis already running on this terminal.\n"
+ "Subshell support will be disabled."),
+ D_ERROR, 2, _("&OK"), _("&Quit"));
+ if (r == 0)
+ {
+ /* parent mc was found and the user wants to continue */
+ ;
+ }
+ else
+ {
+ /* parent mc was found and the user wants to quit mc */
+ mc_global.midnight_shutdown = TRUE;
+ }
+
+ mc_global.tty.use_subshell = FALSE;
+ }
+
+ if (mc_global.tty.use_subshell)
+ init_subshell ();
+#endif /* ENABLE_SUBSHELL */
+
+ if (!mc_global.midnight_shutdown)
+ {
+ /* Also done after init_subshell, to save any shell init file messages */
+ if (mc_global.tty.console_flag != '\0')
+ handle_console (CONSOLE_SAVE);
+
+ if (mc_global.tty.alternate_plus_minus)
+ application_keypad_mode ();
+
+ /* Done after subshell initialization to allow select and paste text by mouse
+ w/o Shift button in subshell in the native console */
+ init_mouse ();
+
+ /* Done after tty_enter_ca_mode (tty_init) because in VTE bracketed mode is
+ separate for the normal and alternate screens */
+ enable_bracketed_paste ();
+
+ /* subshell_prompt is NULL here */
+ mc_prompt = (geteuid () == 0) ? "# " : "$ ";
+ }
+
+ /* Program main loop */
+ if (mc_global.midnight_shutdown)
+ exit_code = EXIT_SUCCESS;
+ else
+ exit_code = do_nc ()? EXIT_SUCCESS : EXIT_FAILURE;
+
+ disable_bracketed_paste ();
+
+ disable_mouse ();
+
+ /* Save the tree store */
+ (void) tree_store_save ();
+
+ keymap_free ();
+
+ /* Virtual File System shutdown */
+ vfs_shut ();
+
+ flush_extension_file (); /* does only free memory */
+
+ mc_skin_deinit ();
+ tty_colors_done ();
+
+ tty_shutdown ();
+
+ done_setup ();
+
+ if (mc_global.tty.console_flag != '\0' && (quit & SUBSHELL_EXIT) == 0)
+ handle_console (CONSOLE_RESTORE);
+ if (mc_global.tty.alternate_plus_minus)
+ numeric_keypad_mode ();
+
+ (void) signal (SIGCHLD, SIG_DFL); /* Disable the SIGCHLD handler */
+
+ if (mc_global.tty.console_flag != '\0')
+ handle_console (CONSOLE_DONE);
+
+ if (mc_global.mc_run_mode == MC_RUN_FULL && mc_args__last_wd_file != NULL
+ && last_wd_string != NULL && !print_last_revert)
+ {
+ int last_wd_fd;
+
+ last_wd_fd = open (mc_args__last_wd_file, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,
+ S_IRUSR | S_IWUSR);
+ if (last_wd_fd != -1)
+ {
+ ssize_t ret1;
+ int ret2;
+ ret1 = write (last_wd_fd, last_wd_string, strlen (last_wd_string));
+ ret2 = close (last_wd_fd);
+ (void) ret1;
+ (void) ret2;
+ }
+ }
+ g_free (last_wd_string);
+
+ mc_shell_deinit ();
+
+ done_key ();
+
+#ifdef USE_INTERNAL_EDIT
+ if (macros_list != NULL)
+ {
+ guint i;
+
+ for (i = 0; i < macros_list->len; i++)
+ {
+ macros_t *macros;
+
+ macros = &g_array_index (macros_list, struct macros_t, i);
+ if (macros != NULL && macros->macro != NULL)
+ (void) g_array_free (macros->macro, TRUE);
+ }
+ (void) g_array_free (macros_list, TRUE);
+ }
+#endif /* USE_INTERNAL_EDIT */
+
+ str_uninit_strings ();
+
+ if (mc_global.mc_run_mode != MC_RUN_EDITOR)
+ g_free (mc_run_param0);
+ else
+ g_list_free_full ((GList *) mc_run_param0, (GDestroyNotify) mcedit_arg_free);
+
+ g_free (mc_run_param1);
+ g_free (saved_other_dir);
+
+ mc_config_deinit_config_paths ();
+
+ (void) mc_event_deinit (&mcerror);
+ if (mcerror != NULL)
+ {
+ fprintf (stderr, _("\nFailed while close:\n%s\n"), mcerror->message);
+ g_error_free (mcerror);
+ exit_code = EXIT_FAILURE;
+ }
+
+ (void) putchar ('\n'); /* Hack to make shell's prompt start at left of screen */
+
+ return exit_code;
+}
+
+/* --------------------------------------------------------------------------------------------- */